Compare commits

...

19 Commits

Author SHA1 Message Date
Bananymous a5318448f5 userspace: Add audioctl utility to configure audio server 2026-01-06 22:26:11 +02:00
Bananymous b7c40eeb57 AudioServer: Handle multiple audio devices with multiple pins
This makes audio server configurable during runtime!
2026-01-06 22:26:11 +02:00
Bananymous e7c9be1875 AudioServer: Increase send buffer size and hardcode volume to 20% 2026-01-06 22:26:11 +02:00
Bananymous 8f1b314802 Kernel: Add ioctls to select audio device's output pin 2026-01-06 22:26:11 +02:00
Bananymous da6794c8ce Kernel: Implement HD audio driver
This is very basic and does not support a lot of stuff (like changing
the output pin :D)
2026-01-06 22:26:10 +02:00
Bananymous e926beba5a test-mouse: Fix mouse fd cleanup
I was creating a local variable shadowing the global one. This prevented
cleanup to close it. (this is not really necessary as the program dies
anyway)
2026-01-06 21:58:56 +02:00
Bananymous 3ad053cf6d image: Add option to change resize algoritm
Also benchmark timing is now done cpu clock, not monotonic one
2026-01-06 21:58:56 +02:00
Bananymous bc11469a0b mkdir: Fix mkdir error handling 2026-01-06 21:58:56 +02:00
Bananymous a00695bdac LibC: Add stubs that I have locally
I'm not sure if these are used by anything but I would assume so as I
have added them :D

functions added:
- getprotobyname
- open_memstream
- munlock
- lockf
- nice
- crypt
- getsid
- wcstoul
2026-01-06 21:58:56 +02:00
Bananymous 89959b800c LibC: Implement random and srandom 2026-01-06 21:58:56 +02:00
Bananymous 3e19c3b62e LibC: Fix timeradd and timersub overflow/underflow 2026-01-06 21:58:56 +02:00
Bananymous d970debb4d LibC: Define MAXPATHLEN in sys/param.h
This file is not POSIX, but some software seems to rely on it
2026-01-06 21:58:56 +02:00
Bananymous d0ba52073f LibC: Add stubs for shm_* functions 2026-01-06 21:58:56 +02:00
Bananymous 943e3b6f51 LibC: Define IN_CLASS* macros
These are not POSIX but some software seems to rely on them
2026-01-06 21:58:56 +02:00
Bananymous 25d43682aa LibC: Bump FOPEN_MAX 16->64 2026-01-06 21:58:56 +02:00
Bananymous ad16de59f8 Kernel: Implement basic F_{GET,SET}LK{,W}
At the moment these lock the whole file which is not what is supposed to
happen. Some port was trying to use these and this seems to work for
that. This may cause deadlocks but that should be easy enough to find
the reason because of the debug warnings
2026-01-06 21:58:56 +02:00
Bananymous 8634bbb792 Kernel: Don't save/restore sse or reschedule on spurious interrupts
I think these should be just fully ignored :D
2026-01-06 21:58:56 +02:00
Bananymous 60ec5d30fd init: Start Clipboard server on startup
I had forgotten to commit this when I added the clipboard server :D
2026-01-06 21:58:56 +02:00
Bananymous 7667fe6ca5 LibC: Implement sem_timedwait 2026-01-06 21:58:56 +02:00
42 changed files with 2015 additions and 107 deletions

View File

@ -9,6 +9,8 @@ set(KERNEL_SOURCES
kernel/APIC.cpp kernel/APIC.cpp
kernel/Audio/AC97/Controller.cpp kernel/Audio/AC97/Controller.cpp
kernel/Audio/Controller.cpp kernel/Audio/Controller.cpp
kernel/Audio/HDAudio/AudioFunctionGroup.cpp
kernel/Audio/HDAudio/Controller.cpp
kernel/BootInfo.cpp kernel/BootInfo.cpp
kernel/CPUID.cpp kernel/CPUID.cpp
kernel/Credentials.cpp kernel/Credentials.cpp

View File

@ -9,7 +9,7 @@ namespace Kernel
class AC97AudioController : public AudioController, public Interruptable class AC97AudioController : public AudioController, public Interruptable
{ {
public: public:
static BAN::ErrorOr<BAN::RefPtr<AC97AudioController>> create(PCI::Device& pci_device); static BAN::ErrorOr<void> create(PCI::Device& pci_device);
void handle_irq() override; void handle_irq() override;
@ -19,6 +19,10 @@ namespace Kernel
uint32_t get_channels() const override { return 2; } uint32_t get_channels() const override { return 2; }
uint32_t get_sample_rate() const override { return 48000; } uint32_t get_sample_rate() const override { return 48000; }
uint32_t get_total_pins() const override { return 1; }
uint32_t get_current_pin() const override { return 0; }
BAN::ErrorOr<void> set_current_pin(uint32_t pin) override { if (pin != 0) return BAN::Error::from_errno(EINVAL); return {}; }
private: private:
AC97AudioController(PCI::Device& pci_device) AC97AudioController(PCI::Device& pci_device)
: m_pci_device(pci_device) : m_pci_device(pci_device)

View File

@ -9,7 +9,7 @@ namespace Kernel
class AudioController : public CharacterDevice class AudioController : public CharacterDevice
{ {
public: public:
static BAN::ErrorOr<BAN::RefPtr<AudioController>> create(PCI::Device& pci_device); static BAN::ErrorOr<void> create(PCI::Device& pci_device);
dev_t rdev() const override { return m_rdev; } dev_t rdev() const override { return m_rdev; }
BAN::StringView name() const override { return m_name; } BAN::StringView name() const override { return m_name; }
@ -22,6 +22,10 @@ namespace Kernel
virtual uint32_t get_channels() const = 0; virtual uint32_t get_channels() const = 0;
virtual uint32_t get_sample_rate() const = 0; virtual uint32_t get_sample_rate() const = 0;
virtual uint32_t get_total_pins() const = 0;
virtual uint32_t get_current_pin() const = 0;
virtual BAN::ErrorOr<void> set_current_pin(uint32_t) = 0;
bool can_read_impl() const override { return false; } bool can_read_impl() const override { return false; }
bool can_write_impl() const override { SpinLockGuard _(m_spinlock); return m_sample_data_size < m_sample_data_capacity; } bool can_write_impl() const override { SpinLockGuard _(m_spinlock); return m_sample_data_size < m_sample_data_capacity; }
bool has_error_impl() const override { return false; } bool has_error_impl() const override { return false; }

View File

@ -0,0 +1,80 @@
#pragma once
#include <kernel/Audio/Controller.h>
#include <kernel/Audio/HDAudio/Controller.h>
namespace Kernel
{
class HDAudioController;
class HDAudioFunctionGroup : public AudioController
{
public:
static BAN::ErrorOr<BAN::RefPtr<HDAudioFunctionGroup>> create(BAN::RefPtr<HDAudioController>, uint8_t cid, HDAudio::AFGNode&&);
void on_stream_interrupt(uint8_t stream_index);
protected:
// FIXME: allow setting these :D
uint32_t get_channels() const override { return 2; }
uint32_t get_sample_rate() const override { return 48000; }
uint32_t get_total_pins() const override;
uint32_t get_current_pin() const override;
BAN::ErrorOr<void> set_current_pin(uint32_t) override;
void handle_new_data() override;
private:
HDAudioFunctionGroup(BAN::RefPtr<HDAudioController> controller, uint8_t cid, HDAudio::AFGNode&& afg_node)
: m_controller(controller)
, m_afg_node(BAN::move(afg_node))
, m_cid(cid)
{ }
~HDAudioFunctionGroup();
BAN::ErrorOr<void> initialize();
BAN::ErrorOr<void> initialize_stream();
BAN::ErrorOr<void> initialize_output();
BAN::ErrorOr<void> enable_output_path(uint8_t index);
BAN::ErrorOr<void> disable_output_path(uint8_t index);
void reset_stream();
BAN::ErrorOr<void> recurse_output_paths(const HDAudio::AFGWidget& widget, BAN::Vector<const HDAudio::AFGWidget*>& path);
uint16_t get_format_data() const;
uint16_t get_volume_data() const;
size_t bdl_offset() const;
void queue_bdl_data();
private:
static constexpr size_t m_max_path_length = 16;
// use 6 512 sample BDL entries
// each entry is ~10.7 ms at 48 kHz
// -> total buffered audio is 64 ms
static constexpr size_t m_bdl_entry_sample_frames = 512;
static constexpr size_t m_bdl_entry_count = 6;
BAN::RefPtr<HDAudioController> m_controller;
const HDAudio::AFGNode m_afg_node;
const uint8_t m_cid;
BAN::Vector<BAN::Vector<const HDAudio::AFGWidget*>> m_output_paths;
size_t m_output_path_index { SIZE_MAX };
uint8_t m_stream_id { 0xFF };
uint8_t m_stream_index { 0xFF };
BAN::UniqPtr<DMARegion> m_bdl_region;
size_t m_bdl_head { 0 };
size_t m_bdl_tail { 0 };
bool m_stream_running { false };
};
}

View File

@ -0,0 +1,77 @@
#pragma once
#include <kernel/Audio/Controller.h>
#include <kernel/Audio/HDAudio/Definitions.h>
#include <kernel/Memory/DMARegion.h>
namespace Kernel
{
class HDAudioController : public Interruptable, public BAN::RefCounted<HDAudioController>
{
public:
static BAN::ErrorOr<void> create(PCI::Device& pci_device);
BAN::ErrorOr<uint32_t> send_command(HDAudio::CORBEntry);
uint8_t get_stream_index(HDAudio::StreamType type, uint8_t index) const;
BAN::ErrorOr<uint8_t> allocate_stream_id();
void deallocate_stream_id(uint8_t id);
BAN::ErrorOr<uint8_t> allocate_stream(HDAudio::StreamType type, void* afg);
void deallocate_stream(uint8_t index);
PCI::BarRegion& bar0() { return *m_bar0; }
bool is_64bit() const { return m_is64bit; }
void handle_irq() override;
private:
HDAudioController(PCI::Device& pci_device)
: m_pci_device(pci_device)
{ }
BAN::ErrorOr<void> initialize();
BAN::ErrorOr<void> initialize_ring_buffers();
BAN::ErrorOr<void> reset_controller();
BAN::ErrorOr<HDAudio::Codec> initialize_codec(uint8_t codec);
BAN::ErrorOr<HDAudio::AFGNode> initialize_node(uint8_t codec, uint8_t node);
BAN::ErrorOr<HDAudio::AFGWidget> initialize_widget(uint8_t codec, uint8_t node);
private:
struct RingBuffer
{
vaddr_t vaddr;
uint32_t index;
uint32_t size;
};
private:
PCI::Device& m_pci_device;
BAN::UniqPtr<PCI::BarRegion> m_bar0;
bool m_is64bit { false };
bool m_use_immediate_command { false };
uint8_t m_output_streams { 0 };
uint8_t m_input_streams { 0 };
uint8_t m_bidir_streams { 0 };
void* m_allocated_streams[30] {};
// NOTE: stream ids are from 1 to 15
uint16_t m_allocated_stream_ids { 0 };
Mutex m_command_mutex;
SpinLock m_rb_lock;
ThreadBlocker m_rb_blocker;
RingBuffer m_corb;
RingBuffer m_rirb;
BAN::UniqPtr<DMARegion> m_ring_buffer_region;
};
}

View File

@ -0,0 +1,78 @@
#pragma once
#include <BAN/Vector.h>
namespace Kernel::HDAudio
{
struct CORBEntry
{
union {
struct {
uint32_t data : 8;
uint32_t command : 12;
uint32_t node_index : 8;
uint32_t codec_address : 4;
};
uint32_t raw;
};
};
static_assert(sizeof(CORBEntry) == sizeof(uint32_t));
struct BDLEntry
{
paddr_t address;
uint32_t length;
uint32_t ioc;
};
static_assert(sizeof(BDLEntry) == 16);
struct AFGWidget
{
enum class Type
{
OutputConverter,
InputConverter,
Mixer,
Selector,
PinComplex,
Power,
VolumeKnob,
BeepGenerator,
};
Type type;
uint8_t id;
union
{
struct
{
bool input;
bool output;
} pin_complex;
};
BAN::Vector<uint16_t> connections;
};
struct AFGNode
{
uint8_t id;
BAN::Vector<AFGWidget> widgets;
};
struct Codec
{
uint8_t id;
BAN::Vector<AFGNode> nodes;
};
enum class StreamType
{
Input,
Output,
Bidirectional,
};
}

View File

@ -0,0 +1,49 @@
#pragma once
#include <stdint.h>
namespace Kernel::HDAudio
{
enum Regs : uint8_t
{
GCAP = 0x00,
VMIN = 0x02,
VMAJ = 0x03,
GCTL = 0x08,
INTCTL = 0x20,
INTSTS = 0x24,
CORBLBASE = 0x40,
CORBUBASE = 0x44,
CORBWP = 0x48,
CORBRP = 0x4A,
CORBCTL = 0x4C,
CORBSTS = 0x4D,
CORBSIZE = 0x4E,
RIRBLBASE = 0x50,
RIRBUBASE = 0x54,
RIRBWP = 0x58,
RINTCNT = 0x5A,
RIRBCTL = 0x5C,
RIRBSTS = 0x5D,
RIRBSIZE = 0x5E,
ICOI = 0x60,
ICII = 0x64,
ICIS = 0x68,
SDCTL = 0x00,
SDSTS = 0x03,
SDLPIB = 0x04,
SDCBL = 0x08,
SDLVI = 0x0C,
SDFIFOD = 0x10,
SDFMT = 0x12,
SDBDPL = 0x18,
SDBDPU = 0x1C,
};
}

View File

@ -70,6 +70,8 @@
#define DEBUG_USB_MOUSE 0 #define DEBUG_USB_MOUSE 0
#define DEBUG_USB_MASS_STORAGE 0 #define DEBUG_USB_MASS_STORAGE 0
#define DEBUG_HDAUDIO 0
namespace Debug namespace Debug
{ {

View File

@ -1,5 +1,6 @@
#include <kernel/Audio/AC97/Controller.h> #include <kernel/Audio/AC97/Controller.h>
#include <kernel/Audio/AC97/Definitions.h> #include <kernel/Audio/AC97/Definitions.h>
#include <kernel/FS/DevFS/FileSystem.h>
namespace Kernel namespace Kernel
{ {
@ -105,14 +106,14 @@ namespace Kernel
IOCE = 1 << 4, IOCE = 1 << 4,
}; };
BAN::ErrorOr<BAN::RefPtr<AC97AudioController>> AC97AudioController::create(PCI::Device& pci_device) BAN::ErrorOr<void> AC97AudioController::create(PCI::Device& pci_device)
{ {
auto* ac97_ptr = new AC97AudioController(pci_device); auto* ac97_ptr = new AC97AudioController(pci_device);
if (ac97_ptr == nullptr) if (ac97_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM); return BAN::Error::from_errno(ENOMEM);
auto ac97 = BAN::RefPtr<AC97AudioController>::adopt(ac97_ptr); auto ac97 = BAN::RefPtr<AC97AudioController>::adopt(ac97_ptr);
TRY(ac97->initialize()); TRY(ac97->initialize());
return ac97; return {};
} }
BAN::ErrorOr<void> AC97AudioController::initialize() BAN::ErrorOr<void> AC97AudioController::initialize()
@ -147,6 +148,8 @@ namespace Kernel
// disable transfer, enable all interrupts // disable transfer, enable all interrupts
m_bus_master->write8(BusMasterRegister::PO_CR, IOCE | FEIE | LVBIE); m_bus_master->write8(BusMasterRegister::PO_CR, IOCE | FEIE | LVBIE);
DevFileSystem::get().add_device(this);
return {}; return {};
} }

View File

@ -1,5 +1,6 @@
#include <kernel/Audio/AC97/Controller.h> #include <kernel/Audio/AC97/Controller.h>
#include <kernel/Audio/Controller.h> #include <kernel/Audio/Controller.h>
#include <kernel/Audio/HDAudio/Controller.h>
#include <kernel/Device/DeviceNumbers.h> #include <kernel/Device/DeviceNumbers.h>
#include <kernel/FS/DevFS/FileSystem.h> #include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/Lock/SpinLockAsMutex.h> #include <kernel/Lock/SpinLockAsMutex.h>
@ -20,23 +21,26 @@ namespace Kernel
BAN::Formatter::print([&ptr](char c) { *ptr++ = c; }, "audio{}", minor(m_rdev)); BAN::Formatter::print([&ptr](char c) { *ptr++ = c; }, "audio{}", minor(m_rdev));
} }
BAN::ErrorOr<BAN::RefPtr<AudioController>> AudioController::create(PCI::Device& pci_device) BAN::ErrorOr<void> AudioController::create(PCI::Device& pci_device)
{ {
switch (pci_device.subclass()) switch (pci_device.subclass())
{ {
case 0x01: case 0x01:
// We should confirm that the card is actually AC97 but I'm trusting osdev wiki on this one // We should confirm that the card is actually AC97 but I'm trusting osdev wiki on this one
// > you can probably expect that every sound card with subclass 0x01 is sound card compatibile with AC97 // > you can probably expect that every sound card with subclass 0x01 is sound card compatibile with AC97
if (auto ret = AC97AudioController::create(pci_device); !ret.is_error()) if (auto ret = AC97AudioController::create(pci_device); ret.is_error())
{
DevFileSystem::get().add_device(ret.value());
return BAN::RefPtr<AudioController>(ret.release_value());
}
else
{ {
dwarnln("Failed to initialize AC97: {}", ret.error()); dwarnln("Failed to initialize AC97: {}", ret.error());
return ret.release_error(); return ret.release_error();
} }
break;
case 0x03:
if (auto ret = HDAudioController::create(pci_device); ret.is_error())
{
dwarnln("Failed to initialize Intel HDA: {}", ret.error());
return ret.release_error();
}
break;
default: default:
dprintln("Unsupported Sound card (PCI {2H}:{2H}:{2H})", dprintln("Unsupported Sound card (PCI {2H}:{2H}:{2H})",
pci_device.class_code(), pci_device.class_code(),
@ -45,8 +49,9 @@ namespace Kernel
); );
return BAN::Error::from_errno(ENOTSUP); return BAN::Error::from_errno(ENOTSUP);
} }
}
return {};
}
BAN::ErrorOr<size_t> AudioController::write_impl(off_t, BAN::ConstByteSpan buffer) BAN::ErrorOr<size_t> AudioController::write_impl(off_t, BAN::ConstByteSpan buffer)
{ {
@ -97,6 +102,15 @@ namespace Kernel
m_sample_data_size = 0; m_sample_data_size = 0;
return 0; return 0;
} }
case SND_GET_TOTAL_PINS:
*static_cast<uint32_t*>(arg) = get_total_pins();
return 0;
case SND_GET_PIN:
*static_cast<uint32_t*>(arg) = get_current_pin();
return 0;
case SND_SET_PIN:
TRY(set_current_pin(*static_cast<uint32_t*>(arg)));
return 0;
} }
return CharacterDevice::ioctl_impl(cmd, arg); return CharacterDevice::ioctl_impl(cmd, arg);

View File

@ -0,0 +1,568 @@
#include <kernel/Audio/HDAudio/AudioFunctionGroup.h>
#include <kernel/Audio/HDAudio/Registers.h>
#include <kernel/FS/DevFS/FileSystem.h>
namespace Kernel
{
BAN::ErrorOr<BAN::RefPtr<HDAudioFunctionGroup>> HDAudioFunctionGroup::create(BAN::RefPtr<HDAudioController> controller, uint8_t cid, HDAudio::AFGNode&& afg_node)
{
auto* audio_group_ptr = new HDAudioFunctionGroup(controller, cid, BAN::move(afg_node));
if (audio_group_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto audio_group = BAN::RefPtr<HDAudioFunctionGroup>::adopt(audio_group_ptr);
TRY(audio_group->initialize());
return audio_group;
}
HDAudioFunctionGroup::~HDAudioFunctionGroup()
{
if (m_stream_id != 0xFF)
m_controller->deallocate_stream_id(m_stream_id);
if (m_stream_index != 0xFF)
m_controller->deallocate_stream(m_stream_index);
}
BAN::ErrorOr<void> HDAudioFunctionGroup::initialize()
{
if constexpr(DEBUG_HDAUDIO)
{
const auto widget_to_string =
[](HDAudio::AFGWidget::Type type) -> const char*
{
using HDAudio::AFGWidget;
switch (type)
{
case AFGWidget::Type::OutputConverter: return "DAC";
case AFGWidget::Type::InputConverter: return "ADC";
case AFGWidget::Type::Mixer: return "Mixer";
case AFGWidget::Type::Selector: return "Selector";
case AFGWidget::Type::PinComplex: return "Pin";
case AFGWidget::Type::Power: return "Power";
case AFGWidget::Type::VolumeKnob: return "VolumeKnob";
case AFGWidget::Type::BeepGenerator: return "BeepGenerator";
}
ASSERT_NOT_REACHED();
};
dprintln("AFG {}", m_afg_node.id);
for (auto widget : m_afg_node.widgets)
{
if (widget.type == HDAudio::AFGWidget::Type::PinComplex)
{
const uint32_t config = TRY(m_controller->send_command({
.data = 0x00,
.command = 0xF1C,
.node_index = widget.id,
.codec_address = m_cid,
}));
dprintln(" widget {}: {} ({}, {}), {32b}",
widget.id,
widget_to_string(widget.type),
(int)widget.pin_complex.output,
(int)widget.pin_complex.input,
config
);
}
else
{
dprintln(" widget {}: {}",
widget.id,
widget_to_string(widget.type)
);
}
if (!widget.connections.empty())
dprintln(" connections {}", widget.connections);
}
}
TRY(initialize_stream());
TRY(initialize_output());
DevFileSystem::get().add_device(this);
return {};
}
uint32_t HDAudioFunctionGroup::get_total_pins() const
{
uint32_t count = 0;
for (const auto& widget : m_afg_node.widgets)
if (widget.type == HDAudio::AFGWidget::Type::PinComplex && widget.pin_complex.output)
count++;
return count;
}
uint32_t HDAudioFunctionGroup::get_current_pin() const
{
const auto current_id = m_output_paths[m_output_path_index].front()->id;
uint32_t pin = 0;
for (const auto& widget : m_afg_node.widgets)
{
if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output)
continue;
if (widget.id == current_id)
return pin;
pin++;
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<void> HDAudioFunctionGroup::set_current_pin(uint32_t pin)
{
uint32_t pin_id = 0;
for (const auto& widget : m_afg_node.widgets)
{
if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output)
continue;
if (pin-- > 0)
continue;
pin_id = widget.id;
break;
}
if (auto ret = disable_output_path(m_output_path_index); ret.is_error())
dwarnln("failed to disable old output path {}", ret.error());
for (size_t i = 0; i < m_output_paths.size(); i++)
{
if (m_output_paths[i].front()->id != pin_id)
continue;
if (auto ret = enable_output_path(i); !ret.is_error())
{
if (ret.error().get_error_code() == ENOTSUP)
continue;
dwarnln("path {} not supported", i);
return ret.release_error();
}
dprintln("set output widget to {}", pin_id);
m_output_path_index = i;
return {};
}
dwarnln("failed to set output widget to {}", pin_id);
return BAN::Error::from_errno(ENOTSUP);
}
size_t HDAudioFunctionGroup::bdl_offset() const
{
const size_t bdl_entry_bytes = m_bdl_entry_sample_frames * get_channels() * sizeof(uint16_t);
const size_t bdl_total_size = bdl_entry_bytes * m_bdl_entry_count;
if (auto rem = bdl_total_size % 128)
return bdl_total_size + (128 - rem);
return bdl_total_size;
}
BAN::ErrorOr<void> HDAudioFunctionGroup::initialize_stream()
{
const size_t bdl_entry_bytes = m_bdl_entry_sample_frames * get_channels() * sizeof(uint16_t);
const size_t bdl_list_size = m_bdl_entry_count * sizeof(HDAudio::BDLEntry);
m_bdl_region = TRY(DMARegion::create(bdl_offset() + bdl_list_size));
if (!m_controller->is_64bit() && (m_bdl_region->paddr() >> 32))
{
dwarnln("no 64 bit support but allocated bdl has 64 bit address :(");
return BAN::Error::from_errno(ENOTSUP);
}
auto* bdl = reinterpret_cast<volatile HDAudio::BDLEntry*>(m_bdl_region->vaddr() + bdl_offset());
for (size_t i = 0; i < m_bdl_entry_count; i++)
{
bdl[i].address = m_bdl_region->paddr() + i * bdl_entry_bytes;
bdl[i].length = bdl_entry_bytes;
bdl[i].ioc = 1;
}
ASSERT(m_stream_id == 0xFF);
m_stream_id = TRY(m_controller->allocate_stream_id());
ASSERT(m_stream_index == 0xFF);
m_stream_index = TRY(m_controller->allocate_stream(HDAudio::StreamType::Output, this));
reset_stream();
return {};
}
void HDAudioFunctionGroup::reset_stream()
{
using Regs = HDAudio::Regs;
auto& bar = m_controller->bar0();
const auto base = 0x80 + m_stream_index * 0x20;
const size_t bdl_entry_bytes = m_bdl_entry_sample_frames * get_channels() * sizeof(uint16_t);
// stop stream
bar.write8(base + Regs::SDCTL, bar.read8(base + Regs::SDCTL) & 0xFD);
// reset stream
bar.write8(base + Regs::SDCTL, (bar.read8(base + Regs::SDCTL) & 0xFE) | 1);
while (!(bar.read8(base + Regs::SDCTL) & 1))
Processor::pause();
bar.write8(base + Regs::SDCTL, (bar.read8(base + Regs::SDCTL) & 0xFE));
while ((bar.read8(base + Regs::SDCTL) & 1))
Processor::pause();
// set bdl address, total size and lvi
const paddr_t bdl_paddr = m_bdl_region->paddr() + bdl_offset();
bar.write32(base + Regs::SDBDPL, bdl_paddr);
if (m_controller->is_64bit())
bar.write32(base + Regs::SDBDPU, bdl_paddr >> 32);
bar.write32(base + Regs::SDCBL, bdl_entry_bytes * m_bdl_entry_count);
bar.write16(base + Regs::SDLVI, (bar.read16(base + Regs::SDLVI) & 0xFF00) | (m_bdl_entry_count - 1));
// set stream format
bar.write16(base + Regs::SDFMT, get_format_data());
// set stream id, not bidirectional
bar.write8(base + Regs::SDCTL + 2, (bar.read8(base + Regs::SDCTL + 2) & 0x07) | (m_stream_id << 4));
m_bdl_head = 0;
m_bdl_tail = 0;
m_stream_running = false;
}
BAN::ErrorOr<void> HDAudioFunctionGroup::initialize_output()
{
BAN::Vector<const HDAudio::AFGWidget*> path;
TRY(path.reserve(m_max_path_length));
for (const auto& widget : m_afg_node.widgets)
{
if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output)
continue;
TRY(path.push_back(&widget));
TRY(recurse_output_paths(widget, path));
path.pop_back();
}
dprintln_if(DEBUG_HDAUDIO, "found {} paths from output to DAC", m_output_paths.size());
// select first supported path
// FIXME: prefer associations
// FIXME: does this pin even have a device?
auto result = BAN::Error::from_errno(ENODEV);
for (size_t i = 0; i < m_output_paths.size(); i++)
{
if (auto ret = enable_output_path(i); ret.is_error())
{
if (ret.error().get_error_code() != ENOTSUP)
return ret.release_error();
dwarnln("path {} not supported", i);
result = BAN::Error::from_errno(ENOTSUP);
continue;
}
m_output_path_index = i;
break;
}
if (m_output_path_index >= m_output_paths.size())
{
dwarnln("could not find any usable output path");
return result;
}
dprintln_if(DEBUG_HDAUDIO, "routed output path");
for (const auto* widget : m_output_paths[m_output_path_index])
dprintln_if(DEBUG_HDAUDIO, " {}", widget->id);
return {};
}
uint16_t HDAudioFunctionGroup::get_format_data() const
{
// TODO: don't hardcode this
// format: PCM, 48 kHz, 16 bit, 2 channels
return 0b0'0'000'000'0'001'0001;
}
uint16_t HDAudioFunctionGroup::get_volume_data() const
{
// TODO: don't hardcode this
// left and right output, no mute, max gain
return 0b1'0'1'1'0000'0'1111111;
}
BAN::ErrorOr<void> HDAudioFunctionGroup::enable_output_path(uint8_t index)
{
ASSERT(index < m_output_paths.size());
const auto& path = m_output_paths[index];
for (const auto* widget : path)
{
switch (widget->type)
{
using HDAudio::AFGWidget;
case AFGWidget::Type::OutputConverter:
case AFGWidget::Type::PinComplex:
break;
default:
dwarnln("FIXME: support enabling widget type {}", static_cast<int>(widget->type));
return BAN::Error::from_errno(ENOTSUP);
}
}
const auto format = get_format_data();
const auto volume = get_volume_data();
for (size_t i = 0; i < path.size(); i++)
{
// set power state D0
TRY(m_controller->send_command({
.data = 0x00,
.command = 0x705,
.node_index = path[i]->id,
.codec_address = m_cid,
}));
// set connection index
if (i + 1 < path.size() && path[i]->connections.size() > 1)
{
uint8_t index = 0;
for (; index < path[i]->connections.size(); index++)
if (path[i]->connections[index] == path[i + 1]->id)
break;
ASSERT(index < path[i]->connections.size());
TRY(m_controller->send_command({
.data = index,
.command = 0x701,
.node_index = path[i]->id,
.codec_address = m_cid,
}));
}
// set volume
TRY(m_controller->send_command({
.data = static_cast<uint8_t>(volume & 0xFF),
.command = static_cast<uint16_t>(0x300 | (volume >> 8)),
.node_index = path[i]->id,
.codec_address = m_cid,
}));
switch (path[i]->type)
{
using HDAudio::AFGWidget;
case AFGWidget::Type::OutputConverter:
// set stream and channel 0
TRY(m_controller->send_command({
.data = static_cast<uint8_t>(m_stream_id << 4),
.command = 0x706,
.node_index = path[i]->id,
.codec_address = m_cid,
}));
// set format
TRY(m_controller->send_command({
.data = static_cast<uint8_t>(format & 0xFF),
.command = static_cast<uint16_t>(0x200 | (format >> 8)),
.node_index = path[i]->id,
.codec_address = m_cid,
}));
break;
case AFGWidget::Type::PinComplex:
// enable output and H-Phn
TRY(m_controller->send_command({
.data = 0x80 | 0x40,
.command = 0x707,
.node_index = path[i]->id,
.codec_address = m_cid,
}));
// enable EAPD
TRY(m_controller->send_command({
.data = 0x02,
.command = 0x70C,
.node_index = path[i]->id,
.codec_address = m_cid,
}));
break;
default:
ASSERT_NOT_REACHED();
}
}
return {};
}
BAN::ErrorOr<void> HDAudioFunctionGroup::disable_output_path(uint8_t index)
{
ASSERT(index < m_output_paths.size());
const auto& path = m_output_paths[index];
for (size_t i = 0; i < path.size(); i++)
{
// set power state D3
TRY(m_controller->send_command({
.data = 0x03,
.command = 0x705,
.node_index = path[i]->id,
.codec_address = m_cid,
}));
switch (path[i]->type)
{
using HDAudio::AFGWidget;
case AFGWidget::Type::OutputConverter:
break;
case AFGWidget::Type::PinComplex:
// disable output and H-Phn
TRY(m_controller->send_command({
.data = 0x00,
.command = 0x707,
.node_index = path[i]->id,
.codec_address = m_cid,
}));
// disable EAPD
TRY(m_controller->send_command({
.data = 0x00,
.command = 0x70C,
.node_index = path[i]->id,
.codec_address = m_cid,
}));
break;
default:
ASSERT_NOT_REACHED();
}
}
return {};
}
BAN::ErrorOr<void> HDAudioFunctionGroup::recurse_output_paths(const HDAudio::AFGWidget& widget, BAN::Vector<const HDAudio::AFGWidget*>& path)
{
// cycle "detection"
if (path.size() >= m_max_path_length)
return {};
// we've reached a DAC
if (widget.type == HDAudio::AFGWidget::Type::OutputConverter)
{
BAN::Vector<const HDAudio::AFGWidget*> path_copy;
TRY(path_copy.resize(path.size()));
for (size_t i = 0; i < path.size(); i++)
path_copy[i] = path[i];
TRY(m_output_paths.push_back(BAN::move(path_copy)));
return {};
}
// check all connections
for (const auto& connection : m_afg_node.widgets)
{
if (!widget.connections.contains(connection.id))
continue;
TRY(path.push_back(&connection));
TRY(recurse_output_paths(connection, path));
path.pop_back();
}
return {};
}
void HDAudioFunctionGroup::handle_new_data()
{
queue_bdl_data();
}
void HDAudioFunctionGroup::queue_bdl_data()
{
ASSERT(m_spinlock.current_processor_has_lock());
const size_t bdl_entry_bytes = m_bdl_entry_sample_frames * get_channels() * sizeof(uint16_t);
while ((m_bdl_head + 1) % m_bdl_entry_count != m_bdl_tail)
{
const size_t sample_data_tail = (m_sample_data_head + m_sample_data_capacity - m_sample_data_size) % m_sample_data_capacity;
const size_t sample_frames = BAN::Math::min(m_sample_data_size / get_channels() / sizeof(uint16_t), m_bdl_entry_sample_frames);
if (sample_frames == 0)
break;
const size_t copy_total_bytes = sample_frames * get_channels() * sizeof(uint16_t);
const size_t copy_before_wrap = BAN::Math::min(copy_total_bytes, m_sample_data_capacity - sample_data_tail);
memcpy(
reinterpret_cast<void*>(m_bdl_region->vaddr() + m_bdl_head * bdl_entry_bytes),
&m_sample_data[sample_data_tail],
copy_before_wrap
);
if (copy_before_wrap < copy_total_bytes)
{
memcpy(
reinterpret_cast<void*>(m_bdl_region->vaddr() + m_bdl_head * bdl_entry_bytes + copy_before_wrap),
&m_sample_data[0],
copy_total_bytes - copy_before_wrap
);
}
if (copy_total_bytes < bdl_entry_bytes)
{
memset(
reinterpret_cast<void*>(m_bdl_region->vaddr() + m_bdl_head * bdl_entry_bytes + copy_total_bytes),
0x00,
bdl_entry_bytes - copy_total_bytes
);
}
m_sample_data_size -= copy_total_bytes;
m_bdl_head = (m_bdl_head + 1) % m_bdl_entry_count;
}
if (m_bdl_head == m_bdl_tail || m_stream_running)
return;
// start the stream and enable IOC and descriptor error interrupts
auto& bar = m_controller->bar0();
const auto base = 0x80 + m_stream_index * 0x20;
bar.write8(base + HDAudio::Regs::SDCTL, bar.read8(base + HDAudio::Regs::SDCTL) | 0x16);
m_stream_running = true;
}
void HDAudioFunctionGroup::on_stream_interrupt(uint8_t stream_index)
{
using Regs = HDAudio::Regs;
ASSERT(stream_index == m_stream_index);
auto& bar = m_controller->bar0();
const uint16_t base = 0x80 + stream_index * 0x20;
const uint8_t sts = bar.read8(base + Regs::SDSTS);
bar.write8(base + Regs::SDSTS, sts & 0x3C);
if (sts & (1 << 4))
derrorln("descriptor error");
// ignore fifo errors as they are too common on real hw :D
//if (sts & (1 << 3))
// derrorln("fifo error");
if (sts & (1 << 2))
{
SpinLockGuard _(m_spinlock);
ASSERT(m_stream_running);
m_bdl_tail = (m_bdl_tail + 1) % m_bdl_entry_count;
if (m_bdl_tail == m_bdl_head)
reset_stream();
queue_bdl_data();
}
}
}

View File

@ -0,0 +1,492 @@
#include <kernel/Audio/HDAudio/AudioFunctionGroup.h>
#include <kernel/Audio/HDAudio/Controller.h>
#include <kernel/Audio/HDAudio/Registers.h>
#include <kernel/Lock/LockGuard.h>
#include <kernel/Lock/SpinLockAsMutex.h>
#include <kernel/MMIO.h>
#include <kernel/Timer/Timer.h>
namespace Kernel
{
BAN::ErrorOr<void> HDAudioController::create(PCI::Device& pci_device)
{
auto intel_hda_ptr = new HDAudioController(pci_device);
if (intel_hda_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto intel_hda = BAN::RefPtr<HDAudioController>::adopt(intel_hda_ptr);
TRY(intel_hda->initialize());
return {};
}
BAN::ErrorOr<void> HDAudioController::initialize()
{
using Regs = HDAudio::Regs;
m_pci_device.enable_bus_mastering();
m_bar0 = TRY(m_pci_device.allocate_bar_region(0));
dprintln("HD audio");
dprintln(" revision {}.{}",
m_bar0->read8(Regs::VMAJ),
m_bar0->read8(Regs::VMIN)
);
const uint16_t global_cap = m_bar0->read16(Regs::GCAP);
m_output_streams = (global_cap >> 12) & 0x0F;
m_input_streams = (global_cap >> 8) & 0x0F;
m_bidir_streams = (global_cap >> 3) & 0x1F;
m_is64bit = (global_cap & 1);
if (m_output_streams + m_input_streams + m_bidir_streams > 30)
{
dwarnln("HD audio controller has {} streams, 30 is the maximum valid count",
m_output_streams + m_input_streams + m_bidir_streams
);
return BAN::Error::from_errno(EINVAL);
}
dprintln(" output streams: {}", m_output_streams);
dprintln(" input streams: {}", m_input_streams);
dprintln(" bidir streams: {}", m_bidir_streams);
dprintln(" 64 bit support: {}", m_is64bit);
TRY(reset_controller());
if (auto ret = initialize_ring_buffers(); ret.is_error())
{
if (ret.error().get_error_code() != ETIMEDOUT)
return ret.release_error();
m_use_immediate_command = true;
}
TRY(m_pci_device.reserve_interrupts(1));
m_pci_device.enable_interrupt(0, *this);
m_bar0->write32(Regs::INTCTL, UINT32_MAX);
for (uint8_t codec_id = 0; codec_id < 0x10; codec_id++)
{
auto codec_or_error = initialize_codec(codec_id);
if (codec_or_error.is_error())
continue;
auto codec = codec_or_error.release_value();
for (auto& node : codec.nodes)
if (auto ret = HDAudioFunctionGroup::create(this, codec.id, BAN::move(node)); ret.is_error())
dwarnln("Failed to initialize AFG: {}", ret.error());
}
return {};
}
BAN::ErrorOr<void> HDAudioController::reset_controller()
{
using HDAudio::Regs;
const auto timeout_ms = SystemTimer::get().ms_since_boot() + 100;
// transition into reset state
if (const uint32_t gcap = m_bar0->read32(Regs::GCTL); gcap & 1)
{
m_bar0->write32(Regs::GCTL, gcap & 0xFFFFFEFC);
while (m_bar0->read32(Regs::GCTL) & 1)
{
if (SystemTimer::get().ms_since_boot() > timeout_ms)
return BAN::Error::from_errno(ETIMEDOUT);
Processor::pause();
}
}
m_bar0->write32(Regs::GCTL, (m_bar0->read32(Regs::GCTL) & 0xFFFFFEFC) | 1);
while (!(m_bar0->read32(Regs::GCTL) & 1))
{
if (SystemTimer::get().ms_since_boot() > timeout_ms)
return BAN::Error::from_errno(ETIMEDOUT);
Processor::pause();
}
// 4.3 The software must wait at least 521 us (25 frames) after reading CRST as a 1
// before assuming that codecs have all made status change requests and have been
// registered by the controller
SystemTimer::get().sleep_ms(1);
return {};
}
BAN::ErrorOr<void> HDAudioController::initialize_ring_buffers()
{
using Regs = HDAudio::Regs;
// CORB: at most 1024 bytes (256 * uint32_t)
// RIRB: at most 2048 bytes (256 * uint32_t * 2)
m_ring_buffer_region = TRY(DMARegion::create(3 * 256 * sizeof(uint32_t)));
struct SizeInfo
{
uint16_t size;
uint8_t value;
};
const auto get_size_info =
[](uint8_t byte) -> BAN::ErrorOr<SizeInfo>
{
if (byte & 0x40)
return SizeInfo { 256, 2 };
if (byte & 0x20)
return SizeInfo { 16, 1 };
if (byte & 0x10)
return SizeInfo { 2, 0 };
return BAN::Error::from_errno(EINVAL);
};
const auto corb_size = TRY(get_size_info(m_bar0->read8(Regs::CORBSIZE)));
const auto rirb_size = TRY(get_size_info(m_bar0->read8(Regs::RIRBSIZE)));
m_corb = {
.vaddr = m_ring_buffer_region->vaddr(),
.index = 1,
.size = corb_size.size,
};
m_rirb = {
.vaddr = m_ring_buffer_region->vaddr() + 1024,
.index = 1,
.size = rirb_size.size,
};
const paddr_t corb_paddr = m_ring_buffer_region->paddr();
const paddr_t rirb_paddr = m_ring_buffer_region->paddr() + 1024;
if (!m_is64bit && ((corb_paddr >> 32) || (rirb_paddr >> 32)))
{
dwarnln("no 64 bit support but allocated ring buffers have 64 bit addresses :(");
return BAN::Error::from_errno(ENOTSUP);
}
// disable corb and rirb
m_bar0->write8(Regs::CORBCTL, (m_bar0->read8(Regs::CORBCTL) & 0xFC));
m_bar0->write8(Regs::RIRBCTL, (m_bar0->read8(Regs::RIRBCTL) & 0xF8));
// set base address
m_bar0->write32(Regs::CORBLBASE, corb_paddr | (m_bar0->read32(Regs::CORBLBASE) & 0x0000007F));
if (m_is64bit)
m_bar0->write32(Regs::CORBUBASE, corb_paddr >> 32);
// set number of entries
m_bar0->write8(Regs::CORBSIZE, (m_bar0->read8(Regs::CORBSIZE) & 0xFC) | corb_size.value);
// zero write pointer
m_bar0->write16(Regs::CORBWP, (m_bar0->read16(Regs::CORBWP) & 0xFF00));
// reset read pointer
const uint64_t corb_timeout_ms = SystemTimer::get().ms_since_boot() + 100;
m_bar0->write16(Regs::CORBRP, (m_bar0->read16(Regs::CORBRP) & 0x7FFF) | 0x8000);
while (!(m_bar0->read16(Regs::CORBRP) & 0x8000))
{
if (SystemTimer::get().ms_since_boot() > corb_timeout_ms)
return BAN::Error::from_errno(ETIMEDOUT);
Processor::pause();
}
m_bar0->write16(Regs::CORBRP, (m_bar0->read16(Regs::CORBRP) & 0x7FFF));
while ((m_bar0->read16(Regs::CORBRP) & 0x8000))
{
if (SystemTimer::get().ms_since_boot() > corb_timeout_ms)
return BAN::Error::from_errno(ETIMEDOUT);
Processor::pause();
}
// set base address
m_bar0->write32(Regs::RIRBLBASE, rirb_paddr | (m_bar0->read32(Regs::RIRBLBASE) & 0x0000007F));
if (m_is64bit)
m_bar0->write32(Regs::RIRBUBASE, rirb_paddr >> 32);
// set number of entries
m_bar0->write8(Regs::RIRBSIZE, (m_bar0->read8(Regs::RIRBSIZE) & 0xFC) | rirb_size.value);
// reset write pointer
m_bar0->write16(Regs::RIRBWP, (m_bar0->read16(Regs::RIRBWP) & 0x7FFF) | 0x8000);
// send interrupt on every packet
m_bar0->write16(Regs::RINTCNT, (m_bar0->read16(Regs::RINTCNT) & 0xFF00) | 0x01);
// enable corb and rirb
m_bar0->write8(Regs::CORBCTL, (m_bar0->read8(Regs::CORBCTL) & 0xFC) | 3);
m_bar0->write8(Regs::RIRBCTL, (m_bar0->read8(Regs::RIRBCTL) & 0xF8) | 7);
return {};
}
BAN::ErrorOr<HDAudio::Codec> HDAudioController::initialize_codec(uint8_t codec)
{
const auto resp = TRY(send_command({
.data = 0x04,
.command = 0xF00,
.node_index = 0,
.codec_address = codec,
}));
const uint8_t start = (resp >> 16) & 0xFF;
const uint8_t count = (resp >> 0) & 0xFF;
if (count == 0)
return BAN::Error::from_errno(ENODEV);
HDAudio::Codec result {};
result.id = codec;
TRY(result.nodes.reserve(count));
for (size_t i = 0; i < count; i++)
if (auto node_or_error = initialize_node(codec, start + i); !node_or_error.is_error())
MUST(result.nodes.emplace_back(node_or_error.release_value()));
return result;
}
BAN::ErrorOr<HDAudio::AFGNode> HDAudioController::initialize_node(uint8_t codec, uint8_t node)
{
{
const auto resp = TRY(send_command({
.data = 0x05,
.command = 0xF00,
.node_index = node,
.codec_address = codec,
}));
const uint8_t type = (resp >> 0) & 0xFF;
if (type != 0x01)
return BAN::Error::from_errno(ENODEV);
}
const auto resp = TRY(send_command({
.data = 0x04,
.command = 0xF00,
.node_index = node,
.codec_address = codec,
}));
const uint8_t start = (resp >> 16) & 0xFF;
const uint8_t count = (resp >> 0) & 0xFF;
HDAudio::AFGNode result {};
result.id = node;
TRY(result.widgets.reserve(count));
for (size_t i = 0; i < count; i++)
if (auto widget_or_error = initialize_widget(codec, start + i); !widget_or_error.is_error())
MUST(result.widgets.emplace_back(widget_or_error.release_value()));
return result;
}
BAN::ErrorOr<HDAudio::AFGWidget> HDAudioController::initialize_widget(uint8_t codec, uint8_t widget)
{
const auto send_command_or_zero =
[codec, widget, this](uint16_t cmd, uint8_t data) -> uint32_t
{
const auto command = HDAudio::CORBEntry {
.data = data,
.command = cmd,
.node_index = widget,
.codec_address = codec,
};
if (auto res = send_command(command); !res.is_error())
return res.release_value();
return 0;
};
using HDAudio::AFGWidget;
const AFGWidget::Type type_list[] {
AFGWidget::Type::OutputConverter,
AFGWidget::Type::InputConverter,
AFGWidget::Type::Mixer,
AFGWidget::Type::Selector,
AFGWidget::Type::PinComplex,
AFGWidget::Type::Power,
AFGWidget::Type::VolumeKnob,
AFGWidget::Type::BeepGenerator,
};
const uint8_t type = (send_command_or_zero(0xF00, 0x09) >> 20) & 0x0F;
if (type > sizeof(type_list) / sizeof(*type_list))
return BAN::Error::from_errno(ENOTSUP);
AFGWidget result {};
result.type = type_list[type];
result.id = widget;
if (result.type == AFGWidget::Type::PinComplex)
{
const uint32_t cap = send_command_or_zero(0xF00, 0x0C);
result.pin_complex.output = !!(cap & (1 << 4));
result.pin_complex.input = !!(cap & (1 << 5));
}
const uint8_t connection_info = send_command_or_zero(0xF00, 0x0E);
const uint8_t conn_width = (connection_info & 0x80) ? 2 : 1;
const uint8_t conn_count = connection_info & 0x3F;
const uint16_t conn_mask = (1 << (8 * conn_width)) - 1;
TRY(result.connections.resize(conn_count, 0));
for (size_t i = 0; i < conn_count; i += 4 / conn_width)
{
const uint32_t conn = send_command_or_zero(0xF02, i);
for (size_t j = 0; j < sizeof(conn) / conn_width && i + j < conn_count; j++)
result.connections[i + j] = (conn >> (8 * conn_width * j)) & conn_mask;
}
return result;
}
BAN::ErrorOr<uint32_t> HDAudioController::send_command(HDAudio::CORBEntry command)
{
using Regs = HDAudio::Regs;
// TODO: allow concurrent commands with CORB/RIRB
LockGuard _(m_command_mutex);
if (!m_use_immediate_command)
{
SpinLockGuard sguard(m_rb_lock);
MMIO::write32(m_corb.vaddr + m_corb.index * sizeof(uint32_t), command.raw);
m_bar0->write16(Regs::CORBWP, (m_bar0->read16(Regs::CORBWP) & 0xFF00) | m_corb.index);
m_corb.index = (m_corb.index + 1) % m_corb.size;
const uint64_t waketime_ms = SystemTimer::get().ms_since_boot() + 10;
while ((m_bar0->read16(Regs::RIRBWP) & 0xFF) != m_rirb.index)
{
if (SystemTimer::get().ms_since_boot() > waketime_ms)
return BAN::Error::from_errno(ETIMEDOUT);
SpinLockGuardAsMutex smutex(sguard);
m_rb_blocker.block_with_timeout_ms(10, &smutex);
}
const size_t offset = 2 * m_rirb.index * sizeof(uint32_t);
m_rirb.index = (m_rirb.index + 1) % m_rirb.size;
return MMIO::read32(m_rirb.vaddr + offset);
}
else
{
uint64_t waketime_ms = SystemTimer::get().ms_since_boot() + 10;
while (m_bar0->read16(Regs::ICIS) & 1)
{
if (SystemTimer::get().ms_since_boot() > waketime_ms)
break;
Processor::pause();
}
// clear ICB if it did not clear "in reasonable timeout period"
// and make sure IRV is cleared
if (m_bar0->read16(Regs::ICIS) & 3)
m_bar0->write16(Regs::ICIS, (m_bar0->read16(Regs::ICIS) & 0x00FC) | 2);
m_bar0->write32(Regs::ICOI, command.raw);
m_bar0->write16(Regs::ICIS, (m_bar0->read16(Regs::ICIS) & 0x00FC) | 1);
waketime_ms = SystemTimer::get().ms_since_boot() + 10;
while (!(m_bar0->read16(Regs::ICIS) & 2))
{
if (SystemTimer::get().ms_since_boot() > waketime_ms)
return BAN::Error::from_errno(ETIMEDOUT);
Processor::pause();
}
return m_bar0->read32(Regs::ICII);
}
}
void HDAudioController::handle_irq()
{
using Regs = HDAudio::Regs;
const uint32_t intsts = m_bar0->read32(Regs::INTSTS);
if (!(intsts & (1u << 31)))
return;
if (intsts & (1 << 30))
{
if (const uint8_t rirbsts = m_bar0->read8(Regs::RIRBSTS) & ((1 << 2) | (1 << 0)))
{
if (rirbsts & (1 << 2))
dwarnln("RIRB response overrun");
if (rirbsts & (1 << 0))
{
SpinLockGuard _(m_rb_lock);
m_rb_blocker.unblock();
}
m_bar0->write8(Regs::RIRBSTS, rirbsts);
}
if (const uint8_t corbsts = m_bar0->read8(Regs::CORBSTS) & (1 << 0))
{
dwarnln("CORB memory error");
m_bar0->write8(Regs::CORBSTS, corbsts);
}
}
for (size_t i = 0; i < 30; i++)
{
if (!(intsts & (1 << i)))
continue;
if (m_allocated_streams[i] == nullptr)
dwarnln("interrupt from an unallocated stream??");
else
static_cast<HDAudioFunctionGroup*>(m_allocated_streams[i])->on_stream_interrupt(i);
}
}
uint8_t HDAudioController::get_stream_index(HDAudio::StreamType type, uint8_t index) const
{
switch (type)
{
case HDAudio::StreamType::Bidirectional:
index += m_output_streams;
[[fallthrough]];
case HDAudio::StreamType::Output:
index += m_input_streams;
[[fallthrough]];
case HDAudio::StreamType::Input:
break;
}
return index;
}
BAN::ErrorOr<uint8_t> HDAudioController::allocate_stream_id()
{
for (uint8_t id = 1; id < 16; id++)
{
if (m_allocated_stream_ids & (1 << id))
continue;
m_allocated_stream_ids |= 1 << id;
return id;
}
return BAN::Error::from_errno(EAGAIN);
}
void HDAudioController::deallocate_stream_id(uint8_t id)
{
ASSERT(m_allocated_stream_ids & (1 << id));
m_allocated_stream_ids &= ~(1 << id);
}
BAN::ErrorOr<uint8_t> HDAudioController::allocate_stream(HDAudio::StreamType type, void* afg)
{
const uint8_t stream_count_lookup[] {
[(int)HDAudio::StreamType::Input] = m_input_streams,
[(int)HDAudio::StreamType::Output] = m_output_streams,
[(int)HDAudio::StreamType::Bidirectional] = m_bidir_streams,
};
const uint8_t stream_count = stream_count_lookup[static_cast<int>(type)];
for (uint8_t i = 0; i < stream_count; i++)
{
const uint8_t index = get_stream_index(type, i);
if (m_allocated_streams[index])
continue;
m_allocated_streams[index] = afg;
return index;
}
return BAN::Error::from_errno(EAGAIN);
}
void HDAudioController::deallocate_stream(uint8_t index)
{
ASSERT(m_allocated_streams[index]);
m_allocated_streams[index] = nullptr;
// TODO: maybe make sure the stream is stopped/reset (?)
}
}

View File

@ -382,16 +382,16 @@ namespace Kernel
asm volatile("cli; 1: hlt; jmp 1b"); asm volatile("cli; 1: hlt; jmp 1b");
} }
if (!InterruptController::get().is_in_service(irq))
return;
Thread::current().save_sse(); Thread::current().save_sse();
if (InterruptController::get().is_in_service(irq))
{
InterruptController::get().eoi(irq); InterruptController::get().eoi(irq);
if (auto* handler = s_interruptables[irq]) if (auto* handler = s_interruptables[irq])
handler->handle_irq(); handler->handle_irq();
else else
dprintln("no handler for irq 0x{2H}", irq); dprintln("no handler for irq 0x{2H}", irq);
}
auto& current_thread = Thread::current(); auto& current_thread = Thread::current();
if (current_thread.can_add_signal_to_execute()) if (current_thread.can_add_signal_to_execute())

View File

@ -252,6 +252,46 @@ namespace Kernel
m_open_files[fd].status_flags() &= O_ACCMODE; m_open_files[fd].status_flags() &= O_ACCMODE;
m_open_files[fd].status_flags() |= extra; m_open_files[fd].status_flags() |= extra;
return 0; return 0;
case F_GETLK:
{
dwarnln("TODO: proper fcntl F_GETLK");
auto* param = reinterpret_cast<struct flock*>(extra);
const auto& flock = m_open_files[fd].description->flock;
if (flock.lockers.empty())
param->l_type = F_UNLCK;
else
{
*param = {
.l_type = static_cast<short>(flock.shared ? F_RDLCK : F_WRLCK),
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = 1,
.l_pid = *flock.lockers.begin(),
};
}
return 0;
}
case F_SETLK:
case F_SETLKW:
{
dwarnln("TODO: proper fcntl F_SETLK(W)");
int op = cmd == F_SETLKW ? LOCK_NB : 0;
switch (reinterpret_cast<const struct flock*>(extra)->l_type)
{
case F_UNLCK: op |= LOCK_UN; break;
case F_RDLCK: op |= LOCK_SH; break;
case F_WRLCK: op |= LOCK_EX; break;
default:
return BAN::Error::from_errno(EINVAL);
}
TRY(flock(fd, op));
return 0;
}
default: default:
break; break;
} }

View File

@ -1,6 +1,7 @@
#include <BAN/ScopeGuard.h> #include <BAN/ScopeGuard.h>
#include <LibAudio/Audio.h> #include <LibAudio/Audio.h>
#include <LibAudio/Protocol.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
@ -121,10 +122,15 @@ namespace LibAudio
{ {
ASSERT(m_server_fd != -1); ASSERT(m_server_fd != -1);
const ssize_t nsend = send(m_server_fd, &m_smo_key, sizeof(m_smo_key), 0); LibAudio::Packet packet {
.type = LibAudio::Packet::RegisterBuffer,
.parameter = static_cast<uint64_t>(m_smo_key),
};
const ssize_t nsend = send(m_server_fd, &packet, sizeof(packet), 0);
if (nsend == -1) if (nsend == -1)
return BAN::Error::from_errno(errno); return BAN::Error::from_errno(errno);
ASSERT(nsend == sizeof(m_smo_key)); ASSERT(nsend == sizeof(packet));
return {}; return {};
} }
@ -137,8 +143,12 @@ namespace LibAudio
return; return;
m_audio_buffer->paused = paused; m_audio_buffer->paused = paused;
long dummy = 0; LibAudio::Packet packet {
send(m_server_fd, &dummy, sizeof(dummy), 0); .type = LibAudio::Packet::Notify,
.parameter = 0,
};
send(m_server_fd, &packet, sizeof(packet), 0);
} }
size_t Audio::queue_samples(BAN::Span<const AudioBuffer::sample_t> samples) size_t Audio::queue_samples(BAN::Span<const AudioBuffer::sample_t> samples)

View File

@ -9,8 +9,6 @@
namespace LibAudio namespace LibAudio
{ {
static constexpr BAN::StringView s_audio_server_socket = "/tmp/audio-server.socket"_sv;
struct AudioBuffer struct AudioBuffer
{ {
using sample_t = double; using sample_t = double;

View File

@ -0,0 +1,50 @@
#pragma once
#include <BAN/StringView.h>
namespace LibAudio
{
struct Packet
{
enum : uint8_t
{
Notify, // parameter: ignored
// response: nothing
// server refereshes buffered data
RegisterBuffer, // paramenter: smo key
// response: nothing
// register audio buffer to server
QueryDevices, // parameter: ignored
// response: (uint32_t)
// query the number of devices available
QueryPins, // parameter: sink number
// response: (uint32_t)
// query the number of pins the sink has
GetDevice, // parameter: ignored
// reponse: (uint32_t)
// get the currently active device
SetDevice, // parameter: device number
// reponse: nothing
// set the currently active device
GetPin, // parameter: ignored
// response: nothing
// get the active pin of the current device
SetPin, // parameter: pin number
// response: nothing
// set the active pin of the current device
} type;
uint64_t parameter;
};
static constexpr BAN::StringView s_audio_server_socket = "/tmp/audio-server.socket"_sv;
}

View File

@ -49,6 +49,7 @@ set(LIBC_SOURCES
sys/mman.cpp sys/mman.cpp
sys/resource.cpp sys/resource.cpp
sys/select.cpp sys/select.cpp
sys/shm.cpp
sys/socket.cpp sys/socket.cpp
sys/stat.cpp sys/stat.cpp
sys/statvfs.cpp sys/statvfs.cpp

View File

@ -62,8 +62,30 @@ enum
#define IPV6_V6ONLY IPV6_V6ONLY #define IPV6_V6ONLY IPV6_V6ONLY
}; };
#define IN_CLASSA(a) ((((in_addr_t)(a)) & 0x80000000) == 0)
#define IN_CLASSA_NET 0xFF000000
#define IN_CLASSA_NSHIFT 24
#define IN_CLASSA_HOST (0xFFFFFFFF & ~IN_CLASSA_NET)
#define IN_CLASSA_MAX 128
#define IN_CLASSB(a) ((((in_addr_t)(a)) & 0xC0000000) == 0x80000000)
#define IN_CLASSB_NET 0xFFFF0000
#define IN_CLASSB_NSHIFT 16
#define IN_CLASSB_HOST (0xFFFFFFFF & ~IN_CLASSB_NET)
#define IN_CLASSB_MAX 65536
#define IN_CLASSC(a) ((((in_addr_t)(a)) & 0xE0000000) == 0xC0000000)
#define IN_CLASSC_NET 0xFFFFFF00
#define IN_CLASSC_NSHIFT 8
#define IN_CLASSC_HOST (0xFFFFFFFF & ~IN_CLASSC_NET)
#define IN_CLASSD(a) ((((in_addr_t)(a)) & 0xF0000000) == 0xE0000000)
#define IN_MULTICAST(a) IN_CLASSD(a)
#define IN_EXPERIMENTAL(a) ((((in_addr_t)(a)) & 0xE0000000) == 0xE0000000)
#define IN_BADCLASS(a) ((((in_addr_t)(a)) & 0xF0000000) == 0xF0000000)
#define IN_LOOPBACKNET 127 #define IN_LOOPBACKNET 127
#define IN_MULTICAST(a) (((in_addr_t)(a) & 0xF0000000) == 0xE0000000)
#define INADDR_ANY 0 #define INADDR_ANY 0
#define INADDR_NONE 0xFFFFFFFF #define INADDR_NONE 0xFFFFFFFF

View File

@ -40,7 +40,7 @@ typedef off_t fpos_t;
#define SEEK_END 2 #define SEEK_END 2
#define FILENAME_MAX 256 #define FILENAME_MAX 256
#define FOPEN_MAX 16 #define FOPEN_MAX 64
#define TMP_MAX 10000 #define TMP_MAX 10000
#define EOF (-1) #define EOF (-1)

View File

@ -54,6 +54,9 @@ struct winsize
#define SND_GET_SAMPLE_RATE 61 /* stores sample rate to uint32_t argument */ #define SND_GET_SAMPLE_RATE 61 /* stores sample rate to uint32_t argument */
#define SND_RESET_BUFFER 62 /* stores the size of internal buffer to uint32_t argument and clears the buffer */ #define SND_RESET_BUFFER 62 /* stores the size of internal buffer to uint32_t argument and clears the buffer */
#define SND_GET_BUFFERSZ 63 /* stores the size of internal buffer to uint32_t argument */ #define SND_GET_BUFFERSZ 63 /* stores the size of internal buffer to uint32_t argument */
#define SND_GET_TOTAL_PINS 64 /* gets the number of pins on the current device */
#define SND_GET_PIN 65 /* gets the currently active pin */
#define SND_SET_PIN 66 /* sets the currently active pin */
int ioctl(int, int, ...); int ioctl(int, int, ...);

View File

@ -0,0 +1,14 @@
#ifndef _SYS_PARAM_H
#define _SYS_PARAM_H 1
#include <sys/cdefs.h>
__BEGIN_DECLS
#include <limits.h>
#define MAXPATHLEN PATH_MAX
__END_DECLS
#endif

View File

@ -14,9 +14,12 @@ __BEGIN_DECLS
#include <sys/ipc.h> #include <sys/ipc.h>
#include <unistd.h>
#define SHM_RDONLY 0x01 #define SHM_RDONLY 0x01
#define SHM_RDONLY 0x02 #define SHM_RND 0x02
#define SHM_RDONLY 0x04
#define SHMLBA (sysconf(_SC_PAGE_SIZE))
typedef unsigned int shmatt_t; typedef unsigned int shmatt_t;

View File

@ -37,16 +37,20 @@ int utimes(const char* path, const struct timeval times[2]);
do { \ do { \
(res)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ (res)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
(res)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ (res)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
(res)->tv_sec += (res)->tv_usec / 1000000; \ if ((res)->tv_usec >= 1000000) { \
(res)->tv_usec %= 1000000; \ (res)->tv_sec++; \
(res)->tv_usec -= 1000000; \
} \
} while (0) } while (0)
#define timersub(a, b, res) \ #define timersub(a, b, res) \
do { \ do { \
(res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
(res)->tv_sec += (res)->tv_usec / 1000000; \ if ((res)->tv_usec < 0) { \
(res)->tv_usec %= 1000000; \ (res)->tv_sec--; \
(res)->tv_usec += 1000000; \
} \
} while (0) } while (0)
#define timerclear(tvp) \ #define timerclear(tvp) \

View File

@ -319,3 +319,9 @@ struct servent* getservbyname(const char* name, const char* proto)
dwarnln("TODO: getservbyname(\"{}\", \"{}\")", name, proto); dwarnln("TODO: getservbyname(\"{}\", \"{}\")", name, proto);
return nullptr; return nullptr;
} }
struct protoent* getprotobynumber(int proto)
{
dwarnln("TODO: getprotobynumber({})", proto);
return nullptr;
}

View File

@ -43,7 +43,7 @@ int sem_trywait(sem_t* sem)
return -1; return -1;
} }
int sem_wait(sem_t* sem) int sem_timedwait(sem_t* __restrict sem, const struct timespec* __restrict abstime)
{ {
for (;;) for (;;)
{ {
@ -51,8 +51,13 @@ int sem_wait(sem_t* sem)
if (expected > 0 && BAN::atomic_compare_exchange(sem->value, expected, expected - 1)) if (expected > 0 && BAN::atomic_compare_exchange(sem->value, expected, expected - 1))
return 0; return 0;
const int op = FUTEX_WAIT | (sem->shared ? 0 : FUTEX_PRIVATE); const int op = FUTEX_WAIT | (sem->shared ? 0 : FUTEX_PRIVATE) | FUTEX_REALTIME;
if (futex(op, &sem->value, expected, nullptr) == -1 && errno == EINTR) if (futex(op, &sem->value, expected, abstime) == -1 && (errno == EINTR || errno == ETIMEDOUT))
return -1; return -1;
} }
} }
int sem_wait(sem_t* sem)
{
return sem_timedwait(sem, nullptr);
}

View File

@ -649,6 +649,13 @@ char* gets(char* buffer)
} }
} }
FILE* open_memstream(char** bufp, size_t* sizep)
{
(void)bufp;
(void)sizep;
ASSERT_NOT_REACHED();
}
int pclose(FILE* file) int pclose(FILE* file)
{ {
if (file->pid == -1) if (file->pid == -1)

View File

@ -851,3 +851,40 @@ void srand(unsigned int seed)
s_rand_state = seed + s_rand_increment; s_rand_state = seed + s_rand_increment;
(void)rand(); (void)rand();
} }
static constexpr size_t s_random_state_size = 31;
struct random_state_t
{
consteval random_state_t() { seed(0); }
constexpr void seed(unsigned seed)
{
uint64_t value = seed;
for (size_t i = 0; i < s_random_state_size; i++)
values[i] = value = (16807 * value) % 0x7FFFFFFF;
}
constexpr uint32_t get_next()
{
const uint32_t result = values[idx1] += values[idx2];
idx1 = (idx1 + 1) % s_random_state_size;
idx2 = (idx2 + 1) % s_random_state_size;
return result >> 1;
}
uint32_t values[s_random_state_size];
size_t idx1 = 0;
size_t idx2 = s_random_state_size - 1;
};
random_state_t s_random_state;
long random(void)
{
return s_random_state.get_next();
}
void srandom(unsigned seed)
{
s_random_state.seed(seed);
}

View File

@ -53,6 +53,11 @@ int mlock(const void*, size_t)
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
int munlock(const void*, size_t)
{
ASSERT_NOT_REACHED();
}
int shm_open(const char* name, int oflag, mode_t mode) int shm_open(const char* name, int oflag, mode_t mode)
{ {
(void)name; (void)name;

View File

@ -0,0 +1,11 @@
#include <BAN/Debug.h>
#include <errno.h>
#include <sys/shm.h>
#define TODO_FUNC(type, name, ...) type name(__VA_ARGS__) { dwarnln("TODO: " #name); errno = ENOTSUP; return (type)-1; }
TODO_FUNC(void*, shmat, int, const void*, int)
TODO_FUNC(int, shmctl, int, int, struct shmid_ds*)
TODO_FUNC(int, shmdt, const void*)
TODO_FUNC(int, shmget, key_t, size_t, int)

View File

@ -689,6 +689,30 @@ int getpagesize(void)
return PAGE_SIZE; return PAGE_SIZE;
} }
int lockf(int fildes, int function, off_t size)
{
(void)fildes;
(void)function;
(void)size;
derrorln("TODO: lockf");
ASSERT_NOT_REACHED();
}
int nice(int incr)
{
dwarnln("TODO: nice({})", incr);
errno = EPERM;
return -1;
}
char* crypt(const char* key, const char* salt)
{
(void)key;
(void)salt;
derrorln("TODO: crypt");
ASSERT_NOT_REACHED();
}
char* getpass(const char* prompt) char* getpass(const char* prompt)
{ {
static char buffer[PASS_MAX]; static char buffer[PASS_MAX];
@ -774,6 +798,12 @@ pid_t getpgid(pid_t pid)
return syscall(SYS_GET_PGID, pid); return syscall(SYS_GET_PGID, pid);
} }
pid_t getsid(pid_t pid)
{
(void)pid;
ASSERT_NOT_REACHED();
}
int tcgetpgrp(int fildes) int tcgetpgrp(int fildes)
{ {
return syscall(SYS_TCGETPGRP, fildes); return syscall(SYS_TCGETPGRP, fildes);

View File

@ -26,6 +26,7 @@ int wcwidth(wchar_t wc)
wchar_t* wcstok(wchar_t* __restrict, const wchar_t* __restrict, wchar_t** __restrict) { ASSERT_NOT_REACHED(); } wchar_t* wcstok(wchar_t* __restrict, const wchar_t* __restrict, wchar_t** __restrict) { ASSERT_NOT_REACHED(); }
long wcstol(const wchar_t* __restrict, wchar_t** __restrict, int) { ASSERT_NOT_REACHED(); } long wcstol(const wchar_t* __restrict, wchar_t** __restrict, int) { ASSERT_NOT_REACHED(); }
unsigned long wcstoul(const wchar_t* __restrict, wchar_t** __restrict, int) { ASSERT_NOT_REACHED(); }
int swprintf(wchar_t* __restrict, size_t, const wchar_t* __restrict, ...) { ASSERT_NOT_REACHED(); } int swprintf(wchar_t* __restrict, size_t, const wchar_t* __restrict, ...) { ASSERT_NOT_REACHED(); }
size_t wcrtomb(char* __restrict s, wchar_t ws, mbstate_t* __restrict ps) size_t wcrtomb(char* __restrict s, wchar_t ws, mbstate_t* __restrict ps)

View File

@ -3,15 +3,12 @@
#include <sys/banan-os.h> #include <sys/banan-os.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
AudioServer::AudioServer(int fd) AudioServer::AudioServer(BAN::Vector<AudioDevice>&& audio_devices)
: m_audio_device_fd(fd) : m_audio_devices(BAN::move(audio_devices))
{ {
if (ioctl(m_audio_device_fd, SND_GET_CHANNELS, &m_channels) != 0)
ASSERT_NOT_REACHED();
if (ioctl(m_audio_device_fd, SND_GET_SAMPLE_RATE, &m_sample_rate) != 0)
ASSERT_NOT_REACHED();
} }
BAN::ErrorOr<void> AudioServer::on_new_client(int fd) BAN::ErrorOr<void> AudioServer::on_new_client(int fd)
@ -34,29 +31,82 @@ void AudioServer::on_client_disconnect(int fd)
m_audio_buffers.remove(it); m_audio_buffers.remove(it);
reset_kernel_buffer(); reset_kernel_buffer();
update();
} }
bool AudioServer::on_client_packet(int fd, long smo_key) bool AudioServer::on_client_packet(int fd, LibAudio::Packet packet)
{ {
auto& audio_buffer = m_audio_buffers[fd]; auto& audio_buffer = m_audio_buffers[fd];
if (smo_key == 0) BAN::Optional<uint32_t> response;
switch (packet.type)
{ {
if (audio_buffer.buffer) case LibAudio::Packet::Notify:
if (audio_buffer.buffer == nullptr)
break;
reset_kernel_buffer(); reset_kernel_buffer();
return true; update();
break;
case LibAudio::Packet::RegisterBuffer:
if (audio_buffer.buffer)
{
dwarnln("Client tried to map second audio buffer??");
return false;
} }
audio_buffer.buffer = static_cast<LibAudio::AudioBuffer*>(smo_map(packet.parameter));
audio_buffer.buffer = static_cast<LibAudio::AudioBuffer*>(smo_map(smo_key));
audio_buffer.queued_head = audio_buffer.buffer->tail; audio_buffer.queued_head = audio_buffer.buffer->tail;
if (audio_buffer.buffer == nullptr) if (audio_buffer.buffer == nullptr)
{ {
dwarnln("Failed to map audio buffer: {}", strerror(errno)); dwarnln("Failed to map audio buffer: {}", strerror(errno));
return false; return false;
} }
reset_kernel_buffer(); reset_kernel_buffer();
update();
break;
case LibAudio::Packet::QueryDevices:
response = m_audio_devices.size();
break;
case LibAudio::Packet::QueryPins:
response = device().total_pins;
break;
case LibAudio::Packet::GetDevice:
response = m_current_audio_device;
break;
case LibAudio::Packet::SetDevice:
if (packet.parameter >= m_audio_devices.size())
{
dwarnln("Client tried to set device {} while there are only {}", packet.parameter, m_audio_devices.size());
return false;
}
reset_kernel_buffer();
m_current_audio_device = packet.parameter;
update();
break;
case LibAudio::Packet::GetPin:
response = device().current_pin;
break;
case LibAudio::Packet::SetPin:
if (packet.parameter >= device().total_pins)
{
dwarnln("Client tried to set pin {} while the device only has {}", packet.parameter, device().total_pins);
return false;
}
reset_kernel_buffer();
if (uint32_t pin = packet.parameter; ioctl(device().fd, SND_SET_PIN, &pin) != 0)
dwarnln("Failed to set pin {}: {}", packet.parameter, strerror(errno));
else
device().current_pin = packet.parameter;
update();
break;
default:
dwarnln("unknown packet type {}", static_cast<uint8_t>(packet.type));
return false;
}
if (response.has_value())
if (send(fd, &response.value(), sizeof(uint32_t), 0) != sizeof(uint32_t))
dwarnln("failed to respond to client :(");
return true; return true;
} }
@ -66,23 +116,25 @@ uint64_t AudioServer::update()
// FIXME: get this from the kernel // FIXME: get this from the kernel
static constexpr uint64_t kernel_buffer_ms = 50; static constexpr uint64_t kernel_buffer_ms = 50;
const auto& device = m_audio_devices[m_current_audio_device];
uint32_t kernel_buffer_size; uint32_t kernel_buffer_size;
if (ioctl(m_audio_device_fd, SND_GET_BUFFERSZ, &kernel_buffer_size) == -1) if (ioctl(device.fd, SND_GET_BUFFERSZ, &kernel_buffer_size) == -1)
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
const size_t kernel_samples = kernel_buffer_size / sizeof(int16_t); const size_t kernel_samples = kernel_buffer_size / sizeof(int16_t);
ASSERT(kernel_samples <= m_samples_sent); ASSERT(kernel_samples <= m_samples_sent);
const uint32_t samples_played = m_samples_sent - kernel_samples; const uint32_t samples_played = m_samples_sent - kernel_samples;
ASSERT(samples_played % m_channels == 0); ASSERT(samples_played % device.channels == 0);
const uint32_t sample_frames_played = samples_played / m_channels; const uint32_t sample_frames_played = samples_played / device.channels;
for (uint32_t i = 0; i < samples_played; i++) for (uint32_t i = 0; i < samples_played; i++)
m_samples.pop(); m_samples.pop();
m_samples_sent -= samples_played; m_samples_sent -= samples_played;
const size_t max_sample_frames = (m_samples.capacity() - m_samples.size()) / m_channels; const size_t max_sample_frames = (m_samples.capacity() - m_samples.size()) / device.channels;
const size_t queued_samples_end = m_samples.size(); const size_t queued_samples_end = m_samples.size();
if (max_sample_frames == 0) if (max_sample_frames == 0)
return kernel_buffer_ms; return kernel_buffer_ms;
@ -97,7 +149,7 @@ uint64_t AudioServer::update()
if (const size_t sample_frames_queued = buffer.sample_frames_queued()) if (const size_t sample_frames_queued = buffer.sample_frames_queued())
{ {
const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_sample_rate); const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(device.sample_rate);
const uint32_t buffer_sample_frames_played = BAN::Math::min<size_t>( const uint32_t buffer_sample_frames_played = BAN::Math::min<size_t>(
BAN::Math::ceil(sample_frames_played * sample_ratio), BAN::Math::ceil(sample_frames_played * sample_ratio),
sample_frames_queued sample_frames_queued
@ -115,10 +167,10 @@ uint64_t AudioServer::update()
if (!anyone_playing) if (!anyone_playing)
return 60'000; return 60'000;
const uint32_t sample_frames_per_10ms = m_sample_rate / 100; const uint32_t sample_frames_per_10ms = device.sample_rate / 100;
if (max_sample_frames_to_queue < sample_frames_per_10ms) if (max_sample_frames_to_queue < sample_frames_per_10ms)
{ {
const uint32_t sample_frames_sent = m_samples_sent / m_channels; const uint32_t sample_frames_sent = m_samples_sent / device.channels;
if (sample_frames_sent >= sample_frames_per_10ms) if (sample_frames_sent >= sample_frames_per_10ms)
return 1; return 1;
max_sample_frames_to_queue = sample_frames_per_10ms; max_sample_frames_to_queue = sample_frames_per_10ms;
@ -129,7 +181,7 @@ uint64_t AudioServer::update()
if (buffer.buffer == nullptr || buffer.buffer->paused) if (buffer.buffer == nullptr || buffer.buffer->paused)
continue; continue;
const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_sample_rate); const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(device.sample_rate);
const size_t sample_frames_to_queue = BAN::Math::min<size_t>( const size_t sample_frames_to_queue = BAN::Math::min<size_t>(
BAN::Math::ceil(buffer.sample_frames_available() / sample_ratio), BAN::Math::ceil(buffer.sample_frames_available() / sample_ratio),
@ -138,17 +190,17 @@ uint64_t AudioServer::update()
if (sample_frames_to_queue == 0) if (sample_frames_to_queue == 0)
continue; continue;
while (m_samples.size() < queued_samples_end + sample_frames_to_queue * m_channels) while (m_samples.size() < queued_samples_end + sample_frames_to_queue * device.channels)
m_samples.push(0.0); m_samples.push(0.0);
const size_t min_channels = BAN::Math::min(m_channels, buffer.buffer->channels); const size_t min_channels = BAN::Math::min(device.channels, buffer.buffer->channels);
const size_t buffer_tail = buffer.queued_head; const size_t buffer_tail = buffer.queued_head;
for (size_t i = 0; i < sample_frames_to_queue; i++) for (size_t i = 0; i < sample_frames_to_queue; i++)
{ {
const size_t buffer_frame = i * sample_ratio; const size_t buffer_frame = i * sample_ratio;
for (size_t j = 0; j < min_channels; j++) for (size_t j = 0; j < min_channels; j++)
m_samples[queued_samples_end + i * m_channels + j] += buffer.buffer->samples[(buffer_tail + buffer_frame * buffer.buffer->channels + j) % buffer.buffer->capacity]; m_samples[queued_samples_end + i * device.channels + j] += buffer.buffer->samples[(buffer_tail + buffer_frame * buffer.buffer->channels + j) % buffer.buffer->capacity];
} }
const uint32_t buffer_sample_frames_queued = BAN::Math::min<uint32_t>( const uint32_t buffer_sample_frames_queued = BAN::Math::min<uint32_t>(
@ -160,24 +212,26 @@ uint64_t AudioServer::update()
send_samples(); send_samples();
const double play_ms = 1000.0 * m_samples_sent / m_channels / m_sample_rate; const double play_ms = 1000.0 * m_samples_sent / device.channels / device.sample_rate;
const uint64_t wake_ms = BAN::Math::max<uint64_t>(play_ms, kernel_buffer_ms) - kernel_buffer_ms; const uint64_t wake_ms = BAN::Math::max<uint64_t>(play_ms, kernel_buffer_ms) - kernel_buffer_ms;
return wake_ms; return wake_ms;
} }
void AudioServer::reset_kernel_buffer() void AudioServer::reset_kernel_buffer()
{ {
const auto& device = m_audio_devices[m_current_audio_device];
uint32_t kernel_buffer_size; uint32_t kernel_buffer_size;
if (ioctl(m_audio_device_fd, SND_RESET_BUFFER, &kernel_buffer_size) != 0) if (ioctl(device.fd, SND_RESET_BUFFER, &kernel_buffer_size) != 0)
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
const size_t kernel_samples = kernel_buffer_size / sizeof(int16_t); const size_t kernel_samples = kernel_buffer_size / sizeof(int16_t);
ASSERT(kernel_samples <= m_samples_sent); ASSERT(kernel_samples <= m_samples_sent);
const uint32_t samples_played = m_samples_sent - kernel_samples; const uint32_t samples_played = m_samples_sent - kernel_samples;
ASSERT(samples_played % m_channels == 0); ASSERT(samples_played % device.channels == 0);
const uint32_t sample_frames_played = samples_played / m_channels; const uint32_t sample_frames_played = samples_played / device.channels;
m_samples_sent = 0; m_samples_sent = 0;
m_samples.clear(); m_samples.clear();
@ -189,7 +243,7 @@ void AudioServer::reset_kernel_buffer()
if (const size_t sample_frames_queued = buffer.sample_frames_queued()) if (const size_t sample_frames_queued = buffer.sample_frames_queued())
{ {
const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_sample_rate); const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(device.sample_rate);
const uint32_t buffer_sample_frames_played = BAN::Math::min<size_t>( const uint32_t buffer_sample_frames_played = BAN::Math::min<size_t>(
BAN::Math::ceil(sample_frames_played * sample_ratio), BAN::Math::ceil(sample_frames_played * sample_ratio),
sample_frames_queued sample_frames_queued
@ -198,8 +252,6 @@ void AudioServer::reset_kernel_buffer()
buffer.queued_head = buffer.buffer->tail; buffer.queued_head = buffer.buffer->tail;
} }
} }
update();
} }
void AudioServer::send_samples() void AudioServer::send_samples()
@ -221,7 +273,7 @@ void AudioServer::send_samples()
for (size_t i = 0; i < samples_to_send; i++) for (size_t i = 0; i < samples_to_send; i++)
{ {
sample_buffer[i] = BAN::Math::clamp<sample_t>( sample_buffer[i] = BAN::Math::clamp<sample_t>(
m_samples[m_samples_sent + i] * BAN::numeric_limits<kernel_sample_t>::max(), 0.2 * m_samples[m_samples_sent + i] * BAN::numeric_limits<kernel_sample_t>::max(),
BAN::numeric_limits<kernel_sample_t>::min(), BAN::numeric_limits<kernel_sample_t>::min(),
BAN::numeric_limits<kernel_sample_t>::max() BAN::numeric_limits<kernel_sample_t>::max()
); );
@ -232,7 +284,7 @@ void AudioServer::send_samples()
while (nwritten < buffer.size()) while (nwritten < buffer.size())
{ {
const ssize_t nwrite = write( const ssize_t nwrite = write(
m_audio_device_fd, device().fd,
buffer.data() + nwritten, buffer.data() + nwritten,
buffer.size() - nwritten buffer.size() - nwritten
); );

View File

@ -6,6 +6,16 @@
#include <BAN/HashMap.h> #include <BAN/HashMap.h>
#include <LibAudio/Audio.h> #include <LibAudio/Audio.h>
#include <LibAudio/Protocol.h>
struct AudioDevice
{
int fd;
uint32_t channels;
uint32_t sample_rate;
uint32_t total_pins;
uint32_t current_pin;
};
class AudioServer class AudioServer
{ {
@ -13,14 +23,17 @@ class AudioServer
BAN_NON_COPYABLE(AudioServer); BAN_NON_COPYABLE(AudioServer);
public: public:
AudioServer(int audio_device_fd); AudioServer(BAN::Vector<AudioDevice>&& audio_devices);
BAN::ErrorOr<void> on_new_client(int fd); BAN::ErrorOr<void> on_new_client(int fd);
void on_client_disconnect(int fd); void on_client_disconnect(int fd);
bool on_client_packet(int fd, long smo_key); bool on_client_packet(int fd, LibAudio::Packet);
uint64_t update(); uint64_t update();
private:
AudioDevice& device() { return m_audio_devices[m_current_audio_device]; }
private: private:
struct ClientInfo struct ClientInfo
{ {
@ -48,12 +61,11 @@ private:
void send_samples(); void send_samples();
private: private:
const int m_audio_device_fd; BAN::Vector<AudioDevice> m_audio_devices;
uint32_t m_sample_rate; size_t m_current_audio_device { 0 };
uint32_t m_channels;
size_t m_samples_sent { 0 }; size_t m_samples_sent { 0 };
BAN::Array<uint8_t, 1024> m_send_buffer; BAN::Array<uint8_t, 4 * 1024> m_send_buffer;
BAN::CircularQueue<sample_t, 64 * 1024> m_samples; BAN::CircularQueue<sample_t, 64 * 1024> m_samples;
BAN::HashMap<int, ClientInfo> m_audio_buffers; BAN::HashMap<int, ClientInfo> m_audio_buffers;

View File

@ -5,6 +5,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -55,6 +56,21 @@ static uint64_t get_current_ms()
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
} }
static BAN::Optional<AudioDevice> initialize_audio_device(int fd)
{
AudioDevice result {};
result.fd = fd;
if (ioctl(fd, SND_GET_CHANNELS, &result.channels) != 0)
return {};
if (ioctl(fd, SND_GET_SAMPLE_RATE, &result.sample_rate) != 0)
return {};
if (ioctl(fd, SND_GET_TOTAL_PINS, &result.total_pins) != 0)
return {};
if (ioctl(fd, SND_GET_PIN, &result.current_pin) != 0)
return {};
return result;
}
int main() int main()
{ {
constexpr int non_terminating_signals[] { constexpr int non_terminating_signals[] {
@ -69,14 +85,30 @@ int main()
for (int sig : non_terminating_signals) for (int sig : non_terminating_signals)
signal(sig, SIG_DFL); signal(sig, SIG_DFL);
const int audio_device_fd = open("/dev/audio0", O_RDWR | O_NONBLOCK); BAN::Vector<AudioDevice> audio_devices;
if (audio_device_fd == -1) for (int i = 0; i < 16; i++)
{ {
dwarnln("failed to open audio device: {}", strerror(errno)); char path[PATH_MAX];
sprintf(path, "/dev/audio%d", i);
const int fd = open(path, O_RDWR | O_NONBLOCK);
if (fd == -1)
continue;
auto device = initialize_audio_device(fd);
if (!device.has_value())
close(fd);
else
MUST(audio_devices.push_back(device.release_value()));
}
if (audio_devices.empty())
{
dwarnln("could not open any audio device");
return 1; return 1;
} }
auto* audio_server = new AudioServer(audio_device_fd); auto* audio_server = new AudioServer(BAN::move(audio_devices));
if (audio_server == nullptr) if (audio_server == nullptr)
{ {
dwarnln("Failed to allocate AudioServer: {}", strerror(errno)); dwarnln("Failed to allocate AudioServer: {}", strerror(errno));
@ -157,17 +189,17 @@ int main()
if (!FD_ISSET(client.fd, &fds)) if (!FD_ISSET(client.fd, &fds))
continue; continue;
long smo_key; LibAudio::Packet packet;
const ssize_t nrecv = recv(client.fd, &smo_key, sizeof(smo_key), 0); const ssize_t nrecv = recv(client.fd, &packet, sizeof(packet), 0);
if (nrecv < static_cast<ssize_t>(sizeof(smo_key)) || !audio_server->on_client_packet(client.fd, smo_key)) if (nrecv < static_cast<ssize_t>(sizeof(packet)) || !audio_server->on_client_packet(client.fd, packet))
{ {
if (nrecv == 0) if (nrecv == 0)
; ;
else if (nrecv < 0) else if (nrecv < 0)
dwarnln("recv: {}", strerror(errno)); dwarnln("recv: {}", strerror(errno));
else if (nrecv < static_cast<ssize_t>(sizeof(smo_key))) else if (nrecv < static_cast<ssize_t>(sizeof(packet)))
dwarnln("client sent only {} bytes, {} expected", nrecv, sizeof(smo_key)); dwarnln("client sent only {} bytes, {} expected", nrecv, sizeof(packet));
audio_server->on_client_disconnect(client.fd); audio_server->on_client_disconnect(client.fd);
close(client.fd); close(client.fd);

View File

@ -1,5 +1,6 @@
set(USERSPACE_PROGRAMS set(USERSPACE_PROGRAMS
audio audio
audioctl
AudioServer AudioServer
bananfetch bananfetch
basename basename

View File

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

View File

@ -0,0 +1,165 @@
#include <BAN/Optional.h>
#include <LibAudio/Protocol.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
static uint32_t parse_u32_or_exit(const char* string)
{
errno = 0;
char* endptr;
const uint32_t result = strtoul(string, &endptr, 0);
if (errno || *endptr != '\0')
{
fprintf(stderr, "invalid integer %s\n", string);
exit(1);
}
return result;
}
static int get_server_fd()
{
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1)
{
perror("Failed to create a socket");
return -1;
}
sockaddr_un addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, LibAudio::s_audio_server_socket.data());
if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
{
perror("Failed to connect to audio server");
return -1;
}
return fd;
}
static uint32_t send_request(int fd, LibAudio::Packet packet, bool wait_response)
{
if (ssize_t ret = send(fd, &packet, sizeof(packet), 0); ret != sizeof(packet))
{
fprintf(stderr, "Failed to send request to server");
if (ret < 0)
fprintf(stderr, ": %s", strerror(errno));
fprintf(stderr, "\n");
exit(1);
}
if (!wait_response)
return 0;
uint32_t response;
if (ssize_t ret = recv(fd, &response, sizeof(response), 0) != sizeof(response))
{
fprintf(stderr, "Failed to receive response from server");
if (ret < 0)
fprintf(stderr, ": %s", strerror(errno));
fprintf(stderr, "\n");
exit(1);
}
return response;
}
static void list_devices(int fd)
{
const uint32_t current_device = send_request(fd, { .type = LibAudio::Packet::GetDevice, .parameter = 0 }, true);
const uint32_t current_pin = send_request(fd, { .type = LibAudio::Packet::GetPin, .parameter = 0 }, true);
const uint32_t total_devices = send_request(fd, { .type = LibAudio::Packet::QueryDevices, .parameter = 0 }, true);
for (uint32_t dev = 0; dev < total_devices; dev++)
{
const uint32_t total_pins = send_request(fd, { .type = LibAudio::Packet::QueryPins, .parameter = dev }, true);
printf("Device %" PRIu32 "", dev);
if (dev == current_device)
printf(" (current)");
printf("\n");
for (uint32_t pin = 0; pin < total_pins; pin++)
{
printf(" Pin %" PRIu32 "", pin);
if (dev == current_device && pin == current_pin)
printf(" (current)");
printf("\n");
}
}
}
int main(int argc, char** argv)
{
bool list { false };
BAN::Optional<uint32_t> device;
BAN::Optional<uint32_t> pin;
for (;;)
{
static option long_options[] {
{ "list", no_argument, nullptr, 'l' },
{ "device", required_argument, nullptr, 'd' },
{ "pin", required_argument, nullptr, 'p' },
{ "help", no_argument, nullptr, 'h' },
};
int ch = getopt_long(argc, argv, "ld:p:h", long_options, nullptr);
if (ch == -1)
break;
switch (ch)
{
case 'h':
fprintf(stderr, "usage: %s [OPTIONS]...\n", argv[0]);
fprintf(stderr, " control the audio server\n");
fprintf(stderr, "OPTIONS:\n");
fprintf(stderr, " -l, --list list devices and their pins\n");
fprintf(stderr, " -d, --device N set device index N as the current one\n");
fprintf(stderr, " -p, --pin N set pin N as the current one\n");
fprintf(stderr, " -h, --help show this message and exit\n");
return 0;
case 'l':
list = true;
break;
case 'd':
device = parse_u32_or_exit(optarg);
break;
case 'p':
pin = parse_u32_or_exit(optarg);
break;
case '?':
fprintf(stderr, "invalid option %c\n", optopt);
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}
}
if (!device.has_value() && !pin.has_value())
list = true;
const int fd = get_server_fd();
if (fd == -1)
return 1;
if (device.has_value())
send_request(fd, { .type = LibAudio::Packet::SetDevice, .parameter = device.value() }, false);
if (pin.has_value())
send_request(fd, { .type = LibAudio::Packet::SetPin, .parameter = pin.value() }, false);
if (list)
list_devices(fd);
close(fd);
return 0;
}

View File

@ -78,6 +78,7 @@ int main(int argc, char** argv)
if (argc < 2) if (argc < 2)
return usage(argv[0], 1); return usage(argv[0], 1);
auto alg = LibImage::Image::ResizeAlgorithm::Cubic;
bool scale = false; bool scale = false;
bool benchmark = false; bool benchmark = false;
for (int i = 1; i < argc - 1; i++) for (int i = 1; i < argc - 1; i++)
@ -86,6 +87,10 @@ int main(int argc, char** argv)
scale = true; scale = true;
else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--benchmark") == 0) else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--benchmark") == 0)
benchmark = true; benchmark = true;
else if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--linear") == 0)
alg = LibImage::Image::ResizeAlgorithm::Linear;
else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--cubic") == 0)
alg = LibImage::Image::ResizeAlgorithm::Cubic;
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
return usage(argv[0], 0); return usage(argv[0], 0);
else else
@ -95,9 +100,9 @@ int main(int argc, char** argv)
auto image_path = BAN::StringView(argv[argc - 1]); auto image_path = BAN::StringView(argv[argc - 1]);
timespec load_start, load_end; timespec load_start, load_end;
clock_gettime(CLOCK_MONOTONIC, &load_start); clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &load_start);
auto image_or_error = LibImage::Image::load_from_file(image_path); auto image_or_error = LibImage::Image::load_from_file(image_path);
clock_gettime(CLOCK_MONOTONIC, &load_end); clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &load_end);
if (image_or_error.is_error()) if (image_or_error.is_error())
{ {
@ -120,9 +125,9 @@ int main(int argc, char** argv)
{ {
timespec scale_start, scale_end; timespec scale_start, scale_end;
clock_gettime(CLOCK_MONOTONIC, &scale_start); clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &scale_start);
auto scaled = MUST(image_or_error.value()->resize(1920, 1080, LibImage::Image::ResizeAlgorithm::Linear)); auto scaled = MUST(image_or_error.value()->resize(1920, 1080, alg));
clock_gettime(CLOCK_MONOTONIC, &scale_end); clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &scale_end);
const uint64_t start_ms = scale_start.tv_sec * 1000 + scale_start.tv_nsec / 1'000'000; const uint64_t start_ms = scale_start.tv_sec * 1000 + scale_start.tv_nsec / 1'000'000;
const uint64_t end_ms = scale_end.tv_sec * 1000 + scale_end.tv_nsec / 1'000'000; const uint64_t end_ms = scale_end.tv_sec * 1000 + scale_end.tv_nsec / 1'000'000;

View File

@ -32,19 +32,25 @@ int main(int argc, char** argv)
if (fork() == 0) if (fork() == 0)
{ {
execl("/bin/dhcp-client", "dhcp-client", NULL); execl("/usr/bin/dhcp-client", "dhcp-client", NULL);
exit(1); exit(1);
} }
if (fork() == 0) if (fork() == 0)
{ {
execl("/bin/resolver", "resolver", NULL); execl("/usr/bin/resolver", "resolver", NULL);
exit(1); exit(1);
} }
if (fork() == 0) if (fork() == 0)
{ {
execl("/bin/AudioServer", "AudioServer", NULL); execl("/usr/bin/AudioServer", "AudioServer", NULL);
exit(1);
}
if (fork() == 0)
{
execl("/usr/bin/ClipboardServer", "ClipboardServer", NULL);
exit(1); exit(1);
} }

View File

@ -16,9 +16,10 @@ int create_directory(const char* path, bool create_parents)
if (!create_parents) if (!create_parents)
{ {
const int ret = mkdir(path, 0755); const int ret = mkdir(path, 0755);
if (ret == -1) if (ret == 0)
return 0;
perror("mkdir"); perror("mkdir");
return -ret; return -1;
} }
int ret = 0; int ret = 0;
@ -30,7 +31,11 @@ int create_directory(const char* path, bool create_parents)
for (; path[i] && path[i] == '/'; i++) for (; path[i] && path[i] == '/'; i++)
buffer[i] = path[i]; buffer[i] = path[i];
buffer[i] = '\0'; buffer[i] = '\0';
ret = mkdir(buffer, 0755); if (mkdir(buffer, 0755) == -1 && errno != EEXIST)
{
perror("mkdir");
ret = -1;
}
} }
return ret; return ret;

View File

@ -103,7 +103,7 @@ int main(int argc, char** argv)
return 1; return 1;
} }
int mouse_fd = open(mouse_path, O_RDONLY); mouse_fd = open(mouse_path, O_RDONLY);
if (mouse_fd == -1) if (mouse_fd == -1)
{ {
fprintf(stderr, "open: "); fprintf(stderr, "open: ");