Kernel/Audio: Introduce a new design architecture for the subsystem

We have 3 new components:
1. The AudioManagement singleton. This class like in other subsystems,
is responsible to find hardware audio controllers and keep a reference
to them.
2. AudioController class - this class is the parent class for hardware
controllers like the Sound Blaster 16 or Intel 82801AA (AC97). For now,
this class has simple interface for getting and controlling sample rate
of audio channels, as well a write interface for specific audio channel
but not reading from it. One AudioController object might have multiple
AudioChannel "child" objects to hold with reference counting.
3. AudioChannel class - this is based on the CharacterDevice class, and
represents hardware PCM audio channel. It facilitates an ioctl interface
which should be consistent across all supported hardware currently.
It has a weak reference to a parent AudioController, and when trying to
write to a channel, it redirects the data to the parent AudioController.
Each audio channel device should be added into a new directory under the
/dev filesystem called "audio".
This commit is contained in:
Liav A 2022-02-11 21:47:45 +02:00 committed by Andreas Kling
parent 6218ec8afa
commit 6efa27537a
Notes: sideshowbarker 2024-07-18 04:38:32 +09:00
13 changed files with 382 additions and 127 deletions

View file

@ -46,6 +46,8 @@ set(KERNEL_SOURCES
Coredump.cpp Coredump.cpp
Devices/AsyncDeviceRequest.cpp Devices/AsyncDeviceRequest.cpp
Devices/Audio/AC97.cpp Devices/Audio/AC97.cpp
Devices/Audio/Channel.cpp
Devices/Audio/Management.cpp
Devices/Audio/SB16.cpp Devices/Audio/SB16.cpp
Devices/BlockDevice.cpp Devices/BlockDevice.cpp
Devices/CharacterDevice.cpp Devices/CharacterDevice.cpp

View file

@ -7,7 +7,6 @@
#include <Kernel/Devices/Audio/AC97.h> #include <Kernel/Devices/Audio/AC97.h>
#include <Kernel/Devices/DeviceManagement.h> #include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Memory/AnonymousVMObject.h> #include <Kernel/Memory/AnonymousVMObject.h>
#include <LibC/sys/ioctl_numbers.h>
namespace Kernel { namespace Kernel {
@ -20,28 +19,14 @@ static constexpr u16 pcm_fixed_sample_rate = 48000;
static constexpr u16 pcm_sample_rate_minimum = 8000; static constexpr u16 pcm_sample_rate_minimum = 8000;
static constexpr u16 pcm_sample_rate_maximum = 48000; static constexpr u16 pcm_sample_rate_maximum = 48000;
UNMAP_AFTER_INIT void AC97::detect() UNMAP_AFTER_INIT ErrorOr<NonnullRefPtr<AC97>> AC97::try_create(PCI::DeviceIdentifier const& pci_device_identifier)
{ {
PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) { return adopt_nonnull_ref_or_enomem(new (nothrow) AC97(pci_device_identifier));
// Only consider PCI audio controllers
if (device_identifier.class_code().value() != to_underlying(PCI::ClassID::Multimedia)
|| device_identifier.subclass_code().value() != to_underlying(PCI::Multimedia::SubclassID::AudioController))
return;
dbgln("AC97: found audio controller at {}", device_identifier.address());
auto device_or_error = DeviceManagement::try_create_device<AC97>(device_identifier);
if (device_or_error.is_error()) {
dbgln("AC97: failed to initialize device {}", device_identifier.address());
return;
}
DeviceManagement::the().attach_audio_device(device_or_error.release_value());
});
} }
UNMAP_AFTER_INIT AC97::AC97(PCI::DeviceIdentifier const& pci_device_identifier) UNMAP_AFTER_INIT AC97::AC97(PCI::DeviceIdentifier const& pci_device_identifier)
: PCI::Device(pci_device_identifier.address()) : PCI::Device(pci_device_identifier.address())
, IRQHandler(pci_device_identifier.interrupt_line().value()) , IRQHandler(pci_device_identifier.interrupt_line().value())
, CharacterDevice(42, 42)
, m_io_mixer_base(PCI::get_BAR0(pci_address()) & ~1) , m_io_mixer_base(PCI::get_BAR0(pci_address()) & ~1)
, m_io_bus_base(PCI::get_BAR1(pci_address()) & ~1) , m_io_bus_base(PCI::get_BAR1(pci_address()) & ~1)
, m_pcm_out_channel(channel("PCMOut"sv, NativeAudioBusChannel::PCMOutChannel)) , m_pcm_out_channel(channel("PCMOut"sv, NativeAudioBusChannel::PCMOutChannel))
@ -129,28 +114,6 @@ UNMAP_AFTER_INIT void AC97::initialize()
enable_irq(); enable_irq();
} }
ErrorOr<void> AC97::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
{
switch (request) {
case SOUNDCARD_IOCTL_GET_SAMPLE_RATE: {
auto output = static_ptr_cast<u32*>(arg);
return copy_to_user(output, &m_sample_rate);
}
case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: {
auto sample_rate = static_cast<u32>(arg.ptr());
TRY(set_pcm_output_sample_rate(sample_rate));
return {};
}
default:
return EINVAL;
}
}
ErrorOr<size_t> AC97::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t)
{
return 0;
}
void AC97::reset_pcm_out() void AC97::reset_pcm_out()
{ {
m_pcm_out_channel.reset(); m_pcm_out_channel.reset();
@ -194,8 +157,36 @@ void AC97::set_pcm_output_volume(u8 left_channel, u8 right_channel, Muted mute)
m_io_mixer_base.offset(NativeAudioMixerRegister::SetPCMOutputVolume).out(volume_value); m_io_mixer_base.offset(NativeAudioMixerRegister::SetPCMOutputVolume).out(volume_value);
} }
ErrorOr<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& data, size_t length) RefPtr<AudioChannel> AC97::audio_channel(u32 index) const
{ {
if (index == 0)
return m_audio_channel;
return {};
}
void AC97::detect_hardware_audio_channels(Badge<AudioManagement>)
{
m_audio_channel = AudioChannel::must_create(*this, 0);
}
ErrorOr<void> AC97::set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate)
{
if (channel_index != 0)
return Error::from_errno(ENODEV);
TRY(set_pcm_output_sample_rate(samples_per_second_rate));
return {};
}
ErrorOr<u32> AC97::get_pcm_output_sample_rate(size_t channel_index)
{
if (channel_index != 0)
return Error::from_errno(ENODEV);
return m_sample_rate;
}
ErrorOr<size_t> AC97::write(size_t channel_index, UserOrKernelBuffer const& data, size_t length)
{
if (channel_index != 0)
return Error::from_errno(ENODEV);
if (!m_output_buffer) { if (!m_output_buffer) {
m_output_buffer = TRY(MM.allocate_dma_buffer_pages(m_output_buffer_page_count * PAGE_SIZE, "AC97 Output buffer"sv, Memory::Region::Access::Write)); m_output_buffer = TRY(MM.allocate_dma_buffer_pages(m_output_buffer_page_count * PAGE_SIZE, "AC97 Output buffer"sv, Memory::Region::Access::Write));
} }

View file

@ -9,6 +9,7 @@
#include <Kernel/Arch/x86/IO.h> #include <Kernel/Arch/x86/IO.h>
#include <Kernel/Bus/PCI/API.h> #include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/PCI/Device.h> #include <Kernel/Bus/PCI/Device.h>
#include <Kernel/Devices/Audio/Controller.h>
#include <Kernel/Devices/CharacterDevice.h> #include <Kernel/Devices/CharacterDevice.h>
#include <Kernel/Interrupts/IRQHandler.h> #include <Kernel/Interrupts/IRQHandler.h>
@ -17,25 +18,18 @@ namespace Kernel {
// See: https://www-inst.eecs.berkeley.edu/~cs150/Documents/ac97_r23.pdf // See: https://www-inst.eecs.berkeley.edu/~cs150/Documents/ac97_r23.pdf
// And: https://www.intel.com/content/dam/doc/manual/io-controller-hub-7-hd-audio-ac97-manual.pdf // And: https://www.intel.com/content/dam/doc/manual/io-controller-hub-7-hd-audio-ac97-manual.pdf
class AC97 final : public PCI::Device class AC97 final
, public IRQHandler : public AudioController
, public CharacterDevice { , public PCI::Device
friend class DeviceManagement; , public IRQHandler {
public: public:
static void detect(); static ErrorOr<NonnullRefPtr<AC97>> try_create(PCI::DeviceIdentifier const&);
virtual ~AC97() override; virtual ~AC97() override;
// ^IRQHandler // ^IRQHandler
virtual StringView purpose() const override { return class_name(); } virtual StringView purpose() const override { return "AC97"sv; }
// ^CharacterDevice
virtual bool can_read(const OpenFileDescription&, u64) const override { return false; }
virtual bool can_write(const OpenFileDescription&, u64) const override { return true; }
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned, Userspace<void*>) override;
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override;
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, const UserOrKernelBuffer&, size_t) override;
private: private:
enum NativeAudioMixerRegister : u8 { enum NativeAudioMixerRegister : u8 {
@ -150,9 +144,6 @@ private:
// ^IRQHandler // ^IRQHandler
virtual bool handle_irq(const RegisterState&) override; virtual bool handle_irq(const RegisterState&) override;
// ^CharacterDevice
virtual StringView class_name() const override { return "AC97"sv; }
AC97Channel channel(StringView name, NativeAudioBusChannel channel) { return AC97Channel(*this, name, m_io_bus_base.offset(channel)); } AC97Channel channel(StringView name, NativeAudioBusChannel channel) { return AC97Channel(*this, name, m_io_bus_base.offset(channel)); }
void initialize(); void initialize();
void reset_pcm_out(); void reset_pcm_out();
@ -161,6 +152,13 @@ private:
void set_pcm_output_volume(u8, u8, Muted); void set_pcm_output_volume(u8, u8, Muted);
ErrorOr<void> write_single_buffer(UserOrKernelBuffer const&, size_t, size_t); ErrorOr<void> write_single_buffer(UserOrKernelBuffer const&, size_t, size_t);
// ^AudioController
virtual RefPtr<AudioChannel> audio_channel(u32 index) const override;
virtual ErrorOr<size_t> write(size_t channel_index, UserOrKernelBuffer const& data, size_t length) override;
virtual void detect_hardware_audio_channels(Badge<AudioManagement>) override;
virtual ErrorOr<void> set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate) override;
virtual ErrorOr<u32> get_pcm_output_sample_rate(size_t channel_index) override;
OwnPtr<Memory::Region> m_buffer_descriptor_list; OwnPtr<Memory::Region> m_buffer_descriptor_list;
u8 m_buffer_descriptor_list_index = 0; u8 m_buffer_descriptor_list_index = 0;
bool m_double_rate_pcm_enabled = false; bool m_double_rate_pcm_enabled = false;
@ -173,6 +171,7 @@ private:
AC97Channel m_pcm_out_channel; AC97Channel m_pcm_out_channel;
u32 m_sample_rate = 0; u32 m_sample_rate = 0;
bool m_variable_rate_pcm_supported = false; bool m_variable_rate_pcm_supported = false;
RefPtr<AudioChannel> m_audio_channel;
}; };
} }

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Devices/Audio/Management.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/RandomDevice.h>
#include <Kernel/Random.h>
#include <Kernel/Sections.h>
#include <LibC/sys/ioctl_numbers.h>
namespace Kernel {
UNMAP_AFTER_INIT NonnullRefPtr<AudioChannel> AudioChannel::must_create(AudioController const& controller, size_t channel_index)
{
auto audio_device_or_error = DeviceManagement::try_create_device<AudioChannel>(controller, channel_index);
// FIXME: Find a way to propagate errors
VERIFY(!audio_device_or_error.is_error());
return audio_device_or_error.release_value();
}
AudioChannel::AudioChannel(AudioController const& controller, size_t channel_index)
: CharacterDevice(AudioManagement::the().audio_type_major_number(), AudioManagement::the().generate_storage_minor_number())
, m_controller(controller)
, m_channel_index(channel_index)
{
}
ErrorOr<void> AudioChannel::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
{
auto controller = m_controller.strong_ref();
if (!controller)
return Error::from_errno(EIO);
switch (request) {
case SOUNDCARD_IOCTL_GET_SAMPLE_RATE: {
auto output = static_ptr_cast<u32*>(arg);
u32 sample_rate = 0;
sample_rate = TRY(controller->get_pcm_output_sample_rate(m_channel_index));
return copy_to_user(output, &sample_rate);
}
case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: {
auto sample_rate = static_cast<u32>(arg.ptr());
TRY(controller->set_pcm_output_sample_rate(m_channel_index, sample_rate));
return {};
}
default:
return EINVAL;
}
}
bool AudioChannel::can_read(const OpenFileDescription&, u64) const
{
// FIXME: Implement input from device
return false;
}
ErrorOr<size_t> AudioChannel::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t)
{
// FIXME: Implement input from device
return Error::from_errno(ENOTIMPL);
}
ErrorOr<size_t> AudioChannel::write(OpenFileDescription&, u64, UserOrKernelBuffer const& buffer, size_t size)
{
auto controller = m_controller.strong_ref();
if (!controller)
return Error::from_errno(EIO);
return controller->write(m_channel_index, buffer, size);
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <Kernel/Devices/CharacterDevice.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Memory/PhysicalPage.h>
#include <Kernel/PhysicalAddress.h>
#include <Kernel/WaitQueue.h>
namespace Kernel {
class AudioController;
class AudioChannel final
: public CharacterDevice {
friend class DeviceManagement;
public:
static NonnullRefPtr<AudioChannel> must_create(AudioController const&, size_t channel_index);
virtual ~AudioChannel() override { }
// ^CharacterDevice
virtual bool can_read(const OpenFileDescription&, u64) const override;
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override;
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, const UserOrKernelBuffer&, size_t) override;
virtual bool can_write(const OpenFileDescription&, u64) const override { return true; }
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned, Userspace<void*>) override;
private:
AudioChannel(AudioController const&, size_t channel_index);
// ^CharacterDevice
virtual StringView class_name() const override { return "AudioChannel"sv; }
WeakPtr<AudioController> m_controller;
const size_t m_channel_index;
};
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/IntrusiveList.h>
#include <AK/OwnPtr.h>
#include <AK/RefPtr.h>
#include <AK/Weakable.h>
#include <Kernel/Bus/PCI/Access.h>
#include <Kernel/Bus/PCI/Device.h>
#include <Kernel/Devices/Audio/Channel.h>
#include <Kernel/Devices/Device.h>
#include <Kernel/Locking/Mutex.h>
#include <Kernel/Memory/PhysicalPage.h>
#include <Kernel/PhysicalAddress.h>
#include <Kernel/Random.h>
namespace Kernel {
class AudioManagement;
class AudioController
: public RefCounted<AudioController>
, public Weakable<AudioController> {
friend class AudioManagement;
public:
virtual ~AudioController() = default;
virtual RefPtr<AudioChannel> audio_channel(u32 index) const = 0;
virtual ErrorOr<size_t> write(size_t channel_index, UserOrKernelBuffer const& data, size_t length) = 0;
virtual void detect_hardware_audio_channels(Badge<AudioManagement>) = 0;
virtual ErrorOr<void> set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate) = 0;
// Note: The return value is rate of samples per second
virtual ErrorOr<u32> get_pcm_output_sample_rate(size_t channel_index) = 0;
private:
IntrusiveListNode<AudioController, RefPtr<AudioController>> m_node;
};
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Singleton.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/PCI/IDs.h>
#include <Kernel/Devices/Audio/AC97.h>
#include <Kernel/Devices/Audio/Management.h>
#include <Kernel/Devices/Audio/SB16.h>
#include <Kernel/Sections.h>
namespace Kernel {
static Singleton<AudioManagement> s_the;
static Atomic<u32> s_device_minor_number;
MajorNumber AudioManagement::audio_type_major_number()
{
return 116;
}
MinorNumber AudioManagement::generate_storage_minor_number()
{
auto minor_number = s_device_minor_number.load();
s_device_minor_number++;
return minor_number;
}
AudioManagement& AudioManagement::the()
{
return *s_the;
}
UNMAP_AFTER_INIT AudioManagement::AudioManagement()
{
}
UNMAP_AFTER_INIT void AudioManagement::enumerate_hardware_controllers()
{
if (auto controller = SB16::try_detect_and_create(); !controller.is_error())
m_controllers_list.append(controller.release_value());
PCI::enumerate([&](PCI::DeviceIdentifier const& device_identifier) {
// Note: Only consider PCI audio controllers
if (device_identifier.class_code().value() != to_underlying(PCI::ClassID::Multimedia)
|| device_identifier.subclass_code().value() != to_underlying(PCI::Multimedia::SubclassID::AudioController))
return;
dbgln("AC97: found audio controller at {}", device_identifier.address());
// FIXME: Propagate errors properly
m_controllers_list.append(AC97::try_create(device_identifier).release_value());
});
}
UNMAP_AFTER_INIT void AudioManagement::enumerate_hardware_audio_channels()
{
for (auto& controller : m_controllers_list)
controller.detect_hardware_audio_channels({});
}
UNMAP_AFTER_INIT bool AudioManagement::initialize()
{
/* Explanation on the flow:
* 1. Enumerate all audio controllers connected to the system:
* a. Try to find the SB16 ISA-based controller.
* b. Enumerate the PCI bus and try to find audio controllers there too
* 2. Ask each controller to detect the audio channels and instantiate AudioChannel objects.
*/
enumerate_hardware_controllers();
enumerate_hardware_audio_channels();
if (m_controllers_list.is_empty()) {
dbgln("No audio controller was initialized.");
return false;
}
return true;
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Badge.h>
#include <AK/Error.h>
#include <AK/IntrusiveList.h>
#include <AK/OwnPtr.h>
#include <AK/RefPtr.h>
#include <AK/Time.h>
#include <AK/Types.h>
#include <Kernel/Devices/Audio/Controller.h>
namespace Kernel {
class AudioManagement {
public:
AudioManagement();
static AudioManagement& the();
static MajorNumber audio_type_major_number();
static MinorNumber generate_storage_minor_number();
bool initialize();
private:
void enumerate_hardware_controllers();
void enumerate_hardware_audio_channels();
IntrusiveList<&AudioController::m_node> m_controllers_list;
};
}

View file

@ -62,8 +62,6 @@ void SB16::set_sample_rate(uint16_t hz)
UNMAP_AFTER_INIT SB16::SB16() UNMAP_AFTER_INIT SB16::SB16()
: IRQHandler(SB16_DEFAULT_IRQ) : IRQHandler(SB16_DEFAULT_IRQ)
// FIXME: We can't change version numbers later, i.e. after the sound card is initialized.
, CharacterDevice(42, 42)
{ {
initialize(); initialize();
} }
@ -72,7 +70,7 @@ UNMAP_AFTER_INIT SB16::~SB16()
{ {
} }
UNMAP_AFTER_INIT RefPtr<SB16> SB16::try_detect_and_create() UNMAP_AFTER_INIT ErrorOr<NonnullRefPtr<SB16>> SB16::try_detect_and_create()
{ {
IO::out8(0x226, 1); IO::out8(0x226, 1);
IO::delay(32); IO::delay(32);
@ -80,13 +78,8 @@ UNMAP_AFTER_INIT RefPtr<SB16> SB16::try_detect_and_create()
auto data = dsp_read(); auto data = dsp_read();
if (data != 0xaa) if (data != 0xaa)
return {}; return Error::from_errno(ENODEV);
auto device_or_error = DeviceManagement::try_create_device<SB16>(); return adopt_nonnull_ref_or_enomem(new (nothrow) SB16());
if (device_or_error.is_error())
return {};
auto device = device_or_error.release_value();
DeviceManagement::the().attach_audio_device(device);
return device;
} }
UNMAP_AFTER_INIT void SB16::initialize() UNMAP_AFTER_INIT void SB16::initialize()
@ -115,27 +108,6 @@ UNMAP_AFTER_INIT void SB16::initialize()
set_sample_rate(m_sample_rate); set_sample_rate(m_sample_rate);
} }
ErrorOr<void> SB16::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
{
switch (request) {
case SOUNDCARD_IOCTL_GET_SAMPLE_RATE: {
auto output = static_ptr_cast<u16*>(arg);
return copy_to_user(output, &m_sample_rate);
}
case SOUNDCARD_IOCTL_SET_SAMPLE_RATE: {
auto sample_rate_input = static_cast<u32>(arg.ptr());
if (sample_rate_input == 0 || sample_rate_input > 44100)
return ENOTSUP;
auto sample_rate_value = static_cast<u16>(sample_rate_input);
if (m_sample_rate != sample_rate_value)
set_sample_rate(sample_rate_value);
return {};
}
default:
return EINVAL;
}
}
void SB16::set_irq_register(u8 irq_number) void SB16::set_irq_register(u8 irq_number)
{ {
u8 bitmask; u8 bitmask;
@ -184,16 +156,6 @@ void SB16::set_irq_line(u8 irq_number)
change_irq_number(irq_number); change_irq_number(irq_number);
} }
bool SB16::can_read(OpenFileDescription const&, u64) const
{
return false;
}
ErrorOr<size_t> SB16::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t)
{
return 0;
}
void SB16::dma_start(uint32_t length) void SB16::dma_start(uint32_t length)
{ {
auto const addr = m_dma_region->physical_page(0)->paddr().get(); auto const addr = m_dma_region->physical_page(0)->paddr().get();
@ -249,8 +211,41 @@ void SB16::wait_for_irq()
disable_irq(); disable_irq();
} }
ErrorOr<size_t> SB16::write(OpenFileDescription&, u64, UserOrKernelBuffer const& data, size_t length) RefPtr<AudioChannel> SB16::audio_channel(u32 index) const
{ {
if (index == 0)
return m_audio_channel;
return {};
}
void SB16::detect_hardware_audio_channels(Badge<AudioManagement>)
{
m_audio_channel = AudioChannel::must_create(*this, 0);
}
ErrorOr<void> SB16::set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate)
{
if (channel_index != 0)
return Error::from_errno(ENODEV);
if (samples_per_second_rate == 0 || samples_per_second_rate > 44100)
return Error::from_errno(ENOTSUP);
auto sample_rate_value = static_cast<u16>(samples_per_second_rate);
if (m_sample_rate != sample_rate_value)
set_sample_rate(sample_rate_value);
return {};
}
ErrorOr<u32> SB16::get_pcm_output_sample_rate(size_t channel_index)
{
if (channel_index != 0)
return Error::from_errno(ENODEV);
return m_sample_rate;
}
ErrorOr<size_t> SB16::write(size_t channel_index, UserOrKernelBuffer const& data, size_t length)
{
if (channel_index != 0)
return Error::from_errno(ENODEV);
if (!m_dma_region) { if (!m_dma_region) {
m_dma_region = TRY(MM.allocate_dma_buffer_page("SB16 DMA buffer", Memory::Region::Access::Write)); m_dma_region = TRY(MM.allocate_dma_buffer_page("SB16 DMA buffer", Memory::Region::Access::Write));
} }

View file

@ -6,6 +6,7 @@
#pragma once #pragma once
#include <Kernel/Devices/Audio/Controller.h>
#include <Kernel/Devices/CharacterDevice.h> #include <Kernel/Devices/CharacterDevice.h>
#include <Kernel/Interrupts/IRQHandler.h> #include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Memory/PhysicalPage.h> #include <Kernel/Memory/PhysicalPage.h>
@ -16,34 +17,30 @@ namespace Kernel {
class SB16; class SB16;
class SB16 final : public IRQHandler class SB16 final
, public CharacterDevice { : public AudioController
friend class DeviceManagement; , public IRQHandler {
public: public:
virtual ~SB16() override; virtual ~SB16() override;
static RefPtr<SB16> try_detect_and_create(); static ErrorOr<NonnullRefPtr<SB16>> try_detect_and_create();
// ^CharacterDevice virtual StringView purpose() const override { return "SB16"sv; }
virtual bool can_read(const OpenFileDescription&, u64) const override;
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override;
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, const UserOrKernelBuffer&, size_t) override;
virtual bool can_write(const OpenFileDescription&, u64) const override { return true; }
virtual StringView purpose() const override { return class_name(); }
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned, Userspace<void*>) override;
private: private:
// ^AudioController
virtual RefPtr<AudioChannel> audio_channel(u32 index) const override;
virtual ErrorOr<size_t> write(size_t channel_index, UserOrKernelBuffer const& data, size_t length) override;
virtual void detect_hardware_audio_channels(Badge<AudioManagement>) override;
virtual ErrorOr<void> set_pcm_output_sample_rate(size_t channel_index, u32 samples_per_second_rate) override;
virtual ErrorOr<u32> get_pcm_output_sample_rate(size_t channel_index) override;
SB16(); SB16();
// ^IRQHandler // ^IRQHandler
virtual bool handle_irq(const RegisterState&) override; virtual bool handle_irq(const RegisterState&) override;
// ^CharacterDevice
virtual StringView class_name() const override { return "SB16"sv; }
void initialize(); void initialize();
void wait_for_irq(); void wait_for_irq();
void dma_start(uint32_t length); void dma_start(uint32_t length);
@ -59,5 +56,6 @@ private:
u16 m_sample_rate { 44100 }; u16 m_sample_rate { 44100 };
WaitQueue m_irq_queue; WaitQueue m_irq_queue;
RefPtr<AudioChannel> m_audio_channel;
}; };
} }

View file

@ -22,11 +22,6 @@ UNMAP_AFTER_INIT void DeviceManagement::initialize()
s_the.ensure_instance(); s_the.ensure_instance();
} }
UNMAP_AFTER_INIT void DeviceManagement::attach_audio_device(CharacterDevice const& device)
{
m_audio_devices.append(device);
}
UNMAP_AFTER_INIT void DeviceManagement::attach_console_device(ConsoleDevice const& device) UNMAP_AFTER_INIT void DeviceManagement::attach_console_device(ConsoleDevice const& device)
{ {
m_console_device = device; m_console_device = device;

View file

@ -38,9 +38,6 @@ public:
bool is_console_device_attached() const { return !m_console_device.is_null(); } bool is_console_device_attached() const { return !m_console_device.is_null(); }
void attach_console_device(ConsoleDevice const&); void attach_console_device(ConsoleDevice const&);
// FIXME: Once we have a singleton for managing many sound cards, remove this from here
void attach_audio_device(CharacterDevice const&);
Optional<DeviceEvent> dequeue_top_device_event(Badge<DeviceControlDevice>); Optional<DeviceEvent> dequeue_top_device_event(Badge<DeviceControlDevice>);
void after_inserting_device(Badge<Device>, Device&); void after_inserting_device(Badge<Device>, Device&);
@ -76,7 +73,6 @@ private:
RefPtr<ConsoleDevice> m_console_device; RefPtr<ConsoleDevice> m_console_device;
RefPtr<DeviceControlDevice> m_device_control_device; RefPtr<DeviceControlDevice> m_device_control_device;
// FIXME: Once we have a singleton for managing many sound cards, remove this from here // FIXME: Once we have a singleton for managing many sound cards, remove this from here
NonnullRefPtrVector<CharacterDevice, 1> m_audio_devices;
SpinlockProtected<HashMap<u64, Device*>> m_devices; SpinlockProtected<HashMap<u64, Device*>> m_devices;
mutable Spinlock m_event_queue_lock; mutable Spinlock m_event_queue_lock;

View file

@ -13,8 +13,7 @@
#include <Kernel/Bus/VirtIO/Device.h> #include <Kernel/Bus/VirtIO/Device.h>
#include <Kernel/CMOS.h> #include <Kernel/CMOS.h>
#include <Kernel/CommandLine.h> #include <Kernel/CommandLine.h>
#include <Kernel/Devices/Audio/AC97.h> #include <Kernel/Devices/Audio/Management.h>
#include <Kernel/Devices/Audio/SB16.h>
#include <Kernel/Devices/DeviceControlDevice.h> #include <Kernel/Devices/DeviceControlDevice.h>
#include <Kernel/Devices/DeviceManagement.h> #include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/FullDevice.h> #include <Kernel/Devices/FullDevice.h>
@ -339,8 +338,7 @@ void init_stage2(void*)
(void)RandomDevice::must_create().leak_ref(); (void)RandomDevice::must_create().leak_ref();
PTYMultiplexer::initialize(); PTYMultiplexer::initialize();
(void)SB16::try_detect_and_create(); AudioManagement::the().initialize();
AC97::detect();
StorageManagement::the().initialize(kernel_command_line().root_device(), kernel_command_line().is_force_pio(), kernel_command_line().is_nvme_polling_enabled()); StorageManagement::the().initialize(kernel_command_line().root_device(), kernel_command_line().is_force_pio(), kernel_command_line().is_nvme_polling_enabled());
if (VirtualFileSystem::the().mount_root(StorageManagement::the().root_filesystem()).is_error()) { if (VirtualFileSystem::the().mount_root(StorageManagement::the().root_filesystem()).is_error()) {