Compare commits

...

2 Commits

Author SHA1 Message Date
alnyan b2f3ab1c7f pci: better capability handling (todo: split/merge into master
separately)
2025-04-09 13:57:36 +03:00
alnyan 6342f0fe07 WIP: snd_hda_intel 2025-04-09 13:57:36 +03:00
26 changed files with 2954 additions and 317 deletions
Generated
+34
View File
@@ -2708,6 +2708,24 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_intel_hda"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"futures-util",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"ygg_driver_pci",
"ygg_driver_sound_core",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_net_core"
version = "0.1.0"
@@ -2836,6 +2854,20 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_sound_core"
version = "0.1.0"
dependencies = [
"async-trait",
"device-api",
"futures-util",
"libk",
"libk-mm",
"libk-util",
"log",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_usb"
version = "0.1.0"
@@ -3000,6 +3032,7 @@ dependencies = [
"ygg_driver_ahci",
"ygg_driver_fat32",
"ygg_driver_input",
"ygg_driver_intel_hda",
"ygg_driver_net_core",
"ygg_driver_net_igbe",
"ygg_driver_net_loopback",
@@ -3007,6 +3040,7 @@ dependencies = [
"ygg_driver_net_stmmac",
"ygg_driver_nvme",
"ygg_driver_pci",
"ygg_driver_sound_core",
"ygg_driver_usb",
"ygg_driver_usb_xhci",
"ygg_driver_virtio_blk",
+2
View File
@@ -36,6 +36,7 @@ ygg_driver_ahci = { path = "driver/block/ahci" }
ygg_driver_input = { path = "driver/input" }
ygg_driver_usb_xhci.path = "driver/usb/xhci"
ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
ygg_driver_sound_core.path = "driver/sound/core"
memfs = { path = "driver/fs/memfs" }
ext2 = { path = "driver/fs/ext2" }
@@ -70,6 +71,7 @@ kernel-arch-x86.workspace = true
ygg_driver_acpi.path = "driver/acpi"
ygg_driver_net_igbe.path = "driver/net/igbe"
ygg_driver_intel_hda.path = "driver/sound/intel-hda"
acpi.workspace = true
@@ -0,0 +1,177 @@
use core::time::Duration;
use libk::{error::Error, task::runtime::psleep};
use tock_registers::{fields::FieldValue, register_bitfields, LocalRegisterCopy};
use crate::{PciCapability, PciCapabilityId, PciConfigurationSpace};
// bitflags! {
// pub struct PcieLinkControl: u16 {
// const ASPM_DISABLE = 0 << 0;
// // Active state power management control
// const ASPM_MASK = 0x3 << 0;
// // Enable clock power management
// const ECPM = 1 << 8;
// }
// }
register_bitfields! {
u32,
pub DeviceCapabilities [
MAX_PAYLOAD_SIZE OFFSET(0) NUMBITS(3) [],
PHANTOM_FUNCTIONS OFFSET(3) NUMBITS(2) [],
L0S_ACCEPTABLE_LATENCY OFFSET(6) NUMBITS(3) [],
L1_ACCEPTABLE_LATENCY OFFSET(9) NUMBITS(3) [],
ROLE_ERROR_REPORTING OFFSET(15) NUMBITS(1) [],
CAPTURED_SLOT_POWER_LIMIT OFFSET(18) NUMBITS(8) [],
CAPTURED_SLOT_POWER_SCALE OFFSET(26) NUMBITS(3) [],
FUNCTION_LEVEL_RESET OFFSET(28) NUMBITS(1) [],
],
pub LinkCapabilities [
MAX_LINK_SPEED OFFSET(0) NUMBITS(4) [],
MAX_LINK_WIDTH OFFSET(4) NUMBITS(6) [],
ASPM OFFSET(10) NUMBITS(2) [],
L0S_EXIT_LATENCY OFFSET(12) NUMBITS(3) [],
L1_EXIT_LATENCY OFFSET(15) NUMBITS(3) [],
CLOCK_PM OFFSET(18) NUMBITS(1) [],
SURPRISE_DOWN_ERROR OFFSET(19) NUMBITS(1) [],
DATA_LINK_ACTIVE_REPORTING OFFSET(20) NUMBITS(1) [],
LINK_BANDWIDTH_NOTIFICATION OFFSET(21) NUMBITS(1) [],
ASPM_COMPLIANCE OFFSET(22) NUMBITS(1) [],
PORT_NUMBER OFFSET(24) NUMBITS(8) [],
],
}
register_bitfields! {
u16,
pub DeviceControl [
CORRECTABLE_ERROR_REPORTING OFFSET(0) NUMBITS(1) [],
NONFATAL_ERROR_REPORTING OFFSET(1) NUMBITS(1) [],
FATAL_ERROR_REPORTING OFFSET(2) NUMBITS(1) [],
UNSUPPORTED_REQ_REPORTING OFFSET(3) NUMBITS(1) [],
RELAXED_ORDERING OFFSET(4) NUMBITS(1) [],
MAX_PAYLOAD_SIZE OFFSET(5) NUMBITS(3) [],
EXTENDED_TAG_FIELD OFFSET(8) NUMBITS(1) [],
PHANTOM_FUNCTIONS OFFSET(9) NUMBITS(1) [],
AUX_POWER_PM_ENABLE OFFSET(10) NUMBITS(1) [],
NO_SNOOP OFFSET(11) NUMBITS(1) [],
MAX_READ_REQ_SIZE OFFSET(12) NUMBITS(3) [],
FUNCTION_LEVEL_RESET OFFSET(15) NUMBITS(1) [],
],
pub LinkControl [
ASPM OFFSET(0) NUMBITS(2) [
Disabled = 0,
L0sEntryEnabled = 1,
L1EntryEnabled = 2,
L0sL1EntryEnabled = 3,
],
READ_COMPLETION_BOUNDARY OFFSET(3) NUMBITS(1) [],
LINK_DISABLE OFFSET(4) NUMBITS(1) [],
RETRAIN_LINK OFFSET(5) NUMBITS(1) [],
COMMON_CLOCK_CONFIG OFFSET(6) NUMBITS(1) [],
EXTENDED_SYNCH OFFSET(7) NUMBITS(1) [],
CLOCK_PM OFFSET(8) NUMBITS(1) [],
HARDWARE_ABW_DISABLE OFFSET(9) NUMBITS(1) [],
LINK_BW_MANAGEMENT_IRQ OFFSET(10) NUMBITS(1) [],
LINK_ABW_IRQ OFFSET(11) NUMBITS(1) [],
],
}
/// PCIe capability
pub struct PciExpressCapability;
/// PCI Express capability data structure
pub struct PciExpressData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
impl PciCapability for PciExpressCapability {
const ID: PciCapabilityId = PciCapabilityId::PciExpress;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PciExpressData<'a, S>;
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
PciExpressData { space, offset }
}
}
macro_rules! reg_read {
($self:expr, $offset:literal, u32) => {
$self.space.read_u32($self.offset + $offset)
};
($self:expr, $offset:literal, u16) => {
$self.space.read_u16($self.offset + $offset)
};
}
macro_rules! reg_write {
($self:expr, $offset:literal, u32, $value:expr) => {
$self.space.write_u32($self.offset + $offset, $value)
};
($self:expr, $offset:literal, u16, $value:expr) => {
$self.space.write_u16($self.offset + $offset, $value)
};
}
macro_rules! make_register {
(
$reg:ident : $ty:ident @ $offset:literal {
$get:ident
$(, $set:ident, $modify:ident)?
$(,)?
}
) => {
pub fn $get(&self) -> LocalRegisterCopy<$ty, $reg::Register> {
LocalRegisterCopy::new(reg_read!(self, $offset, $ty))
}
$(
pub fn $set(&mut self, value: $ty) {
reg_write!(self, $offset, $ty, value)
}
pub fn $modify(&mut self, field: FieldValue<$ty, $reg::Register>) {
let mut value = self.$get();
value.modify(field);
self.$set(value.get());
}
)?
};
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PciExpressData<'s, S> {
make_register!(DeviceCapabilities : u32 @ 0x04 { device_capabilities });
make_register!(DeviceControl : u16 @ 0x08 {
device_control,
set_device_control,
modify_device_control,
});
make_register!(LinkCapabilities : u32 @ 0x0C { link_capabilities });
make_register!(LinkControl : u16 @ 0x10 {
link_control,
set_link_control,
modify_link_control,
});
pub fn function_level_reset(&mut self) -> Result<(), Error> {
if self
.device_capabilities()
.matches_all(DeviceCapabilities::FUNCTION_LEVEL_RESET::SET)
{
self.modify_device_control(DeviceControl::FUNCTION_LEVEL_RESET::SET);
psleep(Duration::from_millis(10));
Ok(())
} else {
Err(Error::NotImplemented)
}
}
pub fn hot_link_reset(&mut self) {
self.modify_link_control(LinkControl::LINK_DISABLE::SET);
psleep(Duration::from_millis(10));
self.modify_link_control(LinkControl::LINK_DISABLE::CLEAR);
psleep(Duration::from_millis(100));
}
}
@@ -0,0 +1,14 @@
//! PCI capability structures and queries
pub mod express;
pub mod msi;
pub mod power;
pub mod virtio;
pub use express::PciExpressCapability;
pub use msi::{MsiCapability, MsiXCapability};
pub use power::PowerManagementCapability;
pub use virtio::{
VirtioCapability, VirtioCommonConfigCapability, VirtioDeviceConfigCapability,
VirtioInterruptStatusCapability, VirtioNotifyConfigCapability,
};
@@ -1,91 +1,21 @@
//! PCI capability structures and queries
use alloc::{sync::Arc, vec, vec::Vec};
use bitflags::bitflags;
use device_api::interrupt::{
InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo,
};
use libk::error::Error;
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use tock_registers::{
interfaces::{Readable, Writeable},
registers::{ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
use crate::PciBaseAddress;
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
bitflags! {
pub struct PcieLinkControl: u16 {
const ASPM_DISABLE = 0 << 0;
// Active state power management control
const ASPM_MASK = 0x3 << 0;
// Enable clock power management
const ECPM = 1 << 8;
}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
use core::mem::offset_of;
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
use kernel_arch_x86::intrinsics;
pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized {
fn from_space_offset(space: &'s S, offset: usize) -> Self;
fn space(&self) -> &'s S;
fn offset(&self) -> usize;
fn bar_index(&self) -> Option<usize> {
let value = self.space().read_u8(self.offset() + 4);
(value <= 0x5).then_some(value as _)
}
fn bar_offset(&self) -> usize {
let value = self.space().read_u32(self.offset() + 8);
value as _
}
fn length(&self) -> usize {
let value = self.space().read_u32(self.offset() + 12);
value as _
}
}
pub trait VirtioCapability {
const CFG_TYPE: u8;
const MIN_LEN: usize = 0;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>;
}
/// Power management capability entry
pub struct PowerManagementCapability;
/// MSI-X capability query
pub struct MsiXCapability;
/// MSI capability query
pub struct MsiCapability;
/// PCIe capability
pub struct PciExpressCapability;
// VirtIO-over-PCI capabilities
/// VirtIO PCI configuration access
pub struct VirtioDeviceConfigCapability;
/// VirtIO common configuration
pub struct VirtioCommonConfigCapability;
/// VirtIO notify configuration
pub struct VirtioNotifyConfigCapability;
/// VirtIO interrupt status
pub struct VirtioInterruptStatusCapability;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DevicePowerState {
D0,
D1,
D2,
D3Cold,
D3Hot,
}
use crate::{PciBaseAddress, PciCapability, PciCapabilityId, PciConfigurationSpace};
/// Represents an entry in MSI-X vector table
#[repr(C)]
@@ -109,11 +39,10 @@ pub struct MsiXVectorTable<'a> {
len: usize,
}
/// PCI Power Management capability data structure
pub struct PowerManagementData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
/// MSI-X capability query
pub struct MsiXCapability;
/// MSI capability query
pub struct MsiCapability;
/// MSI-X capability data structure
pub struct MsiXData<'s, S: PciConfigurationSpace + ?Sized + 's> {
@@ -127,63 +56,6 @@ pub struct MsiData<'s, S: PciConfigurationSpace + ?Sized + 's> {
offset: usize,
}
/// PCI Express capability data structure
pub struct PcieData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioDeviceConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioCommonConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioNotifyConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioInterruptStatusData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
impl<T: VirtioCapability> PciCapability for T {
const ID: PciCapabilityId = PciCapabilityId::VendorSpecific;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = T::Output<'a, S>;
fn check<S: PciConfigurationSpace + ?Sized>(space: &S, offset: usize, len: usize) -> bool {
let cfg_type = space.read_u8(offset + 3);
cfg_type == T::CFG_TYPE && len >= T::MIN_LEN
}
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
T::Output::from_space_offset(space, offset)
}
}
impl PciCapability for PowerManagementCapability {
const ID: PciCapabilityId = PciCapabilityId::PowerManagement;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PowerManagementData<'a, S>;
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
PowerManagementData { space, offset }
}
}
impl PciCapability for MsiXCapability {
const ID: PciCapabilityId = PciCapabilityId::MsiX;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiXData<'a, S>;
@@ -210,167 +82,6 @@ impl PciCapability for MsiCapability {
}
}
impl PciCapability for PciExpressCapability {
const ID: PciCapabilityId = PciCapabilityId::PciExpress;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PcieData<'a, S>;
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
PcieData { space, offset }
}
}
impl VirtioCapability for VirtioDeviceConfigCapability {
const CFG_TYPE: u8 = 0x04;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioDeviceConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioDeviceConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioCommonConfigCapability {
const CFG_TYPE: u8 = 0x01;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioCommonConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioCommonConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioNotifyConfigCapability {
const CFG_TYPE: u8 = 0x02;
const MIN_LEN: usize = 0x14;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioNotifyConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioNotifyConfigData<'s, S> {
pub fn offset_multiplier(&self) -> usize {
self.space.read_u32(self.offset + 16) as usize
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioNotifyConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioInterruptStatusCapability {
const CFG_TYPE: u8 = 0x03;
const MIN_LEN: usize = 1;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioInterruptStatusData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioInterruptStatusData<'s, S> {
pub fn read_status(&self) -> (bool, bool) {
todo!()
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioInterruptStatusData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PowerManagementData<'s, S> {
pub fn set_device_power_state(&self, state: DevicePowerState) {
let pmcsr = self.space.read_u16(self.offset + 4) & !0x3;
let current = self.get_device_power_state();
if state == current {
return;
}
log::info!("Set device power state: {state:?}");
match state {
DevicePowerState::D0 => {
// power = 0b00 | PME_EN
self.space.write_u16(self.offset + 4, pmcsr);
}
_ => {
log::warn!("TODO: {state:?} power state");
}
}
}
pub fn set_pme_en(&self, state: bool) {
let pmcsr = self.space.read_u16(self.offset + 4);
let new = if state {
pmcsr | (1 << 8)
} else {
pmcsr & !(1 << 8)
};
if pmcsr == new {
return;
}
log::info!("Set PMCSR.PME_En = {state}");
self.space.write_u16(self.offset + 4, new);
}
pub fn get_device_power_state(&self) -> DevicePowerState {
let pmcsr = self.space.read_u16(self.offset + 4);
match pmcsr & 0x3 {
0b00 => DevicePowerState::D0,
0b01 => DevicePowerState::D1,
0b10 => DevicePowerState::D2,
0b11 => DevicePowerState::D3Hot,
_ => unreachable!(),
}
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
// TODO use pending bits as well
/// Maps and returns the vector table associated with the device's MSI-X capability
@@ -601,13 +312,3 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> {
Ok(info)
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PcieData<'s, S> {
pub fn link_control(&self) -> PcieLinkControl {
PcieLinkControl::from_bits_retain(self.space.read_u16(self.offset + 0x10))
}
pub fn set_link_control(&mut self, value: PcieLinkControl) {
self.space.write_u16(self.offset + 0x10, value.bits());
}
}
@@ -0,0 +1,82 @@
use crate::{PciCapability, PciCapabilityId, PciConfigurationSpace};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DevicePowerState {
D0,
D1,
D2,
D3Cold,
D3Hot,
}
/// Power management capability entry
pub struct PowerManagementCapability;
/// PCI Power Management capability data structure
pub struct PowerManagementData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
impl PciCapability for PowerManagementCapability {
const ID: PciCapabilityId = PciCapabilityId::PowerManagement;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PowerManagementData<'a, S>;
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
PowerManagementData { space, offset }
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PowerManagementData<'s, S> {
pub fn set_device_power_state(&self, state: DevicePowerState) {
let pmcsr = self.space.read_u16(self.offset + 4) & !0x3;
let current = self.get_device_power_state();
if state == current {
return;
}
log::info!("Set device power state: {state:?}");
match state {
DevicePowerState::D0 => {
// power = 0b00 | PME_EN
self.space.write_u16(self.offset + 4, pmcsr);
}
_ => {
log::warn!("TODO: {state:?} power state");
}
}
}
pub fn set_pme_en(&self, state: bool) {
let pmcsr = self.space.read_u16(self.offset + 4);
let new = if state {
pmcsr | (1 << 8)
} else {
pmcsr & !(1 << 8)
};
if pmcsr == new {
return;
}
log::info!("Set PMCSR.PME_En = {state}");
self.space.write_u16(self.offset + 4, new);
}
pub fn get_device_power_state(&self) -> DevicePowerState {
let pmcsr = self.space.read_u16(self.offset + 4);
match pmcsr & 0x3 {
0b00 => DevicePowerState::D0,
0b01 => DevicePowerState::D1,
0b10 => DevicePowerState::D2,
0b11 => DevicePowerState::D3Hot,
_ => unreachable!(),
}
}
}
@@ -0,0 +1,175 @@
use crate::{PciCapability, PciCapabilityId, PciConfigurationSpace};
pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized {
fn from_space_offset(space: &'s S, offset: usize) -> Self;
fn space(&self) -> &'s S;
fn offset(&self) -> usize;
fn bar_index(&self) -> Option<usize> {
let value = self.space().read_u8(self.offset() + 4);
(value <= 0x5).then_some(value as _)
}
fn bar_offset(&self) -> usize {
let value = self.space().read_u32(self.offset() + 8);
value as _
}
fn length(&self) -> usize {
let value = self.space().read_u32(self.offset() + 12);
value as _
}
}
pub trait VirtioCapability {
const CFG_TYPE: u8;
const MIN_LEN: usize = 0;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>;
}
// VirtIO-over-PCI capabilities
/// VirtIO PCI configuration access
pub struct VirtioDeviceConfigCapability;
/// VirtIO common configuration
pub struct VirtioCommonConfigCapability;
/// VirtIO notify configuration
pub struct VirtioNotifyConfigCapability;
/// VirtIO interrupt status
pub struct VirtioInterruptStatusCapability;
pub struct VirtioDeviceConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioCommonConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioNotifyConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
pub struct VirtioInterruptStatusData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
impl<T: VirtioCapability> PciCapability for T {
const ID: PciCapabilityId = PciCapabilityId::VendorSpecific;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = T::Output<'a, S>;
fn check<S: PciConfigurationSpace + ?Sized>(space: &S, offset: usize, len: usize) -> bool {
let cfg_type = space.read_u8(offset + 3);
cfg_type == T::CFG_TYPE && len >= T::MIN_LEN
}
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
T::Output::from_space_offset(space, offset)
}
}
impl VirtioCapability for VirtioDeviceConfigCapability {
const CFG_TYPE: u8 = 0x04;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioDeviceConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioDeviceConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioCommonConfigCapability {
const CFG_TYPE: u8 = 0x01;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioCommonConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioCommonConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioNotifyConfigCapability {
const CFG_TYPE: u8 = 0x02;
const MIN_LEN: usize = 0x14;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioNotifyConfigData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioNotifyConfigData<'s, S> {
pub fn offset_multiplier(&self) -> usize {
self.space.read_u32(self.offset + 16) as usize
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioNotifyConfigData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
impl VirtioCapability for VirtioInterruptStatusCapability {
const CFG_TYPE: u8 = 0x03;
const MIN_LEN: usize = 1;
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioInterruptStatusData<'a, S>;
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioInterruptStatusData<'s, S> {
pub fn read_status(&self) -> (bool, bool) {
todo!()
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
for VirtioInterruptStatusData<'s, S>
{
fn from_space_offset(space: &'s S, offset: usize) -> Self {
Self { space, offset }
}
fn space(&self) -> &'s S {
self.space
}
fn offset(&self) -> usize {
self.offset
}
}
+18 -1
View File
@@ -13,7 +13,7 @@ use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use yggdrasil_abi::error::Error;
use crate::{
capability::{MsiCapability, MsiXCapability, MsiXVectorTable},
capability::{msi::MsiXVectorTable, MsiCapability, MsiXCapability, PciExpressCapability},
driver::PciDriver,
PciAddress, PciCommandRegister, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
};
@@ -142,6 +142,23 @@ impl PciDeviceInfo {
}
}
pub fn function_level_reset(&self) -> Result<(), Error> {
if let Some(mut pcie) = self.config_space.capability::<PciExpressCapability>() {
pcie.function_level_reset()
} else {
Err(Error::NotImplemented)
}
}
pub fn hot_link_reset(&self) -> Result<(), Error> {
if let Some(mut pcie) = self.config_space.capability::<PciExpressCapability>() {
pcie.hot_link_reset();
Ok(())
} else {
Err(Error::NotImplemented)
}
}
pub fn init_interrupts(&self, preferred_mode: PreferredInterruptMode) -> Result<(), Error> {
self.interrupt_config
.try_init_with(|| {
+4 -7
View File
@@ -7,7 +7,8 @@ use rtl8139::Rtl8139;
use rtl8168::Rtl8168;
use ygg_driver_pci::{
capability::{
DevicePowerState, PciExpressCapability, PcieLinkControl, PowerManagementCapability,
express::LinkControl, power::DevicePowerState, PciExpressCapability,
PowerManagementCapability,
},
device::{PciDeviceInfo, PreferredInterruptMode},
macros::pci_driver,
@@ -30,9 +31,7 @@ pci_driver! {
info.init_interrupts(PreferredInterruptMode::Msi(false))?;
if let Some(mut pcie) = info.config_space.capability::<PciExpressCapability>() {
let mut lcr = pcie.link_control();
lcr.remove(PcieLinkControl::ASPM_MASK | PcieLinkControl::ECPM);
pcie.set_link_control(lcr);
pcie.modify_link_control(LinkControl::ASPM::CLEAR + LinkControl::CLOCK_PM::CLEAR);
}
// Enable MMIO + interrupts + bus mastering
@@ -76,9 +75,7 @@ pci_driver! {
}
if let Some(mut pcie) = info.config_space.capability::<PciExpressCapability>() {
let mut lcr = pcie.link_control();
lcr.remove(PcieLinkControl::ASPM_MASK | PcieLinkControl::ECPM);
pcie.set_link_control(lcr);
pcie.modify_link_control(LinkControl::ASPM::CLEAR + LinkControl::CLOCK_PM::CLEAR);
}
let device = Rtl8168::new(dma.clone(), base, info.clone())?;
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "ygg_driver_sound_core"
version = "0.1.0"
edition = "2024"
[dependencies]
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
log.workspace = true
async-trait.workspace = true
futures-util.workspace = true
+122
View File
@@ -0,0 +1,122 @@
#![no_std]
use core::{
sync::atomic::{AtomicU32, Ordering},
task::{Context, Poll},
};
use alloc::{boxed::Box, collections::btree_map::BTreeMap, format, sync::Arc, vec::Vec};
use async_trait::async_trait;
use device_api::device::Device;
use libk::{block, device::char::CharDevice, error::Error, fs::devfs, vfs::FileReadiness};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use yggdrasil_abi::io::FileMode;
extern crate alloc;
#[derive(Debug, Clone)]
pub enum SampleFormat {
S8,
S16Le,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(usize)]
pub enum SampleRate {
Rate8000 = 8000,
Rate11025 = 11025,
Rate16000 = 16000,
Rate22050 = 22050,
Rate32000 = 32000,
Rate44100 = 44100,
Rate48000 = 48000,
Rate88200 = 88200,
Rate96000 = 96000,
Rate176400 = 176400,
Rate192000 = 192000,
Rate384000 = 384000,
}
#[derive(Debug, Clone)]
pub struct SinkFormat {
pub sample_rate: SampleRate,
pub sample_format: SampleFormat,
pub channels: usize,
}
#[derive(Debug, Clone)]
pub struct SinkSupportedFormats {
pub rates: Vec<usize>,
pub formats: Vec<SampleFormat>,
pub channels: usize,
}
#[async_trait]
pub trait AudioSink: Sync + Send {
async fn set_format(&self, format: SinkFormat) -> Result<(), Error>;
async fn current_format(&self) -> Result<SinkFormat, Error>;
async fn supported_formats(&self) -> Result<SinkSupportedFormats, Error>;
async fn write(&self, data: &[u8]) -> Result<usize, Error>;
async fn start(&self) -> Result<(), Error>;
fn stop(&self) -> Result<(), Error>;
fn display_name(&self) -> &str {
"Audio Sink"
}
}
pub struct AudioSinkWrapper(Arc<dyn AudioSink>);
impl Device for AudioSinkWrapper {
fn display_name(&self) -> &str {
self.0.display_name()
}
}
impl FileReadiness for AudioSinkWrapper {
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
todo!()
}
}
#[async_trait]
impl CharDevice for AudioSinkWrapper {
fn close(&self) -> Result<(), Error> {
self.0.stop()
}
async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
self.0.write(buffer).await
}
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
let _ = option;
let _ = buffer;
let _ = len;
log::warn!("device_request unimplemented: {option:#x}");
Err(Error::InvalidOperation)
}
}
static SINKS: IrqSafeRwLock<BTreeMap<u32, Arc<AudioSinkWrapper>>> =
IrqSafeRwLock::new(BTreeMap::new());
static LAST_SINK_ID: AtomicU32 = AtomicU32::new(0);
pub fn register_audio_sink(sink: Arc<dyn AudioSink>) {
let id = LAST_SINK_ID.fetch_add(1, Ordering::Relaxed);
let name = format!("snd{id}");
let sink = Arc::new(AudioSinkWrapper(sink));
SINKS.write().insert(id, sink.clone());
devfs::add_named_char_device(sink, &name, FileMode::new(0o222)).ok();
}
impl SampleFormat {
pub fn sample_size(&self) -> usize {
match self {
Self::S8 => 1,
Self::S16Le => 2,
}
}
}
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "ygg_driver_intel_hda"
version = "0.1.0"
edition = "2024"
[dependencies]
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
ygg_driver_pci.path = "../../bus/pci"
ygg_driver_sound_core.path = "../core"
log.workspace = true
tock-registers.workspace = true
futures-util.workspace = true
async-trait.workspace = true
bytemuck.workspace = true
@@ -0,0 +1,527 @@
use core::fmt;
use alloc::{sync::Arc, vec::Vec};
use libk::error::Error;
use tock_registers::{fields::FieldValue, register_bitfields, LocalRegisterCopy};
use yggdrasil_abi::bitflags;
use crate::{
ring::{Command, PinControl, PinDefaultConfig, PinDevice, Verb},
HdAudio,
};
pub mod parameter;
pub use parameter::*;
#[derive(Debug, Clone, Copy)]
pub struct Node {
pub codec: u8,
pub nid: u8,
}
pub struct WidgetConnectionChain<'a> {
afg: &'a AudioNode,
current: Option<u8>,
}
#[derive(Debug)]
pub struct AudioOutputWidget {
channel_count: usize,
}
#[derive(Debug)]
pub struct AudioInputWidget {
channel_count: usize,
}
#[derive(Debug)]
pub struct PinComplexWidget {
default_config: PinDefaultConfig,
}
#[derive(Debug)]
pub struct MixerWidget {}
#[derive(Debug)]
pub enum AudioWidget {
AudioOutput(AudioOutputWidget),
AudioInput(AudioInputWidget),
PinComplex(PinComplexWidget),
Mixer(MixerWidget),
Other,
}
#[derive(Debug)]
pub enum AudioFormat {
Pcm { rates: Vec<usize>, bits: Vec<u8> },
Other,
}
#[derive(Debug)]
pub struct AudioWidgetNode {
parent_nid: u8,
node: Node,
output_amp: Option<LocalRegisterCopy<u32, AmpCapabilities::Register>>,
input_amp: Option<LocalRegisterCopy<u32, AmpCapabilities::Register>>,
connection: Option<u8>,
formats: Vec<AudioFormat>,
widget: AudioWidget,
}
pub struct AudioNode {
node: Node,
widget_connection: Vec<(u8, u8)>,
widgets: Vec<AudioWidgetNode>,
}
pub struct Codec {
hda: Arc<HdAudio>,
root: Node,
nodes: Vec<AudioNode>,
}
impl AudioFormat {
async fn query_from_node(hda: &HdAudio, node: Node) -> Result<Vec<Self>, Error> {
let supported_formats = node
.get_parameter::<SupportedStreamFormats::Register>(hda)
.await?;
let mut formats = Vec::new();
if supported_formats.matches_all(SupportedStreamFormats::PCM::SET) {
formats.push(Self::query_pcm(hda, node).await?);
}
if supported_formats.matches_all(SupportedStreamFormats::F32::SET) {
formats.push(Self::Other);
}
if supported_formats.matches_all(SupportedStreamFormats::AC3::SET) {
formats.push(Self::Other);
}
Ok(formats)
}
async fn query_pcm(hda: &HdAudio, node: Node) -> Result<Self, Error> {
// Conversion tables
const RATES: &[(usize, FieldValue<u32, SupportedPcmFormats::Register>)] = &[
(8000, SupportedPcmFormats::RATE_8::SET),
(11024, SupportedPcmFormats::RATE_11_025::SET),
(16000, SupportedPcmFormats::RATE_16::SET),
(22050, SupportedPcmFormats::RATE_22_05::SET),
(32000, SupportedPcmFormats::RATE_32::SET),
(44100, SupportedPcmFormats::RATE_44_1::SET),
(48000, SupportedPcmFormats::RATE_48::SET),
(88200, SupportedPcmFormats::RATE_88_2::SET),
(96000, SupportedPcmFormats::RATE_96::SET),
(176400, SupportedPcmFormats::RATE_176_4::SET),
(192000, SupportedPcmFormats::RATE_192::SET),
(384000, SupportedPcmFormats::RATE_384::SET),
];
const BITS: &[(u8, FieldValue<u32, SupportedPcmFormats::Register>)] = &[
(8, SupportedPcmFormats::BITS_8::SET),
(16, SupportedPcmFormats::BITS_16::SET),
(20, SupportedPcmFormats::BITS_20::SET),
(24, SupportedPcmFormats::BITS_24::SET),
(32, SupportedPcmFormats::BITS_32::SET),
];
let supported_pcm_formats = node
.get_parameter::<SupportedPcmFormats::Register>(hda)
.await?;
let mut supported_rates = Vec::new();
let mut supported_bits = Vec::new();
for &(rate, bit) in RATES {
if supported_pcm_formats.matches_all(bit) {
supported_rates.push(rate);
}
}
for &(bits, bit) in BITS {
if supported_pcm_formats.matches_all(bit) {
supported_bits.push(bits);
}
}
Ok(Self::Pcm {
rates: supported_rates,
bits: supported_bits,
})
}
}
impl Node {
pub async fn perform_command(
&self,
hda: &HdAudio,
verb: Verb,
parameter: u32,
) -> Result<u32, Error> {
hda.perform_command(Command::new(self.codec, self.nid, verb, parameter))
.await
}
pub async fn get_parameter<P: NodeParameter>(&self, hda: &HdAudio) -> Result<P::Value, Error> {
self.perform_command(hda, Verb::GetParameter, P::NUMBER as u32)
.await
.map(P::from_response)
}
pub async fn get_connection_list_entry(
&self,
hda: &HdAudio,
index: usize,
) -> Result<u32, Error> {
self.perform_command(hda, Verb::GetConnectionListEntry, index as u32)
.await
}
pub async fn set_stream(&self, hda: &HdAudio, stream: u8, channel: u8) -> Result<(), Error> {
self.perform_command(
hda,
Verb::SetConverterStreamChannel,
((stream as u32) << 4) | (channel as u32),
)
.await?;
Ok(())
}
pub async fn get_pin_default_config(&self, hda: &HdAudio) -> Result<PinDefaultConfig, Error> {
self.perform_command(hda, Verb::GetPinWidgetDefaultConfig, 0)
.await
.map(PinDefaultConfig::from)
}
pub async fn set_power_state(&self, hda: &HdAudio, power: bool) -> Result<(), Error> {
let ps_set = if power { 0x0 } else { 0x4 };
self.perform_command(hda, Verb::SetPowerState, ps_set)
.await?;
Ok(())
}
pub async fn set_unsolicited_response(
&self,
hda: &HdAudio,
tag: Option<u8>,
) -> Result<(), Error> {
let value = match tag {
Some(tag) => (tag as u32) | (1 << 7),
None => 0,
};
self.perform_command(hda, Verb::SetUnsolicitedResponse, value)
.await?;
Ok(())
}
pub async fn set_pin_control(&self, hda: &HdAudio, pin_control: u32) -> Result<(), Error> {
self.perform_command(hda, Verb::SetPinWidgetControl, pin_control & 0xFF)
.await?;
Ok(())
}
pub async fn get_pin_control(
&self,
hda: &HdAudio,
) -> Result<LocalRegisterCopy<u32, PinControl::Register>, Error> {
self.perform_command(hda, Verb::GetPinWidgetControl, 0)
.await
.map(LocalRegisterCopy::new)
}
pub async fn modify_pin_control(
&self,
hda: &HdAudio,
change: FieldValue<u32, PinControl::Register>,
) -> Result<(), Error> {
let mut value = self.get_pin_control(hda).await?;
value.modify(change);
self.set_pin_control(hda, value.get()).await
}
}
impl AudioWidgetNode {
pub async fn probe(hda: &HdAudio, parent_nid: u8, node: Node) -> Result<Self, Error> {
let capabilities = node
.get_parameter::<AudioWidgetCapabilities::Register>(hda)
.await?;
let channel_count = ((capabilities.read(AudioWidgetCapabilities::EXT_CHANNEL_COUNT) << 1)
| capabilities.read(AudioWidgetCapabilities::CHANNEL_COUNT))
+ 1;
let in_amp = capabilities.matches_all(AudioWidgetCapabilities::IN_AMP::SET);
let out_amp = capabilities.matches_all(AudioWidgetCapabilities::OUT_AMP::SET);
let in_amp =
if in_amp && capabilities.matches_all(AudioWidgetCapabilities::AMP_OVERRIDE::SET) {
node.get_parameter::<InputAmpCapabilities>(hda).await?
} else {
None
};
let out_amp =
if out_amp && capabilities.matches_all(AudioWidgetCapabilities::AMP_OVERRIDE::SET) {
node.get_parameter::<OutputAmpCapabilities>(hda).await?
} else {
None
};
let connection = if capabilities.matches_all(AudioWidgetCapabilities::CONN_LIST::SET) {
let (length, long) = node.get_parameter::<ConnectionListLength>(hda).await?;
// if length > 1 || long {
// todo!()
// }
let connection = node.get_connection_list_entry(hda, 0).await?;
Some(connection as u8)
} else {
None
};
let formats = if capabilities.matches_all(AudioWidgetCapabilities::FORMAT_OVERRIDE::SET) {
AudioFormat::query_from_node(hda, node).await?
} else {
Vec::new()
};
let widget = match capabilities.read_as_enum(AudioWidgetCapabilities::TYPE) {
Some(AudioWidgetCapabilities::TYPE::Value::AudioOutput) => {
// Disable audio outputs immediately
node.set_stream(hda, 0x00, 0x00).await?;
AudioWidget::AudioOutput(AudioOutputWidget {
channel_count: channel_count as usize,
})
}
Some(AudioWidgetCapabilities::TYPE::Value::AudioInput) => {
AudioWidget::AudioInput(AudioInputWidget {
channel_count: channel_count as usize,
})
}
Some(AudioWidgetCapabilities::TYPE::Value::PinComplex) => {
let default_config = node.get_pin_default_config(hda).await?;
AudioWidget::PinComplex(PinComplexWidget { default_config })
}
Some(AudioWidgetCapabilities::TYPE::Value::AudioMixer) => {
AudioWidget::Mixer(MixerWidget {})
}
v => {
log::warn!("hda: unknown audio widget: {v:?}");
AudioWidget::Other
}
};
Ok(Self {
parent_nid,
node,
widget,
output_amp: out_amp,
input_amp: in_amp,
formats,
connection,
})
}
}
impl AudioNode {
pub async fn probe(hda: &HdAudio, node: Node) -> Result<Self, Error> {
// Query widgets
let subnodes = node.get_parameter::<SubNodeInfo>(hda).await?;
let mut widgets = Vec::new();
for wid in subnodes.iter() {
let widget_node = Node {
codec: node.codec,
nid: wid,
};
match AudioWidgetNode::probe(hda, node.nid, widget_node).await {
Ok(widget) => {
widgets.push(widget);
}
Err(error) => {
log::warn!(
"hda: could not enumerate {}.{}.{}: {error:?}",
node.codec,
node.nid,
wid
);
}
}
}
let mut widget_connection = Vec::new();
for widget in &widgets {
if let Some(connection) = widget.connection {
widget_connection.push((widget.node.nid, connection));
}
}
Ok(Self {
node,
widgets,
widget_connection,
})
}
pub fn widget(&self, wid: u8) -> Option<&AudioWidgetNode> {
self.widgets.iter().find(|w| w.node.nid == wid)
}
pub fn widget_supported_formats<'a>(&self, widget: &'a AudioWidgetNode) -> &'a [AudioFormat] {
if !widget.formats.is_empty() {
&widget.formats
} else {
// TODO node supported formats
&[]
}
}
// Returns (pin, kind) pair
pub fn default_output_pin(&self) -> Option<(Node, PinDevice)> {
let mut best: Option<(Node, PinDevice)> = None;
for widget in &self.widgets {
if let AudioWidget::PinComplex(pin) = &widget.widget {
let Some(connection) = widget.connection else {
continue;
};
let dev = pin.default_config.default_device;
let better = match best {
None => true,
Some((_, other)) => dev.output_score() > other.output_score(),
};
if better {
best = Some((widget.node, dev));
}
}
}
best
}
pub fn connection_chain(&self, source: u8) -> WidgetConnectionChain<'_> {
WidgetConnectionChain {
afg: self,
current: Some(source),
}
}
}
impl fmt::Debug for AudioNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AudioNode")
.field("id", &self.node.nid)
.field("widgets", &self.widgets)
.finish()
}
}
impl Codec {
pub async fn probe(hda: Arc<HdAudio>, codec: u8) -> Result<Self, Error> {
let root_node = Node { codec, nid: 0 };
let id = root_node.get_parameter::<DeviceId>(&*hda).await?;
if (id.vendor_id == 0 && id.device_id == 0)
|| (id.vendor_id == 0xFFFF && id.device_id == 0xFFFF)
{
return Err(Error::DoesNotExist);
}
let root_subnodes = root_node.get_parameter::<SubNodeInfo>(&*hda).await?;
let mut nodes = Vec::new();
for nid in root_subnodes.iter() {
let node = Node { codec, nid };
let node_type = node.get_parameter::<NodeType>(&*hda).await?;
// Ignore non-audio nodes
if node_type != 0x01 {
continue;
}
match AudioNode::probe(&*hda, node).await {
Ok(node) => nodes.push(node),
Err(error) => {
log::warn!("hda: could not enumerate {}.{}: {error:?}", codec, nid);
}
}
}
Ok(Self {
hda,
root: root_node,
nodes,
})
}
pub fn node(&self, nid: u8) -> Option<&AudioNode> {
self.nodes.iter().find(|n| n.node.nid == nid)
}
pub async fn setup_default_output(&self) -> Result<Node, Error> {
let (pin_id, dev, node_id) = self
.nodes
.iter()
.find_map(|node| {
node.default_output_pin()
.map(|(pin, dev)| (pin, dev, node.node))
})
.ok_or(Error::DoesNotExist)?;
log::info!(
"hda#{}: default output {pin_id:?}, {dev:?}",
self.root.codec
);
let audio_node = self.node(node_id.nid).ok_or(Error::DoesNotExist)?;
let mut audio_widget = None;
for widget in audio_node.connection_chain(pin_id.nid) {
let wid = widget.node;
match &widget.widget {
AudioWidget::PinComplex(_) => {
log::info!(" Configure pin @ {wid:?}");
wid.set_power_state(&*self.hda, true).await?;
// TODO only set for jacks
wid.set_unsolicited_response(&*self.hda, Some(wid.nid))
.await?;
wid.modify_pin_control(
&*self.hda,
PinControl::OutEnable::SET + PinControl::HPhnEnable::SET,
)
.await?;
}
AudioWidget::AudioOutput(out) => {
log::info!(" Configure audio output @ {wid:?}");
audio_widget = Some(widget.node);
wid.set_power_state(&*self.hda, true).await?;
wid.set_unsolicited_response(&*self.hda, None).await?;
}
AudioWidget::Mixer(_) => {
log::info!(" Configure mixer @ {wid:?}");
wid.set_power_state(&*self.hda, true).await?;
}
_ => (),
}
}
Ok(audio_widget.unwrap())
}
}
impl fmt::Debug for Codec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Codec")
.field("id", &self.root.codec)
.field("nodes", &self.nodes)
.finish()
}
}
impl<'a> Iterator for WidgetConnectionChain<'a> {
type Item = &'a AudioWidgetNode;
fn next(&mut self) -> Option<Self::Item> {
let current = self.current?;
let widget = self.afg.widget(current)?;
self.current = widget.connection;
Some(widget)
}
}
@@ -0,0 +1,198 @@
use libk::error::Error;
use tock_registers::{register_bitfields, LocalRegisterCopy};
use crate::ring::NodeParameterNumber;
pub trait NodeParameter {
const NUMBER: NodeParameterNumber;
type Value;
fn data(&self) -> u32 {
0
}
fn from_response(response: u32) -> Self::Value;
}
#[derive(Debug)]
pub struct DeviceId {
pub vendor_id: u16,
pub device_id: u16,
}
#[derive(Debug)]
pub struct SubNodeInfo {
pub start_number: u8,
pub total_number: u8,
}
pub struct NodeType;
register_bitfields! {
u32,
pub AudioWidgetCapabilities [
CHANNEL_COUNT OFFSET(0) NUMBITS(1) [],
IN_AMP OFFSET(1) NUMBITS(1) [],
OUT_AMP OFFSET(2) NUMBITS(1) [],
AMP_OVERRIDE OFFSET(3) NUMBITS(1) [],
FORMAT_OVERRIDE OFFSET(4) NUMBITS(1) [],
STRIPE OFFSET(5) NUMBITS(1) [],
PROC_WIDGET OFFSET(6) NUMBITS(1) [],
UNSOL_CAPABLE OFFSET(7) NUMBITS(1) [],
CONN_LIST OFFSET(8) NUMBITS(1) [],
DIGITAL OFFSET(9) NUMBITS(1) [],
POWER_CONTROL OFFSET(10) NUMBITS(1) [],
LR_SWAP OFFSET(11) NUMBITS(1) [],
CP_CAPS OFFSET(12) NUMBITS(1) [],
EXT_CHANNEL_COUNT OFFSET(13) NUMBITS(3) [],
DELAY OFFSET(16) NUMBITS(4) [],
TYPE OFFSET(20) NUMBITS(4) [
AudioOutput = 0x0,
AudioInput = 0x1,
AudioMixer = 0x2,
AudioSelector = 0x3,
PinComplex = 0x4,
Power = 0x5,
VolumeKnob = 0x6,
BeepGenerator = 0x7,
VendorDefined = 0xF,
],
],
}
register_bitfields! {
u32,
pub AmpCapabilities [
OFFSET OFFSET(0) NUMBITS(7) [],
NUM_STEPS OFFSET(8) NUMBITS(7) [],
STEP_SIZE OFFSET(16) NUMBITS(7) [],
MUTE_CAPABLE OFFSET(31) NUMBITS(1) [],
],
}
pub struct InputAmpCapabilities;
pub struct OutputAmpCapabilities;
pub struct ConnectionListLength;
register_bitfields! {
u32,
pub SupportedStreamFormats [
PCM OFFSET(0) NUMBITS(1) [],
F32 OFFSET(1) NUMBITS(1) [],
AC3 OFFSET(2) NUMBITS(1) [],
],
}
register_bitfields! {
u32,
pub SupportedPcmFormats [
RATE_8 OFFSET(0) NUMBITS(1) [],
RATE_11_025 OFFSET(1) NUMBITS(1) [],
RATE_16 OFFSET(2) NUMBITS(1) [],
RATE_22_05 OFFSET(3) NUMBITS(1) [],
RATE_32 OFFSET(4) NUMBITS(1) [],
RATE_44_1 OFFSET(5) NUMBITS(1) [],
RATE_48 OFFSET(6) NUMBITS(1) [],
RATE_88_2 OFFSET(7) NUMBITS(1) [],
RATE_96 OFFSET(8) NUMBITS(1) [],
RATE_176_4 OFFSET(9) NUMBITS(1) [],
RATE_192 OFFSET(10) NUMBITS(1) [],
RATE_384 OFFSET(11) NUMBITS(1) [],
BITS_8 OFFSET(16) NUMBITS(1) [],
BITS_16 OFFSET(17) NUMBITS(1) [],
BITS_20 OFFSET(18) NUMBITS(1) [],
BITS_24 OFFSET(19) NUMBITS(1) [],
BITS_32 OFFSET(20) NUMBITS(1) [],
],
}
impl NodeParameter for DeviceId {
const NUMBER: NodeParameterNumber = NodeParameterNumber::DeviceId;
type Value = Self;
fn from_response(response: u32) -> Self::Value {
Self {
vendor_id: (response >> 16) as u16,
device_id: response as u16,
}
}
}
impl NodeParameter for SubNodeInfo {
const NUMBER: NodeParameterNumber = NodeParameterNumber::NodeCount;
type Value = Self;
fn from_response(response: u32) -> Self::Value {
Self {
start_number: (response >> 16) as u8,
total_number: response as u8,
}
}
}
impl NodeParameter for NodeType {
const NUMBER: NodeParameterNumber = NodeParameterNumber::FunctionGroupType;
type Value = u8;
fn from_response(response: u32) -> Self::Value {
response as u8
}
}
impl NodeParameter for AudioWidgetCapabilities::Register {
const NUMBER: NodeParameterNumber = NodeParameterNumber::AudioWidgetCapabilities;
type Value = LocalRegisterCopy<u32, Self>;
fn from_response(response: u32) -> Self::Value {
LocalRegisterCopy::new(response)
}
}
impl NodeParameter for InputAmpCapabilities {
const NUMBER: NodeParameterNumber = NodeParameterNumber::InputAmplifierCapabilities;
type Value = Option<LocalRegisterCopy<u32, AmpCapabilities::Register>>;
fn from_response(response: u32) -> Self::Value {
if response != 0 {
Some(LocalRegisterCopy::new(response))
} else {
None
}
}
}
impl NodeParameter for OutputAmpCapabilities {
const NUMBER: NodeParameterNumber = NodeParameterNumber::OutputAmplifierCapabilities;
type Value = Option<LocalRegisterCopy<u32, AmpCapabilities::Register>>;
fn from_response(response: u32) -> Self::Value {
if response != 0 {
Some(LocalRegisterCopy::new(response))
} else {
None
}
}
}
impl NodeParameter for ConnectionListLength {
const NUMBER: NodeParameterNumber = NodeParameterNumber::ConnectionListLength;
type Value = (usize, bool);
fn from_response(response: u32) -> Self::Value {
let long_form = response & (1 << 7) != 0;
let length = (response & 0x3F) as usize;
(length, long_form)
}
}
impl NodeParameter for SupportedStreamFormats::Register {
const NUMBER: NodeParameterNumber = NodeParameterNumber::SupportedFormats;
type Value = LocalRegisterCopy<u32, Self>;
fn from_response(response: u32) -> Self::Value {
LocalRegisterCopy::new(response)
}
}
impl NodeParameter for SupportedPcmFormats::Register {
const NUMBER: NodeParameterNumber = NodeParameterNumber::SupportedPcmRates;
type Value = LocalRegisterCopy<u32, Self>;
fn from_response(response: u32) -> Self::Value {
LocalRegisterCopy::new(response)
}
}
impl SubNodeInfo {
pub fn iter(&self) -> impl Iterator<Item = u8> {
// Exclude root
let start_subnode = self.start_number.max(1);
let end_subnode = self.start_number.saturating_add(self.total_number);
start_subnode..end_subnode
}
}
+344
View File
@@ -0,0 +1,344 @@
#![no_std]
#![feature(let_chains)]
use core::{
sync::atomic::{AtomicU8, Ordering},
time::Duration,
};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use async_trait::async_trait;
use codec::{AudioFormat, Codec, Node};
use device_api::{
device::{Device, DeviceInitContext},
dma::DmaAllocator,
interrupt::{InterruptAffinity, InterruptHandler, IrqVector},
};
use futures_util::task::AtomicWaker;
use libk::{
dma::DmaBuffer,
error::Error,
task::runtime::{self, psleep, with_timeout},
time::monotonic_time,
};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{
event::{BitmapEvent, BoolEvent, CounterEvent},
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
OneTimeInit,
};
use regs::{Regs, SDxCTL0, CORBRP, GCAP, RIRBWP};
use ring::{Command, CommandRing, NodeParameterNumber, Verb};
use sink::HdAudioSink;
use stream::{BufferDescriptorList, OutputStream};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use ygg_driver_pci::{
capability::{power::DevicePowerState, PowerManagementCapability},
device::{PciDeviceInfo, PreferredInterruptMode},
macros::pci_driver,
PciBaseAddress, PciConfigurationSpace,
};
extern crate alloc;
mod codec;
mod regs;
mod ring;
mod sink;
mod stream;
struct HdAudio {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
dma: Arc<dyn DmaAllocator>,
pci: PciDeviceInfo,
input_stream_count: usize,
output_stream_count: usize,
bidi_stream_count: usize,
sinks: Vec<IrqSafeSpinlock<Option<Arc<HdAudioSink>>>>,
last_stream_tag: AtomicU8,
softirq_event: BitmapEvent<AtomicWaker>,
cring: OneTimeInit<CommandRing>,
}
impl HdAudio {
fn new(
pci: PciDeviceInfo,
dma: Arc<dyn DmaAllocator>,
regs: DeviceMemoryIo<'static, Regs>,
input_stream_count: usize,
output_stream_count: usize,
bidi_stream_count: usize,
) -> Self {
Self {
pci,
dma,
regs: IrqSafeSpinlock::new(regs),
input_stream_count,
output_stream_count,
bidi_stream_count,
sinks: (0..output_stream_count)
.map(|_| IrqSafeSpinlock::new(None))
.collect(),
last_stream_tag: AtomicU8::new(1),
softirq_event: BitmapEvent::new(AtomicWaker::new()),
cring: OneTimeInit::new(),
}
}
}
impl HdAudio {
async fn late_init(self: &Arc<Self>) -> Result<(), Error> {
// Interrogate present codecs
let codecs = self.regs.lock().take_attached_codecs();
for index in (0..16).filter(|&i| codecs & (1 << i) != 0) {
match Codec::probe(self.clone(), index).await {
Ok(codec) => {
let codec = Arc::new(codec);
log::info!("{codec:#?}");
match codec.setup_default_output().await {
Ok(id) => match self.add_sink(codec, id).await {
Ok(_) => {
log::info!("hda#{}: sink added", id.codec);
}
Err(error) => {
log::info!("hda#{}: could not add sink: {error:?}", id.codec);
}
},
Err(error) => {
log::error!("Default output setup error: {error:?}");
}
}
}
Err(error) => {
log::error!("hda: codec #{index} setup error: {error:?}");
}
}
}
loop {
runtime::sleep(Duration::from_secs(1)).await;
}
}
async fn add_sink(
self: &Arc<Self>,
codec: Arc<Codec>,
audio_widget: Node,
) -> Result<Arc<HdAudioSink>, Error> {
for (index, slot) in self.sinks.iter().enumerate() {
let mut slot = slot.lock();
if slot.is_none() {
log::info!("hda: add sink #{index}");
let stream_tag = self.last_stream_tag.fetch_add(1, Ordering::Relaxed);
let sink = HdAudioSink::create(
self.clone(),
index + self.input_stream_count,
stream_tag,
codec.clone(),
audio_widget,
)
.await?;
*slot = Some(sink.clone());
return Ok(sink);
}
}
log::warn!("hda: too many sinks");
Err(Error::InvalidOperation)
}
async fn softirq(&self) -> Result<(), Error> {
let cring = self.cring.get();
loop {
match with_timeout(self.softirq_event.wait(), Duration::from_millis(100)).await {
// IRQs happened
Ok(events) => {
log::info!("softirq()");
for sink_index in
self.input_stream_count..self.input_stream_count + self.output_stream_count
{
// Handle sink events
if events & (1 << sink_index) != 0
&& let Some(sink) = self.sink(sink_index - self.input_stream_count)
{
let position = self.regs.lock().STREAMS[sink_index].SDxLPIB.get();
sink.handle_softirq(position).await;
}
}
//
}
// Just poll
Err(_) => {}
}
let regs = self.regs.lock();
let rirb_head = regs.RIRBWP.read(RIRBWP::RIRBWP) as u8;
let corb_tail = regs.CORBRP.read(CORBRP::CORBRP) as u8;
cring.process_completions(corb_tail, rirb_head);
}
}
async fn perform_command(&self, command: Command) -> Result<u32, Error> {
let cring = self.cring.get();
let codec = command.codec();
let head = cring.submit_command(command).await?;
{
let regs = self.regs.lock();
regs.CORBWP.set(head as u16);
}
with_timeout(cring.wait_codec(codec), Duration::from_secs(3)).await
}
fn sink(&self, index: usize) -> Option<Arc<HdAudioSink>> {
self.sinks[index].lock().clone()
}
}
impl InterruptHandler for HdAudio {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let regs = self.regs.lock();
let rirbsts = regs.RIRBSTS.extract();
let status = regs.INTSTS.get() & 0x3FFFFFFF;
if rirbsts.get() != 0 {
regs.RIRBSTS.set(rirbsts.get());
self.softirq_event.signal(1 << 30);
}
if status != 0 {
for stream in 0..30 {
if status & (1 << stream) != 0 {
let stream_status = regs.STREAMS[stream].SDxSTS.extract();
regs.STREAMS[stream].SDxSTS.set(stream_status.get());
}
}
self.softirq_event.signal(status as u64);
}
true
}
}
impl Device for HdAudio {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
// FLR if capable
self.pci.function_level_reset().ok();
self.pci
.map_interrupt(InterruptAffinity::Any, self.clone())?;
let regs = self.regs.lock();
let vmaj = regs.VMAJ.get();
let vmin = regs.VMIN.get();
log::info!(
"hda: version={}.{}, iss={}, oss={}, bss={}",
vmaj,
vmin,
self.input_stream_count,
self.output_stream_count,
self.bidi_stream_count
);
regs.reset(
Duration::from_millis(100),
self.input_stream_count + self.output_stream_count + self.bidi_stream_count,
)?;
regs.disable_interrupts();
regs.disable_control();
let corb_size = regs.set_max_corb_size()?;
let rirb_size = regs.set_max_rirb_size()?;
log::info!("hda: corb size = {corb_size}, rirb size = {rirb_size}");
let (cring, corb_base, rirb_base) =
CommandRing::with_capacity(&*self.dma, corb_size, rirb_size)?;
regs.initialize_corb_rirb(corb_base, rirb_base)?;
regs.start_corb_rirb();
regs.enable_interrupts();
self.cring.init(cring);
drop(regs);
// Spawn softirq worker
let this = self.clone();
runtime::spawn(async move { this.softirq().await })?;
// Do the main initialization in background
let this = self.clone();
runtime::spawn(async move { this.late_init().await })?;
Ok(())
}
fn display_name(&self) -> &str {
"High Definition Audio"
}
}
pci_driver! {
matches: [
device (0x8086:0x2668), // Intel ICH6 82801FB/FBM/FR/FW/FRW HDA
device (0x8086:0x293E), // Intel ICH9 82801I HDA
device (0x1022:0x1487), // AMD Starship/Matisse HDA
],
driver: {
fn probe(
&self,
info: &PciDeviceInfo,
dma: &Arc<dyn DmaAllocator>,
) -> Result<Arc<dyn Device>, Error> {
info.set_command(true, true, false, true);
let base = info
.config_space
.bar(0)
.and_then(PciBaseAddress::as_memory)
.ok_or(Error::InvalidArgument)?;
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
let regs = unsafe { DeviceMemoryIo::<Regs>::map(base, Default::default()) }?;
let gcap = regs.GCAP.extract();
if gcap.matches_all(GCAP::OK64::CLEAR) {
log::error!("Non 64-bit HD Audio not supported yet");
return Err(Error::NotImplemented);
}
let input_stream_count = gcap.read(GCAP::ISS) as usize;
let output_stream_count = gcap.read(GCAP::OSS).min(30) as usize;
let bidi_stream_count = gcap.read(GCAP::BSS) as usize;
let hda = Arc::new(HdAudio::new(
info.clone(),
dma.clone(),
regs,
input_stream_count,
output_stream_count,
bidi_stream_count,
));
Ok(hda)
}
fn driver_name(&self) -> &str {
"intel-hda"
}
}
}
+453
View File
@@ -0,0 +1,453 @@
use core::time::Duration;
use libk::{
dma::BusAddress,
error::Error,
task::runtime::{psleep, pwait},
};
use tock_registers::{
fields::FieldValue,
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use ygg_driver_sound_core::{SampleFormat, SampleRate, SinkFormat};
use crate::stream::OutputStream;
register_bitfields! {
u8,
pub CORBCTL [
/// Enable CORB DMA engine
CORBRUN OFFSET(1) NUMBITS(1) [],
/// CORB memory error interrupt enable
CMEIE OFFSET(0) NUMBITS(1) [],
],
pub CORBSTS [
/// CORB memory error
CMEI OFFSET(0) NUMBITS(1) [],
],
pub CORBSIZE [
CORBSZCAP OFFSET(4) NUMBITS(4) [],
CORBSIZE OFFSET(0) NUMBITS(2) [
Size2 = 0b00,
Size16 = 0b01,
Size256 = 0b10,
],
],
pub RIRBCTL [
/// RIRB overrun interrupt control
RURBOIC OFFSET(2) NUMBITS(1) [],
/// Enable RIRB DMA engine
RIRBDMAEN OFFSET(1) NUMBITS(1) [],
/// Response interrupt control
RINTCTL OFFSET(0) NUMBITS(1) [],
],
pub RIRBSTS [
/// RIRB overrun interrupt
RIRBOIS OFFSET(2) NUMBITS(1) [],
/// Response interrupt
RINTFL OFFSET(0) NUMBITS(1) [],
],
pub RIRBSIZE [
/// RIRB size capability
RIRBSZCAP OFFSET(4) NUMBITS(4) [],
RIRBSIZE OFFSET(0) NUMBITS(2) [
Size2 = 0b00,
Size16 = 0b01,
Size256 = 0b10,
],
],
pub SDxCTL0 [
SRST OFFSET(0) NUMBITS(1) [],
RUN OFFSET(1) NUMBITS(1) [],
IOCE OFFSET(2) NUMBITS(1) [],
FEIE OFFSET(3) NUMBITS(1) [],
DEIE OFFSET(4) NUMBITS(1) [],
],
pub SDxCTL2 [
STRIPE OFFSET(0) NUMBITS(2) [],
TP OFFSET(2) NUMBITS(1) [],
DIR OFFSET(3) NUMBITS(1) [],
STRM OFFSET(4) NUMBITS(4) [],
],
pub SDxSTS [
BCIS OFFSET(2) NUMBITS(1) [],
FIFOE OFFSET(3) NUMBITS(1) [],
DESE OFFSET(4) NUMBITS(1) [],
FIFORDY OFFSET(5) NUMBITS(1) [],
],
}
register_bitfields! {
u16,
pub GCAP [
/// Number of output streams supported
OSS OFFSET(12) NUMBITS(4) [],
/// Number of input streams supported
ISS OFFSET(8) NUMBITS(4) [],
/// Number of bidirectional streams supported
BSS OFFSET(3) NUMBITS(5) [],
/// Number of serial data out signals
NSDO OFFSET(1) NUMBITS(2) [],
/// 64-bit addresses supported
OK64 OFFSET(0) NUMBITS(1) [],
],
pub GSTS [
/// Flush status bit
FSTS OFFSET(1) NUMBITS(1) [],
],
pub CORBRP [
/// CORB read pointer reset
CORBRPRST OFFSET(15) NUMBITS(1) [],
/// CORB read pointer
CORBRP OFFSET(0) NUMBITS(8) [],
],
pub RIRBWP [
/// RIRB write pointer reset
RIRBWPRST OFFSET(15) NUMBITS(1) [],
/// RIRB write pointer
RIRBWP OFFSET(0) NUMBITS(8) [],
],
pub SDxFMT [
CHAN OFFSET(0) NUMBITS(4) [],
BITS OFFSET(4) NUMBITS(3) [
Bits8 = 0b000,
Bits16 = 0b001,
Bits20 = 0b010,
Bits24 = 0b011,
Bits32 = 0b100,
],
DIV OFFSET(8) NUMBITS(3) [],
MULT OFFSET(11) NUMBITS(3) [],
BASE OFFSET(14) NUMBITS(1) [],
],
}
register_bitfields! {
u32,
pub GCTL [
/// Accept unsolicited response enable
UNSOL OFFSET(8) NUMBITS(1) [],
/// Flush control
FCNTRL OFFSET(1) NUMBITS(1) [],
/// Controller reset
CRST OFFSET(0) NUMBITS(1) [],
],
pub INTCTL [
/// Global interrupt enable
GIE OFFSET(31) NUMBITS(1) [],
/// Controller interrupt enable
CIE OFFSET(30) NUMBITS(1) [],
// Stream interrupt enable bits are based on stream counts
],
pub INTSTS [
/// Global interrupt status
GIS OFFSET(31) NUMBITS(1) [],
/// Controller interrupt status
CIS OFFSET(30) NUMBITS(1) [],
// Stream interrupt status bits are based on stream counts
],
}
register_structs! {
#[allow(non_snake_case)]
pub Regs {
(0x0000 => pub GCAP: ReadOnly<u16, GCAP::Register>),
(0x0002 => pub VMIN: ReadOnly<u8>),
(0x0003 => pub VMAJ: ReadOnly<u8>),
(0x0004 => pub OUTPAY: ReadOnly<u16>),
(0x0006 => pub INPAY: ReadOnly<u16>),
(0x0008 => pub GCTL: ReadWrite<u32, GCTL::Register>),
(0x000C => pub WAKEEN: ReadWrite<u16>),
(0x000E => pub WAKESTS: ReadWrite<u16>),
(0x0010 => pub GSTS: ReadWrite<u16, GSTS::Register>),
(0x0012 => _0),
(0x0018 => pub OUTSTRMPAY: ReadOnly<u16>),
(0x001A => pub INSTRMPAY: ReadOnly<u16>),
(0x001C => _1),
(0x0020 => pub INTCTL: ReadWrite<u32, INTCTL::Register>),
(0x0024 => pub INTSTS: ReadOnly<u32, INTSTS::Register>),
(0x0028 => _2),
(0x0030 => pub WALCLK: ReadOnly<u32>),
(0x0034 => _3),
(0x0038 => pub SSYNC: ReadWrite<u32>),
(0x003C => _4),
(0x0040 => pub CORBLBASE: ReadWrite<u32>),
(0x0044 => pub CORBUBASE: ReadWrite<u32>),
(0x0048 => pub CORBWP: ReadWrite<u16>),
(0x004A => pub CORBRP: ReadWrite<u16, CORBRP::Register>),
(0x004C => pub CORBCTL: ReadWrite<u8, CORBCTL::Register>),
(0x004D => pub CORBSTS: ReadWrite<u8, CORBSTS::Register>),
(0x004E => pub CORBSIZE: ReadWrite<u8, CORBSIZE::Register>),
(0x004F => _5),
(0x0050 => pub RIRBLBASE: ReadWrite<u32>),
(0x0054 => pub RIRBUBASE: ReadWrite<u32>),
(0x0058 => pub RIRBWP: ReadWrite<u16, RIRBWP::Register>),
(0x005A => pub RINTCNT: ReadWrite<u16>),
(0x005C => pub RIRBCTL: ReadWrite<u8, RIRBCTL::Register>),
(0x005D => pub RIRBSTS: ReadWrite<u8, RIRBSTS::Register>),
(0x005E => pub RIRBSIZE: ReadWrite<u8, RIRBSIZE::Register>),
(0x005F => _6),
(0x0060 => pub ICOI: ReadWrite<u32>),
(0x0064 => pub ICII: ReadWrite<u32>),
(0x0068 => pub ICIS: ReadOnly<u16>),
(0x006A => _7),
(0x0070 => pub DPLBASE: ReadWrite<u32>),
(0x0074 => pub DPUBASE: ReadWrite<u32>),
(0x0078 => _8),
(0x0080 => pub STREAMS: [StreamRegs; 128]),
(0x1080 => _9),
(0x2030 => pub WALCLKA: ReadOnly<u32>),
(0x2034 => _10),
(0x2080 => pub SDxLPIBA: [StreamLinkRegs; 128]),
(0x3080 => _11),
(0x4000 => @END),
}
}
register_structs! {
#[allow(non_snake_case)]
pub StreamRegs {
(0x00 => pub SDxCTL0: ReadWrite<u8, SDxCTL0::Register>),
(0x01 => pub SDxCTL1: ReadWrite<u8>),
(0x02 => pub SDxCTL2: ReadWrite<u8, SDxCTL2::Register>),
(0x03 => pub SDxSTS: ReadWrite<u8, SDxSTS::Register>),
(0x04 => pub SDxLPIB: ReadWrite<u32>),
(0x08 => pub SDxCBL: ReadWrite<u32>),
(0x0C => pub SDxLVI: ReadWrite<u16>),
(0x0E => _0),
(0x10 => pub SDxFIFOD: ReadWrite<u16>),
(0x12 => pub SDxFMT: ReadWrite<u16, SDxFMT::Register>),
(0x14 => _1),
(0x18 => pub SDxBDPL: ReadWrite<u32>),
(0x1C => pub SDxBDPU: ReadWrite<u32>),
(0x20 => @END),
}
}
register_structs! {
#[allow(non_snake_case)]
pub StreamLinkRegs {
(0x00 => _0),
(0x04 => pub SDxLPIBA: ReadOnly<u32>),
(0x08 => _1),
(0x20 => @END),
}
}
impl Regs {
pub fn reset(&self, timeout: Duration, stream_count: usize) -> Result<(), Error> {
// Go through all the streams, disable their DMA
for i in 0..stream_count {
let stream = &self.STREAMS[i];
stream
.SDxCTL0
.write(SDxCTL0::RUN::CLEAR + SDxCTL0::SRST::SET);
}
self.CORBCTL.write(CORBCTL::CORBRUN::CLEAR);
self.RIRBCTL.write(RIRBCTL::RIRBDMAEN::CLEAR);
// Flush the FIFO
self.GCTL.modify(GCTL::FCNTRL::SET);
psleep(Duration::from_millis(10));
pwait(timeout, Duration::from_millis(10), || {
self.GSTS.matches_all(GSTS::FSTS::SET)
})?;
self.GCTL.modify(GCTL::FCNTRL::CLEAR);
self.GSTS.write(GSTS::FSTS::SET);
// TODO does FIFO need to be flushed here as well?
// Begin controller reset
self.GCTL.write(GCTL::CRST::CLEAR);
psleep(Duration::from_millis(10));
// End controller reset
self.GCTL.write(GCTL::CRST::SET);
psleep(Duration::from_millis(10));
pwait(timeout, Duration::from_millis(10), || {
self.GCTL.matches_all(GCTL::CRST::SET)
})?;
Ok(())
}
pub fn disable_interrupts(&self) {
self.INTCTL.set(0);
}
pub fn enable_interrupts(&self) {
// Also accept unsolicited responses from codecs
self.GCTL.modify(GCTL::UNSOL::SET);
self.INTCTL.write(INTCTL::GIE::SET + INTCTL::CIE::SET);
}
pub fn enable_stream_interrupts(&self, stream: usize) {
self.INTCTL.set(self.INTCTL.get() | (1 << stream));
}
pub fn disable_control(&self) {
self.DPLBASE.set(0);
self.DPUBASE.set(0);
self.CORBCTL.write(CORBCTL::CORBRUN::CLEAR);
self.RIRBCTL.write(RIRBCTL::RIRBDMAEN::CLEAR);
}
pub fn set_max_corb_size(&self) -> Result<usize, Error> {
const SIZES: &[(usize, u8, FieldValue<u8, CORBSIZE::Register>)] = &[
(256, 0b0100, CORBSIZE::CORBSIZE::Size256),
(16, 0b0010, CORBSIZE::CORBSIZE::Size16),
(2, 0b0001, CORBSIZE::CORBSIZE::Size2),
];
let corbsize = self.CORBSIZE.extract();
let cap = corbsize.read(CORBSIZE::CORBSZCAP);
for &(size, bit, cfg) in SIZES {
if cap & bit != 0 {
self.CORBSIZE.modify_no_read(corbsize, cfg);
return Ok(size);
}
}
log::error!("hda: no supported CORB size");
Err(Error::InvalidOperation)
}
pub fn set_max_rirb_size(&self) -> Result<usize, Error> {
const SIZES: &[(usize, u8, FieldValue<u8, RIRBSIZE::Register>)] = &[
(256, 0b0100, RIRBSIZE::RIRBSIZE::Size256),
(16, 0b0010, RIRBSIZE::RIRBSIZE::Size16),
(2, 0b0001, RIRBSIZE::RIRBSIZE::Size2),
];
let rirbsize = self.RIRBSIZE.extract();
let cap = rirbsize.read(RIRBSIZE::RIRBSZCAP);
for &(size, bit, cfg) in SIZES {
if cap & bit != 0 {
self.RIRBSIZE.modify_no_read(rirbsize, cfg);
return Ok(size);
}
}
log::error!("hda: no supported RIRB size");
Err(Error::InvalidOperation)
}
pub fn initialize_corb_rirb(
&self,
corb_base: BusAddress,
rirb_base: BusAddress,
) -> Result<(), Error> {
let corb_base = corb_base.into_u64();
let rirb_base = rirb_base.into_u64();
self.CORBUBASE.set((corb_base >> 32) as u32);
self.CORBLBASE.set(corb_base as u32);
self.RIRBUBASE.set((rirb_base >> 32) as u32);
self.RIRBLBASE.set(rirb_base as u32);
// Reset write/read pointers
self.CORBWP.set(0);
self.CORBRP.write(CORBRP::CORBRPRST::SET);
self.RIRBSTS
.write(RIRBSTS::RIRBOIS::SET + RIRBSTS::RINTFL::SET);
self.RINTCNT.set(255);
// Ensure CORBRP is reset properly
pwait(
Duration::from_millis(100),
Duration::from_millis(10),
|| self.CORBRP.matches_all(CORBRP::CORBRPRST::SET),
)?;
self.CORBRP.write(CORBRP::CORBRPRST::CLEAR);
pwait(
Duration::from_millis(100),
Duration::from_millis(10),
|| self.CORBRP.matches_all(CORBRP::CORBRPRST::CLEAR),
)?;
Ok(())
}
pub fn start_corb_rirb(&self) {
self.CORBCTL
.modify(CORBCTL::CORBRUN::SET + CORBCTL::CMEIE::SET);
self.RIRBCTL
.modify(RIRBCTL::RIRBDMAEN::SET + RIRBCTL::RINTCTL::SET);
}
pub fn take_attached_codecs(&self) -> u16 {
let state = self.WAKESTS.get();
self.WAKESTS.set(0xFFFF);
state
}
pub fn configure_stream(
&self,
index: usize,
config: &SinkFormat,
bdl_base: BusAddress,
buffer_size: usize,
buffer_count: usize,
number: u8,
) -> Result<(), Error> {
log::info!("Configure stream: #{index}");
let bdl_base = bdl_base.into_u64();
// TODO don't use pwait in async context
let regs = &self.STREAMS[index];
// Stop and reset the stream
regs.SDxCTL0.write(SDxCTL0::SRST::SET);
pwait(
Duration::from_millis(100),
Duration::from_millis(10),
|| regs.SDxCTL0.matches_all(SDxCTL0::SRST::SET),
)?;
regs.SDxCTL0.write(SDxCTL0::SRST::CLEAR);
pwait(
Duration::from_millis(100),
Duration::from_millis(10),
|| regs.SDxCTL0.matches_all(SDxCTL0::SRST::CLEAR),
)?;
regs.SDxCTL2.write(SDxCTL2::STRM.val(number));
regs.SDxSTS.write(SDxSTS::BCIS::SET);
regs.SDxLVI.set(buffer_count as u16 - 1);
regs.SDxCBL.set((buffer_count * buffer_size) as _);
regs.SDxBDPU.set((bdl_base >> 32) as u32);
regs.SDxBDPL.set(bdl_base as u32);
let mut format = SDxFMT::CHAN.val(config.channels as u16 - 1);
match config.sample_format {
SampleFormat::S8 => format += SDxFMT::BITS::Bits8,
SampleFormat::S16Le => format += SDxFMT::BITS::Bits16,
}
match config.sample_rate {
SampleRate::Rate8000 => format += SDxFMT::DIV.val(5),
SampleRate::Rate11025 => format += SDxFMT::DIV.val(3) + SDxFMT::BASE::SET,
SampleRate::Rate16000 => format += SDxFMT::DIV.val(2),
SampleRate::Rate22050 => format += SDxFMT::DIV.val(1) + SDxFMT::BASE::SET,
SampleRate::Rate32000 => format += SDxFMT::DIV.val(2) + SDxFMT::MULT.val(1),
SampleRate::Rate44100 => format += SDxFMT::BASE::SET,
SampleRate::Rate48000 => (),
SampleRate::Rate88200 => format += SDxFMT::MULT.val(1) + SDxFMT::BASE::SET,
SampleRate::Rate96000 => format += SDxFMT::MULT.val(1),
SampleRate::Rate176400 => format += SDxFMT::MULT.val(3) + SDxFMT::BASE::SET,
SampleRate::Rate192000 => format += SDxFMT::MULT.val(3),
SampleRate::Rate384000 => todo!(),
}
regs.SDxFMT.write(format);
regs.SDxCTL0.modify(SDxCTL0::IOCE::SET);
Ok(())
}
}
+396
View File
@@ -0,0 +1,396 @@
use core::{future::poll_fn, task::Poll};
use device_api::dma::DmaAllocator;
use futures_util::task::AtomicWaker;
use libk::{
dma::{BusAddress, DmaBuffer},
error::Error,
};
use libk_util::{sync::IrqSafeSpinlock, waker::QueueWaker};
use tock_registers::register_bitfields;
use yggdrasil_abi::primitive_enum;
// 4 bit address
const MAX_CODEC: usize = 16;
// Combines CORB and RIRB
struct Inner {
corb: DmaBuffer<[u32]>,
rirb: DmaBuffer<[(u32, u32)]>,
codec_responses: [Option<u32>; MAX_CODEC],
corb_head: u32,
corb_tail: u32,
rirb_tail: u32,
}
pub struct CommandRing {
inner: IrqSafeSpinlock<Inner>,
// 16 possible codec addresses
codec_notify: [AtomicWaker; MAX_CODEC],
// Unsolicited response notify
unsol_notify: QueueWaker,
}
primitive_enum! {
pub enum Verb: u32 {
GetParameter = 0xF00,
GetConnectionListEntry = 0xF02,
SetSelectedInput = 0x701,
SetPowerState = 0x705,
SetConverterStreamChannel = 0x706,
SetPinWidgetControl = 0x707,
GetPinWidgetControl = 0xF07,
GetPinWidgetSense = 0xF09,
SetUnsolicitedResponse = 0x708,
GetUnsolicitedResponse = 0xF08,
SetEapdBtl = 0x70C,
GetPinWidgetDefaultConfig = 0xF1C,
SetOutputConverterChannelCount = 0x72D,
AudioFunctionNodeReset = 0x7FF,
SetAmplifierGain = 0x003,
SetStreamConverterFormat = 0x002,
SetStreamFormat = 0x200,
}
}
primitive_enum! {
pub enum NodeParameterNumber: u32 {
DeviceId = 0x00,
RevisionId = 0x02,
NodeCount = 0x04,
FunctionGroupType = 0x05,
AudioGroupCapabilities = 0x08,
AudioWidgetCapabilities = 0x09,
SupportedPcmRates = 0x0A,
SupportedFormats = 0x0B,
PinCapabilities = 0x0C,
InputAmplifierCapabilities = 0x0D,
OutputAmplifierCapabilities = 0x12,
ConnectionListLength = 0x0E,
SupportedPowerStates = 0x0F,
ProcessingCapabilities = 0x10,
GpioCount = 0x11,
VolumeCapabilities = 0x13,
}
}
register_bitfields! {
u32,
pub PinControl [
VRefEn OFFSET(0) NUMBITS(3) [],
InEnable OFFSET(5) NUMBITS(1) [],
OutEnable OFFSET(6) NUMBITS(1) [],
HPhnEnable OFFSET(7) NUMBITS(1) [],
],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PinColor {
Black,
Gray,
Blue,
Green,
Red,
Orange,
Yellow,
Purple,
Pink,
White,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PinType {
I1_8,
I1_4,
AtapiInternal,
Rca,
Optical,
OtherDigital,
OtherAnalog,
MultichannelAnalog,
Xlr,
Rj11,
Combination,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PinDevice {
LineOut,
Speaker,
HeadphoneOut,
Cd,
SpdifOut,
OtherDigitalOut,
ModemLineSide,
ModemHandsetSide,
LineIn,
Aux,
MicIn,
Telephony,
SpdifIn,
OtherDigitalIn,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PinConnectivity {
None,
Jack,
Fixed,
Both,
}
#[derive(Debug)]
pub struct PinDefaultConfig {
pub sequence: u8,
pub default_association: u8,
pub color: PinColor,
pub connection_type: PinType,
pub default_device: PinDevice,
pub connectivity: PinConnectivity,
}
#[derive(Clone, Copy)]
pub struct Command(u32);
impl Inner {
// Submit a command, returns new head and command token
pub fn push(&mut self, command: Command) -> Option<u8> {
if (self.corb_head + 1) % (self.corb.len() as u32) == self.corb_tail {
return None;
}
self.corb_head = (self.corb_head + 1) % self.corb.len() as u32;
let index = self.corb_head as usize % self.corb.len();
self.corb[index] = command.0;
self.corb.cache_flush_element(index, true);
Some(index as u8)
}
pub fn process_responses<U: Fn(u8, u32), C: Fn(u8)>(
&mut self,
unsol_handler: U,
codec_handler: C,
corb_tail: u32,
rirb_head: u32,
) -> usize {
self.corb_tail = corb_tail;
let mut count = 0;
while self.rirb_tail != rirb_head {
self.rirb_tail = (self.rirb_tail + 1) % self.rirb.len() as u32;
let index = self.rirb_tail as usize;
self.rirb.cache_flush_element(index, false);
let (w0, w1) = self.rirb[index];
let codec = (w1 & 0xF) as u8;
if w1 & (1 << 4) == 0 {
// Solicited
self.codec_responses[codec as usize] = Some(w0);
codec_handler(codec);
} else {
// Unsolicited
unsol_handler(codec, w0);
}
count += 1;
}
count
}
pub fn take_codec_response(&mut self, codec: u8) -> Option<u32> {
self.codec_responses[codec as usize].take()
}
}
impl CommandRing {
pub fn with_capacity(
dma: &dyn DmaAllocator,
corb_size: usize,
rirb_size: usize,
) -> Result<(Self, BusAddress, BusAddress), Error> {
let corb = DmaBuffer::new_slice(dma, 0, corb_size)?;
let rirb = DmaBuffer::new_slice(dma, (0, 0), rirb_size)?;
let corb_base = corb.bus_address();
let rirb_base = rirb.bus_address();
Ok((
Self {
inner: IrqSafeSpinlock::new(Inner {
corb,
rirb,
corb_head: 0,
corb_tail: 0,
rirb_tail: 0,
codec_responses: [None; MAX_CODEC],
}),
codec_notify: [const { AtomicWaker::new() }; 16],
unsol_notify: QueueWaker::new(),
},
corb_base,
rirb_base,
))
}
// Wait for a specific codec to send its response
pub async fn wait_codec(&self, codec: u8) -> u32 {
poll_fn(|cx| {
if let Some(response) = self.inner.lock().take_codec_response(codec) {
Poll::Ready(response)
} else {
self.codec_notify[codec as usize].register(cx.waker());
Poll::Pending
}
})
.await
}
pub async fn submit_command(&self, command: Command) -> Result<u8, Error> {
// TODO block and wait for free slots in CORB
let mut inner = self.inner.lock();
// Clear previous codec response
let head = inner.push(command).ok_or(Error::WouldBlock)?;
inner.codec_responses[command.codec() as usize] = None;
Ok(head)
}
pub(super) fn process_completions(&self, corb_tail: u8, rirb_head: u8) -> usize {
let mut inner = self.inner.lock();
inner.process_responses(
|codec, message| {
log::info!("Unsolicited message: {message:#x} from codec {codec:#x}");
},
|codec| self.codec_notify[codec as usize].wake(),
corb_tail as u32,
rirb_head as u32,
)
}
}
impl Command {
pub const fn new(codec: u8, node: u8, verb: Verb, payload: u32) -> Self {
Self(((codec as u32) << 28) | ((node as u32) << 20) | ((verb as u32) << 8) | payload)
}
pub const fn get_parameter(codec: u8, node: u8, parameter: NodeParameterNumber) -> Self {
Self::new(codec, node, Verb::GetParameter, parameter as u32)
}
pub const fn get_connection_list_entry(codec: u8, node: u8, index: usize) -> Self {
Self::new(codec, node, Verb::GetConnectionListEntry, index as u32)
}
pub const fn set_stream_number(codec: u8, node: u8, stream: usize) -> Self {
Self::new(codec, node, Verb::SetConverterStreamChannel, stream as u32)
}
pub fn codec(&self) -> u8 {
((self.0 >> 28) & 0xF) as u8
}
}
impl From<u32> for PinConnectivity {
fn from(value: u32) -> Self {
match value {
0b00 => Self::Jack,
0b10 => Self::Fixed,
0b11 => Self::Both,
_ => Self::None,
}
}
}
impl From<u32> for PinColor {
fn from(value: u32) -> Self {
match value {
1 => Self::Black,
2 => Self::Gray,
3 => Self::Blue,
4 => Self::Green,
5 => Self::Red,
6 => Self::Orange,
7 => Self::Yellow,
8 => Self::Purple,
9 => Self::Pink,
14 => Self::White,
_ => Self::Other,
}
}
}
impl From<u32> for PinType {
fn from(value: u32) -> Self {
match value {
1 => Self::I1_8,
2 => Self::I1_4,
3 => Self::AtapiInternal,
4 => Self::Rca,
5 => Self::Optical,
6 => Self::OtherDigital,
7 => Self::OtherAnalog,
8 => Self::MultichannelAnalog,
9 => Self::Xlr,
10 => Self::Rj11,
11 => Self::Combination,
_ => Self::Other,
}
}
}
impl PinDevice {
pub fn output_score(&self) -> u32 {
match self {
Self::HeadphoneOut => 10,
Self::Speaker => 9,
Self::LineOut => 8,
Self::SpdifOut => 7,
_ => 0,
}
}
}
impl From<u32> for PinDevice {
fn from(value: u32) -> Self {
match value {
0 => Self::LineOut,
1 => Self::Speaker,
2 => Self::HeadphoneOut,
3 => Self::Cd,
4 => Self::SpdifOut,
5 => Self::OtherDigitalOut,
6 => Self::ModemLineSide,
7 => Self::ModemHandsetSide,
8 => Self::LineIn,
9 => Self::Aux,
10 => Self::MicIn,
11 => Self::Telephony,
12 => Self::SpdifIn,
13 => Self::OtherDigitalIn,
_ => Self::Other,
}
}
}
impl From<u32> for PinDefaultConfig {
fn from(value: u32) -> Self {
let sequence = (value & 0xF) as u8;
let default_association = ((value >> 4) & 0xF) as u8;
let color = PinColor::from((value >> 12) & 0xF);
let connection_type = PinType::from((value >> 16) & 0xF);
let default_device = PinDevice::from((value >> 20) & 0xF);
let connectivity = PinConnectivity::from(value >> 30);
Self {
sequence,
color,
connectivity,
connection_type,
default_device,
default_association,
}
}
}
+171
View File
@@ -0,0 +1,171 @@
use core::{
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll},
time::Duration,
};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use async_trait::async_trait;
use device_api::device::Device;
use futures_util::task::AtomicWaker;
use libk::{device::char::CharDevice, error::Error, fs::devfs, task::runtime, vfs::FileReadiness};
use libk_mm::PageBox;
use libk_util::{event::BoolEvent, sync::spin_rwlock::IrqSafeRwLock};
use tock_registers::interfaces::ReadWriteable;
use ygg_driver_sound_core::{
AudioSink, SampleFormat, SampleRate, SinkFormat, SinkSupportedFormats,
};
use yggdrasil_abi::io::FileMode;
use crate::{
codec::{Codec, Node},
regs::SDxCTL0,
ring::Verb,
stream::OutputStream,
HdAudio,
};
pub struct HdAudioSink {
hda: Arc<HdAudio>,
codec: Arc<Codec>,
audio_widget: Node,
pub index: usize,
stream_tag: u8,
stream: OutputStream,
notify: BoolEvent,
config: IrqSafeRwLock<SinkFormat>,
playing: AtomicBool,
}
impl HdAudioSink {
pub async fn create(
hda: Arc<HdAudio>,
index: usize,
stream_tag: u8,
codec: Arc<Codec>,
audio_widget: Node,
) -> Result<Arc<Self>, Error> {
// TODO use default/current stream format (if PCM), don't always set 48KHz S16 x2
log::info!("hda: create sink #{index}, tag {stream_tag}");
let config = SinkFormat {
sample_rate: SampleRate::Rate48000,
sample_format: SampleFormat::S16Le,
channels: 2,
};
let one_millisecond =
(config.sample_rate as usize * config.sample_format.sample_size() * config.channels)
/ 1000;
let bdl_capacity = 32;
log::info!("buffer_count = {bdl_capacity}, buffer_size = {one_millisecond}");
let (stream, bdl_address) = OutputStream::new(hda.clone(), one_millisecond, bdl_capacity)?;
// TODO check if the widget actually supports such format
audio_widget
.perform_command(&*hda, Verb::SetStreamFormat, (1 << 4) | 1)
.await?;
{
let regs = hda.regs.lock();
regs.configure_stream(
index,
&config,
bdl_address,
one_millisecond,
bdl_capacity,
stream_tag,
)?;
regs.enable_stream_interrupts(index);
}
audio_widget.set_stream(&*hda, stream_tag, 0).await?;
let this = Arc::new(Self {
hda,
codec,
audio_widget,
index,
stream_tag,
stream,
notify: BoolEvent::new(),
playing: AtomicBool::new(false),
config: IrqSafeRwLock::new(config),
});
ygg_driver_sound_core::register_audio_sink(this.clone());
Ok(this)
}
pub async fn handle_softirq(&self, position: u32) {
self.stream.update_tail(position);
self.notify.signal_saturating();
}
async fn write_blocking(&self, data: &[u8]) -> Result<(), Error> {
let mut position = 0;
while position != data.len() {
let written = self.stream.write(&data[position..]);
position += written;
if written == 0 {
self.notify.wait_reset().await;
}
}
Ok(())
}
}
#[async_trait]
impl AudioSink for HdAudioSink {
async fn set_format(&self, format: SinkFormat) -> Result<(), Error> {
todo!()
}
async fn current_format(&self) -> Result<SinkFormat, Error> {
todo!()
}
async fn supported_formats(&self) -> Result<SinkSupportedFormats, Error> {
todo!()
}
async fn write(&self, data: &[u8]) -> Result<usize, Error> {
let amount = data.len().min(48000 * 2);
if !self.playing.swap(true, Ordering::Acquire) {
self.start().await?;
}
self.write_blocking(&data[..amount]).await?;
Ok(data.len())
}
async fn start(&self) -> Result<(), Error> {
self.playing.store(true, Ordering::Release);
let regs = self.hda.regs.lock();
log::info!("hda: start stream #{}", self.index);
self.stream.reset();
regs.STREAMS[self.index].SDxCTL0.modify(SDxCTL0::RUN::SET);
Ok(())
}
fn stop(&self) -> Result<(), Error> {
self.playing.store(false, Ordering::Release);
let regs = self.hda.regs.lock();
log::info!("hda: stop stream #{}", self.index);
regs.STREAMS[self.index].SDxCTL0.modify(SDxCTL0::RUN::CLEAR);
Ok(())
}
}
#[async_trait]
impl CharDevice for HdAudioSink {
async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
AudioSink::write(self, buffer).await
}
}
impl FileReadiness for HdAudioSink {
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
todo!()
}
}
impl Device for HdAudioSink {
fn display_name(&self) -> &str {
"HD Audio Sink"
}
}
+135
View File
@@ -0,0 +1,135 @@
use alloc::{sync::Arc, vec::Vec};
use device_api::dma::DmaAllocator;
use libk::{
dma::{BusAddress, DmaBuffer},
error::Error,
};
use libk_util::{
event::BoolEvent,
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
};
use ygg_driver_sound_core::SinkFormat;
use crate::HdAudio;
#[derive(Debug)]
#[repr(C)]
pub struct BufferDescriptor {
address: BusAddress,
length: u32,
flags: u32,
}
pub struct BufferDescriptorList {
entries: DmaBuffer<[BufferDescriptor]>,
periods: Vec<DmaBuffer<[u8]>>,
wr: usize,
rd: usize,
buffer_size: usize,
}
pub enum StreamKind {
Input,
Output,
Bidi,
}
pub struct OutputStream {
pub hda: Arc<HdAudio>,
pub bdl: IrqSafeSpinlock<BufferDescriptorList>,
}
impl BufferDescriptorList {
pub fn with_capacity(
dma: &dyn DmaAllocator,
capacity: usize,
frame_size: usize,
) -> Result<Self, Error> {
let periods: Vec<DmaBuffer<[u8]>> = (0..capacity)
.map(|_| DmaBuffer::new_slice(dma, 0, frame_size))
.collect::<Result<_, _>>()?;
let entries =
DmaBuffer::new_slice_with(dma, |i| BufferDescriptor::new(&periods[i]), capacity)?;
Ok(Self {
entries,
wr: 0,
rd: 0,
periods,
buffer_size: frame_size,
})
}
pub fn write(&mut self, data: &[u8]) -> usize {
let capacity = self.periods.len() * self.buffer_size;
let mut position = 0;
while (self.wr + 1) % capacity != self.rd && position != data.len() {
let p = self.wr / self.buffer_size;
let o = self.wr % self.buffer_size;
self.periods[p][o] = data[position];
position += 1;
self.wr = (self.wr + 1) % capacity;
}
position
}
pub fn update_tail(&mut self, tail: u32) {
self.rd = tail as usize;
}
pub fn reset(&mut self) {
self.wr = self.rd;
}
}
impl BufferDescriptor {
pub fn new(buffer: &DmaBuffer<[u8]>) -> Self {
Self {
address: buffer.bus_address(),
length: buffer.len() as _,
flags: 1,
}
}
}
impl OutputStream {
pub fn new(
hda: Arc<HdAudio>,
buffer_size: usize,
bdl_capacity: usize,
) -> Result<(Self, BusAddress), Error> {
let bdl = BufferDescriptorList::with_capacity(&*hda.dma, bdl_capacity, buffer_size)?;
let bdl_base = bdl.entries.bus_address();
Ok((
Self {
hda,
bdl: IrqSafeSpinlock::new(bdl),
},
bdl_base,
))
}
pub fn params(&self) -> (BusAddress, usize, usize) {
let bdl = self.bdl.lock();
(
bdl.entries.bus_address(),
bdl.buffer_size,
bdl.entries.len(),
)
}
pub fn reset(&self) {
self.bdl.lock().reset()
}
pub fn write(&self, bytes: &[u8]) -> usize {
self.bdl.lock().write(bytes)
}
pub fn update_tail(&self, tail: u32) {
self.bdl.lock().update_tail(tail)
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ use controller::Xhci;
use device_api::{device::Device, dma::DmaAllocator, interrupt::InterruptAffinity};
use regs::Regs;
use ygg_driver_pci::{
capability::{DevicePowerState, PowerManagementCapability},
capability::{power::DevicePowerState, PowerManagementCapability},
device::{PciDeviceInfo, PreferredInterruptMode},
macros::pci_driver,
PciCommandRegister, PciConfigurationSpace,
@@ -5,7 +5,7 @@ use tock_registers::{
};
use ygg_driver_pci::{
capability::{
VirtioCapabilityData, VirtioCommonConfigCapability, VirtioDeviceConfigCapability,
virtio::VirtioCapabilityData, VirtioCommonConfigCapability, VirtioDeviceConfigCapability,
VirtioInterruptStatusCapability, VirtioNotifyConfigCapability,
},
PciCommandRegister, PciConfigurationSpace,
+41
View File
@@ -35,6 +35,11 @@ pub struct OneTimeEvent<T> {
notify: QueueWaker,
}
pub struct CounterEvent<N: EventNotify> {
counter: AtomicU64,
notify: N,
}
pub struct BitmapEvent<N: EventNotify> {
value: AtomicU64,
notify: N,
@@ -218,3 +223,39 @@ impl<N: EventNotify> BitmapEvent<N> {
.await
}
}
impl<N: EventNotify> CounterEvent<N> {
pub const fn new(notify: N) -> Self {
Self {
counter: AtomicU64::new(0),
notify,
}
}
pub fn signal(&self) {
self.counter.fetch_add(1, Ordering::Release);
self.notify.notify_all();
}
pub fn try_take(&self) -> Option<u64> {
let value = self.counter.swap(0, Ordering::Acquire);
if value > 0 {
Some(value)
} else {
None
}
}
pub async fn take(&self) -> u64 {
poll_fn(|cx| {
if let Some(value) = self.try_take() {
self.notify.unsubscribe(cx.waker());
Poll::Ready(value)
} else {
self.notify.subscribe(cx.waker());
Poll::Pending
}
})
.await
}
}
+7
View File
@@ -9,6 +9,13 @@ use crate::vfs::{CommonImpl, FileReadiness, NodeRef};
#[async_trait]
pub trait CharDevice: Device + FileReadiness {
fn open(&self) -> Result<(), Error> {
Ok(())
}
fn close(&self) -> Result<(), Error> {
Ok(())
}
async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
self.read_nonblocking(buffer)
}
+6
View File
@@ -103,3 +103,9 @@ impl CharFile {
self.device.0.is_terminal()
}
}
impl Drop for CharFile {
fn drop(&mut self) {
self.device.close().ok();
}
}
+2
View File
@@ -264,6 +264,8 @@ impl File {
return Err(Error::ReadOnly);
}
device.open()?;
Ok(Self::from_inner(FileInner::Char(CharFile {
device,
node,
+2 -1
View File
@@ -72,6 +72,7 @@ extern crate ygg_driver_virtio_net;
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
extern crate ygg_driver_net_igbe;
extern crate ygg_driver_intel_hda;
}
}
@@ -154,7 +155,7 @@ pub fn kernel_main() -> ! {
libk::panic::set_handler(panic::panic_handler);
unsafe {
PLATFORM.start_application_processors();
// PLATFORM.start_application_processors();
}
Cpu::init_ipi_queues(ArchitectureImpl::cpu_count());