Compare commits

..

24 Commits

Author SHA1 Message Date
Bananymous 4b5a8196c3 Kernel: Fix bitmap byte index calculation in PMM
No idea how I had not crashed here earlier, but running on real hw with
a bit initrd ended up crashing :D
2026-01-04 01:16:43 +02:00
Bananymous 706c0816dd Kernel: Move smp_initialized flag after schedulers are initialized
Before this real hardware failed to boot with smp enabled. Allocating
the idle thread does a page mapping which ends up broadcasting TLB
shootdown to other processes. This ends up failing somewhere halting the
processors never allowing them to initialize their scheduler
2026-01-03 23:39:07 +02:00
Bananymous a8aa89362d ports/SDL2: Add support for game controllers
SuperTux and tuxracer now work with controller support!
2026-01-03 20:42:42 +02:00
Bananymous 7964698ae5 userspace: Add simple joystick test app 2026-01-03 20:42:42 +02:00
Bananymous 65664b0d65 Kernel: Add support for DualShock 3 controllers
This driver accepts any HID joystick devices but button and axis
mappings will only work on a PS3 controller
2026-01-03 20:07:08 +02:00
Bananymous 08bfa0971e Kernel: Rework storage device and disk cache locking
Syncing the disk cache no longer blocks the underlying storage device
and the disk cache itself during sync
2026-01-02 18:06:56 +02:00
Bananymous 912c5ea0bf Kernel: Implement basic RWLock 2026-01-02 17:50:22 +02:00
Bananymous 6cdf5a5a7f Kernel: Make AHCI controller thread safe 2026-01-02 17:50:22 +02:00
Bananymous 50ba743faf Terminal: Cleanup CSI handling and add =c,>c,c,E,F 2026-01-02 17:13:46 +02:00
Bananymous e26aac3067 Kernel: Decrease the number of syncs done by ext2 inodes
Each allocated inode used to call sync(). Each sync reads and writes
a block from the filesystem. Doing a 1 MiB write ended up syncing around
257 times
2026-01-01 23:54:09 +02:00
Bananymous 941e8aa5d5 Kernel: Optimize ext2 filesystem
block lookup can now also allocate blocks so there is no need to do
multiple lookups of the block did not exist
2026-01-01 23:15:35 +02:00
Bananymous 33b6536e6b Kernel: Make disk cache entry lookup O(log n)
I have absolutely no idea why i was doing a linear lookup here :D
2026-01-01 20:40:38 +02:00
Bananymous 9fbd9288b2 Kernel: Fix symlink deletion from tmpfs
Small symlinks store their target in inline storage of data block
indices. Freeing blocks did not account for this and tried to interpret
the link target as block inidices to delete
2026-01-01 20:40:38 +02:00
Bananymous bef1a56007 ports: Update git 2.48.1->2.52.0
also remove curl link hack as updating curl port fixed the issue
2026-01-01 20:40:38 +02:00
Bananymous bc71ff5e81 ports: Update curl 8.11.1->8.17.0
This fixes broken DT_NEEDED dependencies in curl!
2026-01-01 20:40:38 +02:00
Bananymous bd50444d06 ports: Update ca-certificates 2024-07-02 -> 2025-12-02 2025-12-31 19:28:55 +02:00
Bananymous 2efd6f92b2 rm: add -f and -i options
rm *should* now be posix compatible
2025-12-31 19:28:55 +02:00
Bananymous 7fdfad4088 Kernel: Mount /tmp with sticky bit set 2025-12-31 19:28:55 +02:00
Bananymous 31a1968798 Kernel: Cleanup scheduling while idling
If there are no threads in the run queue and we are idle, attempt to
wake up threads from the sleep queue
2025-12-31 19:28:55 +02:00
Bananymous b0bd4ad546 Kernel: Fix dup2 return value
We returned the wrong fd number :D
2025-12-31 19:28:55 +02:00
Bananymous dc454b9a6a Kernel: Fix SA_RESETHAND handling order
if SA_RESETHAND was set, alt stack would not get used
2025-12-31 19:28:55 +02:00
Bananymous f06e5d33e7 Kernel: Rework socket binding to an address
Sockets are no longer bound to an interface, but an ipv4 address. This
allows servers at 0.0.0.0 talk to multiple different interfaces
2025-12-31 19:28:55 +02:00
Bananymous efdbd1576f ports/SDL2: disable static lib and enable semaphores
Also add libiconv as a dep so SDL_iconv_string etc works
2025-12-28 15:30:02 +02:00
Bananymous 0421fbdc25 LibC: Implement sem_getvalue 2025-12-28 15:29:37 +02:00
47 changed files with 1671 additions and 468 deletions

View File

@ -108,6 +108,7 @@ set(KERNEL_SOURCES
kernel/USB/Controller.cpp kernel/USB/Controller.cpp
kernel/USB/Device.cpp kernel/USB/Device.cpp
kernel/USB/HID/HIDDriver.cpp kernel/USB/HID/HIDDriver.cpp
kernel/USB/HID/Joystick.cpp
kernel/USB/HID/Keyboard.cpp kernel/USB/HID/Keyboard.cpp
kernel/USB/HID/Mouse.cpp kernel/USB/HID/Mouse.cpp
kernel/USB/Hub/HubDriver.cpp kernel/USB/Hub/HubDriver.cpp

View File

@ -16,6 +16,7 @@ namespace Kernel
Debug, Debug,
Keyboard, Keyboard,
Mouse, Mouse,
Joystick,
SCSI, SCSI,
NVMeController, NVMeController,
NVMeNamespace, NVMeNamespace,

View File

@ -61,8 +61,8 @@ namespace Kernel
// NOTE: the inode might have more blocks than what this suggests if it has been shrinked // NOTE: the inode might have more blocks than what this suggests if it has been shrinked
uint32_t max_used_data_block_count() const { return size() / blksize(); } uint32_t max_used_data_block_count() const { return size() / blksize(); }
BAN::ErrorOr<BAN::Optional<uint32_t>> block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth); BAN::ErrorOr<BAN::Optional<uint32_t>> block_from_indirect_block(uint32_t& block, uint32_t index, uint32_t depth, bool allocate);
BAN::ErrorOr<BAN::Optional<uint32_t>> fs_block_of_data_block_index(uint32_t data_block_index); BAN::ErrorOr<BAN::Optional<uint32_t>> fs_block_of_data_block_index(uint32_t data_block_index, bool allocate);
BAN::ErrorOr<void> link_inode_to_directory(Ext2Inode&, BAN::StringView name); BAN::ErrorOr<void> link_inode_to_directory(Ext2Inode&, BAN::StringView name);
BAN::ErrorOr<void> remove_inode_from_directory(BAN::StringView name, bool cleanup_directory); BAN::ErrorOr<void> remove_inode_from_directory(BAN::StringView name, bool cleanup_directory);
@ -72,8 +72,6 @@ namespace Kernel
BAN::ErrorOr<void> cleanup_default_links(); BAN::ErrorOr<void> cleanup_default_links();
BAN::ErrorOr<void> cleanup_from_fs(); BAN::ErrorOr<void> cleanup_from_fs();
BAN::ErrorOr<uint32_t> allocate_new_block_to_indirect_block(uint32_t& block, uint32_t index, uint32_t depth);
BAN::ErrorOr<uint32_t> allocate_new_block(uint32_t data_block_index);
BAN::ErrorOr<void> sync(); BAN::ErrorOr<void> sync();
uint32_t block_group() const; uint32_t block_group() const;
@ -86,6 +84,26 @@ namespace Kernel
{} {}
static BAN::ErrorOr<BAN::RefPtr<Ext2Inode>> create(Ext2FS&, uint32_t); static BAN::ErrorOr<BAN::RefPtr<Ext2Inode>> create(Ext2FS&, uint32_t);
private:
struct ScopedSync
{
ScopedSync(Ext2Inode& inode)
: inode(inode)
, inode_info(inode.m_inode)
{ }
~ScopedSync()
{
if (memcmp(&inode.m_inode, &inode_info, sizeof(Ext2::Inode)) == 0)
return;
if (auto ret = inode.sync(); ret.is_error())
dwarnln("failed to sync inode: {}", ret.error());
}
Ext2Inode& inode;
Ext2::Inode inode_info;
};
private: private:
Ext2FS& m_fs; Ext2FS& m_fs;
Ext2::Inode m_inode; Ext2::Inode m_inode;

View File

@ -15,6 +15,7 @@ namespace Kernel
{ {
Mouse, Mouse,
Keyboard, Keyboard,
Joystick,
}; };
public: public:

View File

@ -0,0 +1,97 @@
#pragma once
#include <kernel/Lock/Mutex.h>
#include <kernel/Lock/LockGuard.h>
namespace Kernel
{
class RWLock
{
BAN_NON_COPYABLE(RWLock);
BAN_NON_MOVABLE(RWLock);
public:
RWLock() = default;
void rd_lock()
{
LockGuard _(m_mutex);
while (m_writers_waiting > 0 || m_writer_active)
m_thread_blocker.block_indefinite(&m_mutex);
m_readers_active++;
}
void rd_unlock()
{
LockGuard _(m_mutex);
if (--m_readers_active == 0)
m_thread_blocker.unblock();
}
void wr_lock()
{
LockGuard _(m_mutex);
m_writers_waiting++;
while (m_readers_active > 0 || m_writer_active)
m_thread_blocker.block_indefinite(&m_mutex);
m_writers_waiting--;
m_writer_active = true;
}
void wr_unlock()
{
LockGuard _(m_mutex);
m_writer_active = false;
m_thread_blocker.unblock();
}
private:
Mutex m_mutex;
ThreadBlocker m_thread_blocker;
uint32_t m_readers_active { 0 };
uint32_t m_writers_waiting { 0 };
bool m_writer_active { false };
};
class RWLockRDGuard
{
BAN_NON_COPYABLE(RWLockRDGuard);
BAN_NON_MOVABLE(RWLockRDGuard);
public:
RWLockRDGuard(RWLock& lock)
: m_lock(lock)
{
m_lock.rd_lock();
}
~RWLockRDGuard()
{
m_lock.rd_unlock();
}
private:
RWLock& m_lock;
};
class RWLockWRGuard
{
BAN_NON_COPYABLE(RWLockWRGuard);
BAN_NON_MOVABLE(RWLockWRGuard);
public:
RWLockWRGuard(RWLock& lock)
: m_lock(lock)
{
m_lock.wr_lock();
}
~RWLockWRGuard()
{
m_lock.wr_unlock();
}
private:
RWLock& m_lock;
};
}

View File

@ -45,7 +45,7 @@ namespace Kernel
void add_ipv4_packet(NetworkInterface&, BAN::ConstByteSpan); void add_ipv4_packet(NetworkInterface&, BAN::ConstByteSpan);
virtual void unbind_socket(uint16_t port) override; virtual void unbind_socket(uint16_t port) override;
virtual BAN::ErrorOr<void> bind_socket_to_unused(BAN::RefPtr<NetworkSocket>, const sockaddr* send_address, socklen_t send_address_len) override; virtual BAN::ErrorOr<void> bind_socket_with_target(BAN::RefPtr<NetworkSocket>, const sockaddr* target_address, socklen_t target_address_len) override;
virtual BAN::ErrorOr<void> bind_socket_to_address(BAN::RefPtr<NetworkSocket>, const sockaddr* address, socklen_t address_len) override; virtual BAN::ErrorOr<void> bind_socket_to_address(BAN::RefPtr<NetworkSocket>, const sockaddr* address, socklen_t address_len) override;
virtual BAN::ErrorOr<void> get_socket_address(BAN::RefPtr<NetworkSocket>, sockaddr* address, socklen_t* address_len) override; virtual BAN::ErrorOr<void> get_socket_address(BAN::RefPtr<NetworkSocket>, sockaddr* address, socklen_t* address_len) override;
@ -59,6 +59,8 @@ namespace Kernel
void add_ipv4_header(BAN::ByteSpan packet, BAN::IPv4Address src_ipv4, BAN::IPv4Address dst_ipv4, uint8_t protocol) const; void add_ipv4_header(BAN::ByteSpan packet, BAN::IPv4Address src_ipv4, BAN::IPv4Address dst_ipv4, uint8_t protocol) const;
BAN::ErrorOr<in_port_t> find_free_port();
void packet_handle_task(); void packet_handle_task();
BAN::ErrorOr<void> handle_ipv4_packet(NetworkInterface&, BAN::ByteSpan); BAN::ErrorOr<void> handle_ipv4_packet(NetworkInterface&, BAN::ByteSpan);

View File

@ -23,7 +23,7 @@ namespace Kernel
virtual ~NetworkLayer() {} virtual ~NetworkLayer() {}
virtual void unbind_socket(uint16_t port) = 0; virtual void unbind_socket(uint16_t port) = 0;
virtual BAN::ErrorOr<void> bind_socket_to_unused(BAN::RefPtr<NetworkSocket>, const sockaddr* send_address, socklen_t send_address_len) = 0; virtual BAN::ErrorOr<void> bind_socket_with_target(BAN::RefPtr<NetworkSocket>, const sockaddr* target_address, socklen_t target_address_len) = 0;
virtual BAN::ErrorOr<void> bind_socket_to_address(BAN::RefPtr<NetworkSocket>, const sockaddr* address, socklen_t address_len) = 0; virtual BAN::ErrorOr<void> bind_socket_to_address(BAN::RefPtr<NetworkSocket>, const sockaddr* address, socklen_t address_len) = 0;
virtual BAN::ErrorOr<void> get_socket_address(BAN::RefPtr<NetworkSocket>, sockaddr* address, socklen_t* address_len) = 0; virtual BAN::ErrorOr<void> get_socket_address(BAN::RefPtr<NetworkSocket>, sockaddr* address, socklen_t* address_len) = 0;

View File

@ -5,6 +5,8 @@
#include <kernel/Networking/NetworkInterface.h> #include <kernel/Networking/NetworkInterface.h>
#include <kernel/Networking/NetworkLayer.h> #include <kernel/Networking/NetworkLayer.h>
#include <netinet/in.h>
namespace Kernel namespace Kernel
{ {
@ -24,10 +26,10 @@ namespace Kernel
static constexpr uint16_t PORT_NONE = 0; static constexpr uint16_t PORT_NONE = 0;
public: public:
void bind_interface_and_port(NetworkInterface*, uint16_t port); void bind_address_and_port(const sockaddr*, socklen_t);
~NetworkSocket(); ~NetworkSocket();
NetworkInterface& interface() { ASSERT(m_interface); return *m_interface; } BAN::ErrorOr<BAN::RefPtr<NetworkInterface>> interface(const sockaddr* target, socklen_t target_len);
virtual size_t protocol_header_size() const = 0; virtual size_t protocol_header_size() const = 0;
virtual void add_protocol_header(BAN::ByteSpan packet, uint16_t dst_port, PseudoHeader) = 0; virtual void add_protocol_header(BAN::ByteSpan packet, uint16_t dst_port, PseudoHeader) = 0;
@ -35,7 +37,19 @@ namespace Kernel
virtual void receive_packet(BAN::ConstByteSpan, const sockaddr* sender, socklen_t sender_len) = 0; virtual void receive_packet(BAN::ConstByteSpan, const sockaddr* sender, socklen_t sender_len) = 0;
bool is_bound() const { return m_interface != nullptr; } bool is_bound() const { return m_address_len >= static_cast<socklen_t>(sizeof(sa_family_t)) && m_address.ss_family != AF_UNSPEC; }
in_port_t bound_port() const
{
ASSERT(is_bound());
ASSERT(m_address.ss_family == AF_INET && m_address_len >= static_cast<socklen_t>(sizeof(sockaddr_in)));
return BAN::network_endian_to_host(reinterpret_cast<const sockaddr_in*>(&m_address)->sin_port);
}
const sockaddr* address() const { return reinterpret_cast<const sockaddr*>(&m_address); }
socklen_t address_len() const { return m_address_len; }
private:
bool can_interface_send_to(const NetworkInterface&, const sockaddr*, socklen_t) const;
protected: protected:
NetworkSocket(NetworkLayer&, const Socket::Info&); NetworkSocket(NetworkLayer&, const Socket::Info&);
@ -46,8 +60,8 @@ namespace Kernel
protected: protected:
NetworkLayer& m_network_layer; NetworkLayer& m_network_layer;
NetworkInterface* m_interface = nullptr; sockaddr_storage m_address { .ss_family = AF_UNSPEC, .ss_storage = {} };
uint16_t m_port { PORT_NONE }; socklen_t m_address_len { 0 };
}; };
} }

View File

@ -59,6 +59,7 @@ namespace Kernel
static uint8_t count() { return s_processor_count; } static uint8_t count() { return s_processor_count; }
static bool is_smp_enabled() { return s_is_smp_enabled; } static bool is_smp_enabled() { return s_is_smp_enabled; }
static void set_smp_enabled() { s_is_smp_enabled = true; }
static void wait_until_processors_ready(); static void wait_until_processors_ready();
static void toggle_should_print_cpu_load() { s_should_print_cpu_load = !s_should_print_cpu_load; } static void toggle_should_print_cpu_load() { s_should_print_cpu_load = !s_should_print_cpu_load; }

View File

@ -82,6 +82,8 @@ namespace Kernel
void update_most_loaded_node_queue(SchedulerQueue::Node*, SchedulerQueue* target_queue); void update_most_loaded_node_queue(SchedulerQueue::Node*, SchedulerQueue* target_queue);
void remove_node_from_most_loaded(SchedulerQueue::Node*); void remove_node_from_most_loaded(SchedulerQueue::Node*);
void wake_up_sleeping_threads();
void do_load_balancing(); void do_load_balancing();
class ProcessorID find_least_loaded_processor() const; class ProcessorID find_least_loaded_processor() const;

View File

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <kernel/ThreadBlocker.h> #include <kernel/Memory/DMARegion.h>
#include <kernel/Storage/ATA/AHCI/Definitions.h> #include <kernel/Storage/ATA/AHCI/Definitions.h>
#include <kernel/Storage/ATA/ATADevice.h> #include <kernel/Storage/ATA/ATADevice.h>
#include <kernel/ThreadBlocker.h>
namespace Kernel namespace Kernel
{ {
@ -34,6 +35,8 @@ namespace Kernel
BAN::ErrorOr<void> block_until_command_completed(uint32_t command_slot); BAN::ErrorOr<void> block_until_command_completed(uint32_t command_slot);
private: private:
Mutex m_mutex;
BAN::RefPtr<AHCIController> m_controller; BAN::RefPtr<AHCIController> m_controller;
volatile HBAPortMemorySpace* const m_port; volatile HBAPortMemorySpace* const m_port;

View File

@ -3,6 +3,7 @@
#include <BAN/Array.h> #include <BAN/Array.h>
#include <BAN/ByteSpan.h> #include <BAN/ByteSpan.h>
#include <BAN/Vector.h> #include <BAN/Vector.h>
#include <kernel/Lock/RWLock.h>
#include <kernel/Memory/Types.h> #include <kernel/Memory/Types.h>
namespace Kernel namespace Kernel
@ -28,6 +29,8 @@ namespace Kernel
private: private:
BAN::ErrorOr<void> sync_cache_index(size_t index); BAN::ErrorOr<void> sync_cache_index(size_t index);
size_t find_sector_cache_index(uint64_t sector) const;
private: private:
struct PageCache struct PageCache
{ {
@ -35,9 +38,13 @@ namespace Kernel
uint64_t first_sector { 0 }; uint64_t first_sector { 0 };
uint8_t sector_mask { 0 }; uint8_t sector_mask { 0 };
uint8_t dirty_mask { 0 }; uint8_t dirty_mask { 0 };
bool syncing { false };
}; };
private: private:
RWLock m_rw_lock;
Mutex m_sync_mutex;
const size_t m_sector_size; const size_t m_sector_size;
StorageDevice& m_device; StorageDevice& m_device;
BAN::Vector<PageCache> m_cache; BAN::Vector<PageCache> m_cache;

View File

@ -51,7 +51,6 @@ namespace Kernel
virtual bool has_hungup_impl() const override { return false; } virtual bool has_hungup_impl() const override { return false; }
private: private:
Mutex m_mutex;
BAN::Optional<DiskCache> m_disk_cache; BAN::Optional<DiskCache> m_disk_cache;
BAN::Vector<BAN::RefPtr<Partition>> m_partitions; BAN::Vector<BAN::RefPtr<Partition>> m_partitions;

View File

@ -55,6 +55,8 @@ namespace Kernel
{} {}
virtual ~USBHIDDevice() = default; virtual ~USBHIDDevice() = default;
virtual BAN::ErrorOr<void> initialize() { return {}; }
virtual void start_report() = 0; virtual void start_report() = 0;
virtual void stop_report() = 0; virtual void stop_report() = 0;

View File

@ -0,0 +1,43 @@
#pragma once
#include <kernel/USB/HID/HIDDriver.h>
#include <LibInput/Joystick.h>
namespace Kernel
{
class USBJoystick final : public USBHIDDevice
{
BAN_NON_COPYABLE(USBJoystick);
BAN_NON_MOVABLE(USBJoystick);
public:
BAN::ErrorOr<void> initialize() override;
void start_report() override;
void stop_report() override;
void handle_array(uint16_t usage_page, uint16_t usage) override;
void handle_variable(uint16_t usage_page, uint16_t usage, int64_t state) override;
void handle_variable_absolute(uint16_t usage_page, uint16_t usage, int64_t state, int64_t min, int64_t max) override;
protected:
BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
bool can_read_impl() const override { return true; }
private:
USBJoystick(USBHIDDriver&);
~USBJoystick() = default;
private:
USBHIDDriver& m_driver;
SpinLock m_state_lock;
InterruptState m_interrupt_state;
LibInput::JoystickState m_state {};
friend class BAN::RefPtr<USBJoystick>;
};
}

View File

@ -56,52 +56,98 @@ namespace Kernel
return &m_fs; return &m_fs;
} }
BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth) BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::block_from_indirect_block(uint32_t& block, uint32_t index, uint32_t depth, bool allocate)
{
const uint32_t inode_blocks_per_fs_block = blksize() / 512;
const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t);
if (depth == 0)
{ {
if (block == 0) if (block == 0)
{
if (!allocate)
return BAN::Optional<uint32_t>(); return BAN::Optional<uint32_t>();
ASSERT(depth >= 1);
block = TRY(m_fs.reserve_free_block(block_group()));
m_inode.blocks += inode_blocks_per_fs_block;
auto block_buffer = TRY(m_fs.get_block_buffer()); auto block_buffer = TRY(m_fs.get_block_buffer());
TRY(m_fs.read_block(block, block_buffer)); memset(block_buffer.data(), 0x00, block_buffer.size());
TRY(m_fs.write_block(block, block_buffer));
}
const uint32_t indices_per_block = blksize() / sizeof(uint32_t); return BAN::Optional<uint32_t>(block);
}
auto block_buffer = TRY(m_fs.get_block_buffer());
bool needs_write = false;
if (block != 0)
TRY(m_fs.read_block(block, block_buffer));
else
{
if (!allocate)
return BAN::Optional<uint32_t>();
block = TRY(m_fs.reserve_free_block(block_group()));
m_inode.blocks += inode_blocks_per_fs_block;
memset(block_buffer.data(), 0, block_buffer.size());
needs_write = true;
}
uint32_t divisor = 1; uint32_t divisor = 1;
for (uint32_t i = 1; i < depth; i++) for (uint32_t i = 1; i < depth; i++)
divisor *= indices_per_block; divisor *= indices_per_fs_block;
const uint32_t next_block = block_buffer.span().as_span<uint32_t>()[(index / divisor) % indices_per_block]; uint32_t& new_block = block_buffer.span().as_span<uint32_t>()[(index / divisor) % indices_per_fs_block];
if (next_block == 0) const auto old_block = new_block;
return BAN::Optional<uint32_t>();
if (depth == 1)
return BAN::Optional<uint32_t>(next_block);
return block_from_indirect_block(next_block, index, depth - 1); const auto result = TRY(block_from_indirect_block(new_block, index, depth - 1, allocate));
if (needs_write || old_block != new_block)
TRY(m_fs.write_block(block, block_buffer));
return result;
} }
BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::fs_block_of_data_block_index(uint32_t data_block_index) BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::fs_block_of_data_block_index(uint32_t data_block_index, bool allocate)
{ {
const uint32_t inode_blocks_per_fs_block = blksize() / 512;
const uint32_t indices_per_block = blksize() / sizeof(uint32_t); const uint32_t indices_per_block = blksize() / sizeof(uint32_t);
if (data_block_index < 12) if (data_block_index < 12)
{ {
if (m_inode.block[data_block_index] == 0) if (m_inode.block[data_block_index] != 0)
return BAN::Optional<uint32_t>();
return BAN::Optional<uint32_t>(m_inode.block[data_block_index]); return BAN::Optional<uint32_t>(m_inode.block[data_block_index]);
if (!allocate)
return BAN::Optional<uint32_t>();
auto block_buffer = TRY(m_fs.get_block_buffer());
memset(block_buffer.data(), 0, block_buffer.size());
const auto block = TRY(m_fs.reserve_free_block(block_group()));
TRY(m_fs.write_block(block, block_buffer));
m_inode.block[data_block_index] = block;
m_inode.blocks += inode_blocks_per_fs_block;
return BAN::Optional<uint32_t>(block);
} }
data_block_index -= 12; data_block_index -= 12;
if (data_block_index < indices_per_block) if (data_block_index < indices_per_block)
return block_from_indirect_block(m_inode.block[12], data_block_index, 1); return block_from_indirect_block(m_inode.block[12], data_block_index, 1, allocate);
data_block_index -= indices_per_block; data_block_index -= indices_per_block;
if (data_block_index < indices_per_block * indices_per_block) if (data_block_index < indices_per_block * indices_per_block)
return block_from_indirect_block(m_inode.block[13], data_block_index, 2); return block_from_indirect_block(m_inode.block[13], data_block_index, 2, allocate);
data_block_index -= indices_per_block * indices_per_block; data_block_index -= indices_per_block * indices_per_block;
if (data_block_index < indices_per_block * indices_per_block * indices_per_block) if (data_block_index < indices_per_block * indices_per_block * indices_per_block)
return block_from_indirect_block(m_inode.block[14], data_block_index, 3); return block_from_indirect_block(m_inode.block[14], data_block_index, 3, allocate);
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@ -152,6 +198,8 @@ namespace Kernel
if (static_cast<BAN::make_unsigned_t<decltype(offset)>>(offset) >= m_inode.size) if (static_cast<BAN::make_unsigned_t<decltype(offset)>>(offset) >= m_inode.size)
return 0; return 0;
ScopedSync _(*this);
uint32_t count = buffer.size(); uint32_t count = buffer.size();
if (offset + buffer.size() > m_inode.size) if (offset + buffer.size() > m_inode.size)
count = m_inode.size - offset; count = m_inode.size - offset;
@ -167,7 +215,7 @@ namespace Kernel
for (uint32_t data_block_index = first_block; data_block_index < last_block; data_block_index++) for (uint32_t data_block_index = first_block; data_block_index < last_block; data_block_index++)
{ {
auto block_index = TRY(fs_block_of_data_block_index(data_block_index)); auto block_index = TRY(fs_block_of_data_block_index(data_block_index, false));
if (block_index.has_value()) if (block_index.has_value())
TRY(m_fs.read_block(block_index.value(), block_buffer)); TRY(m_fs.read_block(block_index.value(), block_buffer));
else else
@ -196,6 +244,8 @@ namespace Kernel
if (m_inode.size < offset + buffer.size()) if (m_inode.size < offset + buffer.size())
TRY(truncate_impl(offset + buffer.size())); TRY(truncate_impl(offset + buffer.size()));
ScopedSync _(*this);
const uint32_t block_size = blksize(); const uint32_t block_size = blksize();
auto block_buffer = TRY(m_fs.get_block_buffer()); auto block_buffer = TRY(m_fs.get_block_buffer());
@ -206,17 +256,13 @@ namespace Kernel
// Write partial block // Write partial block
if (offset % block_size) if (offset % block_size)
{ {
auto block_index = TRY(fs_block_of_data_block_index(offset / block_size)); const auto block_index = TRY(fs_block_of_data_block_index(offset / block_size, true));
if (block_index.has_value()) ASSERT(block_index.has_value());
TRY(m_fs.read_block(block_index.value(), block_buffer));
else
{
block_index = TRY(allocate_new_block(offset / block_size));;
memset(block_buffer.data(), 0x00, block_buffer.size());
}
uint32_t block_offset = offset % block_size; TRY(m_fs.read_block(block_index.value(), block_buffer));
uint32_t to_copy = BAN::Math::min<uint32_t>(block_size - block_offset, to_write);
const uint32_t block_offset = offset % block_size;
const uint32_t to_copy = BAN::Math::min<uint32_t>(block_size - block_offset, to_write);
memcpy(block_buffer.data() + block_offset, buffer.data(), to_copy); memcpy(block_buffer.data() + block_offset, buffer.data(), to_copy);
TRY(m_fs.write_block(block_index.value(), block_buffer)); TRY(m_fs.write_block(block_index.value(), block_buffer));
@ -228,9 +274,8 @@ namespace Kernel
while (to_write >= block_size) while (to_write >= block_size)
{ {
auto block_index = TRY(fs_block_of_data_block_index(offset / block_size)); const auto block_index = TRY(fs_block_of_data_block_index(offset / block_size, true));
if (!block_index.has_value()) ASSERT(block_index.has_value());
block_index = TRY(allocate_new_block(offset / block_size));
memcpy(block_buffer.data(), buffer.data() + written, block_buffer.size()); memcpy(block_buffer.data(), buffer.data() + written, block_buffer.size());
TRY(m_fs.write_block(block_index.value(), block_buffer)); TRY(m_fs.write_block(block_index.value(), block_buffer));
@ -242,14 +287,10 @@ namespace Kernel
if (to_write > 0) if (to_write > 0)
{ {
auto block_index = TRY(fs_block_of_data_block_index(offset / block_size)); const auto block_index = TRY(fs_block_of_data_block_index(offset / block_size, true));
if (block_index.has_value()) ASSERT(block_index.has_value());
TRY(m_fs.read_block(block_index.value(), block_buffer)); TRY(m_fs.read_block(block_index.value(), block_buffer));
else
{
block_index = TRY(allocate_new_block(offset / block_size));
memset(block_buffer.data(), 0x00, block_buffer.size());
}
memcpy(block_buffer.data(), buffer.data() + written, to_write); memcpy(block_buffer.data(), buffer.data() + written, to_write);
TRY(m_fs.write_block(block_index.value(), block_buffer)); TRY(m_fs.write_block(block_index.value(), block_buffer));
@ -340,7 +381,7 @@ namespace Kernel
BAN::ErrorOr<void> Ext2Inode::fsync_impl() BAN::ErrorOr<void> Ext2Inode::fsync_impl()
{ {
for (size_t i = 0; i < max_used_data_block_count(); i++) for (size_t i = 0; i < max_used_data_block_count(); i++)
if (const auto fs_block = TRY(fs_block_of_data_block_index(i)); fs_block.has_value()) if (const auto fs_block = TRY(fs_block_of_data_block_index(i, false)); fs_block.has_value())
TRY(m_fs.sync_block(fs_block.value())); TRY(m_fs.sync_block(fs_block.value()));
return {}; return {};
} }
@ -412,12 +453,12 @@ done:
if (static_cast<BAN::make_unsigned_t<decltype(offset)>>(offset) >= max_used_data_block_count()) if (static_cast<BAN::make_unsigned_t<decltype(offset)>>(offset) >= max_used_data_block_count())
return 0; return 0;
// FIXME: can we actually assume directories have all their blocks allocated const auto block_index = TRY(fs_block_of_data_block_index(offset, false));
const uint32_t block_index = TRY(fs_block_of_data_block_index(offset)).value(); if (!block_index.has_value())
return BAN::Error::from_errno(ENODATA);
auto block_buffer = TRY(m_fs.get_block_buffer()); auto block_buffer = TRY(m_fs.get_block_buffer());
TRY(m_fs.read_block(block_index.value(), block_buffer));
TRY(m_fs.read_block(block_index, block_buffer));
// First determine if we have big enough list // First determine if we have big enough list
size_t entry_count = 0; size_t entry_count = 0;
@ -681,7 +722,7 @@ done:
goto needs_new_block; goto needs_new_block;
// Try to insert inode to last data block // Try to insert inode to last data block
block_index = TRY(fs_block_of_data_block_index(data_block_count - 1)).value(); block_index = TRY(fs_block_of_data_block_index(data_block_count - 1, true)).value();
TRY(m_fs.read_block(block_index, block_buffer)); TRY(m_fs.read_block(block_index, block_buffer));
while (entry_offset < block_size) while (entry_offset < block_size)
@ -712,7 +753,7 @@ done:
} }
needs_new_block: needs_new_block:
block_index = TRY(allocate_new_block(data_block_count)); block_index = TRY(fs_block_of_data_block_index(data_block_count, true)).value();
m_inode.size += blksize(); m_inode.size += blksize();
memset(block_buffer.data(), 0x00, block_buffer.size()); memset(block_buffer.data(), 0x00, block_buffer.size());
@ -731,9 +772,10 @@ needs_new_block:
// Confirm that this doesn't contain anything else than '.' or '..' // Confirm that this doesn't contain anything else than '.' or '..'
for (uint32_t i = 0; i < max_used_data_block_count(); i++) for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{ {
// FIXME: can we actually assume directories have all their blocks allocated const auto block_index = TRY(fs_block_of_data_block_index(i, false));
const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value(); if (!block_index.has_value())
TRY(m_fs.read_block(block_index, block_buffer)); continue;
TRY(m_fs.read_block(block_index.value(), block_buffer));
blksize_t offset = 0; blksize_t offset = 0;
while (offset < blksize()) while (offset < blksize())
@ -767,9 +809,10 @@ needs_new_block:
for (uint32_t i = 0; i < max_used_data_block_count(); i++) for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{ {
// FIXME: can we actually assume directories have all their blocks allocated const auto block_index = TRY(fs_block_of_data_block_index(i, false));
const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value(); if (!block_index.has_value())
TRY(m_fs.read_block(block_index, block_buffer)); continue;
TRY(m_fs.read_block(block_index.value(), block_buffer));
bool modified = false; bool modified = false;
@ -804,7 +847,7 @@ needs_new_block:
} }
if (modified) if (modified)
TRY(m_fs.write_block(block_index, block_buffer)); TRY(m_fs.write_block(block_index.value(), block_buffer));
} }
return {}; return {};
@ -823,9 +866,10 @@ needs_new_block:
for (uint32_t i = 0; i < max_used_data_block_count(); i++) for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{ {
// FIXME: can we actually assume directories have all their blocks allocated const auto block_index = TRY(fs_block_of_data_block_index(i, false));
const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value(); if (!block_index.has_value())
TRY(m_fs.read_block(block_index, block_buffer)); continue;
TRY(m_fs.read_block(block_index.value(), block_buffer));
blksize_t offset = 0; blksize_t offset = 0;
while (offset < blksize()) while (offset < blksize())
@ -859,7 +903,7 @@ needs_new_block:
// FIXME: This should expand the last inode if exists // FIXME: This should expand the last inode if exists
entry.inode = 0; entry.inode = 0;
TRY(m_fs.write_block(block_index, block_buffer)); TRY(m_fs.write_block(block_index.value(), block_buffer));
} }
offset += entry.rec_len; offset += entry.rec_len;
} }
@ -874,73 +918,6 @@ needs_new_block:
return {}; return {};
} }
BAN::ErrorOr<uint32_t> Ext2Inode::allocate_new_block_to_indirect_block(uint32_t& block, uint32_t index, uint32_t depth)
{
const uint32_t inode_blocks_per_fs_block = blksize() / 512;
const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t);
if (depth == 0)
ASSERT(block == 0);
if (block == 0)
{
block = TRY(m_fs.reserve_free_block(block_group()));
m_inode.blocks += inode_blocks_per_fs_block;
auto block_buffer = TRY(m_fs.get_block_buffer());
memset(block_buffer.data(), 0x00, block_buffer.size());
TRY(m_fs.write_block(block, block_buffer));
}
if (depth == 0)
return block;
auto block_buffer = TRY(m_fs.get_block_buffer());
TRY(m_fs.read_block(block, block_buffer));
uint32_t divisor = 1;
for (uint32_t i = 1; i < depth; i++)
divisor *= indices_per_fs_block;
uint32_t& new_block = block_buffer.span().as_span<uint32_t>()[(index / divisor) % indices_per_fs_block];
uint32_t allocated_block = TRY(allocate_new_block_to_indirect_block(new_block, index, depth - 1));
TRY(m_fs.write_block(block, block_buffer));
TRY(sync());
return allocated_block;
}
BAN::ErrorOr<uint32_t> Ext2Inode::allocate_new_block(uint32_t data_block_index)
{
const uint32_t inode_blocks_per_fs_block = blksize() / 512;
const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t);
if (data_block_index < 12)
{
ASSERT(m_inode.block[data_block_index] == 0);
m_inode.block[data_block_index] = TRY(m_fs.reserve_free_block(block_group()));
m_inode.blocks += inode_blocks_per_fs_block;
TRY(sync());
return m_inode.block[data_block_index];
}
data_block_index -= 12;
if (data_block_index < indices_per_fs_block)
return TRY(allocate_new_block_to_indirect_block(m_inode.block[12], data_block_index, 1));
data_block_index -= indices_per_fs_block;
if (data_block_index < indices_per_fs_block * indices_per_fs_block)
return TRY(allocate_new_block_to_indirect_block(m_inode.block[13], data_block_index, 2));
data_block_index -= indices_per_fs_block * indices_per_fs_block;
if (data_block_index < indices_per_fs_block * indices_per_fs_block * indices_per_fs_block)
return TRY(allocate_new_block_to_indirect_block(m_inode.block[14], data_block_index, 3));
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<void> Ext2Inode::sync() BAN::ErrorOr<void> Ext2Inode::sync()
{ {
auto inode_location = TRY(m_fs.locate_inode(ino())); auto inode_location = TRY(m_fs.locate_inode(ino()));
@ -964,9 +941,10 @@ needs_new_block:
for (uint32_t i = 0; i < max_used_data_block_count(); i++) for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{ {
// FIXME: can we actually assume directories have all their blocks allocated const auto block_index = TRY(fs_block_of_data_block_index(i, false));
const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value(); if (!block_index.has_value())
TRY(m_fs.read_block(block_index, block_buffer)); continue;
TRY(m_fs.read_block(block_index.value(), block_buffer));
BAN::ConstByteSpan entry_span = block_buffer.span(); BAN::ConstByteSpan entry_span = block_buffer.span();
while (entry_span.size() >= sizeof(Ext2::LinkedDirectoryEntry)) while (entry_span.size() >= sizeof(Ext2::LinkedDirectoryEntry))

View File

@ -123,6 +123,8 @@ namespace Kernel
void TmpInode::free_all_blocks() void TmpInode::free_all_blocks()
{ {
if (mode().iflnk() && m_inode_info.size <= sizeof(TmpInodeInfo::block))
goto free_all_blocks_done;
for (size_t i = 0; i < TmpInodeInfo::direct_block_count; i++) for (size_t i = 0; i < TmpInodeInfo::direct_block_count; i++)
if (m_inode_info.block[i]) if (m_inode_info.block[i])
m_fs.free_block(m_inode_info.block[i]); m_fs.free_block(m_inode_info.block[i]);
@ -132,6 +134,7 @@ namespace Kernel
free_indirect_blocks(block, 2); free_indirect_blocks(block, 2);
if (size_t block = m_inode_info.block[TmpInodeInfo::direct_block_count + 2]) if (size_t block = m_inode_info.block[TmpInodeInfo::direct_block_count + 2])
free_indirect_blocks(block, 3); free_indirect_blocks(block, 3);
free_all_blocks_done:
for (auto& block : m_inode_info.block) for (auto& block : m_inode_info.block)
block = 0; block = 0;
} }

View File

@ -174,7 +174,7 @@ namespace Kernel
MUST(s_instance->mount(root_creds, &ProcFileSystem::get(), "/proc"_sv)); MUST(s_instance->mount(root_creds, &ProcFileSystem::get(), "/proc"_sv));
auto tmpfs = MUST(TmpFileSystem::create(-1, 0777, 0, 0)); auto tmpfs = MUST(TmpFileSystem::create(-1, 01777, 0, 0));
MUST(s_instance->mount(root_creds, tmpfs, "/tmp"_sv)); MUST(s_instance->mount(root_creds, tmpfs, "/tmp"_sv));
} }

View File

@ -3,6 +3,7 @@
#include <kernel/Input/InputDevice.h> #include <kernel/Input/InputDevice.h>
#include <kernel/Lock/SpinLockAsMutex.h> #include <kernel/Lock/SpinLockAsMutex.h>
#include <LibInput/Joystick.h>
#include <LibInput/KeyEvent.h> #include <LibInput/KeyEvent.h>
#include <LibInput/MouseEvent.h> #include <LibInput/MouseEvent.h>
@ -18,6 +19,8 @@ namespace Kernel
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_mice; static BAN::Vector<BAN::WeakPtr<InputDevice>> s_mice;
static BAN::RefPtr<MouseDevice> s_mouse_device; static BAN::RefPtr<MouseDevice> s_mouse_device;
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_joysticks;
static const char* get_name_format(InputDevice::Type type) static const char* get_name_format(InputDevice::Type type)
{ {
switch (type) switch (type)
@ -26,6 +29,8 @@ namespace Kernel
return "keyboard{}"; return "keyboard{}";
case InputDevice::Type::Mouse: case InputDevice::Type::Mouse:
return "mouse{}"; return "mouse{}";
case InputDevice::Type::Joystick:
return "joystick{}";
} }
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@ -44,6 +49,11 @@ namespace Kernel
if (!s_mice[i].valid()) if (!s_mice[i].valid())
return makedev(DeviceNumber::Mouse, i + 1); return makedev(DeviceNumber::Mouse, i + 1);
return makedev(DeviceNumber::Mouse, s_mice.size() + 1); return makedev(DeviceNumber::Mouse, s_mice.size() + 1);
case InputDevice::Type::Joystick:
for (size_t i = 0; i < s_joysticks.size(); i++)
if (!s_joysticks[i].valid())
return makedev(DeviceNumber::Joystick, i + 1);
return makedev(DeviceNumber::Joystick, s_joysticks.size() + 1);
} }
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@ -56,6 +66,8 @@ namespace Kernel
return sizeof(LibInput::RawKeyEvent); return sizeof(LibInput::RawKeyEvent);
case InputDevice::Type::Mouse: case InputDevice::Type::Mouse:
return sizeof(LibInput::MouseEvent); return sizeof(LibInput::MouseEvent);
case InputDevice::Type::Joystick:
return sizeof(LibInput::JoystickEvent);
} }
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@ -69,18 +81,23 @@ namespace Kernel
{ {
MUST(m_event_buffer.resize(m_event_size * m_max_event_count, 0)); MUST(m_event_buffer.resize(m_event_size * m_max_event_count, 0));
if (m_type == Type::Keyboard) switch (m_type)
{ {
case Type::Keyboard:
if (s_keyboards.size() < minor(m_rdev)) if (s_keyboards.size() < minor(m_rdev))
MUST(s_keyboards.resize(minor(m_rdev))); MUST(s_keyboards.resize(minor(m_rdev)));
s_keyboards[minor(m_rdev) - 1] = MUST(get_weak_ptr()); s_keyboards[minor(m_rdev) - 1] = MUST(get_weak_ptr());
} break;
case Type::Mouse:
if (m_type == Type::Mouse)
{
if (s_mice.size() < minor(m_rdev)) if (s_mice.size() < minor(m_rdev))
MUST(s_mice.resize(minor(m_rdev))); MUST(s_mice.resize(minor(m_rdev)));
s_mice[minor(m_rdev) - 1] = MUST(get_weak_ptr()); s_mice[minor(m_rdev) - 1] = MUST(get_weak_ptr());
break;
case Type::Joystick:
if (s_joysticks.size() < minor(m_rdev))
MUST(s_joysticks.resize(minor(m_rdev)));
s_joysticks[minor(m_rdev) - 1] = MUST(get_weak_ptr());
break;
} }
} }

View File

@ -98,8 +98,9 @@ namespace Kernel
[this](size_t buffer_bit) -> bool [this](size_t buffer_bit) -> bool
{ {
const size_t page_index = buffer_bit / bits_per_page; const size_t page_index = buffer_bit / bits_per_page;
const size_t byte = buffer_bit / 8; const size_t bit_index = buffer_bit % bits_per_page;
const size_t bit = buffer_bit % 8; const size_t byte = bit_index / 8;
const size_t bit = bit_index % 8;
uint8_t current; uint8_t current;
PageTable::with_fast_page(m_paddr + page_index * PAGE_SIZE, [&current, byte] { PageTable::with_fast_page(m_paddr + page_index * PAGE_SIZE, [&current, byte] {
@ -113,8 +114,9 @@ namespace Kernel
[this](size_t buffer_bit) -> void [this](size_t buffer_bit) -> void
{ {
const size_t page_index = buffer_bit / bits_per_page; const size_t page_index = buffer_bit / bits_per_page;
const size_t byte = buffer_bit / 8; const size_t bit_index = buffer_bit % bits_per_page;
const size_t bit = buffer_bit % 8; const size_t byte = bit_index / 8;
const size_t bit = bit_index % 8;
PageTable::with_fast_page(m_paddr + page_index * PAGE_SIZE, [byte, bit] { PageTable::with_fast_page(m_paddr + page_index * PAGE_SIZE, [byte, bit] {
volatile uint8_t& current = PageTable::fast_page_as_sized<volatile uint8_t>(byte); volatile uint8_t& current = PageTable::fast_page_as_sized<volatile uint8_t>(byte);
current = current | (1u << bit); current = current | (1u << bit);

View File

@ -75,35 +75,58 @@ namespace Kernel
m_bound_sockets.remove(it); m_bound_sockets.remove(it);
} }
BAN::ErrorOr<void> IPv4Layer::bind_socket_to_unused(BAN::RefPtr<NetworkSocket> socket, const sockaddr* address, socklen_t address_len) BAN::ErrorOr<in_port_t> IPv4Layer::find_free_port()
{ {
if (!address || address_len < (socklen_t)sizeof(sockaddr_in))
return BAN::Error::from_errno(EINVAL);
if (address->sa_family != AF_INET)
return BAN::Error::from_errno(EAFNOSUPPORT);
auto& sockaddr_in = *reinterpret_cast<const struct sockaddr_in*>(address);
SpinLockGuard _(m_bound_socket_lock); SpinLockGuard _(m_bound_socket_lock);
uint16_t port = NetworkSocket::PORT_NONE; for (uint32_t i = 0; i < 100; i++)
for (uint32_t i = 0; i < 100 && port == NetworkSocket::PORT_NONE; i++) if (uint32_t port = 0xC000 | (Random::get_u32() & 0x3FFF); !m_bound_sockets.contains(port))
if (uint32_t temp = 0xC000 | (Random::get_u32() & 0x3FFF); !m_bound_sockets.contains(temp)) return port;
port = temp;
for (uint32_t temp = 0xC000; temp < 0xFFFF && port == NetworkSocket::PORT_NONE; temp++) for (uint32_t port = 0xC000; port < 0xFFFF; port++)
if (!m_bound_sockets.contains(temp)) if (!m_bound_sockets.contains(port))
port = temp; return port;
if (port == NetworkSocket::PORT_NONE)
{
dwarnln("No ports available"); dwarnln("No ports available");
return BAN::Error::from_errno(EAGAIN); return BAN::Error::from_errno(EAGAIN);
} }
dprintln_if(DEBUG_IPV4, "using port {}", port);
struct sockaddr_in target; BAN::ErrorOr<void> IPv4Layer::bind_socket_with_target(BAN::RefPtr<NetworkSocket> socket, const sockaddr* target, socklen_t target_len)
target.sin_family = AF_INET; {
target.sin_port = BAN::host_to_network_endian(port); if (!target || target_len < (socklen_t)sizeof(sockaddr_in))
target.sin_addr.s_addr = sockaddr_in.sin_addr.s_addr; return BAN::Error::from_errno(EINVAL);
return bind_socket_to_address(socket, (sockaddr*)&target, sizeof(sockaddr_in)); if (target->sa_family != AF_INET)
return BAN::Error::from_errno(EAFNOSUPPORT);
auto& sockaddr_in = *reinterpret_cast<const struct sockaddr_in*>(target);
auto interface =
TRY([&sockaddr_in]() -> BAN::ErrorOr<BAN::RefPtr<NetworkInterface>> {
const auto ipv4 = BAN::IPv4Address { sockaddr_in.sin_addr.s_addr };
// try to find an interface in the same subnet
const auto& all_interfaces = NetworkManager::get().interfaces();
for (const auto& interface : all_interfaces)
{
const auto netmask = interface->get_netmask();
if (ipv4.mask(netmask) == interface->get_ipv4_address().mask(netmask))
return interface;
}
// fallback to non-loopback interface
// FIXME: make sure target is reachable
for (const auto& interface : all_interfaces)
if (interface->type() != NetworkInterface::Type::Loopback)
return interface;
return BAN::Error::from_errno(EHOSTUNREACH);
}());
// FIXME: race condition with port allocation/binding
struct sockaddr_in bind_address;
bind_address.sin_family = AF_INET;
bind_address.sin_port = BAN::host_to_network_endian(TRY(find_free_port()));
bind_address.sin_addr.s_addr = interface->get_ipv4_address().raw;
return bind_socket_to_address(socket, (sockaddr*)&bind_address, sizeof(bind_address));
} }
BAN::ErrorOr<void> IPv4Layer::bind_socket_to_address(BAN::RefPtr<NetworkSocket> socket, const sockaddr* address, socklen_t address_len) BAN::ErrorOr<void> IPv4Layer::bind_socket_to_address(BAN::RefPtr<NetworkSocket> socket, const sockaddr* address, socklen_t address_len)
@ -114,33 +137,47 @@ namespace Kernel
return BAN::Error::from_errno(EAFNOSUPPORT); return BAN::Error::from_errno(EAFNOSUPPORT);
auto& sockaddr_in = *reinterpret_cast<const struct sockaddr_in*>(address); auto& sockaddr_in = *reinterpret_cast<const struct sockaddr_in*>(address);
const uint16_t port = BAN::host_to_network_endian(sockaddr_in.sin_port);
if (port == NetworkSocket::PORT_NONE) TRY([&sockaddr_in]() -> BAN::ErrorOr<void> {
return bind_socket_to_unused(socket, address, address_len);
const auto ipv4 = BAN::IPv4Address { sockaddr_in.sin_addr.s_addr }; const auto ipv4 = BAN::IPv4Address { sockaddr_in.sin_addr.s_addr };
BAN::RefPtr<NetworkInterface> bind_interface; if (ipv4 == 0)
for (auto interface : NetworkManager::get().interfaces()) return {};
const auto& all_interfaces = NetworkManager::get().interfaces();
for (const auto& interface : all_interfaces)
{ {
if (interface->type() != NetworkInterface::Type::Loopback) switch (interface->type())
bind_interface = interface; {
case NetworkInterface::Type::Ethernet:
if (ipv4 == interface->get_ipv4_address())
return {};
break;
case NetworkInterface::Type::Loopback:
const auto netmask = interface->get_netmask(); const auto netmask = interface->get_netmask();
if (ipv4.mask(netmask) != interface->get_ipv4_address().mask(netmask)) if (ipv4.mask(netmask) == interface->get_ipv4_address().mask(netmask))
continue; return {};
bind_interface = interface;
break; break;
} }
}
if (!bind_interface)
return BAN::Error::from_errno(EADDRNOTAVAIL); return BAN::Error::from_errno(EADDRNOTAVAIL);
}());
struct sockaddr_in bind_address;
memcpy(&bind_address, address, sizeof(sockaddr_in));
SpinLockGuard _(m_bound_socket_lock); SpinLockGuard _(m_bound_socket_lock);
if (bind_address.sin_port == 0)
bind_address.sin_port = TRY(find_free_port());
const uint16_t port = BAN::host_to_network_endian(bind_address.sin_port);
if (m_bound_sockets.contains(port)) if (m_bound_sockets.contains(port))
return BAN::Error::from_errno(EADDRINUSE); return BAN::Error::from_errno(EADDRINUSE);
TRY(m_bound_sockets.insert(port, TRY(socket->get_weak_ptr()))); TRY(m_bound_sockets.insert(port, TRY(socket->get_weak_ptr())));
socket->bind_interface_and_port(bind_interface.ptr(), port); socket->bind_address_and_port(reinterpret_cast<struct sockaddr*>(&bind_address), sizeof(bind_address));
return {}; return {};
} }
@ -173,18 +210,36 @@ namespace Kernel
return BAN::Error::from_errno(EINVAL); return BAN::Error::from_errno(EINVAL);
if (address == nullptr || address_len != sizeof(sockaddr_in)) if (address == nullptr || address_len != sizeof(sockaddr_in))
return BAN::Error::from_errno(EINVAL); return BAN::Error::from_errno(EINVAL);
auto& sockaddr_in = *reinterpret_cast<const struct sockaddr_in*>(address);
auto interface = TRY(socket.interface(address, address_len));
auto& sockaddr_in = *reinterpret_cast<const struct sockaddr_in*>(address);
auto dst_port = BAN::host_to_network_endian(sockaddr_in.sin_port); auto dst_port = BAN::host_to_network_endian(sockaddr_in.sin_port);
auto dst_ipv4 = BAN::IPv4Address { sockaddr_in.sin_addr.s_addr }; auto dst_ipv4 = BAN::IPv4Address { sockaddr_in.sin_addr.s_addr };
auto dst_mac = TRY(m_arp_table->get_mac_from_ipv4(socket.interface(), dst_ipv4)); auto dst_mac = TRY(m_arp_table->get_mac_from_ipv4(*interface, dst_ipv4));
if (interface->type() == NetworkInterface::Type::Loopback)
{
BAN::RefPtr<NetworkSocket> receiver;
{
SpinLockGuard _(m_bound_socket_lock);
auto receiver_it = m_bound_sockets.find(dst_port);
if (receiver_it != m_bound_sockets.end())
receiver = receiver_it->value.lock();
}
if (!receiver)
return BAN::Error::from_errno(EADDRNOTAVAIL);
TRY(socket.interface(receiver->address(), receiver->address_len()));
}
BAN::Vector<uint8_t> packet_buffer; BAN::Vector<uint8_t> packet_buffer;
TRY(packet_buffer.resize(buffer.size() + sizeof(IPv4Header) + socket.protocol_header_size())); TRY(packet_buffer.resize(buffer.size() + sizeof(IPv4Header) + socket.protocol_header_size()));
auto packet = BAN::ByteSpan { packet_buffer.span() }; auto packet = BAN::ByteSpan { packet_buffer.span() };
auto pseudo_header = PseudoHeader { auto pseudo_header = PseudoHeader {
.src_ipv4 = socket.interface().get_ipv4_address(), .src_ipv4 = interface->get_ipv4_address(),
.dst_ipv4 = dst_ipv4, .dst_ipv4 = dst_ipv4,
.protocol = socket.protocol() .protocol = socket.protocol()
}; };
@ -201,12 +256,12 @@ namespace Kernel
); );
add_ipv4_header( add_ipv4_header(
packet, packet,
socket.interface().get_ipv4_address(), interface->get_ipv4_address(),
dst_ipv4, dst_ipv4,
socket.protocol() socket.protocol()
); );
TRY(socket.interface().send_bytes(dst_mac, EtherType::IPv4, packet)); TRY(interface->send_bytes(dst_mac, EtherType::IPv4, packet));
return buffer.size(); return buffer.size();
} }

View File

@ -15,12 +15,97 @@ namespace Kernel
{ {
} }
void NetworkSocket::bind_interface_and_port(NetworkInterface* interface, uint16_t port) bool NetworkSocket::can_interface_send_to(const NetworkInterface& interface, const sockaddr* target, socklen_t target_len) const
{ {
ASSERT(!m_interface); ASSERT(target);
ASSERT(interface); ASSERT(target_len >= static_cast<socklen_t>(sizeof(sockaddr_in)));
m_interface = interface; ASSERT(target->sa_family == AF_INET);
m_port = port;
const auto target_ipv4 = BAN::IPv4Address {
reinterpret_cast<const sockaddr_in*>(target)->sin_addr.s_addr
};
switch (interface.type())
{
case NetworkInterface::Type::Ethernet:
// FIXME: this is not really correct :D
return target_ipv4.octets[0] != IN_LOOPBACKNET;
case NetworkInterface::Type::Loopback:
return target_ipv4.octets[0] == IN_LOOPBACKNET;
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<BAN::RefPtr<NetworkInterface>> NetworkSocket::interface(const sockaddr* target, socklen_t target_len)
{
ASSERT(m_network_layer.domain() == NetworkSocket::Domain::INET);
ASSERT(is_bound());
if (target != nullptr)
{
ASSERT(target_len >= static_cast<socklen_t>(sizeof(sockaddr_in)));
ASSERT(target->sa_family == AF_INET);
}
const auto& all_interfaces = NetworkManager::get().interfaces();
const auto bound_ipv4 = BAN::IPv4Address {
reinterpret_cast<const sockaddr_in*>(&m_address)->sin_addr.s_addr
};
// find the bound interface
if (bound_ipv4 != 0)
{
for (const auto& interface : all_interfaces)
{
const auto netmask = interface->get_netmask();
if (bound_ipv4.mask(netmask) != interface->get_ipv4_address().mask(netmask))
continue;
if (target && !can_interface_send_to(*interface, target, target_len))
continue;
return interface;
}
return BAN::Error::from_errno(EADDRNOTAVAIL);
}
// try to find an interface in the same subnet as target
if (target != nullptr)
{
const auto target_ipv4 = BAN::IPv4Address {
reinterpret_cast<const sockaddr_in*>(target)->sin_addr.s_addr
};
for (const auto& interface : all_interfaces)
{
const auto netmask = interface->get_netmask();
if (target_ipv4.mask(netmask) == interface->get_ipv4_address().mask(netmask))
return interface;
}
}
// return any interface (prefer non-loopback)
for (const auto& interface : all_interfaces)
if (interface->type() != NetworkInterface::Type::Loopback)
if (!target || can_interface_send_to(*interface, target, target_len))
return interface;
for (const auto& interface : all_interfaces)
if (interface->type() == NetworkInterface::Type::Loopback)
if (!target || can_interface_send_to(*interface, target, target_len))
return interface;
return BAN::Error::from_errno(EHOSTUNREACH);
}
void NetworkSocket::bind_address_and_port(const sockaddr* addr, socklen_t addr_len)
{
ASSERT(!is_bound());
ASSERT(addr->sa_family != AF_UNSPEC);
ASSERT(addr_len <= static_cast<socklen_t>(sizeof(sockaddr_storage)));
memcpy(&m_address, addr, addr_len);
m_address_len = addr_len;
} }
BAN::ErrorOr<long> NetworkSocket::ioctl_impl(int request, void* arg) BAN::ErrorOr<long> NetworkSocket::ioctl_impl(int request, void* arg)
@ -30,12 +115,8 @@ namespace Kernel
dprintln("No argument provided"); dprintln("No argument provided");
return BAN::Error::from_errno(EINVAL); return BAN::Error::from_errno(EINVAL);
} }
if (m_interface == nullptr)
{
dprintln("No interface bound");
return BAN::Error::from_errno(EADDRNOTAVAIL);
}
auto interface = TRY(this->interface(nullptr, 0));
auto* ifreq = reinterpret_cast<struct ifreq*>(arg); auto* ifreq = reinterpret_cast<struct ifreq*>(arg);
switch (request) switch (request)
@ -44,7 +125,7 @@ namespace Kernel
{ {
auto& ifru_addr = *reinterpret_cast<sockaddr_in*>(&ifreq->ifr_ifru.ifru_addr); auto& ifru_addr = *reinterpret_cast<sockaddr_in*>(&ifreq->ifr_ifru.ifru_addr);
ifru_addr.sin_family = AF_INET; ifru_addr.sin_family = AF_INET;
ifru_addr.sin_addr.s_addr = m_interface->get_ipv4_address().raw; ifru_addr.sin_addr.s_addr = interface->get_ipv4_address().raw;
return 0; return 0;
} }
case SIOCSIFADDR: case SIOCSIFADDR:
@ -52,15 +133,15 @@ namespace Kernel
auto& ifru_addr = *reinterpret_cast<const sockaddr_in*>(&ifreq->ifr_ifru.ifru_addr); auto& ifru_addr = *reinterpret_cast<const sockaddr_in*>(&ifreq->ifr_ifru.ifru_addr);
if (ifru_addr.sin_family != AF_INET) if (ifru_addr.sin_family != AF_INET)
return BAN::Error::from_errno(EADDRNOTAVAIL); return BAN::Error::from_errno(EADDRNOTAVAIL);
m_interface->set_ipv4_address(BAN::IPv4Address { ifru_addr.sin_addr.s_addr }); interface->set_ipv4_address(BAN::IPv4Address { ifru_addr.sin_addr.s_addr });
dprintln("IPv4 address set to {}", m_interface->get_ipv4_address()); dprintln("IPv4 address set to {}", interface->get_ipv4_address());
return 0; return 0;
} }
case SIOCGIFNETMASK: case SIOCGIFNETMASK:
{ {
auto& ifru_netmask = *reinterpret_cast<sockaddr_in*>(&ifreq->ifr_ifru.ifru_netmask); auto& ifru_netmask = *reinterpret_cast<sockaddr_in*>(&ifreq->ifr_ifru.ifru_netmask);
ifru_netmask.sin_family = AF_INET; ifru_netmask.sin_family = AF_INET;
ifru_netmask.sin_addr.s_addr = m_interface->get_netmask().raw; ifru_netmask.sin_addr.s_addr = interface->get_netmask().raw;
return 0; return 0;
} }
case SIOCSIFNETMASK: case SIOCSIFNETMASK:
@ -68,15 +149,15 @@ namespace Kernel
auto& ifru_netmask = *reinterpret_cast<const sockaddr_in*>(&ifreq->ifr_ifru.ifru_netmask); auto& ifru_netmask = *reinterpret_cast<const sockaddr_in*>(&ifreq->ifr_ifru.ifru_netmask);
if (ifru_netmask.sin_family != AF_INET) if (ifru_netmask.sin_family != AF_INET)
return BAN::Error::from_errno(EADDRNOTAVAIL); return BAN::Error::from_errno(EADDRNOTAVAIL);
m_interface->set_netmask(BAN::IPv4Address { ifru_netmask.sin_addr.s_addr }); interface->set_netmask(BAN::IPv4Address { ifru_netmask.sin_addr.s_addr });
dprintln("Netmask set to {}", m_interface->get_netmask()); dprintln("Netmask set to {}", interface->get_netmask());
return 0; return 0;
} }
case SIOCGIFGWADDR: case SIOCGIFGWADDR:
{ {
auto& ifru_gwaddr = *reinterpret_cast<sockaddr_in*>(&ifreq->ifr_ifru.ifru_gwaddr); auto& ifru_gwaddr = *reinterpret_cast<sockaddr_in*>(&ifreq->ifr_ifru.ifru_gwaddr);
ifru_gwaddr.sin_family = AF_INET; ifru_gwaddr.sin_family = AF_INET;
ifru_gwaddr.sin_addr.s_addr = m_interface->get_gateway().raw; ifru_gwaddr.sin_addr.s_addr = interface->get_gateway().raw;
return 0; return 0;
} }
case SIOCSIFGWADDR: case SIOCSIFGWADDR:
@ -84,13 +165,13 @@ namespace Kernel
auto& ifru_gwaddr = *reinterpret_cast<const sockaddr_in*>(&ifreq->ifr_ifru.ifru_gwaddr); auto& ifru_gwaddr = *reinterpret_cast<const sockaddr_in*>(&ifreq->ifr_ifru.ifru_gwaddr);
if (ifru_gwaddr.sin_family != AF_INET) if (ifru_gwaddr.sin_family != AF_INET)
return BAN::Error::from_errno(EADDRNOTAVAIL); return BAN::Error::from_errno(EADDRNOTAVAIL);
m_interface->set_gateway(BAN::IPv4Address { ifru_gwaddr.sin_addr.s_addr }); interface->set_gateway(BAN::IPv4Address { ifru_gwaddr.sin_addr.s_addr });
dprintln("Gateway set to {}", m_interface->get_gateway()); dprintln("Gateway set to {}", interface->get_gateway());
return 0; return 0;
} }
case SIOCGIFHWADDR: case SIOCGIFHWADDR:
{ {
auto mac_address = m_interface->get_mac_address(); auto mac_address = interface->get_mac_address();
ifreq->ifr_ifru.ifru_hwaddr.sa_family = AF_INET; ifreq->ifr_ifru.ifru_hwaddr.sa_family = AF_INET;
memcpy(ifreq->ifr_ifru.ifru_hwaddr.sa_data, &mac_address, sizeof(mac_address)); memcpy(ifreq->ifr_ifru.ifru_hwaddr.sa_data, &mac_address, sizeof(mac_address));
return 0; return 0;
@ -98,9 +179,9 @@ namespace Kernel
case SIOCGIFNAME: case SIOCGIFNAME:
{ {
auto& ifrn_name = ifreq->ifr_ifrn.ifrn_name; auto& ifrn_name = ifreq->ifr_ifrn.ifrn_name;
ASSERT(m_interface->name().size() < sizeof(ifrn_name)); ASSERT(interface->name().size() < sizeof(ifrn_name));
memcpy(ifrn_name, m_interface->name().data(), m_interface->name().size()); memcpy(ifrn_name, interface->name().data(), interface->name().size());
ifrn_name[m_interface->name().size()] = '\0'; ifrn_name[interface->name().size()] = '\0';
return 0; return 0;
} }
default: default:

View File

@ -95,8 +95,8 @@ namespace Kernel
} }
return_inode->m_mutex.lock(); return_inode->m_mutex.lock();
return_inode->m_port = m_port; memcpy(&return_inode->m_address, &connection.target.address, connection.target.address_len);
return_inode->m_interface = m_interface; return_inode->m_address_len = connection.target.address_len;
return_inode->m_listen_parent = this; return_inode->m_listen_parent = this;
return_inode->m_connection_info.emplace(connection.target); return_inode->m_connection_info.emplace(connection.target);
return_inode->m_recv_window.start_seq = connection.target_start_seq; return_inode->m_recv_window.start_seq = connection.target_start_seq;
@ -152,13 +152,18 @@ namespace Kernel
}; };
if (!is_bound()) if (!is_bound())
TRY(m_network_layer.bind_socket_to_unused(this, address, address_len)); TRY(m_network_layer.bind_socket_with_target(this, address, address_len));
m_connection_info.emplace(sockaddr_storage {}, address_len, true); m_connection_info.emplace(sockaddr_storage {}, address_len, true);
memcpy(&m_connection_info->address, address, address_len); memcpy(&m_connection_info->address, address, address_len);
m_next_flags = SYN; m_next_flags = SYN;
TRY(m_network_layer.sendto(*this, {}, address, address_len)); if (m_network_layer.sendto(*this, {}, address, address_len).is_error())
{
set_connection_as_closed();
return BAN::Error::from_errno(ECONNREFUSED);
}
m_next_flags = 0; m_next_flags = 0;
m_state = State::SynSent; m_state = State::SynSent;
@ -410,8 +415,8 @@ namespace Kernel
memset(&header, 0, sizeof(TCPHeader)); memset(&header, 0, sizeof(TCPHeader));
memset(header.options, TCPOption::End, m_tcp_options_bytes); memset(header.options, TCPOption::End, m_tcp_options_bytes);
header.src_port = bound_port();
header.dst_port = dst_port; header.dst_port = dst_port;
header.src_port = m_port;
header.seq_number = m_send_window.current_seq + m_send_window.has_ghost_byte; header.seq_number = m_send_window.current_seq + m_send_window.has_ghost_byte;
header.ack_number = m_recv_window.start_seq + m_recv_window.data_size + m_recv_window.has_ghost_byte; header.ack_number = m_recv_window.start_seq + m_recv_window.data_size + m_recv_window.has_ghost_byte;
header.data_offset = (sizeof(TCPHeader) + m_tcp_options_bytes) / sizeof(uint32_t); header.data_offset = (sizeof(TCPHeader) + m_tcp_options_bytes) / sizeof(uint32_t);
@ -423,7 +428,15 @@ namespace Kernel
if (m_state == State::Closed || m_state == State::SynReceived) if (m_state == State::Closed || m_state == State::SynReceived)
{ {
add_tcp_header_option<0, TCPOption::MaximumSeqmentSize>(header, m_interface->payload_mtu() - m_network_layer.header_size()); const sockaddr_in target {
.sin_family = AF_INET,
.sin_port = dst_port,
.sin_addr = { .s_addr = pseudo_header.dst_ipv4.raw },
.sin_zero = {},
};
auto interface = MUST(this->interface(reinterpret_cast<const sockaddr*>(&target), sizeof(target)));
add_tcp_header_option<0, TCPOption::MaximumSeqmentSize>(header, interface->payload_mtu() - m_network_layer.header_size());
if (m_connection_info->has_window_scale) if (m_connection_info->has_window_scale)
add_tcp_header_option<4, TCPOption::WindowScale>(header, m_recv_window.scale_shift); add_tcp_header_option<4, TCPOption::WindowScale>(header, m_recv_window.scale_shift);
@ -451,11 +464,16 @@ namespace Kernel
if (sender->sa_family == AF_INET) if (sender->sa_family == AF_INET)
{ {
auto interface_or_error = interface(sender, sender_len);
if (interface_or_error.is_error())
return;
auto interface = interface_or_error.release_value();
auto& addr_in = *reinterpret_cast<const sockaddr_in*>(sender); auto& addr_in = *reinterpret_cast<const sockaddr_in*>(sender);
checksum = calculate_internet_checksum(buffer, checksum = calculate_internet_checksum(buffer,
PseudoHeader { PseudoHeader {
.src_ipv4 = BAN::IPv4Address(addr_in.sin_addr.s_addr), .src_ipv4 = BAN::IPv4Address(addr_in.sin_addr.s_addr),
.dst_ipv4 = m_interface->get_ipv4_address(), .dst_ipv4 = interface->get_ipv4_address(),
.protocol = NetworkProtocol::TCP, .protocol = NetworkProtocol::TCP,
.extra = buffer.size() .extra = buffer.size()
} }
@ -663,11 +681,11 @@ namespace Kernel
// NOTE: Only listen socket can unbind the socket as // NOTE: Only listen socket can unbind the socket as
// listen socket is always alive to redirect packets // listen socket is always alive to redirect packets
if (!m_listen_parent) if (!m_listen_parent)
m_network_layer.unbind_socket(m_port); m_network_layer.unbind_socket(bound_port());
else else
m_listen_parent->remove_listen_child(this); m_listen_parent->remove_listen_child(this);
m_interface = nullptr; m_address.ss_family = AF_UNSPEC;
m_port = PORT_NONE; m_address_len = 0;
dprintln_if(DEBUG_TCP, "Socket unbound"); dprintln_if(DEBUG_TCP, "Socket unbound");
} }

View File

@ -30,15 +30,15 @@ namespace Kernel
UDPSocket::~UDPSocket() UDPSocket::~UDPSocket()
{ {
if (is_bound()) if (is_bound())
m_network_layer.unbind_socket(m_port); m_network_layer.unbind_socket(bound_port());
m_port = PORT_NONE; m_address.ss_family = AF_UNSPEC;
m_interface = nullptr; m_address_len = 0;
} }
void UDPSocket::add_protocol_header(BAN::ByteSpan packet, uint16_t dst_port, PseudoHeader) void UDPSocket::add_protocol_header(BAN::ByteSpan packet, uint16_t dst_port, PseudoHeader)
{ {
auto& header = packet.as<UDPHeader>(); auto& header = packet.as<UDPHeader>();
header.src_port = m_port; header.src_port = bound_port();
header.dst_port = dst_port; header.dst_port = dst_port;
header.length = packet.size(); header.length = packet.size();
header.checksum = 0; header.checksum = 0;
@ -115,7 +115,6 @@ namespace Kernel
dprintln("No interface bound"); dprintln("No interface bound");
return BAN::Error::from_errno(EINVAL); return BAN::Error::from_errno(EINVAL);
} }
ASSERT(m_port != PORT_NONE);
SpinLockGuard guard(m_packet_lock); SpinLockGuard guard(m_packet_lock);
@ -176,7 +175,7 @@ namespace Kernel
dwarnln("ignoring sendmsg control message"); dwarnln("ignoring sendmsg control message");
if (!is_bound()) if (!is_bound())
TRY(m_network_layer.bind_socket_to_unused(this, static_cast<sockaddr*>(message.msg_name), message.msg_namelen)); TRY(m_network_layer.bind_socket_with_target(this, static_cast<sockaddr*>(message.msg_name), message.msg_namelen));
const size_t total_send_size = const size_t total_send_size =
[&message]() -> size_t { [&message]() -> size_t {

View File

@ -214,7 +214,7 @@ namespace Kernel
open_file.descriptor_flags = 0; open_file.descriptor_flags = 0;
open_file.inode()->on_clone(open_file.status_flags()); open_file.inode()->on_clone(open_file.status_flags());
return fildes; return fildes2;
} }
BAN::ErrorOr<int> OpenFileDescriptorSet::fcntl(int fd, int cmd, uintptr_t extra) BAN::ErrorOr<int> OpenFileDescriptorSet::fcntl(int fd, int cmd, uintptr_t extra)

View File

@ -189,8 +189,6 @@ namespace Kernel
__builtin_ia32_pause(); __builtin_ia32_pause();
} }
} }
s_is_smp_enabled = true;
} }
void Processor::handle_ipi() void Processor::handle_ipi()

View File

@ -136,6 +136,9 @@ namespace Kernel
while (s_schedulers_initialized < Processor::count()) while (s_schedulers_initialized < Processor::count())
__builtin_ia32_pause(); __builtin_ia32_pause();
if (Processor::count() > 1)
Processor::set_smp_enabled();
return {}; return {};
} }
@ -290,20 +293,7 @@ namespace Kernel
m_current->last_start_ns = SystemTimer::get().ns_since_boot(); m_current->last_start_ns = SystemTimer::get().ns_since_boot();
} }
void Scheduler::reschedule_if_idle() void Scheduler::wake_up_sleeping_threads()
{
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
if (!m_current && !m_run_queue.empty())
Processor::yield();
}
void Scheduler::timer_interrupt()
{
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
if (Processor::is_smp_enabled())
do_load_balancing();
{ {
const uint64_t current_ns = SystemTimer::get().ns_since_boot(); const uint64_t current_ns = SystemTimer::get().ns_since_boot();
while (!m_block_queue.empty() && current_ns >= m_block_queue.front()->wake_time_ns) while (!m_block_queue.empty() && current_ns >= m_block_queue.front()->wake_time_ns)
@ -320,6 +310,29 @@ namespace Kernel
} }
} }
void Scheduler::reschedule_if_idle()
{
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
if (m_current != nullptr)
return;
if (m_run_queue.empty())
wake_up_sleeping_threads();
if (!m_run_queue.empty())
Processor::yield();
}
void Scheduler::timer_interrupt()
{
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
if (Processor::is_smp_enabled())
do_load_balancing();
wake_up_sleeping_threads();
{ {
const uint64_t current_ns = SystemTimer::get().ns_since_boot(); const uint64_t current_ns = SystemTimer::get().ns_since_boot();
if (current_ns >= m_last_reschedule_ns + s_reschedule_interval_ns) if (current_ns >= m_last_reschedule_ns + s_reschedule_interval_ns)

View File

@ -1,3 +1,4 @@
#include <kernel/Lock/LockGuard.h>
#include <kernel/Scheduler.h> #include <kernel/Scheduler.h>
#include <kernel/Storage/ATA/AHCI/Controller.h> #include <kernel/Storage/ATA/AHCI/Controller.h>
#include <kernel/Storage/ATA/AHCI/Device.h> #include <kernel/Storage/ATA/AHCI/Device.h>
@ -182,6 +183,8 @@ namespace Kernel
BAN::ErrorOr<void> AHCIDevice::read_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ByteSpan buffer) BAN::ErrorOr<void> AHCIDevice::read_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ByteSpan buffer)
{ {
LockGuard _(m_mutex);
ASSERT(buffer.size() >= sector_count * sector_size()); ASSERT(buffer.size() >= sector_count * sector_size());
const size_t sectors_per_page = PAGE_SIZE / sector_size(); const size_t sectors_per_page = PAGE_SIZE / sector_size();
for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += sectors_per_page) for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += sectors_per_page)
@ -197,6 +200,8 @@ namespace Kernel
BAN::ErrorOr<void> AHCIDevice::write_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ConstByteSpan buffer) BAN::ErrorOr<void> AHCIDevice::write_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ConstByteSpan buffer)
{ {
LockGuard _(m_mutex);
ASSERT(buffer.size() >= sector_count * sector_size()); ASSERT(buffer.size() >= sector_count * sector_size());
const size_t sectors_per_page = PAGE_SIZE / sector_size(); const size_t sectors_per_page = PAGE_SIZE / sector_size();
for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += sectors_per_page) for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += sectors_per_page)

View File

@ -1,3 +1,4 @@
#include <BAN/ScopeGuard.h>
#include <kernel/BootInfo.h> #include <kernel/BootInfo.h>
#include <kernel/Memory/Heap.h> #include <kernel/Memory/Heap.h>
#include <kernel/Memory/PageTable.h> #include <kernel/Memory/PageTable.h>
@ -21,52 +22,90 @@ namespace Kernel
release_all_pages(); release_all_pages();
} }
size_t DiskCache::find_sector_cache_index(uint64_t sector) const
{
const uint64_t sectors_per_page = PAGE_SIZE / m_sector_size;
const uint64_t page_cache_offset = sector % sectors_per_page;
const uint64_t page_cache_start = sector - page_cache_offset;
size_t l = 0, r = m_cache.size();
while (l < r)
{
const size_t mid = (l + r) / 2;
if (m_cache[mid].first_sector == page_cache_start)
return mid;
if (m_cache[mid].first_sector < page_cache_start)
l = mid + 1;
else
r = mid;
}
return l;
}
bool DiskCache::read_from_cache(uint64_t sector, BAN::ByteSpan buffer) bool DiskCache::read_from_cache(uint64_t sector, BAN::ByteSpan buffer)
{ {
ASSERT(buffer.size() >= m_sector_size); ASSERT(buffer.size() >= m_sector_size);
uint64_t sectors_per_page = PAGE_SIZE / m_sector_size; const uint64_t sectors_per_page = PAGE_SIZE / m_sector_size;
uint64_t page_cache_offset = sector % sectors_per_page; const uint64_t page_cache_offset = sector % sectors_per_page;
uint64_t page_cache_start = sector - page_cache_offset; const uint64_t page_cache_start = sector - page_cache_offset;
for (auto& cache : m_cache) RWLockRDGuard _(m_rw_lock);
{
if (cache.first_sector + sectors_per_page <= page_cache_start)
continue;
if (cache.first_sector > page_cache_start)
break;
const auto index = find_sector_cache_index(sector);
if (index >= m_cache.size())
return false;
const auto& cache = m_cache[index];
if (cache.first_sector != page_cache_start)
return false;
if (!(cache.sector_mask & (1 << page_cache_offset))) if (!(cache.sector_mask & (1 << page_cache_offset)))
continue; return false;
PageTable::with_fast_page(cache.paddr, [&] { PageTable::with_fast_page(cache.paddr, [&] {
memcpy(buffer.data(), PageTable::fast_page_as_ptr(page_cache_offset * m_sector_size), m_sector_size); memcpy(buffer.data(), PageTable::fast_page_as_ptr(page_cache_offset * m_sector_size), m_sector_size);
}); });
return true; return true;
}
return false;
}; };
BAN::ErrorOr<void> DiskCache::write_to_cache(uint64_t sector, BAN::ConstByteSpan buffer, bool dirty) BAN::ErrorOr<void> DiskCache::write_to_cache(uint64_t sector, BAN::ConstByteSpan buffer, bool dirty)
{ {
ASSERT(buffer.size() >= m_sector_size); ASSERT(buffer.size() >= m_sector_size);
uint64_t sectors_per_page = PAGE_SIZE / m_sector_size;
uint64_t page_cache_offset = sector % sectors_per_page;
uint64_t page_cache_start = sector - page_cache_offset;
size_t index = 0; const uint64_t sectors_per_page = PAGE_SIZE / m_sector_size;
const uint64_t page_cache_offset = sector % sectors_per_page;
const uint64_t page_cache_start = sector - page_cache_offset;
// Search the cache if the have this sector in memory RWLockWRGuard _(m_rw_lock);
for (; index < m_cache.size(); index++)
const auto index = find_sector_cache_index(sector);
if (index >= m_cache.size() || m_cache[index].first_sector != page_cache_start)
{ {
auto& cache = m_cache[index]; paddr_t paddr = Heap::get().take_free_page();
if (paddr == 0)
return BAN::Error::from_errno(ENOMEM);
if (cache.first_sector + sectors_per_page <= page_cache_start) PageCache cache {
continue; .paddr = paddr,
if (cache.first_sector > page_cache_start) .first_sector = page_cache_start,
break; .sector_mask = 0,
.dirty_mask = 0,
};
if (auto ret = m_cache.insert(index, cache); ret.is_error())
{
Heap::get().release_page(paddr);
return ret.error();
}
}
auto& cache = m_cache[index];
PageTable::with_fast_page(cache.paddr, [&] { PageTable::with_fast_page(cache.paddr, [&] {
memcpy(PageTable::fast_page_as_ptr(page_cache_offset * m_sector_size), buffer.data(), m_sector_size); memcpy(PageTable::fast_page_as_ptr(page_cache_offset * m_sector_size), buffer.data(), m_sector_size);
@ -79,32 +118,17 @@ namespace Kernel
return {}; return {};
} }
// Try to add new page to the cache
paddr_t paddr = Heap::get().take_free_page();
if (paddr == 0)
return BAN::Error::from_errno(ENOMEM);
PageCache cache;
cache.paddr = paddr;
cache.first_sector = page_cache_start;
cache.sector_mask = 1 << page_cache_offset;
cache.dirty_mask = dirty ? cache.sector_mask : 0;
if (auto ret = m_cache.insert(index, cache); ret.is_error())
{
Heap::get().release_page(paddr);
return ret.error();
}
PageTable::with_fast_page(cache.paddr, [&] {
memcpy(PageTable::fast_page_as_ptr(page_cache_offset * m_sector_size), buffer.data(), m_sector_size);
});
return {};
}
BAN::ErrorOr<void> DiskCache::sync_cache_index(size_t index) BAN::ErrorOr<void> DiskCache::sync_cache_index(size_t index)
{ {
LockGuard _(m_sync_mutex);
PageCache temp_cache;
{
RWLockWRGuard _(m_rw_lock);
if (index >= m_cache.size())
return {};
auto& cache = m_cache[index]; auto& cache = m_cache[index];
if (cache.dirty_mask == 0) if (cache.dirty_mask == 0)
return {}; return {};
@ -113,20 +137,35 @@ namespace Kernel
memcpy(m_sync_cache.data(), PageTable::fast_page_as_ptr(), PAGE_SIZE); memcpy(m_sync_cache.data(), PageTable::fast_page_as_ptr(), PAGE_SIZE);
}); });
temp_cache = cache;
cache.dirty_mask = 0;
cache.syncing = true;
}
// restores dirty mask if write to disk fails
BAN::ScopeGuard dirty_guard([&] {
RWLockWRGuard _(m_rw_lock);
const auto new_index = find_sector_cache_index(temp_cache.first_sector);
ASSERT(new_index < m_cache.size() && m_cache[new_index].first_sector == temp_cache.first_sector);
m_cache[new_index].dirty_mask |= temp_cache.dirty_mask;
m_cache[new_index].syncing = false;
});
uint8_t sector_start = 0; uint8_t sector_start = 0;
uint8_t sector_count = 0; uint8_t sector_count = 0;
while (sector_start + sector_count <= PAGE_SIZE / m_sector_size) while (sector_start + sector_count <= PAGE_SIZE / m_sector_size)
{ {
if (cache.dirty_mask & (1 << (sector_start + sector_count))) if (temp_cache.dirty_mask & (1 << (sector_start + sector_count)))
sector_count++; sector_count++;
else if (sector_count == 0) else if (sector_count == 0)
sector_start++; sector_start++;
else else
{ {
dprintln_if(DEBUG_DISK_SYNC, "syncing {}->{}", cache.first_sector + sector_start, cache.first_sector + sector_start + sector_count); dprintln_if(DEBUG_DISK_SYNC, "syncing {}->{}", temp_cache.first_sector + sector_start, temp_cache.first_sector + sector_start + sector_count);
auto data_slice = m_sync_cache.span().slice(sector_start * m_sector_size, sector_count * m_sector_size); auto data_slice = m_sync_cache.span().slice(sector_start * m_sector_size, sector_count * m_sector_size);
TRY(m_device.write_sectors_impl(cache.first_sector + sector_start, sector_count, data_slice)); TRY(m_device.write_sectors_impl(temp_cache.first_sector + sector_start, sector_count, data_slice));
temp_cache.dirty_mask &= ~(((1 << sector_count) - 1) << sector_start);
sector_start += sector_count + 1; sector_start += sector_count + 1;
sector_count = 0; sector_count = 0;
} }
@ -134,13 +173,12 @@ namespace Kernel
if (sector_count > 0) if (sector_count > 0)
{ {
dprintln_if(DEBUG_DISK_SYNC, "syncing {}->{}", cache.first_sector + sector_start, cache.first_sector + sector_start + sector_count); dprintln_if(DEBUG_DISK_SYNC, "syncing {}->{}", temp_cache.first_sector + sector_start, temp_cache.first_sector + sector_start + sector_count);
auto data_slice = m_sync_cache.span().slice(sector_start * m_sector_size, sector_count * m_sector_size); auto data_slice = m_sync_cache.span().slice(sector_start * m_sector_size, sector_count * m_sector_size);
TRY(m_device.write_sectors_impl(cache.first_sector + sector_start, sector_count, data_slice)); TRY(m_device.write_sectors_impl(temp_cache.first_sector + sector_start, sector_count, data_slice));
temp_cache.dirty_mask &= ~(((1 << sector_count) - 1) << sector_start);
} }
cache.dirty_mask = 0;
return {}; return {};
} }
@ -158,19 +196,17 @@ namespace Kernel
if (g_disable_disk_write) if (g_disable_disk_write)
return {}; return {};
uint64_t sectors_per_page = PAGE_SIZE / m_sector_size; m_rw_lock.rd_lock();
uint64_t page_cache_offset = sector % sectors_per_page; for (size_t i = find_sector_cache_index(sector); i < m_cache.size(); i++)
uint64_t page_cache_start = sector - page_cache_offset;
for (size_t i = 0; i < m_cache.size(); i++)
{ {
auto& cache = m_cache[i]; auto& cache = m_cache[i];
if (cache.first_sector + sectors_per_page <= page_cache_start) if (cache.first_sector >= sector + block_count)
continue;
if (cache.first_sector * sectors_per_page >= page_cache_start * sectors_per_page + block_count)
break; break;
m_rw_lock.rd_unlock();
TRY(sync_cache_index(i)); TRY(sync_cache_index(i));
m_rw_lock.rd_lock();
} }
m_rw_lock.rd_unlock();
return {}; return {};
} }
@ -180,10 +216,12 @@ namespace Kernel
// NOTE: There might not actually be page_count pages after this // NOTE: There might not actually be page_count pages after this
// function returns. The synchronization must be done elsewhere. // function returns. The synchronization must be done elsewhere.
RWLockWRGuard _(m_rw_lock);
size_t released = 0; size_t released = 0;
for (size_t i = 0; i < m_cache.size() && released < page_count;) for (size_t i = 0; i < m_cache.size() && released < page_count;)
{ {
if (m_cache[i].dirty_mask == 0) if (!m_cache[i].syncing && m_cache[i].dirty_mask == 0)
{ {
Heap::get().release_page(m_cache[i].paddr); Heap::get().release_page(m_cache[i].paddr);
m_cache.remove(i); m_cache.remove(i);

View File

@ -211,14 +211,12 @@ namespace Kernel
void StorageDevice::add_disk_cache() void StorageDevice::add_disk_cache()
{ {
LockGuard _(m_mutex);
ASSERT(!m_disk_cache.has_value()); ASSERT(!m_disk_cache.has_value());
m_disk_cache.emplace(sector_size(), *this); m_disk_cache.emplace(sector_size(), *this);
} }
size_t StorageDevice::drop_disk_cache() size_t StorageDevice::drop_disk_cache()
{ {
LockGuard _(m_mutex);
if (m_disk_cache.has_value()) if (m_disk_cache.has_value())
return m_disk_cache->release_pages(-1); return m_disk_cache->release_pages(-1);
return 0; return 0;
@ -226,7 +224,6 @@ namespace Kernel
BAN::ErrorOr<void> StorageDevice::sync_disk_cache() BAN::ErrorOr<void> StorageDevice::sync_disk_cache()
{ {
LockGuard _(m_mutex);
if (m_disk_cache.has_value()) if (m_disk_cache.has_value())
TRY(m_disk_cache->sync()); TRY(m_disk_cache->sync());
return {}; return {};
@ -236,8 +233,6 @@ namespace Kernel
{ {
ASSERT(buffer.size() >= sector_count * sector_size()); ASSERT(buffer.size() >= sector_count * sector_size());
LockGuard _(m_mutex);
if (!m_disk_cache.has_value()) if (!m_disk_cache.has_value())
return read_sectors_impl(lba, sector_count, buffer); return read_sectors_impl(lba, sector_count, buffer);
@ -275,8 +270,6 @@ namespace Kernel
{ {
ASSERT(buffer.size() >= sector_count * sector_size()); ASSERT(buffer.size() >= sector_count * sector_size());
LockGuard _(m_mutex);
if (g_disable_disk_write) if (g_disable_disk_write)
{ {
if (!m_disk_cache.has_value()) if (!m_disk_cache.has_value())

View File

@ -636,12 +636,12 @@ namespace Kernel
: reinterpret_cast<vaddr_t>(handler.sa_handler); : reinterpret_cast<vaddr_t>(handler.sa_handler);
has_sa_restart = !!(handler.sa_flags & SA_RESTART); has_sa_restart = !!(handler.sa_flags & SA_RESTART);
if (handler.sa_flags & SA_RESETHAND)
handler = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
const auto& alt_stack = m_signal_alt_stack; const auto& alt_stack = m_signal_alt_stack;
if (alt_stack.ss_flags != SS_DISABLE && (handler.sa_flags & SA_ONSTACK) && !currently_on_alternate_stack()) if (alt_stack.ss_flags != SS_DISABLE && (handler.sa_flags & SA_ONSTACK) && !currently_on_alternate_stack())
signal_stack_top = reinterpret_cast<vaddr_t>(alt_stack.ss_sp) + alt_stack.ss_size; signal_stack_top = reinterpret_cast<vaddr_t>(alt_stack.ss_sp) + alt_stack.ss_size;
if (handler.sa_flags & SA_RESETHAND)
handler = { .sa_handler = SIG_DFL, .sa_mask = 0, .sa_flags = 0 };
} }
m_signal_pending_mask &= ~(1ull << signal); m_signal_pending_mask &= ~(1ull << signal);

View File

@ -3,6 +3,7 @@
#include <kernel/FS/DevFS/FileSystem.h> #include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/USB/HID/HIDDriver.h> #include <kernel/USB/HID/HIDDriver.h>
#include <kernel/USB/HID/Joystick.h>
#include <kernel/USB/HID/Keyboard.h> #include <kernel/USB/HID/Keyboard.h>
#include <kernel/USB/HID/Mouse.h> #include <kernel/USB/HID/Mouse.h>
@ -208,6 +209,10 @@ namespace Kernel
return BAN::Error::from_errno(EINVAL); return BAN::Error::from_errno(EINVAL);
} }
for (auto& report : m_device_inputs)
if (report.device && report.device->initialize().is_error())
report.device.clear();
m_device.send_data_buffer(m_data_endpoint_id, m_data_buffer->paddr(), m_data_buffer->size()); m_device.send_data_buffer(m_data_endpoint_id, m_data_buffer->paddr(), m_data_buffer->size());
return {}; return {};
@ -256,6 +261,10 @@ namespace Kernel
report.device = TRY(BAN::RefPtr<USBMouse>::create()); report.device = TRY(BAN::RefPtr<USBMouse>::create());
dprintln("Initialized an USB Mouse"); dprintln("Initialized an USB Mouse");
break; break;
case 0x04:
report.device = TRY(BAN::RefPtr<USBJoystick>::create(*this));
dprintln("Initialized an USB Joystick");
break;
case 0x06: case 0x06:
report.device = TRY(BAN::RefPtr<USBKeyboard>::create(*this, BAN::move(outputs))); report.device = TRY(BAN::RefPtr<USBKeyboard>::create(*this, BAN::move(outputs)));
dprintln("Initialized an USB Keyboard"); dprintln("Initialized an USB Keyboard");
@ -376,13 +385,13 @@ namespace Kernel
continue; continue;
} }
const uint32_t usage_base = input.usage_id ? input.usage_id : input.usage_minimum;
const bool relative = !!(input.flags & 0x04); const bool relative = !!(input.flags & 0x04);
const bool variable = !!(input.flags & 0x02); const bool variable = !!(input.flags & 0x02);
const auto usage = input.usage_id ? input.usage_id : (input.usage_minimum + (variable ? i : logical));
if (!variable) if (!variable)
device_input.device->handle_array(input.usage_page, usage_base + logical); device_input.device->handle_array(input.usage_page, usage);
else else
{ {
const int64_t physical = const int64_t physical =
@ -392,9 +401,9 @@ namespace Kernel
input.physical_minimum; input.physical_minimum;
if (relative) if (relative)
device_input.device->handle_variable(input.usage_page, usage_base + i, physical); device_input.device->handle_variable(input.usage_page, usage, physical);
else else
device_input.device->handle_variable_absolute(input.usage_page, usage_base + i, physical, input.physical_minimum, input.physical_maximum); device_input.device->handle_variable_absolute(input.usage_page, usage, physical, input.physical_minimum, input.physical_maximum);
} }
bit_offset += input.report_size; bit_offset += input.report_size;
@ -595,15 +604,16 @@ namespace Kernel
break; break;
case 0b1010: // collection case 0b1010: // collection
{ {
if (local_state.usage_stack.size() != 1) if (local_state.usage_stack.size() > 1)
{ {
dwarnln("{} usages specified for collection", local_state.usage_stack.empty() ? "No" : "Multiple"); dwarnln("Multiple usages specified for a collection");
return BAN::Error::from_errno(EFAULT); return BAN::Error::from_errno(EFAULT);
} }
uint16_t usage_page = 0; uint16_t usage_page = 0;
if (global_state.usage_page.has_value()) if (global_state.usage_page.has_value())
usage_page = global_state.usage_page.value(); usage_page = global_state.usage_page.value();
if (local_state.usage_stack.front() >> 16) if (!local_state.usage_stack.empty() && local_state.usage_stack.front() >> 16)
usage_page = local_state.usage_stack.front() >> 16; usage_page = local_state.usage_stack.front() >> 16;
if (usage_page == 0) if (usage_page == 0)
{ {
@ -612,8 +622,11 @@ namespace Kernel
} }
TRY(collection_stack.emplace_back()); TRY(collection_stack.emplace_back());
collection_stack.back().type = report_data[1];
collection_stack.back().usage_page = usage_page; collection_stack.back().usage_page = usage_page;
if (!local_state.usage_stack.empty())
collection_stack.back().usage_id = local_state.usage_stack.front(); collection_stack.back().usage_id = local_state.usage_stack.front();
break; break;
} }
case 0b1100: // end collection case 0b1100: // end collection

View File

@ -0,0 +1,114 @@
#include <kernel/Input/InputDevice.h>
#include <kernel/USB/HID/Joystick.h>
namespace Kernel
{
USBJoystick::USBJoystick(USBHIDDriver& driver)
: USBHIDDevice(InputDevice::Type::Joystick)
, m_driver(driver)
{
}
BAN::ErrorOr<void> USBJoystick::initialize()
{
// TODO: this is not a generic USB HID joystick driver but one for PS3 controller.
// this may still work with other HID joysticks so i won't limit this only
// based on the device.
// linux hid-sony.c
auto temp_region = TRY(DMARegion::create(17));
USBDeviceRequest request;
// move ps3 controller to "operational" state
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Interface;
request.bRequest = 0x01;
request.wValue = 0x03F2;
request.wIndex = m_driver.interface().descriptor.bInterfaceNumber;
request.wLength = 17;
TRY(m_driver.device().send_request(request, temp_region->paddr()));
// some compatible controllers need this too
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Interface;
request.bRequest = 0x01;
request.wValue = 0x03F5;
request.wIndex = m_driver.interface().descriptor.bInterfaceNumber;
request.wLength = 8;
TRY(m_driver.device().send_request(request, temp_region->paddr()));
return {};
}
void USBJoystick::start_report()
{
m_interrupt_state = m_state_lock.lock();
for (auto& axis : m_state.axis)
axis = {};
for (auto& button : m_state.buttons)
button = false;
}
void USBJoystick::stop_report()
{
m_state_lock.unlock(m_interrupt_state);
}
void USBJoystick::handle_array(uint16_t usage_page, uint16_t usage)
{
(void)usage;
dprintln("Unsupported array {2H}", usage_page);
}
void USBJoystick::handle_variable(uint16_t usage_page, uint16_t usage, int64_t state)
{
(void)usage;
(void)state;
dprintln("Unsupported relative usage page {2H}", usage_page);
}
void USBJoystick::handle_variable_absolute(uint16_t usage_page, uint16_t usage, int64_t state, int64_t min, int64_t max)
{
switch (usage_page)
{
case 0x01:
switch (usage)
{
case 0x01:
// TODO: PS3 controller sends some extra data with this usage
break;
case 0x30:
m_state.axis[0] = { state, min, max };
break;
case 0x31:
m_state.axis[1] = { state, min, max };
break;
case 0x32:
m_state.axis[2] = { state, min, max };
break;
case 0x35:
m_state.axis[3] = { state, min, max };
break;
}
break;
case 0x09:
if (usage > 0 && usage <= sizeof(m_state.buttons))
m_state.buttons[usage - 1] = state;
break;
default:
dprintln("Unsupported absolute usage page {2H}", usage_page);
break;
}
}
BAN::ErrorOr<size_t> USBJoystick::read_impl(off_t, BAN::ByteSpan buffer)
{
SpinLockGuard _(m_state_lock);
const size_t to_copy = BAN::Math::min(buffer.size(), sizeof(m_state));
memcpy(buffer.data(), &m_state, to_copy);
return to_copy;
}
}

View File

@ -3,7 +3,7 @@
NAME='SDL2' NAME='SDL2'
VERSION='2.32.8' VERSION='2.32.8'
DOWNLOAD_URL="https://github.com/libsdl-org/SDL/releases/download/release-$VERSION/SDL2-$VERSION.tar.gz#0ca83e9c9b31e18288c7ec811108e58bac1f1bb5ec6577ad386830eac51c787e" DOWNLOAD_URL="https://github.com/libsdl-org/SDL/releases/download/release-$VERSION/SDL2-$VERSION.tar.gz#0ca83e9c9b31e18288c7ec811108e58bac1f1bb5ec6577ad386830eac51c787e"
DEPENDENCIES=('mesa') DEPENDENCIES=('mesa' 'libiconv')
configure() { configure() {
$BANAN_CMAKE --fresh -S . -B build -G Ninja \ $BANAN_CMAKE --fresh -S . -B build -G Ninja \
@ -11,7 +11,7 @@ configure() {
-DCMAKE_INSTALL_PREFIX='/usr' \ -DCMAKE_INSTALL_PREFIX='/usr' \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DSDL_LIBSAMPLERATE=OFF \ -DSDL_LIBSAMPLERATE=OFF \
-DSDL_PTHREADS_SEM=OFF \ -DSDL_STATIC=OFF \
|| exit 1 || exit 1
} }

View File

@ -1,6 +1,6 @@
diff -ruN SDL2-2.32.8/cmake/sdlplatform.cmake SDL2-2.32.8-banan_os/cmake/sdlplatform.cmake diff -ruN SDL2-2.32.8/cmake/sdlplatform.cmake SDL2-2.32.8-banan_os/cmake/sdlplatform.cmake
--- SDL2-2.32.8/cmake/sdlplatform.cmake 2024-08-14 13:35:43.000000000 +0300 --- SDL2-2.32.8/cmake/sdlplatform.cmake 2024-08-14 13:35:43.000000000 +0300
+++ SDL2-2.32.8-banan_os/cmake/sdlplatform.cmake 2025-11-22 00:45:00.922311100 +0200 +++ SDL2-2.32.8-banan_os/cmake/sdlplatform.cmake 2026-01-03 19:50:45.777185459 +0200
@@ -28,6 +28,8 @@ @@ -28,6 +28,8 @@
set(SDL_CMAKE_PLATFORM AIX) set(SDL_CMAKE_PLATFORM AIX)
elseif(CMAKE_SYSTEM_NAME MATCHES "Minix.*") elseif(CMAKE_SYSTEM_NAME MATCHES "Minix.*")
@ -12,7 +12,7 @@ diff -ruN SDL2-2.32.8/cmake/sdlplatform.cmake SDL2-2.32.8-banan_os/cmake/sdlplat
endif() endif()
diff -ruN SDL2-2.32.8/CMakeLists.txt SDL2-2.32.8-banan_os/CMakeLists.txt diff -ruN SDL2-2.32.8/CMakeLists.txt SDL2-2.32.8-banan_os/CMakeLists.txt
--- SDL2-2.32.8/CMakeLists.txt 2025-06-03 02:00:39.000000000 +0300 --- SDL2-2.32.8/CMakeLists.txt 2025-06-03 02:00:39.000000000 +0300
+++ SDL2-2.32.8-banan_os/CMakeLists.txt 2025-11-22 00:45:00.923441418 +0200 +++ SDL2-2.32.8-banan_os/CMakeLists.txt 2026-01-03 19:51:02.494165302 +0200
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
set(SDL2_SUBPROJECT ON) set(SDL2_SUBPROJECT ON)
endif() endif()
@ -31,7 +31,7 @@ diff -ruN SDL2-2.32.8/CMakeLists.txt SDL2-2.32.8-banan_os/CMakeLists.txt
if(SDL_AUDIO) if(SDL_AUDIO)
if(SYSV5 OR SOLARIS OR HPUX) if(SYSV5 OR SOLARIS OR HPUX)
set(SDL_AUDIO_DRIVER_SUNAUDIO 1) set(SDL_AUDIO_DRIVER_SUNAUDIO 1)
@@ -2459,6 +2459,57 @@ @@ -2459,6 +2459,64 @@
CheckPTHREAD() CheckPTHREAD()
list(APPEND EXTRA_LIBS root be media game device textencoding) list(APPEND EXTRA_LIBS root be media game device textencoding)
@ -50,6 +50,13 @@ diff -ruN SDL2-2.32.8/CMakeLists.txt SDL2-2.32.8-banan_os/CMakeLists.txt
+ list(APPEND EXTRA_LIBS audio) + list(APPEND EXTRA_LIBS audio)
+ endif() + endif()
+ +
+ if(SDL_JOYSTICK)
+ set(SDL_JOYSTICK_BANANOS 1)
+ file(GLOB JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/banan_os/*.cpp)
+ list(APPEND SOURCE_FILES ${JOYSTICK_SOURCES})
+ set(HAVE_SDL_JOYSTICK TRUE)
+ endif()
+
+ if(SDL_VIDEO) + if(SDL_VIDEO)
+ set(SDL_VIDEO_DRIVER_BANANOS 1) + set(SDL_VIDEO_DRIVER_BANANOS 1)
+ file(GLOB VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/banan_os/*.cpp) + file(GLOB VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/banan_os/*.cpp)
@ -91,7 +98,7 @@ diff -ruN SDL2-2.32.8/CMakeLists.txt SDL2-2.32.8-banan_os/CMakeLists.txt
file(GLOB MISC_SOURCES ${SDL2_SOURCE_DIR}/src/misc/riscos/*.c) file(GLOB MISC_SOURCES ${SDL2_SOURCE_DIR}/src/misc/riscos/*.c)
diff -ruN SDL2-2.32.8/include/SDL_config.h.cmake SDL2-2.32.8-banan_os/include/SDL_config.h.cmake diff -ruN SDL2-2.32.8/include/SDL_config.h.cmake SDL2-2.32.8-banan_os/include/SDL_config.h.cmake
--- SDL2-2.32.8/include/SDL_config.h.cmake 2025-01-01 17:47:53.000000000 +0200 --- SDL2-2.32.8/include/SDL_config.h.cmake 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/include/SDL_config.h.cmake 2025-11-22 00:45:00.924001549 +0200 +++ SDL2-2.32.8-banan_os/include/SDL_config.h.cmake 2026-01-03 19:55:22.679890924 +0200
@@ -307,6 +307,7 @@ @@ -307,6 +307,7 @@
#cmakedefine SDL_AUDIO_DRIVER_FUSIONSOUND @SDL_AUDIO_DRIVER_FUSIONSOUND@ #cmakedefine SDL_AUDIO_DRIVER_FUSIONSOUND @SDL_AUDIO_DRIVER_FUSIONSOUND@
#cmakedefine SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC @SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC@ #cmakedefine SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC @SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC@
@ -100,7 +107,15 @@ diff -ruN SDL2-2.32.8/include/SDL_config.h.cmake SDL2-2.32.8-banan_os/include/SD
#cmakedefine SDL_AUDIO_DRIVER_JACK @SDL_AUDIO_DRIVER_JACK@ #cmakedefine SDL_AUDIO_DRIVER_JACK @SDL_AUDIO_DRIVER_JACK@
#cmakedefine SDL_AUDIO_DRIVER_JACK_DYNAMIC @SDL_AUDIO_DRIVER_JACK_DYNAMIC@ #cmakedefine SDL_AUDIO_DRIVER_JACK_DYNAMIC @SDL_AUDIO_DRIVER_JACK_DYNAMIC@
#cmakedefine SDL_AUDIO_DRIVER_NAS @SDL_AUDIO_DRIVER_NAS@ #cmakedefine SDL_AUDIO_DRIVER_NAS @SDL_AUDIO_DRIVER_NAS@
@@ -406,6 +407,7 @@ @@ -337,6 +338,7 @@
#cmakedefine SDL_INPUT_WSCONS @SDL_INPUT_WSCONS@
#cmakedefine SDL_JOYSTICK_ANDROID @SDL_JOYSTICK_ANDROID@
#cmakedefine SDL_JOYSTICK_HAIKU @SDL_JOYSTICK_HAIKU@
+#cmakedefine SDL_JOYSTICK_BANANOS @SDL_JOYSTICK_BANANOS@
#cmakedefine SDL_JOYSTICK_WGI @SDL_JOYSTICK_WGI@
#cmakedefine SDL_JOYSTICK_DINPUT @SDL_JOYSTICK_DINPUT@
#cmakedefine SDL_JOYSTICK_XINPUT @SDL_JOYSTICK_XINPUT@
@@ -406,6 +408,7 @@
#cmakedefine SDL_VIDEO_DRIVER_ANDROID @SDL_VIDEO_DRIVER_ANDROID@ #cmakedefine SDL_VIDEO_DRIVER_ANDROID @SDL_VIDEO_DRIVER_ANDROID@
#cmakedefine SDL_VIDEO_DRIVER_EMSCRIPTEN @SDL_VIDEO_DRIVER_EMSCRIPTEN@ #cmakedefine SDL_VIDEO_DRIVER_EMSCRIPTEN @SDL_VIDEO_DRIVER_EMSCRIPTEN@
#cmakedefine SDL_VIDEO_DRIVER_HAIKU @SDL_VIDEO_DRIVER_HAIKU@ #cmakedefine SDL_VIDEO_DRIVER_HAIKU @SDL_VIDEO_DRIVER_HAIKU@
@ -110,7 +125,7 @@ diff -ruN SDL2-2.32.8/include/SDL_config.h.cmake SDL2-2.32.8-banan_os/include/SD
#cmakedefine SDL_VIDEO_DRIVER_DIRECTFB @SDL_VIDEO_DRIVER_DIRECTFB@ #cmakedefine SDL_VIDEO_DRIVER_DIRECTFB @SDL_VIDEO_DRIVER_DIRECTFB@
diff -ruN SDL2-2.32.8/include/SDL_platform.h SDL2-2.32.8-banan_os/include/SDL_platform.h diff -ruN SDL2-2.32.8/include/SDL_platform.h SDL2-2.32.8-banan_os/include/SDL_platform.h
--- SDL2-2.32.8/include/SDL_platform.h 2025-01-01 17:47:53.000000000 +0200 --- SDL2-2.32.8/include/SDL_platform.h 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/include/SDL_platform.h 2025-11-22 00:45:00.924303894 +0200 +++ SDL2-2.32.8-banan_os/include/SDL_platform.h 2026-01-03 19:50:45.778818009 +0200
@@ -36,6 +36,10 @@ @@ -36,6 +36,10 @@
#undef __HAIKU__ #undef __HAIKU__
#define __HAIKU__ 1 #define __HAIKU__ 1
@ -124,7 +139,7 @@ diff -ruN SDL2-2.32.8/include/SDL_platform.h SDL2-2.32.8-banan_os/include/SDL_pl
#define __BSDI__ 1 #define __BSDI__ 1
diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.cpp SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.cpp diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.cpp SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.cpp
--- SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.cpp 1970-01-01 02:00:00.000000000 +0200 --- SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.cpp 2025-11-22 00:45:00.924702550 +0200 +++ SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.cpp 2026-01-03 19:50:45.779146754 +0200
@@ -0,0 +1,150 @@ @@ -0,0 +1,150 @@
+/* +/*
+ Simple DirectMedia Layer + Simple DirectMedia Layer
@ -278,7 +293,7 @@ diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.cpp SDL2-2.32.8-bana
+#endif +#endif
diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.h SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.h diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.h SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.h
--- SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.h 1970-01-01 02:00:00.000000000 +0200 --- SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.h 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.h 2025-11-22 00:45:00.924820303 +0200 +++ SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.h 2026-01-03 19:50:45.779253053 +0200
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
+/* +/*
+ Simple DirectMedia Layer + Simple DirectMedia Layer
@ -316,7 +331,7 @@ diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.h SDL2-2.32.8-banan_
+}; +};
diff -ruN SDL2-2.32.8/src/audio/SDL_audio.c SDL2-2.32.8-banan_os/src/audio/SDL_audio.c diff -ruN SDL2-2.32.8/src/audio/SDL_audio.c SDL2-2.32.8-banan_os/src/audio/SDL_audio.c
--- SDL2-2.32.8/src/audio/SDL_audio.c 2025-01-01 17:47:53.000000000 +0200 --- SDL2-2.32.8/src/audio/SDL_audio.c 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/audio/SDL_audio.c 2025-11-22 00:45:00.925178591 +0200 +++ SDL2-2.32.8-banan_os/src/audio/SDL_audio.c 2026-01-03 19:50:45.779381073 +0200
@@ -87,6 +87,9 @@ @@ -87,6 +87,9 @@
#ifdef SDL_AUDIO_DRIVER_HAIKU #ifdef SDL_AUDIO_DRIVER_HAIKU
&HAIKUAUDIO_bootstrap, &HAIKUAUDIO_bootstrap,
@ -329,7 +344,7 @@ diff -ruN SDL2-2.32.8/src/audio/SDL_audio.c SDL2-2.32.8-banan_os/src/audio/SDL_a
#endif #endif
diff -ruN SDL2-2.32.8/src/audio/SDL_sysaudio.h SDL2-2.32.8-banan_os/src/audio/SDL_sysaudio.h diff -ruN SDL2-2.32.8/src/audio/SDL_sysaudio.h SDL2-2.32.8-banan_os/src/audio/SDL_sysaudio.h
--- SDL2-2.32.8/src/audio/SDL_sysaudio.h 2025-01-01 17:47:53.000000000 +0200 --- SDL2-2.32.8/src/audio/SDL_sysaudio.h 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/audio/SDL_sysaudio.h 2025-11-22 00:45:00.925759535 +0200 +++ SDL2-2.32.8-banan_os/src/audio/SDL_sysaudio.h 2026-01-03 19:50:45.779975705 +0200
@@ -196,6 +196,7 @@ @@ -196,6 +196,7 @@
extern AudioBootStrap WINMM_bootstrap; extern AudioBootStrap WINMM_bootstrap;
extern AudioBootStrap PAUDIO_bootstrap; extern AudioBootStrap PAUDIO_bootstrap;
@ -338,9 +353,345 @@ diff -ruN SDL2-2.32.8/src/audio/SDL_sysaudio.h SDL2-2.32.8-banan_os/src/audio/SD
extern AudioBootStrap COREAUDIO_bootstrap; extern AudioBootStrap COREAUDIO_bootstrap;
extern AudioBootStrap DISKAUDIO_bootstrap; extern AudioBootStrap DISKAUDIO_bootstrap;
extern AudioBootStrap DUMMYAUDIO_bootstrap; extern AudioBootStrap DUMMYAUDIO_bootstrap;
diff -ruN SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp SDL2-2.32.8-banan_os/src/joystick/banan_os/SDL_banan_os_joystick.cpp
--- SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/joystick/banan_os/SDL_banan_os_joystick.cpp 2026-01-03 19:51:21.437329388 +0200
@@ -0,0 +1,308 @@
+/*
+Simple DirectMedia Layer
+Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_BANANOS
+
+#include <BAN/Math.h>
+
+#include <LibInput/Joystick.h>
+
+extern "C"
+{
+
+#include "SDL_joystick.h"
+#include "../SDL_sysjoystick.h"
+#include "../SDL_joystick_c.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#define MAX_JOYSTICKS 16
+
+struct joystick_hwdata
+{
+ int fd;
+};
+
+static int s_joystick_count = 0;
+static char* s_joystick_path[MAX_JOYSTICKS];
+
+static void BANANOS_JoystickDetect(void);
+
+static int BANANOS_JoystickInit(void)
+{
+ s_joystick_count = 0;
+
+ for (int i = 0; i < MAX_JOYSTICKS; i++)
+ {
+ char path[PATH_MAX];
+ sprintf(path, "/dev/joystick%d", i);
+
+ struct stat st;
+ if (stat(path, &st) == -1)
+ continue;
+
+ s_joystick_path[i] = SDL_strdup(path);
+ if (s_joystick_path[i] == NULL)
+ continue;
+ s_joystick_count++;
+ }
+
+ return 0;
+}
+
+static int BANANOS_JoystickGetCount(void)
+{
+ return s_joystick_count;
+}
+
+static void BANANOS_JoystickDetect(void)
+{
+}
+
+static const char* BANANOS_JoystickGetDeviceName(int device_index)
+{
+ return s_joystick_path[device_index] + strlen("/dev/");
+}
+
+static const char* BANANOS_JoystickGetDevicePath(int device_index)
+{
+ return s_joystick_path[device_index];
+}
+
+static int BANANOS_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
+{
+ (void)device_index;
+ return -1;
+}
+
+static int BANANOS_JoystickGetDevicePlayerIndex(int device_index)
+{
+ (void)device_index;
+ return -1;
+}
+
+static void BANANOS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
+{
+ (void)device_index;
+ (void)player_index;
+}
+
+static SDL_JoystickID BANANOS_JoystickGetDeviceInstanceID(int device_index)
+{
+ return device_index;
+}
+
+static void BANANOS_JoystickClose(SDL_Joystick* joystick)
+{
+ if (joystick->hwdata == NULL)
+ return;
+
+ if (joystick->hwdata->fd != -1)
+ close(joystick->hwdata->fd);
+ SDL_free(joystick->hwdata);
+}
+
+static int BANANOS_JoystickOpen(SDL_Joystick* joystick, int device_index)
+{
+ joystick->instance_id = device_index;
+ joystick->hwdata = static_cast<joystick_hwdata*>(SDL_calloc(1, sizeof(joystick_hwdata)));
+ if (joystick->hwdata == NULL)
+ return SDL_OutOfMemory();
+
+ joystick->hwdata->fd = open(s_joystick_path[device_index], O_RDONLY);
+ if (joystick->hwdata->fd == -1)
+ {
+ BANANOS_JoystickClose(joystick);
+ return SDL_SetError("Unable to open joystick");
+ }
+
+ joystick->nbuttons = sizeof(LibInput::JoystickState::buttons) / sizeof(*LibInput::JoystickState::buttons);
+ joystick->naxes = sizeof(LibInput::JoystickState::axis) / sizeof(*LibInput::JoystickState::axis);
+ joystick->nhats = 0;
+
+ return 0;
+}
+
+static void BANANOS_JoystickUpdate(SDL_Joystick *joystick)
+{
+ LibInput::JoystickState state;
+ if (read(joystick->hwdata->fd, &state, sizeof(state)) < static_cast<ssize_t>(sizeof(state)))
+ return;
+
+ const auto map_joystick_value =
+ [](const LibInput::JoystickState::Axis& axis) -> Sint16
+ {
+ if (axis.min == axis.max)
+ return 0;
+ const float mapped = (axis.value - axis.min) * 65534.0f / (axis.max - axis.min) - 32767.0f;
+ return BAN::Math::clamp<Sint32>(mapped, -32767, 32767);
+ };
+
+ for (int i = 0; i < joystick->naxes; i++)
+ SDL_PrivateJoystickAxis(joystick, i, map_joystick_value(state.axis[i]));
+
+ for (int i = 0; i < joystick->nbuttons; i++)
+ SDL_PrivateJoystickButton(joystick, i, state.buttons[i]);
+}
+
+static void BANANOS_JoystickQuit(void)
+{
+ for (int i = 0; i < s_joystick_count; i++)
+ SDL_free(s_joystick_path[i]);
+}
+
+static SDL_JoystickGUID BANANOS_JoystickGetDeviceGUID(int device_index)
+{
+ return SDL_CreateJoystickGUIDForName(BANANOS_JoystickGetDeviceName(device_index));
+}
+
+static int BANANOS_JoystickRumble(SDL_Joystick* joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
+{
+ (void)joystick;
+ (void)low_frequency_rumble;
+ (void)high_frequency_rumble;
+ return SDL_Unsupported();
+}
+
+static int BANANOS_JoystickRumbleTriggers(SDL_Joystick* joystick, Uint16 left_rumble, Uint16 right_rumble)
+{
+ (void)joystick;
+ (void)left_rumble;
+ (void)right_rumble;
+ return SDL_Unsupported();
+}
+
+static SDL_bool BANANOS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping* out)
+{
+ (void)device_index;
+
+ // FIXME: this is hardcoded to a PS3 controller layout :D
+
+ memset(out, 0, sizeof(SDL_GamepadMapping));
+
+#define BANANOS_MAP_BUTTON(name, button) \
+ out->name.kind = EMappingKind_Button; \
+ out->name.target = button
+
+#define BANANOS_MAP_AXIS(name, axis) \
+ out->name.kind = EMappingKind_Axis; \
+ out->name.target = axis
+
+ BANANOS_MAP_BUTTON(a, 14);
+ BANANOS_MAP_BUTTON(b, 13);
+ BANANOS_MAP_BUTTON(x, 15);
+ BANANOS_MAP_BUTTON(y, 12);
+
+ BANANOS_MAP_BUTTON(back, 0);
+ BANANOS_MAP_BUTTON(guide, 16);
+ BANANOS_MAP_BUTTON(start, 3);
+
+ BANANOS_MAP_BUTTON(leftstick, 1);
+ BANANOS_MAP_BUTTON(rightstick, 2);
+
+ BANANOS_MAP_BUTTON(leftshoulder, 10);
+ BANANOS_MAP_BUTTON(rightshoulder, 11);
+
+ BANANOS_MAP_BUTTON(dpup, 4);
+ BANANOS_MAP_BUTTON(dpdown, 6);
+ BANANOS_MAP_BUTTON(dpleft, 7);
+ BANANOS_MAP_BUTTON(dpright, 5);
+
+ BANANOS_MAP_AXIS(leftx, 0);
+ BANANOS_MAP_AXIS(lefty, 1);
+ BANANOS_MAP_AXIS(rightx, 2);
+ BANANOS_MAP_AXIS(righty, 3);
+
+ // TODO: these should probably be axis
+ BANANOS_MAP_BUTTON(lefttrigger, 8);
+ BANANOS_MAP_BUTTON(righttrigger, 9);
+
+#undef BANANOS_MAP_BUTTON
+#undef BANANOS_MAP_AXIS
+
+ return SDL_TRUE;
+}
+
+static Uint32 BANANOS_JoystickGetCapabilities(SDL_Joystick* joystick)
+{
+ (void)joystick;
+ return 0;
+}
+
+static int BANANOS_JoystickSetLED(SDL_Joystick* joystick, Uint8 red, Uint8 green, Uint8 blue)
+{
+ (void)joystick;
+ (void)red;
+ (void)green;
+ (void)blue;
+ return SDL_Unsupported();
+}
+
+static int BANANOS_JoystickSendEffect(SDL_Joystick* joystick, const void* data, int size)
+{
+ (void)joystick;
+ (void)data;
+ (void)size;
+ return SDL_Unsupported();
+}
+
+static int BANANOS_JoystickSetSensorsEnabled(SDL_Joystick* joystick, SDL_bool enabled)
+{
+ (void)joystick;
+ (void)enabled;
+ return SDL_Unsupported();
+}
+
+SDL_JoystickDriver SDL_BANANOS_JoystickDriver =
+{
+ BANANOS_JoystickInit,
+ BANANOS_JoystickGetCount,
+ BANANOS_JoystickDetect,
+ BANANOS_JoystickGetDeviceName,
+ BANANOS_JoystickGetDevicePath,
+ BANANOS_JoystickGetDeviceSteamVirtualGamepadSlot,
+ BANANOS_JoystickGetDevicePlayerIndex,
+ BANANOS_JoystickSetDevicePlayerIndex,
+ BANANOS_JoystickGetDeviceGUID,
+ BANANOS_JoystickGetDeviceInstanceID,
+ BANANOS_JoystickOpen,
+ BANANOS_JoystickRumble,
+ BANANOS_JoystickRumbleTriggers,
+ BANANOS_JoystickGetCapabilities,
+ BANANOS_JoystickSetLED,
+ BANANOS_JoystickSendEffect,
+ BANANOS_JoystickSetSensorsEnabled,
+ BANANOS_JoystickUpdate,
+ BANANOS_JoystickClose,
+ BANANOS_JoystickQuit,
+ BANANOS_JoystickGetGamepadMapping
+};
+
+} // extern "C"
+
+#endif /* SDL_JOYSTICK_BANANOS */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/joystick/SDL_joystick.c SDL2-2.32.8-banan_os/src/joystick/SDL_joystick.c
--- SDL2-2.32.8/src/joystick/SDL_joystick.c 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/joystick/SDL_joystick.c 2026-01-03 19:52:38.740921854 +0200
@@ -85,6 +85,9 @@
#ifdef SDL_JOYSTICK_HAIKU
&SDL_HAIKU_JoystickDriver,
#endif
+#ifdef SDL_JOYSTICK_BANANOS
+ &SDL_BANANOS_JoystickDriver,
+#endif
#ifdef SDL_JOYSTICK_USBHID /* !!! FIXME: "USBHID" is a generic name, and doubly-confusing with HIDAPI next to it. This is the *BSD interface, rename this. */
&SDL_BSD_JoystickDriver,
#endif
diff -ruN SDL2-2.32.8/src/joystick/SDL_sysjoystick.h SDL2-2.32.8-banan_os/src/joystick/SDL_sysjoystick.h
--- SDL2-2.32.8/src/joystick/SDL_sysjoystick.h 2025-01-01 17:47:53.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/joystick/SDL_sysjoystick.h 2026-01-03 19:52:50.906765671 +0200
@@ -235,6 +235,7 @@
/* The available joystick drivers */
extern SDL_JoystickDriver SDL_ANDROID_JoystickDriver;
+extern SDL_JoystickDriver SDL_BANANOS_JoystickDriver;
extern SDL_JoystickDriver SDL_BSD_JoystickDriver;
extern SDL_JoystickDriver SDL_DARWIN_JoystickDriver;
extern SDL_JoystickDriver SDL_DUMMY_JoystickDriver;
diff -ruN SDL2-2.32.8/src/misc/banan_os/SDL_sysurl.cpp SDL2-2.32.8-banan_os/src/misc/banan_os/SDL_sysurl.cpp diff -ruN SDL2-2.32.8/src/misc/banan_os/SDL_sysurl.cpp SDL2-2.32.8-banan_os/src/misc/banan_os/SDL_sysurl.cpp
--- SDL2-2.32.8/src/misc/banan_os/SDL_sysurl.cpp 1970-01-01 02:00:00.000000000 +0200 --- SDL2-2.32.8/src/misc/banan_os/SDL_sysurl.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/misc/banan_os/SDL_sysurl.cpp 2025-11-22 00:45:00.926117334 +0200 +++ SDL2-2.32.8-banan_os/src/misc/banan_os/SDL_sysurl.cpp 2026-01-03 19:50:45.780251300 +0200
@@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
+/* +/*
+ Simple DirectMedia Layer + Simple DirectMedia Layer
@ -374,7 +725,7 @@ diff -ruN SDL2-2.32.8/src/misc/banan_os/SDL_sysurl.cpp SDL2-2.32.8-banan_os/src/
+ +
diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.cpp diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.cpp
--- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.cpp 1970-01-01 02:00:00.000000000 +0200 --- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.cpp 2025-11-22 01:10:01.840984523 +0200 +++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.cpp 2026-01-03 19:50:45.780447206 +0200
@@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
+/* +/*
+ Simple DirectMedia Layer + Simple DirectMedia Layer
@ -429,7 +780,7 @@ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.cpp SDL2-2.32.8-
+/* vi: set ts=4 sw=4 expandtab: */ +/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.h SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.h diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.h SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.h
--- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.h 1970-01-01 02:00:00.000000000 +0200 --- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.h 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.h 2025-11-22 01:10:16.932880273 +0200 +++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.h 2026-01-03 19:50:45.780567334 +0200
@@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
+/* +/*
+ Simple DirectMedia Layer + Simple DirectMedia Layer
@ -476,7 +827,7 @@ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.h SDL2-2.32.8-ba
+/* vi: set ts=4 sw=4 expandtab: */ +/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp
--- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.cpp 1970-01-01 02:00:00.000000000 +0200 --- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp 2025-11-22 00:45:00.926337964 +0200 +++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp 2026-01-03 19:50:45.780690325 +0200
@@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
+/* +/*
+ Simple DirectMedia Layer + Simple DirectMedia Layer
@ -540,7 +891,7 @@ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.cpp SDL2-2.32.
+/* vi: set ts=4 sw=4 expandtab: */ +/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.h SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.h diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.h SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.h
--- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.h 1970-01-01 02:00:00.000000000 +0200 --- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.h 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.h 2025-11-22 00:45:00.926434625 +0200 +++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.h 2026-01-03 19:50:45.780815551 +0200
@@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
+/* +/*
+ Simple DirectMedia Layer + Simple DirectMedia Layer
@ -589,7 +940,7 @@ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.h SDL2-2.32.8-
+/* vi: set ts=4 sw=4 expandtab: */ +/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_video.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_video.cpp diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_video.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_video.cpp
--- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_video.cpp 1970-01-01 02:00:00.000000000 +0200 --- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_video.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_video.cpp 2025-11-22 01:08:26.204647073 +0200 +++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_video.cpp 2026-01-03 19:50:45.780934771 +0200
@@ -0,0 +1,729 @@ @@ -0,0 +1,729 @@
+/* +/*
+ Simple DirectMedia Layer + Simple DirectMedia Layer
@ -1322,7 +1673,7 @@ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_video.cpp SDL2-2.32.8-bana
+/* vi: set ts=4 sw=4 expandtab: */ +/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL2-2.32.8/src/video/SDL_sysvideo.h SDL2-2.32.8-banan_os/src/video/SDL_sysvideo.h diff -ruN SDL2-2.32.8/src/video/SDL_sysvideo.h SDL2-2.32.8-banan_os/src/video/SDL_sysvideo.h
--- SDL2-2.32.8/src/video/SDL_sysvideo.h 2025-05-20 00:24:41.000000000 +0300 --- SDL2-2.32.8/src/video/SDL_sysvideo.h 2025-05-20 00:24:41.000000000 +0300
+++ SDL2-2.32.8-banan_os/src/video/SDL_sysvideo.h 2025-11-22 00:45:00.927152737 +0200 +++ SDL2-2.32.8-banan_os/src/video/SDL_sysvideo.h 2026-01-03 19:50:45.781358361 +0200
@@ -462,6 +462,7 @@ @@ -462,6 +462,7 @@
extern VideoBootStrap WINDOWS_bootstrap; extern VideoBootStrap WINDOWS_bootstrap;
extern VideoBootStrap WINRT_bootstrap; extern VideoBootStrap WINRT_bootstrap;
@ -1333,7 +1684,7 @@ diff -ruN SDL2-2.32.8/src/video/SDL_sysvideo.h SDL2-2.32.8-banan_os/src/video/SD
extern VideoBootStrap Android_bootstrap; extern VideoBootStrap Android_bootstrap;
diff -ruN SDL2-2.32.8/src/video/SDL_video.c SDL2-2.32.8-banan_os/src/video/SDL_video.c diff -ruN SDL2-2.32.8/src/video/SDL_video.c SDL2-2.32.8-banan_os/src/video/SDL_video.c
--- SDL2-2.32.8/src/video/SDL_video.c 2025-05-20 00:24:41.000000000 +0300 --- SDL2-2.32.8/src/video/SDL_video.c 2025-05-20 00:24:41.000000000 +0300
+++ SDL2-2.32.8-banan_os/src/video/SDL_video.c 2025-11-22 00:45:00.928264617 +0200 +++ SDL2-2.32.8-banan_os/src/video/SDL_video.c 2026-01-03 19:50:45.782037083 +0200
@@ -96,6 +96,9 @@ @@ -96,6 +96,9 @@
#ifdef SDL_VIDEO_DRIVER_HAIKU #ifdef SDL_VIDEO_DRIVER_HAIKU
&HAIKU_bootstrap, &HAIKU_bootstrap,

View File

@ -1,8 +1,8 @@
#!/bin/bash ../install.sh #!/bin/bash ../install.sh
NAME='ca-certificates' NAME='ca-certificates'
VERSION='2024-07-02' VERSION='2025-12-02'
DOWNLOAD_URL="https://curl.se/ca/cacert-$VERSION.pem#1bf458412568e134a4514f5e170a328d11091e071c7110955c9884ed87972ac9" DOWNLOAD_URL="https://curl.se/ca/cacert-$VERSION.pem#f1407d974c5ed87d544bd931a278232e13925177e239fca370619aba63c757b4"
configure() { configure() {
: :

View File

@ -1,8 +1,8 @@
#!/bin/bash ../install.sh #!/bin/bash ../install.sh
NAME='curl' NAME='curl'
VERSION='8.11.1' VERSION='8.17.0'
DOWNLOAD_URL="https://curl.se/download/curl-$VERSION.tar.gz#a889ac9dbba3644271bd9d1302b5c22a088893719b72be3487bc3d401e5c4e80" DOWNLOAD_URL="https://curl.se/download/curl-$VERSION.tar.xz#955f6e729ad6b3566260e8fef68620e76ba3c31acf0a18524416a185acf77992"
DEPENDENCIES=('ca-certificates' 'openssl' 'zlib' 'zstd') DEPENDENCIES=('ca-certificates' 'openssl' 'zlib' 'zstd')
CONFIG_SUB=('config.sub') CONFIG_SUB=('config.sub')
CONFIGURE_OPTIONS=( CONFIGURE_OPTIONS=(
@ -10,6 +10,7 @@ CONFIGURE_OPTIONS=(
'--disable-ipv6' '--disable-ipv6'
'--disable-docs' '--disable-docs'
'--disable-ntlm' '--disable-ntlm'
'--disable-static'
'--enable-optimize' '--enable-optimize'
'--with-openssl' '--with-openssl'
'--with-zlib' '--with-zlib'

View File

@ -1,7 +1,18 @@
diff -ruN curl-8.11.1/configure curl-8.11.1-banan_os/configure diff -ruN curl-8.17.0/configure curl-8.17.0-banan_os/configure
--- curl-8.11.1/configure 2024-12-11 09:08:12.000000000 +0200 --- curl-8.17.0/configure 2025-11-05 09:00:46.000000000 +0200
+++ curl-8.11.1-banan_os/configure 2025-01-22 18:29:34.167111729 +0200 +++ curl-8.17.0-banan_os/configure 2025-12-31 18:52:05.140006351 +0200
@@ -15537,6 +15537,16 @@ @@ -9487,6 +9487,10 @@
lt_cv_deplibs_check_method=pass_all
;;
+banan_os*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
beos*)
lt_cv_deplibs_check_method=pass_all
;;
@@ -15245,6 +15249,16 @@
esac esac
;; ;;

View File

@ -1,8 +1,8 @@
#!/bin/bash ../install.sh #!/bin/bash ../install.sh
NAME='git' NAME='git'
VERSION='2.48.1' VERSION='2.52.0'
DOWNLOAD_URL="https://www.kernel.org/pub/software/scm/git/git-$VERSION.tar.gz#51b4d03b1e311ba673591210f94f24a4c5781453e1eb188822e3d9cdc04c2212" DOWNLOAD_URL="https://www.kernel.org/pub/software/scm/git/git-$VERSION.tar.xz#3cd8fee86f69a949cb610fee8cd9264e6873d07fa58411f6060b3d62729ed7c5"
DEPENDENCIES=('zlib' 'openssl' 'curl') DEPENDENCIES=('zlib' 'openssl' 'curl')
CONFIGURE_OPTIONS=( CONFIGURE_OPTIONS=(
'--with-curl' '--with-curl'
@ -11,7 +11,3 @@ CONFIGURE_OPTIONS=(
ac_cv_snprintf_returns_bogus=no ac_cv_snprintf_returns_bogus=no
ac_cv_lib_curl_curl_global_init=yes ac_cv_lib_curl_curl_global_init=yes
) )
build() {
make -j$(nproc) CURL_LDFLAGS='-lcurl -lssl -lcrypto -lz -lzstd' || exit 1
}

View File

@ -19,6 +19,12 @@ int sem_init(sem_t* sem, int pshared, unsigned value)
return 0; return 0;
} }
int sem_getvalue(sem_t* __restrict sem, int* __restrict sval)
{
*sval = BAN::atomic_load(sem->value);
return 0;
}
int sem_post(sem_t* sem) int sem_post(sem_t* sem)
{ {
const auto old = BAN::atomic_fetch_add(sem->value, 1); const auto old = BAN::atomic_fetch_add(sem->value, 1);

View File

@ -0,0 +1,28 @@
#pragma once
#include <stdint.h>
namespace LibInput
{
// TODO: not used but here if we ever make controller
// support generating events instead of being polled
struct JoystickEvent
{
};
struct JoystickState
{
struct Axis
{
int64_t value;
int64_t min;
int64_t max;
};
Axis axis[4];
bool buttons[32];
};
}

View File

@ -632,9 +632,9 @@ Rectangle Terminal::handle_csi(char ch)
return {}; return {};
} }
if (ch == '?') if (ch == '?' || ch == '>' || ch == '=')
{ {
m_csi_info.question = true; m_csi_info.param = ch;
return {}; return {};
} }
@ -651,6 +651,10 @@ Rectangle Terminal::handle_csi(char ch)
Rectangle should_invalidate; Rectangle should_invalidate;
auto& texture = m_window->texture(); auto& texture = m_window->texture();
switch (m_csi_info.param)
{
case 0:
switch (ch) switch (ch)
{ {
case 'A': case 'A':
@ -673,6 +677,18 @@ Rectangle Terminal::handle_csi(char ch)
m_csi_info.fields[0] = 1; m_csi_info.fields[0] = 1;
m_cursor.x = BAN::Math::max<int32_t>(m_cursor.x - m_csi_info.fields[0], 0); m_cursor.x = BAN::Math::max<int32_t>(m_cursor.x - m_csi_info.fields[0], 0);
break; break;
case 'E':
if (m_csi_info.fields[0] == -1)
m_csi_info.fields[0] = 1;
m_cursor.y = BAN::Math::min<int32_t>(m_cursor.y + m_csi_info.fields[0], rows() - 1);
m_cursor.x = 0;
break;
case 'F':
if (m_csi_info.fields[0] == -1)
m_csi_info.fields[0] = 1;
m_cursor.y = BAN::Math::max<int32_t>(m_cursor.y - m_csi_info.fields[0], 0);
m_cursor.x = 0;
break;
case 'G': case 'G':
m_cursor.x = BAN::Math::clamp<int32_t>(m_csi_info.fields[0], 1, cols()) - 1; m_cursor.x = BAN::Math::clamp<int32_t>(m_csi_info.fields[0], 1, cols()) - 1;
break; break;
@ -907,6 +923,9 @@ Rectangle Terminal::handle_csi(char ch)
for (int32_t i = 0; i < m_csi_info.fields[0]; i++) for (int32_t i = 0; i < m_csi_info.fields[0]; i++)
should_invalidate = should_invalidate.get_bounding_box(putcodepoint(m_last_graphic_char)); should_invalidate = should_invalidate.get_bounding_box(putcodepoint(m_last_graphic_char));
break; break;
case 'c':
write(m_shell_info.pts_master, "\e[?1;0c", 7);
break;
case 'd': case 'd':
m_cursor.y = BAN::Math::clamp<int32_t>(m_csi_info.fields[0], 1, rows()) - 1; m_cursor.y = BAN::Math::clamp<int32_t>(m_csi_info.fields[0], 1, rows()) - 1;
break; break;
@ -915,7 +934,7 @@ Rectangle Terminal::handle_csi(char ch)
{ {
if (m_csi_info.fields[1] != 5 && m_csi_info.fields[1] != 2) if (m_csi_info.fields[1] != 5 && m_csi_info.fields[1] != 2)
{ {
dprintln("unsupported ANSI SGR {}", m_csi_info.fields[1]); dprintln("TODO: SGR {}", m_csi_info.fields[1]);
break; break;
} }
const auto color = (m_csi_info.fields[1] == 5) const auto color = (m_csi_info.fields[1] == 5)
@ -934,10 +953,26 @@ Rectangle Terminal::handle_csi(char ch)
case 'u': case 'u':
m_cursor = m_saved_cursor; m_cursor = m_saved_cursor;
break; break;
case 'n':
if (m_csi_info.fields[0] != 6)
{
dprintln("TODO: CSI {} n", m_csi_info.fields[0]);
break;
}
char buffer[2 + 10 + 1 + 10 + 2];
sprintf(buffer, "\e[%u;%uR", m_cursor.y + 1, m_cursor.x + 1);
write(m_shell_info.pts_master, buffer, strlen(buffer));
break;
default:
dprintln("TODO: CSI {}", ch);
break;
}
break;
case '?':
switch (ch)
{
case 'h': case 'h':
case 'l': case 'l':
if (m_csi_info.question)
{
switch (m_csi_info.fields[0]) switch (m_csi_info.fields[0])
{ {
case 25: case 25:
@ -947,28 +982,36 @@ Rectangle Terminal::handle_csi(char ch)
m_brackted_paste_mode = (ch == 'h'); m_brackted_paste_mode = (ch == 'h');
break; break;
default: default:
dwarnln("unsupported ANSI CSI ? {} {}", m_csi_info.fields[0], ch); dprintln("TODO: CSI ? {} {}", m_csi_info.fields[0], ch);
break; break;
} }
}
else
{
dwarnln("unsupported ANSI CSI {} {}", m_csi_info.fields[0], ch);
break;
}
break;
case 'n':
if (m_csi_info.fields[0] != 6)
{
dprintln("unsupported ANSI CSI n");
break;
}
char buffer[2 + 10 + 1 + 10 + 2];
sprintf(buffer, "\e[%u;%uR", m_cursor.y + 1, m_cursor.x + 1);
write(m_shell_info.pts_master, buffer, strlen(buffer));
break; break;
default: default:
dprintln("TODO: CSI {}", ch); dprintln("TODO: CSI ? {}", ch);
break;
}
break;
case '>':
switch (ch)
{
case 'c':
write(m_shell_info.pts_master, "\e[>0;10;1c", 10);
break;
default:
dprintln("TODO: CSI > {}", ch);
break;
}
break;
case '=':
switch (ch)
{
case 'c':
write(m_shell_info.pts_master, "\eP!|00000000\e\\", 14);
break;
default:
dprintln("TODO: CSI = {}", ch);
break;
}
break; break;
} }
@ -1131,7 +1174,7 @@ Rectangle Terminal::putchar(uint8_t ch)
m_csi_info = { m_csi_info = {
.fields = { -1, -1, -1, -1, -1 }, .fields = { -1, -1, -1, -1, -1 },
.index = 0, .index = 0,
.question = false, .param = '\0',
}; };
return {}; return {};
} }

View File

@ -79,7 +79,7 @@ private:
static constexpr size_t max_fields = 5; static constexpr size_t max_fields = 5;
int32_t fields[max_fields]; int32_t fields[max_fields];
size_t index; size_t index;
bool question; char param;
}; };
struct Cell struct Cell

View File

@ -1,20 +1,50 @@
#include <BAN/String.h> #include <BAN/String.h>
#include <dirent.h> #include <dirent.h>
#include <getopt.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
bool delete_recursive(const char* path) bool prompt_removal(const char* path, struct stat st)
{
const char* type = "file";
if (S_ISREG(st.st_mode))
type = st.st_size ? "regular file" : "regular empty file";
else if (S_ISDIR(st.st_mode))
type = "directory";
else if (S_ISCHR(st.st_mode))
type = "character special file";
else if (S_ISBLK(st.st_mode))
type = "block special file";
else if (S_ISLNK(st.st_mode))
type = "symbolic link";
else if (S_ISFIFO(st.st_mode))
type = "fifo";
fprintf(stderr, "remove %s '%s'? ", type, path);
char buffer[128];
if (fgets(buffer, sizeof(buffer), stdin) == nullptr)
return false;
return buffer[0] == 'Y' || buffer[0] == 'y';
}
bool delete_recursive(const char* path, bool force, bool interactive)
{ {
struct stat st; struct stat st;
if (stat(path, &st) == -1) if (stat(path, &st) == -1)
{ {
if (force && errno == ENOENT)
return true;
perror(path); perror(path);
return false; return false;
} }
if (interactive && !prompt_removal(path, st))
return true;
bool ret = true; bool ret = true;
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
@ -32,11 +62,13 @@ bool delete_recursive(const char* path)
if (dirent->d_type == DT_DIR) if (dirent->d_type == DT_DIR)
{ {
if (!delete_recursive(dirent_path.data())) if (!delete_recursive(dirent_path.data(), force, interactive))
ret = false; ret = false;
} }
else else
{ {
if (interactive && !prompt_removal(dirent_path.data(), st))
continue;
if (unlink(dirent_path.data()) == -1) if (unlink(dirent_path.data()) == -1)
{ {
perror(dirent_path.data()); perror(dirent_path.data());
@ -57,65 +89,87 @@ bool delete_recursive(const char* path)
return ret; return ret;
} }
void usage(const char* argv0, int ret)
{
FILE* out = (ret == 0) ? stdout : stderr;
fprintf(out, "usage: %s [OPTIONS]... FILE...\n", argv0);
fprintf(out, " remove each FILE\n");
fprintf(out, "OPTIONS:\n");
fprintf(out, " -r remove directories and their contents recursively\n");
fprintf(out, " -h, --help show this message and exit\n");
exit(ret);
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
bool force = false;
bool interactive = false;
bool recursive = false; bool recursive = false;
int i = 1; for (;;)
for (; i < argc; i++)
{ {
if (argv[i][0] != '-') static option long_options[] {
{ "recursive", no_argument, nullptr, 'r' },
{ "interactive", no_argument, nullptr, 'i' },
{ "force", no_argument, nullptr, 'f' },
{ "help", no_argument, nullptr, 'h' },
};
int ch = getopt_long(argc, argv, "rRifh", long_options, nullptr);
if (ch == -1)
break; break;
if (strcmp(argv[i], "-r") == 0) switch (ch)
recursive = true;
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
usage(argv[0], 0);
else
{ {
fprintf(stderr, "unrecognized argument %s. use --help for more information\n", argv[i]); case 'h':
fprintf(stderr, "usage: %s [OPTIONS]... FILE...\n", argv[0]);
fprintf(stderr, " remove each FILE\n");
fprintf(stderr, "OPTIONS:\n");
fprintf(stderr, " -r, -R, --recursive remove directories and their contents recursively\n");
fprintf(stderr, " -i, --interactive prompt removal for all files\n");
fprintf(stderr, " -f, --force ignore nonexistent files and never prompt\n");
fprintf(stderr, " -h, --help show this message and exit\n");
return 0;
case 'r': case 'R':
recursive = true;
break;
case 'f':
force = true;
interactive = false;
break;
case 'i':
force = false;
interactive = true;
break;
case '?':
fprintf(stderr, "invalid option %c\n", optopt);
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1; return 1;
} }
} }
if (i >= argc) if (optind >= argc && !force)
{ {
fprintf(stderr, "missing operand. use --help for more information\n"); fprintf(stderr, "missing operand. use --help for more information\n");
return 1; return 1;
} }
int ret = 0; int ret = 0;
for (; i < argc; i++) for (int i = optind; i < argc; i++)
{ {
if (recursive) if (recursive)
{ {
if (!delete_recursive(argv[i])) if (!delete_recursive(argv[i], force, interactive))
ret = 1; ret = 1;
continue;
} }
else
{
struct stat st; struct stat st;
if (stat(argv[i], &st) == -1) if (stat(argv[i], &st) == -1)
{ {
if (force && errno == ENOENT)
continue;
perror(argv[i]); perror(argv[i]);
ret = 1; ret = 1;
continue; continue;
} }
if (interactive && !prompt_removal(argv[i], st))
continue;
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
{ {
fprintf(stderr, "%s: %s\n", argv[i], strerror(EISDIR)); errno = EISDIR;
perror(argv[i]);
ret = 1; ret = 1;
continue; continue;
} }
@ -126,6 +180,6 @@ int main(int argc, char** argv)
ret = 1; ret = 1;
} }
} }
}
return ret; return ret;
} }

View File

@ -2,6 +2,7 @@ set(USERSPACE_TESTS
test-fork test-fork
test-framebuffer test-framebuffer
test-globals test-globals
test-joystick
test-mmap-shared test-mmap-shared
test-mouse test-mouse
test-popen test-popen

View File

@ -0,0 +1,10 @@
set(SOURCES
main.cpp
)
add_executable(test-joystick ${SOURCES})
banan_include_headers(test-joystick ban)
banan_include_headers(test-joystick libinput)
banan_link_library(test-joystick libc)
install(TARGETS test-joystick OPTIONAL)

View File

@ -0,0 +1,184 @@
#include <BAN/Math.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/framebuffer.h>
#include <sys/mman.h>
#include <termios.h>
#include <LibInput/Joystick.h>
framebuffer_info_t fb_info;
void* fb_mmap = nullptr;
int joystick_fd = -1;
termios original_termios {};
void draw_circle(int cx, int cy, int r, uint32_t color)
{
int min_x = BAN::Math::max<int>(cx - r, 0);
int max_x = BAN::Math::min<int>(cx + r + 1, fb_info.width);
int min_y = BAN::Math::max<int>(cy - r, 0);
int max_y = BAN::Math::min<int>(cy + r + 1, fb_info.height);
for (int y = min_y; y < max_y; y++)
{
for (int x = min_x; x < max_x; x++)
{
int dx = x - cx;
int dy = y - cy;
if (dx * dx + dy * dy > r * r)
continue;
static_cast<uint32_t*>(fb_mmap)[y * fb_info.width + x] = color;
}
}
}
void cleanup()
{
if (fb_mmap)
munmap(fb_mmap, fb_info.height * fb_info.width * (BANAN_FB_BPP / 8));
if (joystick_fd != -1)
close(joystick_fd);
if (original_termios.c_lflag & ECHO)
tcsetattr(STDIN_FILENO, TCSANOW, &original_termios);
}
int map_joystick(const LibInput::JoystickState::Axis& axis, float min, float max)
{
if (axis.min == axis.max)
return (min + max) / 2;
return (axis.value - axis.min) * (max - min) / (axis.max - axis.min) + min;
}
int main(int argc, char** argv)
{
const char* fb_path = "/dev/fb0";
const char* joystick_path = "/dev/joystick0";
if (argc == 1)
;
else if (argc == 3)
{
fb_path = argv[1];
joystick_path = argv[2];
}
else
{
fprintf(stderr, "usage: %s [FB_PATH JOYSTICK_PATH]", argv[0]);
return 1;
}
signal(SIGINT, [](int) { exit(0); });
if (atexit(cleanup) == -1)
{
perror("atexit");
return 1;
}
if (BANAN_FB_BPP != 32)
{
fprintf(stderr, "unsupported bpp\n");
return 1;
}
int fb_fd = open(fb_path, O_RDWR);
if (fb_fd == -1)
{
fprintf(stderr, "open: ");
perror(fb_path);
return 1;
}
if (pread(fb_fd, &fb_info, sizeof(fb_info), -1) == -1)
{
fprintf(stderr, "read: ");
perror(fb_path);
return 1;
}
size_t fb_bytes = fb_info.width * fb_info.height * (BANAN_FB_BPP / 8);
fb_mmap = mmap(nullptr, fb_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
close(fb_fd);
if (fb_mmap == MAP_FAILED)
{
fprintf(stderr, "mmap: ");
perror(fb_path);
return 1;
}
joystick_fd = open(joystick_path, O_RDONLY);
if (joystick_fd == -1)
{
fprintf(stderr, "open: ");
perror(joystick_path);
return 1;
}
if (tcgetattr(STDIN_FILENO, &original_termios) == -1)
{
perror("tcgetattr");
return 1;
}
termios termios = original_termios;
termios.c_lflag &= ~ECHO;
if (tcsetattr(STDIN_FILENO, TCSANOW, &termios) == -1)
{
perror("tcsetattr");
return 1;
}
uint32_t color = 0xFF0000;
int circle_x = fb_info.width / 2;
int circle_y = fb_info.height / 2;
int radius = 10;
// clear screen and render
memset(fb_mmap, 0x00, fb_bytes);
draw_circle(circle_x, circle_y, radius, color);
msync(fb_mmap, fb_bytes, MS_SYNC);
while (true)
{
LibInput::JoystickState state {};
if (read(joystick_fd, &state, sizeof(state)) == -1)
{
fprintf(stderr, "read: ");
perror(joystick_path);
return 1;
}
const int dx = map_joystick(state.axis[0], -50, 50);
const int dy = map_joystick(state.axis[1], -50, 50);
const int dr = map_joystick(state.axis[3], 5, -5);
draw_circle(circle_x, circle_y, radius, 0x000000);
if (BAN::Math::abs(dx) >= 10)
circle_x = BAN::Math::clamp<int>(circle_x + dx, 0, fb_info.width);
if (BAN::Math::abs(dy) >= 10)
circle_y = BAN::Math::clamp<int>(circle_y + dy, 0, fb_info.height);
radius = BAN::Math::clamp<int>(radius + dr, 1, 100);
if (state.buttons[12])
color = 0xFF0000;
if (state.buttons[13])
color = 0x00FF00;
if (state.buttons[14])
color = 0x0000FF;
if (state.buttons[15])
color = 0xFFFFFF;
draw_circle(circle_x, circle_y, radius, color);
msync(fb_mmap, fb_bytes, MS_SYNC);
usleep(16666);
msync(fb_mmap, fb_bytes, MS_SYNC);
}
}