From 8f1b31480245b44d97ed7a635abd013751f75a6e Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 6 Jan 2026 21:54:32 +0200 Subject: [PATCH] Kernel: Add ioctls to select audio device's output pin --- kernel/include/kernel/Audio/AC97/Controller.h | 4 + kernel/include/kernel/Audio/Controller.h | 4 + .../kernel/Audio/HDAudio/AudioFunctionGroup.h | 6 + kernel/kernel/Audio/Controller.cpp | 9 ++ .../Audio/HDAudio/AudioFunctionGroup.cpp | 112 ++++++++++++++++++ userspace/libraries/LibC/include/sys/ioctl.h | 3 + 6 files changed, 138 insertions(+) diff --git a/kernel/include/kernel/Audio/AC97/Controller.h b/kernel/include/kernel/Audio/AC97/Controller.h index 64d096c6..404faa7f 100644 --- a/kernel/include/kernel/Audio/AC97/Controller.h +++ b/kernel/include/kernel/Audio/AC97/Controller.h @@ -19,6 +19,10 @@ namespace Kernel uint32_t get_channels() const override { return 2; } 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 set_current_pin(uint32_t pin) override { if (pin != 0) return BAN::Error::from_errno(EINVAL); return {}; } + private: AC97AudioController(PCI::Device& pci_device) : m_pci_device(pci_device) diff --git a/kernel/include/kernel/Audio/Controller.h b/kernel/include/kernel/Audio/Controller.h index dbc2359a..367d191e 100644 --- a/kernel/include/kernel/Audio/Controller.h +++ b/kernel/include/kernel/Audio/Controller.h @@ -22,6 +22,10 @@ namespace Kernel virtual uint32_t get_channels() 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 set_current_pin(uint32_t) = 0; + 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 has_error_impl() const override { return false; } diff --git a/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h b/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h index 99bc0c30..efe15d69 100644 --- a/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h +++ b/kernel/include/kernel/Audio/HDAudio/AudioFunctionGroup.h @@ -20,6 +20,10 @@ namespace Kernel 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 set_current_pin(uint32_t) override; + void handle_new_data() override; private: @@ -33,7 +37,9 @@ namespace Kernel BAN::ErrorOr initialize(); BAN::ErrorOr initialize_stream(); BAN::ErrorOr initialize_output(); + BAN::ErrorOr enable_output_path(uint8_t index); + BAN::ErrorOr disable_output_path(uint8_t index); void reset_stream(); diff --git a/kernel/kernel/Audio/Controller.cpp b/kernel/kernel/Audio/Controller.cpp index d5817c93..940d21f1 100644 --- a/kernel/kernel/Audio/Controller.cpp +++ b/kernel/kernel/Audio/Controller.cpp @@ -102,6 +102,15 @@ namespace Kernel m_sample_data_size = 0; return 0; } + case SND_GET_TOTAL_PINS: + *static_cast(arg) = get_total_pins(); + return 0; + case SND_GET_PIN: + *static_cast(arg) = get_current_pin(); + return 0; + case SND_SET_PIN: + TRY(set_current_pin(*static_cast(arg))); + return 0; } return CharacterDevice::ioctl_impl(cmd, arg); diff --git a/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp b/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp index e858e64d..230c775e 100644 --- a/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp +++ b/kernel/kernel/Audio/HDAudio/AudioFunctionGroup.cpp @@ -84,6 +84,71 @@ namespace Kernel 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 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); @@ -328,6 +393,53 @@ namespace Kernel return {}; } + BAN::ErrorOr 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 HDAudioFunctionGroup::recurse_output_paths(const HDAudio::AFGWidget& widget, BAN::Vector& path) { // cycle "detection" diff --git a/userspace/libraries/LibC/include/sys/ioctl.h b/userspace/libraries/LibC/include/sys/ioctl.h index bf50157e..30646a03 100644 --- a/userspace/libraries/LibC/include/sys/ioctl.h +++ b/userspace/libraries/LibC/include/sys/ioctl.h @@ -54,6 +54,9 @@ struct winsize #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_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, ...);