Kernel: Add ioctls to select audio device's output pin

This commit is contained in:
Bananymous 2026-01-06 21:54:32 +02:00
parent da6794c8ce
commit 8f1b314802
6 changed files with 138 additions and 0 deletions

View File

@ -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<void> 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)

View File

@ -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<void> 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; }

View File

@ -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<void> set_current_pin(uint32_t) override;
void handle_new_data() override;
private:
@ -33,7 +37,9 @@ namespace Kernel
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();

View File

@ -102,6 +102,15 @@ namespace Kernel
m_sample_data_size = 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);

View File

@ -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<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);
@ -328,6 +393,53 @@ namespace Kernel
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"

View File

@ -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, ...);