Compare commits
No commits in common. "a5318448f51882cde44e7035fe123d6218bd5f8e" and "4b5a8196c37502110f09e2f055bab5ea622242e9" have entirely different histories.
a5318448f5
...
4b5a8196c3
|
|
@ -9,8 +9,6 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Kernel
|
||||||
class AC97AudioController : public AudioController, public Interruptable
|
class AC97AudioController : public AudioController, public Interruptable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<void> create(PCI::Device& pci_device);
|
static BAN::ErrorOr<BAN::RefPtr<AC97AudioController>> create(PCI::Device& pci_device);
|
||||||
|
|
||||||
void handle_irq() override;
|
void handle_irq() override;
|
||||||
|
|
||||||
|
|
@ -19,10 +19,6 @@ 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)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Kernel
|
||||||
class AudioController : public CharacterDevice
|
class AudioController : public CharacterDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<void> create(PCI::Device& pci_device);
|
static BAN::ErrorOr<BAN::RefPtr<AudioController>> 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,10 +22,6 @@ 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; }
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
#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 };
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
#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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
#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,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
#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,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -70,8 +70,6 @@
|
||||||
#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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#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
|
||||||
{
|
{
|
||||||
|
|
@ -106,14 +105,14 @@ namespace Kernel
|
||||||
IOCE = 1 << 4,
|
IOCE = 1 << 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
BAN::ErrorOr<void> AC97AudioController::create(PCI::Device& pci_device)
|
BAN::ErrorOr<BAN::RefPtr<AC97AudioController>> 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 {};
|
return ac97;
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> AC97AudioController::initialize()
|
BAN::ErrorOr<void> AC97AudioController::initialize()
|
||||||
|
|
@ -148,8 +147,6 @@ 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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#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>
|
||||||
|
|
@ -21,26 +20,23 @@ 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<void> AudioController::create(PCI::Device& pci_device)
|
BAN::ErrorOr<BAN::RefPtr<AudioController>> 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(),
|
||||||
|
|
@ -49,10 +45,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)
|
||||||
{
|
{
|
||||||
SpinLockGuard lock_guard(m_spinlock);
|
SpinLockGuard lock_guard(m_spinlock);
|
||||||
|
|
@ -102,15 +97,6 @@ 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);
|
||||||
|
|
|
||||||
|
|
@ -1,568 +0,0 @@
|
||||||
#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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,492 +0,0 @@
|
||||||
#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 (?)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
InterruptController::get().eoi(irq);
|
if (InterruptController::get().is_in_service(irq))
|
||||||
if (auto* handler = s_interruptables[irq])
|
{
|
||||||
handler->handle_irq();
|
InterruptController::get().eoi(irq);
|
||||||
else
|
if (auto* handler = s_interruptables[irq])
|
||||||
dprintln("no handler for irq 0x{2H}", irq);
|
handler->handle_irq();
|
||||||
|
else
|
||||||
|
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())
|
||||||
|
|
|
||||||
|
|
@ -252,46 +252,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#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>
|
||||||
|
|
@ -122,15 +121,10 @@ namespace LibAudio
|
||||||
{
|
{
|
||||||
ASSERT(m_server_fd != -1);
|
ASSERT(m_server_fd != -1);
|
||||||
|
|
||||||
LibAudio::Packet packet {
|
const ssize_t nsend = send(m_server_fd, &m_smo_key, sizeof(m_smo_key), 0);
|
||||||
.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(packet));
|
ASSERT(nsend == sizeof(m_smo_key));
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -143,12 +137,8 @@ namespace LibAudio
|
||||||
return;
|
return;
|
||||||
m_audio_buffer->paused = paused;
|
m_audio_buffer->paused = paused;
|
||||||
|
|
||||||
LibAudio::Packet packet {
|
long dummy = 0;
|
||||||
.type = LibAudio::Packet::Notify,
|
send(m_server_fd, &dummy, sizeof(dummy), 0);
|
||||||
.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)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
#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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -49,7 +49,6 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -62,30 +62,8 @@ enum
|
||||||
#define IPV6_V6ONLY IPV6_V6ONLY
|
#define IPV6_V6ONLY IPV6_V6ONLY
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IN_CLASSA(a) ((((in_addr_t)(a)) & 0x80000000) == 0)
|
#define IN_LOOPBACKNET 127
|
||||||
#define IN_CLASSA_NET 0xFF000000
|
#define IN_MULTICAST(a) (((in_addr_t)(a) & 0xF0000000) == 0xE0000000)
|
||||||
#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 INADDR_ANY 0
|
#define INADDR_ANY 0
|
||||||
#define INADDR_NONE 0xFFFFFFFF
|
#define INADDR_NONE 0xFFFFFFFF
|
||||||
|
|
|
||||||
|
|
@ -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 64
|
#define FOPEN_MAX 16
|
||||||
#define TMP_MAX 10000
|
#define TMP_MAX 10000
|
||||||
|
|
||||||
#define EOF (-1)
|
#define EOF (-1)
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,6 @@ 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, ...);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -14,12 +14,9 @@ __BEGIN_DECLS
|
||||||
|
|
||||||
#include <sys/ipc.h>
|
#include <sys/ipc.h>
|
||||||
|
|
||||||
#include <unistd.h>
|
#define SHM_RDONLY 0x01
|
||||||
|
#define SHM_RDONLY 0x02
|
||||||
#define SHM_RDONLY 0x01
|
#define SHM_RDONLY 0x04
|
||||||
#define SHM_RND 0x02
|
|
||||||
|
|
||||||
#define SHMLBA (sysconf(_SC_PAGE_SIZE))
|
|
||||||
|
|
||||||
typedef unsigned int shmatt_t;
|
typedef unsigned int shmatt_t;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,20 +37,16 @@ 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; \
|
||||||
if ((res)->tv_usec >= 1000000) { \
|
(res)->tv_sec += (res)->tv_usec / 1000000; \
|
||||||
(res)->tv_sec++; \
|
(res)->tv_usec %= 1000000; \
|
||||||
(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; \
|
||||||
if ((res)->tv_usec < 0) { \
|
(res)->tv_sec += (res)->tv_usec / 1000000; \
|
||||||
(res)->tv_sec--; \
|
(res)->tv_usec %= 1000000; \
|
||||||
(res)->tv_usec += 1000000; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define timerclear(tvp) \
|
#define timerclear(tvp) \
|
||||||
|
|
|
||||||
|
|
@ -319,9 +319,3 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ int sem_trywait(sem_t* sem)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sem_timedwait(sem_t* __restrict sem, const struct timespec* __restrict abstime)
|
int sem_wait(sem_t* sem)
|
||||||
{
|
{
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
|
@ -51,13 +51,8 @@ int sem_timedwait(sem_t* __restrict sem, const struct timespec* __restrict absti
|
||||||
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) | FUTEX_REALTIME;
|
const int op = FUTEX_WAIT | (sem->shared ? 0 : FUTEX_PRIVATE);
|
||||||
if (futex(op, &sem->value, expected, abstime) == -1 && (errno == EINTR || errno == ETIMEDOUT))
|
if (futex(op, &sem->value, expected, nullptr) == -1 && errno == EINTR)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int sem_wait(sem_t* sem)
|
|
||||||
{
|
|
||||||
return sem_timedwait(sem, nullptr);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -649,13 +649,6 @@ 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)
|
||||||
|
|
|
||||||
|
|
@ -851,40 +851,3 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,6 @@ 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;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
#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)
|
|
||||||
|
|
@ -689,30 +689,6 @@ 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];
|
||||||
|
|
@ -798,12 +774,6 @@ 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);
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ 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)
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,15 @@
|
||||||
#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(BAN::Vector<AudioDevice>&& audio_devices)
|
AudioServer::AudioServer(int fd)
|
||||||
: m_audio_devices(BAN::move(audio_devices))
|
: m_audio_device_fd(fd)
|
||||||
{
|
{
|
||||||
|
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)
|
||||||
|
|
@ -31,82 +34,29 @@ 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, LibAudio::Packet packet)
|
bool AudioServer::on_client_packet(int fd, long smo_key)
|
||||||
{
|
{
|
||||||
auto& audio_buffer = m_audio_buffers[fd];
|
auto& audio_buffer = m_audio_buffers[fd];
|
||||||
|
|
||||||
BAN::Optional<uint32_t> response;
|
if (smo_key == 0)
|
||||||
|
|
||||||
switch (packet.type)
|
|
||||||
{
|
{
|
||||||
case LibAudio::Packet::Notify:
|
if (audio_buffer.buffer)
|
||||||
if (audio_buffer.buffer == nullptr)
|
|
||||||
break;
|
|
||||||
reset_kernel_buffer();
|
reset_kernel_buffer();
|
||||||
update();
|
return true;
|
||||||
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.queued_head = audio_buffer.buffer->tail;
|
|
||||||
if (audio_buffer.buffer == nullptr)
|
|
||||||
{
|
|
||||||
dwarnln("Failed to map audio buffer: {}", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
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())
|
audio_buffer.buffer = static_cast<LibAudio::AudioBuffer*>(smo_map(smo_key));
|
||||||
if (send(fd, &response.value(), sizeof(uint32_t), 0) != sizeof(uint32_t))
|
audio_buffer.queued_head = audio_buffer.buffer->tail;
|
||||||
dwarnln("failed to respond to client :(");
|
|
||||||
|
if (audio_buffer.buffer == nullptr)
|
||||||
|
{
|
||||||
|
dwarnln("Failed to map audio buffer: {}", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_kernel_buffer();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -116,25 +66,23 @@ 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(device.fd, SND_GET_BUFFERSZ, &kernel_buffer_size) == -1)
|
if (ioctl(m_audio_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 % device.channels == 0);
|
ASSERT(samples_played % m_channels == 0);
|
||||||
|
|
||||||
const uint32_t sample_frames_played = samples_played / device.channels;
|
const uint32_t sample_frames_played = samples_played / m_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()) / device.channels;
|
const size_t max_sample_frames = (m_samples.capacity() - m_samples.size()) / m_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;
|
||||||
|
|
@ -149,7 +97,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>(device.sample_rate);
|
const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_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
|
||||||
|
|
@ -167,10 +115,10 @@ uint64_t AudioServer::update()
|
||||||
if (!anyone_playing)
|
if (!anyone_playing)
|
||||||
return 60'000;
|
return 60'000;
|
||||||
|
|
||||||
const uint32_t sample_frames_per_10ms = device.sample_rate / 100;
|
const uint32_t sample_frames_per_10ms = m_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 / device.channels;
|
const uint32_t sample_frames_sent = m_samples_sent / m_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;
|
||||||
|
|
@ -181,7 +129,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>(device.sample_rate);
|
const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_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),
|
||||||
|
|
@ -190,17 +138,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 * device.channels)
|
while (m_samples.size() < queued_samples_end + sample_frames_to_queue * m_channels)
|
||||||
m_samples.push(0.0);
|
m_samples.push(0.0);
|
||||||
|
|
||||||
const size_t min_channels = BAN::Math::min(device.channels, buffer.buffer->channels);
|
const size_t min_channels = BAN::Math::min(m_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 * device.channels + j] += buffer.buffer->samples[(buffer_tail + buffer_frame * buffer.buffer->channels + j) % buffer.buffer->capacity];
|
m_samples[queued_samples_end + i * m_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>(
|
||||||
|
|
@ -212,26 +160,24 @@ uint64_t AudioServer::update()
|
||||||
|
|
||||||
send_samples();
|
send_samples();
|
||||||
|
|
||||||
const double play_ms = 1000.0 * m_samples_sent / device.channels / device.sample_rate;
|
const double play_ms = 1000.0 * m_samples_sent / m_channels / m_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(device.fd, SND_RESET_BUFFER, &kernel_buffer_size) != 0)
|
if (ioctl(m_audio_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 % device.channels == 0);
|
ASSERT(samples_played % m_channels == 0);
|
||||||
|
|
||||||
const uint32_t sample_frames_played = samples_played / device.channels;
|
const uint32_t sample_frames_played = samples_played / m_channels;
|
||||||
|
|
||||||
m_samples_sent = 0;
|
m_samples_sent = 0;
|
||||||
m_samples.clear();
|
m_samples.clear();
|
||||||
|
|
@ -243,7 +189,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>(device.sample_rate);
|
const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_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
|
||||||
|
|
@ -252,6 +198,8 @@ 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()
|
||||||
|
|
@ -273,7 +221,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>(
|
||||||
0.2 * m_samples[m_samples_sent + i] * BAN::numeric_limits<kernel_sample_t>::max(),
|
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()
|
||||||
);
|
);
|
||||||
|
|
@ -284,7 +232,7 @@ void AudioServer::send_samples()
|
||||||
while (nwritten < buffer.size())
|
while (nwritten < buffer.size())
|
||||||
{
|
{
|
||||||
const ssize_t nwrite = write(
|
const ssize_t nwrite = write(
|
||||||
device().fd,
|
m_audio_device_fd,
|
||||||
buffer.data() + nwritten,
|
buffer.data() + nwritten,
|
||||||
buffer.size() - nwritten
|
buffer.size() - nwritten
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,6 @@
|
||||||
#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
|
||||||
{
|
{
|
||||||
|
|
@ -23,17 +13,14 @@ class AudioServer
|
||||||
BAN_NON_COPYABLE(AudioServer);
|
BAN_NON_COPYABLE(AudioServer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AudioServer(BAN::Vector<AudioDevice>&& audio_devices);
|
AudioServer(int audio_device_fd);
|
||||||
|
|
||||||
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, LibAudio::Packet);
|
bool on_client_packet(int fd, long smo_key);
|
||||||
|
|
||||||
uint64_t update();
|
uint64_t update();
|
||||||
|
|
||||||
private:
|
|
||||||
AudioDevice& device() { return m_audio_devices[m_current_audio_device]; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ClientInfo
|
struct ClientInfo
|
||||||
{
|
{
|
||||||
|
|
@ -61,11 +48,12 @@ private:
|
||||||
void send_samples();
|
void send_samples();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::Vector<AudioDevice> m_audio_devices;
|
const int m_audio_device_fd;
|
||||||
size_t m_current_audio_device { 0 };
|
uint32_t m_sample_rate;
|
||||||
|
uint32_t m_channels;
|
||||||
|
|
||||||
size_t m_samples_sent { 0 };
|
size_t m_samples_sent { 0 };
|
||||||
BAN::Array<uint8_t, 4 * 1024> m_send_buffer;
|
BAN::Array<uint8_t, 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;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
#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>
|
||||||
|
|
@ -56,21 +55,6 @@ 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[] {
|
||||||
|
|
@ -85,30 +69,14 @@ int main()
|
||||||
for (int sig : non_terminating_signals)
|
for (int sig : non_terminating_signals)
|
||||||
signal(sig, SIG_DFL);
|
signal(sig, SIG_DFL);
|
||||||
|
|
||||||
BAN::Vector<AudioDevice> audio_devices;
|
const int audio_device_fd = open("/dev/audio0", O_RDWR | O_NONBLOCK);
|
||||||
for (int i = 0; i < 16; i++)
|
if (audio_device_fd == -1)
|
||||||
{
|
{
|
||||||
char path[PATH_MAX];
|
dwarnln("failed to open audio device: {}", strerror(errno));
|
||||||
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(BAN::move(audio_devices));
|
auto* audio_server = new AudioServer(audio_device_fd);
|
||||||
if (audio_server == nullptr)
|
if (audio_server == nullptr)
|
||||||
{
|
{
|
||||||
dwarnln("Failed to allocate AudioServer: {}", strerror(errno));
|
dwarnln("Failed to allocate AudioServer: {}", strerror(errno));
|
||||||
|
|
@ -189,17 +157,17 @@ int main()
|
||||||
if (!FD_ISSET(client.fd, &fds))
|
if (!FD_ISSET(client.fd, &fds))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
LibAudio::Packet packet;
|
long smo_key;
|
||||||
const ssize_t nrecv = recv(client.fd, &packet, sizeof(packet), 0);
|
const ssize_t nrecv = recv(client.fd, &smo_key, sizeof(smo_key), 0);
|
||||||
|
|
||||||
if (nrecv < static_cast<ssize_t>(sizeof(packet)) || !audio_server->on_client_packet(client.fd, packet))
|
if (nrecv < static_cast<ssize_t>(sizeof(smo_key)) || !audio_server->on_client_packet(client.fd, smo_key))
|
||||||
{
|
{
|
||||||
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(packet)))
|
else if (nrecv < static_cast<ssize_t>(sizeof(smo_key)))
|
||||||
dwarnln("client sent only {} bytes, {} expected", nrecv, sizeof(packet));
|
dwarnln("client sent only {} bytes, {} expected", nrecv, sizeof(smo_key));
|
||||||
|
|
||||||
audio_server->on_client_disconnect(client.fd);
|
audio_server->on_client_disconnect(client.fd);
|
||||||
close(client.fd);
|
close(client.fd);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
set(USERSPACE_PROGRAMS
|
set(USERSPACE_PROGRAMS
|
||||||
audio
|
audio
|
||||||
audioctl
|
|
||||||
AudioServer
|
AudioServer
|
||||||
bananfetch
|
bananfetch
|
||||||
basename
|
basename
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
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)
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
@ -78,7 +78,6 @@ 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++)
|
||||||
|
|
@ -87,10 +86,6 @@ 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
|
||||||
|
|
@ -100,9 +95,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_PROCESS_CPUTIME_ID, &load_start);
|
clock_gettime(CLOCK_MONOTONIC, &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_PROCESS_CPUTIME_ID, &load_end);
|
clock_gettime(CLOCK_MONOTONIC, &load_end);
|
||||||
|
|
||||||
if (image_or_error.is_error())
|
if (image_or_error.is_error())
|
||||||
{
|
{
|
||||||
|
|
@ -125,9 +120,9 @@ int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
timespec scale_start, scale_end;
|
timespec scale_start, scale_end;
|
||||||
|
|
||||||
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &scale_start);
|
clock_gettime(CLOCK_MONOTONIC, &scale_start);
|
||||||
auto scaled = MUST(image_or_error.value()->resize(1920, 1080, alg));
|
auto scaled = MUST(image_or_error.value()->resize(1920, 1080, LibImage::Image::ResizeAlgorithm::Linear));
|
||||||
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &scale_end);
|
clock_gettime(CLOCK_MONOTONIC, &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;
|
||||||
|
|
|
||||||
|
|
@ -32,25 +32,19 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
if (fork() == 0)
|
if (fork() == 0)
|
||||||
{
|
{
|
||||||
execl("/usr/bin/dhcp-client", "dhcp-client", NULL);
|
execl("/bin/dhcp-client", "dhcp-client", NULL);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fork() == 0)
|
if (fork() == 0)
|
||||||
{
|
{
|
||||||
execl("/usr/bin/resolver", "resolver", NULL);
|
execl("/bin/resolver", "resolver", NULL);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fork() == 0)
|
if (fork() == 0)
|
||||||
{
|
{
|
||||||
execl("/usr/bin/AudioServer", "AudioServer", NULL);
|
execl("/bin/AudioServer", "AudioServer", NULL);
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fork() == 0)
|
|
||||||
{
|
|
||||||
execl("/usr/bin/ClipboardServer", "ClipboardServer", NULL);
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,9 @@ 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 == 0)
|
if (ret == -1)
|
||||||
return 0;
|
perror("mkdir");
|
||||||
perror("mkdir");
|
return -ret;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
@ -31,11 +30,7 @@ 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';
|
||||||
if (mkdir(buffer, 0755) == -1 && errno != EEXIST)
|
ret = mkdir(buffer, 0755);
|
||||||
{
|
|
||||||
perror("mkdir");
|
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ int main(int argc, char** argv)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mouse_fd = open(mouse_path, O_RDONLY);
|
int mouse_fd = open(mouse_path, O_RDONLY);
|
||||||
if (mouse_fd == -1)
|
if (mouse_fd == -1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "open: ");
|
fprintf(stderr, "open: ");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue