mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
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:
parent
6218ec8afa
commit
6efa27537a
Notes:
sideshowbarker
2024-07-18 04:38:32 +09:00
Author: https://github.com/supercomputer7 Commit: https://github.com/SerenityOS/serenity/commit/6efa27537a Pull-request: https://github.com/SerenityOS/serenity/pull/12445 Reviewed-by: https://github.com/kleinesfilmroellchen ✅
13 changed files with 382 additions and 127 deletions
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
73
Kernel/Devices/Audio/Channel.cpp
Normal file
73
Kernel/Devices/Audio/Channel.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
43
Kernel/Devices/Audio/Channel.h
Normal file
43
Kernel/Devices/Audio/Channel.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
45
Kernel/Devices/Audio/Controller.h
Normal file
45
Kernel/Devices/Audio/Controller.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
82
Kernel/Devices/Audio/Management.cpp
Normal file
82
Kernel/Devices/Audio/Management.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
Kernel/Devices/Audio/Management.h
Normal file
38
Kernel/Devices/Audio/Management.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
Loading…
Reference in a new issue