ihdad: add many more timeouts and error handling

This commit is contained in:
Jeremy Soller
2025-11-26 17:30:03 -07:00
parent 84d43ecbf3
commit 8ca163d575
2 changed files with 190 additions and 118 deletions
+116 -46
View File
@@ -1,5 +1,7 @@
use common::dma::Dma;
use common::io::{Io, Mmio};
use common::timeout::Timeout;
use syscall::error::{Error, Result, EIO};
use super::common::*;
@@ -85,8 +87,8 @@ impl Corb {
}
//Intel 4.4.1.3
pub fn init(&mut self) {
self.stop();
pub fn init(&mut self) -> Result<()> {
self.stop()?;
//Determine CORB and RIRB size and allocate buffer
//3.3.24
@@ -118,9 +120,11 @@ impl Corb {
self.set_address(addr);
self.regs.corbsize.write((corbsize_reg & 0xFC) | corbsize);
self.reset_read_pointer();
self.reset_read_pointer()?;
let old_wp = self.regs.corbwp.read();
self.regs.corbwp.write(old_wp & 0xFF00);
Ok(())
}
pub fn start(&mut self) {
@@ -128,10 +132,16 @@ impl Corb {
}
#[inline(never)]
pub fn stop(&mut self) {
pub fn stop(&mut self) -> Result<()> {
let timeout = Timeout::from_secs(1);
while self.regs.corbctl.readf(CORBRUN) {
self.regs.corbctl.writef(CORBRUN, false);
timeout.run().map_err(|()| {
log::error!("timeout on clearing CORBRUN");
Error::new(EIO)
})?;
}
Ok(())
}
pub fn set_address(&mut self, addr: usize) {
@@ -139,36 +149,60 @@ impl Corb {
self.regs.corbubase.write(((addr as u64) >> 32) as u32);
}
pub fn reset_read_pointer(&mut self) {
pub fn reset_read_pointer(&mut self) -> Result<()> {
// 3.3.21
self.stop();
self.stop()?;
// Set CORBRPRST to 1
log::trace!("CORBRP {:X}", self.regs.corbrp.read());
self.regs.corbrp.writef(CORBRPRST, true);
log::trace!("CORBRP {:X}", self.regs.corbrp.read());
// Wait for it to become 1
while !self.regs.corbrp.readf(CORBRPRST) {
self.regs.corbrp.writef(CORBRPRST, true);
{
// Wait for it to become 1
let timeout = Timeout::from_secs(1);
while !self.regs.corbrp.readf(CORBRPRST) {
self.regs.corbrp.writef(CORBRPRST, true);
timeout.run().map_err(|()| {
log::error!("timeout on setting CORBRPRST");
Error::new(EIO)
})?;
}
}
// Clear the bit again
self.regs.corbrp.writef(CORBRPRST, false);
// Read back the bit until zero to verify that it is cleared.
loop {
if !self.regs.corbrp.readf(CORBRPRST) {
break;
{
// Read back the bit until zero to verify that it is cleared.
let timeout = Timeout::from_secs(1);
loop {
if !self.regs.corbrp.readf(CORBRPRST) {
break;
}
self.regs.corbrp.writef(CORBRPRST, false);
timeout.run().map_err(|()| {
log::error!("timeout on clearing CORBRPRST");
Error::new(EIO)
})?;
}
self.regs.corbrp.writef(CORBRPRST, false);
}
Ok(())
}
fn send_command(&mut self, cmd: u32) {
// wait for the commands to finish
while (self.regs.corbwp.read() & 0xff) != (self.regs.corbrp.read() & 0xff) {}
fn send_command(&mut self, cmd: u32) -> Result<()> {
{
// wait for the commands to finish
let timeout = Timeout::from_secs(1);
while (self.regs.corbwp.read() & 0xff) != (self.regs.corbrp.read() & 0xff) {
timeout.run().map_err(|()| {
log::error!("timeout on CORB command");
Error::new(EIO)
})?;
}
}
let write_pos: usize = ((self.regs.corbwp.read() as usize & 0xFF) + 1) % self.corb_count;
unsafe {
*self.corb_base.offset(write_pos as isize) = cmd;
@@ -177,6 +211,7 @@ impl Corb {
self.regs.corbwp.write(write_pos as u16);
log::trace!("Corb: {:08X}", cmd);
Ok(())
}
}
@@ -212,8 +247,8 @@ impl Rirb {
}
}
//Intel 4.4.1.3
pub fn init(&mut self) {
self.stop();
pub fn init(&mut self) -> Result<()> {
self.stop()?;
let rirbsize_reg = self.regs.rirbsize.read();
let rirbszcap = (rirbsize_reg >> 4) & 0xF;
@@ -247,16 +282,24 @@ impl Rirb {
self.rirb_rp = 0;
self.regs.rintcnt.write(1);
Ok(())
}
pub fn start(&mut self) {
self.regs.rirbctl.writef(RIRBDMAEN | RINTCTL, true);
}
pub fn stop(&mut self) {
pub fn stop(&mut self) -> Result<()> {
let timeout = Timeout::from_secs(1);
while self.regs.rirbctl.readf(RIRBDMAEN) {
self.regs.rirbctl.writef(RIRBDMAEN, false);
timeout.run().map_err(|()| {
log::error!("timeout on clearing RIRBDMAEN");
Error::new(EIO)
})?;
}
Ok(())
}
pub fn set_address(&mut self, addr: usize) {
@@ -268,9 +311,17 @@ impl Rirb {
self.regs.rirbwp.writef(RIRBWPRST, true);
}
fn read_response(&mut self) -> u64 {
// wait for response
while (self.regs.rirbwp.read() & 0xff) == (self.rirb_rp & 0xff) {}
fn read_response(&mut self) -> Result<u64> {
{
// wait for response
let timeout = Timeout::from_secs(1);
while (self.regs.rirbwp.read() & 0xff) == (self.rirb_rp & 0xff) {
timeout.run().map_err(|()| {
log::error!("timeout on RIRB response");
Error::new(EIO)
})?;
}
}
let read_pos: u16 = (self.rirb_rp + 1) % self.rirb_count as u16;
let res: u64;
@@ -279,7 +330,7 @@ impl Rirb {
}
self.rirb_rp = read_pos;
log::trace!("Rirb: {:08X}", res);
res
Ok(res)
}
}
@@ -303,9 +354,17 @@ impl ImmediateCommand {
}
}
pub fn cmd(&mut self, cmd: u32) -> u64 {
// wait for ready
while self.regs.ics.readf(ICB) {}
pub fn cmd(&mut self, cmd: u32) -> Result<u64> {
{
// wait for ready
let timeout = Timeout::from_secs(1);
while self.regs.ics.readf(ICB) {
timeout.run().map_err(|()| {
log::error!("timeout on immediate command");
Error::new(EIO)
})?;
}
}
// write command
self.regs.icoi.write(cmd);
@@ -313,8 +372,16 @@ impl ImmediateCommand {
// set ICB bit to send command
self.regs.ics.writef(ICB, true);
// wait for IRV bit to be set to indicate a response is latched
while !self.regs.ics.readf(IRV) {}
{
// wait for IRV bit to be set to indicate a response is latched
let timeout = Timeout::from_secs(1);
while !self.regs.ics.readf(IRV) {
timeout.run().map_err(|()| {
log::error!("timeout on immediate response");
Error::new(EIO)
})?;
}
}
// read the result register twice, total of 8 bytes
// highest 4 will most likely be zeros (so I've heard)
@@ -324,7 +391,7 @@ impl ImmediateCommand {
// clear the bit so we know when the next response comes
self.regs.ics.writef(IRV, false);
res
Ok(res)
}
}
@@ -370,18 +437,20 @@ impl CommandBuffer {
cmdbuff
}
pub fn init(&mut self, use_imm_cmds: bool) {
self.corb.init();
self.rirb.init();
self.set_use_imm_cmds(use_imm_cmds);
pub fn init(&mut self, use_imm_cmds: bool) -> Result<()> {
self.corb.init()?;
self.rirb.init()?;
self.set_use_imm_cmds(use_imm_cmds)?;
Ok(())
}
pub fn stop(&mut self) {
self.corb.stop();
self.rirb.stop();
pub fn stop(&mut self) -> Result<()> {
self.corb.stop()?;
self.rirb.stop()?;
Ok(())
}
pub fn cmd12(&mut self, addr: WidgetAddr, command: u32, data: u8) -> u64 {
pub fn cmd12(&mut self, addr: WidgetAddr, command: u32, data: u8) -> Result<u64> {
let mut ncmd: u32 = 0;
ncmd |= (addr.0 as u32 & 0x00F) << 28;
@@ -390,7 +459,7 @@ impl CommandBuffer {
ncmd |= (data as u32 & 0x0FF) << 0;
self.cmd(ncmd)
}
pub fn cmd4(&mut self, addr: WidgetAddr, command: u32, data: u16) -> u64 {
pub fn cmd4(&mut self, addr: WidgetAddr, command: u32, data: u16) -> Result<u64> {
let mut ncmd: u32 = 0;
ncmd |= (addr.0 as u32 & 0x000F) << 28;
@@ -400,7 +469,7 @@ impl CommandBuffer {
self.cmd(ncmd)
}
pub fn cmd(&mut self, cmd: u32) -> u64 {
pub fn cmd(&mut self, cmd: u32) -> Result<u64> {
if self.use_immediate_cmd {
self.cmd_imm(cmd)
} else {
@@ -408,24 +477,25 @@ impl CommandBuffer {
}
}
pub fn cmd_imm(&mut self, cmd: u32) -> u64 {
pub fn cmd_imm(&mut self, cmd: u32) -> Result<u64> {
self.icmd.cmd(cmd)
}
pub fn cmd_buff(&mut self, cmd: u32) -> u64 {
self.corb.send_command(cmd);
pub fn cmd_buff(&mut self, cmd: u32) -> Result<u64> {
self.corb.send_command(cmd)?;
self.rirb.read_response()
}
pub fn set_use_imm_cmds(&mut self, use_imm: bool) {
pub fn set_use_imm_cmds(&mut self, use_imm: bool) -> Result<()> {
self.use_immediate_cmd = use_imm;
if self.use_immediate_cmd {
self.corb.stop();
self.rirb.stop();
self.corb.stop()?;
self.rirb.stop()?;
} else {
self.corb.start();
self.rirb.start();
}
Ok(())
}
}
+74 -72
View File
@@ -12,7 +12,7 @@ use std::time::Duration;
use common::dma::Dma;
use common::io::{Io, Mmio};
use common::timeout::Timeout;
use syscall::error::{Error, Result, EACCES, EBADF, EIO, EINVAL};
use syscall::error::{Error, Result, EACCES, EBADF, EIO, EINVAL, ENODEV};
use syscall::flag::{SEEK_CUR, SEEK_END, SEEK_SET};
use syscall::scheme::SchemeBlockMut;
@@ -207,9 +207,9 @@ impl IntelHDA {
module.init()?;
module.info();
module.enumerate();
module.enumerate()?;
module.configure();
module.configure()?;
log::debug!("IHDA: Initialization finished.");
Ok(module)
}
@@ -222,7 +222,7 @@ impl IntelHDA {
_ => true,
};
self.cmd.init(use_immediate_command_interface);
self.cmd.init(use_immediate_command_interface)?;
self.init_interrupts();
Ok(())
@@ -248,42 +248,42 @@ impl IntelHDA {
self.int_counter
}
pub fn read_node(&mut self, addr: WidgetAddr) -> HDANode {
pub fn read_node(&mut self, addr: WidgetAddr) -> Result<HDANode> {
let mut node = HDANode::new();
let mut temp: u64;
node.addr = addr;
temp = self.cmd.cmd12(addr, 0xF00, 0x04);
temp = self.cmd.cmd12(addr, 0xF00, 0x04)?;
node.subnode_count = (temp & 0xff) as u16;
node.subnode_start = ((temp >> 16) & 0xff) as u16;
if addr == (0, 0) {
return node;
return Ok(node);
}
temp = self.cmd.cmd12(addr, 0xF00, 0x04);
temp = self.cmd.cmd12(addr, 0xF00, 0x04)?;
node.function_group_type = (temp & 0xff) as u8;
temp = self.cmd.cmd12(addr, 0xF00, 0x09);
temp = self.cmd.cmd12(addr, 0xF00, 0x09)?;
node.capabilities = temp as u32;
temp = self.cmd.cmd12(addr, 0xF00, 0x0E);
temp = self.cmd.cmd12(addr, 0xF00, 0x0E)?;
node.conn_list_len = (temp & 0xFF) as u8;
node.connections = self.node_get_connection_list(&node);
node.connections = self.node_get_connection_list(&node)?;
node.connection_default = self.cmd.cmd12(addr, 0xF01, 0x00) as u8;
node.connection_default = self.cmd.cmd12(addr, 0xF01, 0x00)? as u8;
node.config_default = self.cmd.cmd12(addr, 0xF1C, 0x00) as u32;
node.config_default = self.cmd.cmd12(addr, 0xF1C, 0x00)? as u32;
node
Ok(node)
}
pub fn node_get_connection_list(&mut self, node: &HDANode) -> Vec<WidgetAddr> {
let len_field: u8 = (self.cmd.cmd12(node.addr, 0xF00, 0x0E) & 0xFF) as u8;
pub fn node_get_connection_list(&mut self, node: &HDANode) -> Result<Vec<WidgetAddr>> {
let len_field: u8 = (self.cmd.cmd12(node.addr, 0xF00, 0x0E)? & 0xFF) as u8;
// Highest bit is if addresses are represented in longer notation
// lower 7 is actual count
@@ -296,7 +296,7 @@ impl IntelHDA {
let mut list = Vec::<WidgetAddr>::new();
while current < count {
let response: u32 = (self.cmd.cmd12(node.addr, 0xF02, current) & 0xFFFFFFFF) as u32;
let response: u32 = (self.cmd.cmd12(node.addr, 0xF02, current)? & 0xFFFFFFFF) as u32;
if use_long_addr {
for i in 0..2 {
@@ -337,16 +337,16 @@ impl IntelHDA {
current = list.len() as u8;
}
list
Ok(list)
}
pub fn enumerate(&mut self) {
pub fn enumerate(&mut self) -> Result<()> {
self.output_pins.clear();
self.input_pins.clear();
let codec: u8 = 0;
let root = self.read_node((codec, 0));
let root = self.read_node((codec, 0))?;
log::debug!("{}", root);
@@ -355,13 +355,13 @@ impl IntelHDA {
//FIXME: So basically the way this is set up is to only support one codec and hopes the first one is an audio
for i in 0..root_count {
let afg = self.read_node((codec, root_start + i));
let afg = self.read_node((codec, root_start + i))?;
log::debug!("{}", afg);
let afg_count = afg.subnode_count;
let afg_start = afg.subnode_start;
for j in 0..afg_count {
let mut widget = self.read_node((codec, afg_start + j));
let mut widget = self.read_node((codec, afg_start + j))?;
widget.is_widget = true;
match widget.widget_type() {
HDAWidgetType::AudioOutput => self.outputs.push(widget.addr),
@@ -382,15 +382,15 @@ impl IntelHDA {
self.widget_map.insert(widget.addr(), widget);
}
}
Ok(())
}
pub fn find_best_output_pin(&mut self) -> Option<WidgetAddr> {
pub fn find_best_output_pin(&mut self) -> Result<WidgetAddr> {
let outs = &self.output_pins;
if outs.len() == 0 {
None
} else if outs.len() == 1 {
Some(outs[0])
} else {
if outs.len() == 1 {
return Ok(outs[0])
} else if outs.len() > 1 {
//TODO: change output based on "unsolicited response" interrupts
// Check for devices in this order: Headphone, Speaker, Line Out
for supported_device in &[DefaultDevice::HPOut, DefaultDevice::Speaker] {
@@ -399,22 +399,22 @@ impl IntelHDA {
let cd = widget.configuration_default();
if cd.sequence() == 0 && &cd.default_device() == supported_device {
// Check for jack detect bit
let pin_caps = self.cmd.cmd12(widget.addr, 0xF00, 0x0C);
let pin_caps = self.cmd.cmd12(widget.addr, 0xF00, 0x0C)?;
if pin_caps & (1 << 2) != 0 {
// Check for presence
let pin_sense = self.cmd.cmd12(widget.addr, 0xF09, 0);
let pin_sense = self.cmd.cmd12(widget.addr, 0xF09, 0)?;
if pin_sense & (1 << 31) == 0 {
// Skip if nothing is plugged in
continue;
}
}
return Some(out);
return Ok(out);
}
}
}
None
}
Err(Error::new(ENODEV))
}
pub fn find_path_to_dac(&self, addr: WidgetAddr) -> Option<Vec<WidgetAddr>> {
@@ -466,8 +466,8 @@ impl IntelHDA {
}
}
pub fn configure(&mut self) {
let outpin = self.find_best_output_pin().expect("IHDA: No output pins?!");
pub fn configure(&mut self) -> Result<()> {
let outpin = self.find_best_output_pin()?;
log::debug!("Best pin: {:01X}:{:02X}", outpin.0, outpin.1);
@@ -480,25 +480,25 @@ impl IntelHDA {
// Set power state 0 (on) for all widgets in path
for &addr in &path {
self.set_power_state(addr, 0);
self.set_power_state(addr, 0)?;
}
// Pin enable (0x80 = headphone amp enable, 0x40 = output enable)
self.cmd.cmd12(pin, 0x707, 0xC0);
self.cmd.cmd12(pin, 0x707, 0xC0)?;
// EAPD enable
self.cmd.cmd12(pin, 0x70C, 2);
self.cmd.cmd12(pin, 0x70C, 2)?;
// Set DAC stream and channel
self.set_stream_channel(dac, 1, 0);
self.set_stream_channel(dac, 1, 0)?;
self.update_sound_buffers();
log::debug!(
"Supported Formats: {:08X}",
self.get_supported_formats((0, 0x1))
self.get_supported_formats((0, 0x1))?
);
log::debug!("Capabilities: {:08X}", self.get_capabilities(path[0]));
log::debug!("Capabilities: {:08X}", self.get_capabilities(path[0])?);
// Create output stream
let output = self.get_output_stream_descriptor(0).unwrap();
@@ -510,16 +510,16 @@ impl IntelHDA {
output.set_interrupt_on_completion(true);
// Set DAC converter format
self.set_converter_format(dac, &super::SR_44_1, BitsPerSample::Bits16, 2);
self.set_converter_format(dac, &super::SR_44_1, BitsPerSample::Bits16, 2)?;
// Get DAC converter format
//TODO: should validate?
self.cmd.cmd12(dac, 0xA00, 0);
self.cmd.cmd12(dac, 0xA00, 0)?;
// Unmute and set gain to 0db for input and output amplifiers on all widgets in path
for &addr in &path {
// Read widget capabilities
let caps = self.cmd.cmd12(addr, 0xF00, 0x09);
let caps = self.cmd.cmd12(addr, 0xF00, 0x09)?;
//TODO: do we need to set any other indexes?
let left = true;
@@ -530,28 +530,28 @@ impl IntelHDA {
// Check for input amp
if (caps & (1 << 1)) != 0 {
// Read input capabilities
let in_caps = self.cmd.cmd12(addr, 0xF00, 0x0D);
let in_caps = self.cmd.cmd12(addr, 0xF00, 0x0D)?;
let in_gain = (in_caps & 0x7f) as u8;
// Set input gain
let output = false;
let input = true;
self.set_amplifier_gain_mute(
addr, output, input, left, right, index, mute, in_gain,
);
)?;
log::debug!("Set {:X?} input gain to 0x{:X}", addr, in_gain);
}
// Check for output amp
if (caps & (1 << 2)) != 0 {
// Read output capabilities
let out_caps = self.cmd.cmd12(addr, 0xF00, 0x12);
let out_caps = self.cmd.cmd12(addr, 0xF00, 0x12)?;
let out_gain = (out_caps & 0x7f) as u8;
// Set output gain
let output = true;
let input = false;
self.set_amplifier_gain_mute(
addr, output, input, left, right, index, mute, out_gain,
);
)?;
log::debug!("Set {:X?} output gain to 0x{:X}", addr, out_gain);
}
}
@@ -559,9 +559,15 @@ impl IntelHDA {
//TODO: implement hda-verb?
output.run();
log::debug!("Waiting for output 0 to start running...");
while output.control() & (1 << 1) == 0 {
//TODO: relax
{
log::debug!("Waiting for output 0 to start running...");
let timeout = Timeout::from_secs(1);
while output.control() & (1 << 1) == 0 {
timeout.run().map_err(|()| {
log::error!("timeout on output running");
Error::new(EIO)
})?;
}
}
log::debug!(
@@ -570,6 +576,7 @@ impl IntelHDA {
output.status(),
output.link_position()
);
Ok(())
}
/*
@@ -644,17 +651,8 @@ impl IntelHDA {
}
}
pub fn read_beep(&mut self) -> u8 {
let addr = self.beep_addr;
if addr != (0, 0) {
self.cmd.cmd12(addr, 0x70A, 0) as u8
} else {
0
}
}
pub fn reset_controller(&mut self) -> Result<()> {
self.cmd.stop();
self.cmd.stop()?;
self.regs.statests.write(0x7FFF);
@@ -800,21 +798,23 @@ impl IntelHDA {
self.regs.dpubase.write((addr_val >> 32) as u32);
}
fn set_stream_channel(&mut self, addr: WidgetAddr, stream: u8, channel: u8) {
fn set_stream_channel(&mut self, addr: WidgetAddr, stream: u8, channel: u8) -> Result<()> {
let val = ((stream & 0xF) << 4) | (channel & 0xF);
self.cmd.cmd12(addr, 0x706, val);
self.cmd.cmd12(addr, 0x706, val)?;
Ok(())
}
fn set_power_state(&mut self, addr: WidgetAddr, state: u8) {
self.cmd.cmd12(addr, 0x705, state & 0xF) as u32;
fn set_power_state(&mut self, addr: WidgetAddr, state: u8) -> Result<()> {
self.cmd.cmd12(addr, 0x705, state & 0xF)?;
Ok(())
}
fn get_supported_formats(&mut self, addr: WidgetAddr) -> u32 {
self.cmd.cmd12(addr, 0xF00, 0x0A) as u32
fn get_supported_formats(&mut self, addr: WidgetAddr) -> Result<u32> {
Ok(self.cmd.cmd12(addr, 0xF00, 0x0A)? as u32)
}
fn get_capabilities(&mut self, addr: WidgetAddr) -> u32 {
self.cmd.cmd12(addr, 0xF00, 0x09) as u32
fn get_capabilities(&mut self, addr: WidgetAddr) -> Result<u32> {
Ok(self.cmd.cmd12(addr, 0xF00, 0x09)? as u32)
}
fn set_converter_format(
@@ -823,9 +823,10 @@ impl IntelHDA {
sr: &super::SampleRate,
bps: BitsPerSample,
channels: u8,
) {
) -> Result<()> {
let fmt = super::format_to_u16(sr, bps, channels);
self.cmd.cmd4(addr, 0x2, fmt);
self.cmd.cmd4(addr, 0x2, fmt)?;
Ok(())
}
fn set_amplifier_gain_mute(
@@ -838,7 +839,7 @@ impl IntelHDA {
index: u8,
mute: bool,
gain: u8,
) {
) -> Result<()> {
let mut payload: u16 = 0;
if output {
@@ -859,7 +860,8 @@ impl IntelHDA {
payload |= ((index as u16) & 0x0F) << 8;
payload |= (gain as u16) & 0x7F;
self.cmd.cmd4(addr, 0x3, payload);
self.cmd.cmd4(addr, 0x3, payload)?;
Ok(())
}
pub fn write_to_output(&mut self, index: u8, buf: &[u8]) -> Result<Option<usize>> {