milestone: desktop path Phases 1-5

Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests
Phase 2 (Wayland Compositor): bounded scaffold, zero warnings
Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick)
Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker
Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker

Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
2026-04-29 09:54:06 +01:00
parent b23714f542
commit 8acc73d774
508 changed files with 76526 additions and 396 deletions
@@ -0,0 +1,21 @@
[package]
name = "ac97d"
description = "AC'97 driver"
version = "0.1.0"
edition = "2021"
[dependencies]
common = { path = "../../common" }
libredox.workspace = true
log.workspace = true
redox_event.workspace = true
redox_syscall.workspace = true
spin.workspace = true
daemon = { path = "../../../daemon" }
pcid = { path = "../../pcid" }
redox-scheme.workspace = true
scheme-utils = { path = "../../../scheme-utils" }
[lints]
workspace = true
@@ -0,0 +1,5 @@
[[drivers]]
name = "AC97 Audio"
class = 0x04
subclass = 0x01
command = ["ac97d"]
@@ -0,0 +1,333 @@
use common::io::Pio;
use redox_scheme::scheme::SchemeSync;
use redox_scheme::CallerCtx;
use redox_scheme::OpenResult;
use scheme_utils::{FpathWriter, HandleMap};
use syscall::error::{Error, Result, EACCES, EBADF, EINVAL, ENOENT};
use syscall::schemev2::NewFdFlags;
use syscall::EWOULDBLOCK;
use common::{
dma::Dma,
io::{Io, Mmio},
};
use spin::Mutex;
const NUM_SUB_BUFFS: usize = 32;
const SUB_BUFF_SIZE: usize = 2048;
enum Handle {
Todo,
SchemeRoot,
}
#[allow(dead_code)]
struct MixerRegs {
/* 0x00 */ reset: Pio<u16>,
/* 0x02 */ master_volume: Pio<u16>,
/* 0x04 */ aux_out_volume: Pio<u16>,
/* 0x06 */ mono_volume: Pio<u16>,
/* 0x08 */ master_tone: Pio<u16>,
/* 0x0A */ pc_beep_volume: Pio<u16>,
/* 0x0C */ phone_volume: Pio<u16>,
/* 0x0E */ mic_volume: Pio<u16>,
/* 0x10 */ line_in_volume: Pio<u16>,
/* 0x12 */ cd_volume: Pio<u16>,
/* 0x14 */ video_volume: Pio<u16>,
/* 0x16 */ aux_in_volume: Pio<u16>,
/* 0x18 */ pcm_out_volume: Pio<u16>,
/* 0x1A */ record_select: Pio<u16>,
/* 0x1C */ record_gain: Pio<u16>,
/* 0x1E */ record_gain_mic: Pio<u16>,
/* 0x20 */ general_purpose: Pio<u16>,
/* 0x22 */ control_3d: Pio<u16>,
/* 0x24 */ audio_int_paging: Pio<u16>,
/* 0x26 */ powerdown: Pio<u16>,
/* 0x28 */ extended_id: Pio<u16>,
/* 0x2A */ extended_ctrl: Pio<u16>,
/* 0x2C */ vra_pcm_front: Pio<u16>,
}
impl MixerRegs {
fn new(bar0: u16) -> Self {
Self {
reset: Pio::new(bar0 + 0x00),
master_volume: Pio::new(bar0 + 0x02),
aux_out_volume: Pio::new(bar0 + 0x04),
mono_volume: Pio::new(bar0 + 0x06),
master_tone: Pio::new(bar0 + 0x08),
pc_beep_volume: Pio::new(bar0 + 0x0A),
phone_volume: Pio::new(bar0 + 0x0C),
mic_volume: Pio::new(bar0 + 0x0E),
line_in_volume: Pio::new(bar0 + 0x10),
cd_volume: Pio::new(bar0 + 0x12),
video_volume: Pio::new(bar0 + 0x14),
aux_in_volume: Pio::new(bar0 + 0x16),
pcm_out_volume: Pio::new(bar0 + 0x18),
record_select: Pio::new(bar0 + 0x1A),
record_gain: Pio::new(bar0 + 0x1C),
record_gain_mic: Pio::new(bar0 + 0x1E),
general_purpose: Pio::new(bar0 + 0x20),
control_3d: Pio::new(bar0 + 0x22),
audio_int_paging: Pio::new(bar0 + 0x24),
powerdown: Pio::new(bar0 + 0x26),
extended_id: Pio::new(bar0 + 0x28),
extended_ctrl: Pio::new(bar0 + 0x2A),
vra_pcm_front: Pio::new(bar0 + 0x2C),
}
}
}
#[allow(dead_code)]
struct BusBoxRegs {
/// Buffer descriptor list base address
/* 0x00 */
bdbar: Pio<u32>,
/// Current index value
/* 0x04 */
civ: Pio<u8>,
/// Last valid index
/* 0x05 */
lvi: Pio<u8>,
/// Status
/* 0x06 */
sr: Pio<u16>,
/// Position in current buffer
/* 0x08 */
picb: Pio<u16>,
/// Prefetched index value
/* 0x0A */
piv: Pio<u8>,
/// Control
/* 0x0B */
cr: Pio<u8>,
}
impl BusBoxRegs {
fn new(base: u16) -> Self {
Self {
bdbar: Pio::new(base + 0x00),
civ: Pio::new(base + 0x04),
lvi: Pio::new(base + 0x05),
sr: Pio::new(base + 0x06),
picb: Pio::new(base + 0x08),
piv: Pio::new(base + 0x0A),
cr: Pio::new(base + 0x0B),
}
}
}
#[allow(dead_code)]
struct BusRegs {
/// PCM in register box
/* 0x00 */
pi: BusBoxRegs,
/// PCM out register box
/* 0x10 */
po: BusBoxRegs,
/// Microphone register box
/* 0x20 */
mc: BusBoxRegs,
}
impl BusRegs {
fn new(bar1: u16) -> Self {
Self {
pi: BusBoxRegs::new(bar1 + 0x00),
po: BusBoxRegs::new(bar1 + 0x10),
mc: BusBoxRegs::new(bar1 + 0x20),
}
}
}
#[repr(C, packed)]
pub struct BufferDescriptor {
/* 0x00 */ addr: Mmio<u32>,
/* 0x04 */ samples: Mmio<u16>,
/* 0x06 */ flags: Mmio<u16>,
}
pub struct Ac97 {
mixer: MixerRegs,
bus: BusRegs,
bdl: Dma<[BufferDescriptor; NUM_SUB_BUFFS]>,
buf: Dma<[u8; NUM_SUB_BUFFS * SUB_BUFF_SIZE]>,
handles: Mutex<HandleMap<Handle>>,
}
impl Ac97 {
pub unsafe fn new(bar0: u16, bar1: u16) -> Result<Self> {
let mut module = Ac97 {
mixer: MixerRegs::new(bar0),
bus: BusRegs::new(bar1),
bdl: Dma::zeroed(
//TODO: PhysBox::new_in_32bit_space(bdl_size)?
)?
.assume_init(),
buf: Dma::zeroed(
//TODO: PhysBox::new_in_32bit_space(buf_size)?
)?
.assume_init(),
handles: Mutex::new(HandleMap::new()),
};
module.init()?;
Ok(module)
}
fn init(&mut self) -> Result<()> {
//TODO: support other sample rates, or just the default of 48000 Hz
{
// Check if VRA is supported
if !self.mixer.extended_id.readf(1 << 0) {
println!("ac97d: VRA not supported and is currently required");
return Err(Error::new(ENOENT));
}
// Enable VRA
self.mixer.extended_ctrl.writef(1 << 0, true);
// Attempt to set sample rate for PCM front to 44100 Hz
let desired_sample_rate = 44100;
self.mixer.vra_pcm_front.write(desired_sample_rate);
// Read back real sample rate
let real_sample_rate = self.mixer.vra_pcm_front.read();
println!("ac97d: set sample rate to {}", real_sample_rate);
// Error if we cannot set the sample rate as desired
if real_sample_rate != desired_sample_rate {
println!(
"ac97d: sample rate is {} but only {} is supported",
real_sample_rate, desired_sample_rate
);
return Err(Error::new(ENOENT));
}
}
// Ensure PCM out is stopped
self.bus.po.cr.writef(1, false);
// Reset PCM out
self.bus.po.cr.writef(1 << 1, true);
while self.bus.po.cr.readf(1 << 1) {
// Spinning on resetting PCM out
//TODO: relax
}
// Initialize BDL for PCM out
for i in 0..NUM_SUB_BUFFS {
self.bdl[i]
.addr
.write((self.buf.physical() + i * SUB_BUFF_SIZE) as u32);
self.bdl[i]
.samples
.write((SUB_BUFF_SIZE / 2/* Each sample is i16 or 2 bytes */) as u16);
self.bdl[i]
.flags
.write(1 << 15 /* Interrupt on completion */);
}
self.bus.po.bdbar.write(self.bdl.physical() as u32);
// Enable interrupt on completion
self.bus.po.cr.writef(1 << 4, true);
// Start bus master
self.bus.po.cr.writef(1 << 0, true);
// Set master volume to 0 db (loudest output, DANGER!)
self.mixer.master_volume.write(0);
// Set PCM output volume to 0 db (medium)
self.mixer.pcm_out_volume.write(0x808);
Ok(())
}
pub fn irq(&mut self) -> bool {
let ints = self.bus.po.sr.read() & 0b11100;
if ints != 0 {
self.bus.po.sr.write(ints);
true
} else {
false
}
}
}
impl SchemeSync for Ac97 {
fn scheme_root(&mut self) -> Result<usize> {
Ok(self.handles.lock().insert(Handle::SchemeRoot))
}
fn openat(
&mut self,
dirfd: usize,
_path: &str,
_flags: usize,
_fcntl_flags: u32,
ctx: &CallerCtx,
) -> Result<OpenResult> {
{
let handles = self.handles.lock();
let handle = handles.get(dirfd)?;
if !matches!(handle, Handle::SchemeRoot) {
return Err(Error::new(EACCES));
}
}
if ctx.uid == 0 {
let id = self.handles.lock().insert(Handle::Todo);
Ok(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::empty(),
})
} else {
Err(Error::new(EACCES))
}
}
fn write(
&mut self,
id: usize,
buf: &[u8],
_offset: u64,
_flags: u32,
_ctx: &CallerCtx,
) -> Result<usize> {
{
let mut handles = self.handles.lock();
let handle = handles.get_mut(id)?;
if !matches!(handle, Handle::Todo) {
return Err(Error::new(EBADF));
}
}
if buf.len() != SUB_BUFF_SIZE {
return Err(Error::new(EINVAL));
}
let civ = self.bus.po.civ.read() as usize;
let mut lvi = self.bus.po.lvi.read() as usize;
if lvi == (civ + 3) % NUM_SUB_BUFFS {
// Block if we already are 3 buffers ahead
Err(Error::new(EWOULDBLOCK))
} else {
// Fill next buffer
lvi = (lvi + 1) % NUM_SUB_BUFFS;
for i in 0..SUB_BUFF_SIZE {
self.buf[lvi * SUB_BUFF_SIZE + i] = buf[i];
}
self.bus.po.lvi.write(lvi as u8);
Ok(SUB_BUFF_SIZE)
}
}
fn fpath(&mut self, _id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
FpathWriter::with(buf, "audiohw", |_| Ok(()))
}
fn on_close(&mut self, id: usize) {
self.handles.lock().remove(id);
}
}
@@ -0,0 +1,134 @@
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::usize;
use event::{user_data, EventQueue};
use pcid_interface::PciFunctionHandle;
use redox_scheme::scheme::register_sync_scheme;
use redox_scheme::Socket;
use scheme_utils::ReadinessBased;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub mod device;
fn main() {
pcid_interface::pci_daemon(daemon);
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
let pci_config = pcid_handle.config();
let mut name = pci_config.func.name();
name.push_str("_ac97");
let bar0 = pci_config.func.bars[0].expect_port();
let bar1 = pci_config.func.bars[1].expect_port();
let irq = pci_config
.func
.legacy_interrupt_line
.expect("ac97d: no legacy interrupts supported");
println!(" + ac97 {}", pci_config.func.display());
common::setup_logging(
"audio",
"pci",
&name,
common::output_level(),
common::file_level(),
);
common::acquire_port_io_rights().expect("ac97d: failed to set I/O privilege level to Ring 3");
let mut irq_file = irq.irq_handle("ac97d");
let socket = Socket::nonblock().expect("ac97d: failed to create socket");
let mut device =
unsafe { device::Ac97::new(bar0, bar1).expect("ac97d: failed to allocate device") };
let mut readiness_based = ReadinessBased::new(&socket, 16);
user_data! {
enum Source {
Irq,
Scheme,
}
}
let event_queue = EventQueue::<Source>::new().expect("ac97d: Could not create event queue.");
event_queue
.subscribe(
irq_file.as_raw_fd() as usize,
Source::Irq,
event::EventFlags::READ,
)
.unwrap();
event_queue
.subscribe(
socket.inner().raw(),
Source::Scheme,
event::EventFlags::READ,
)
.unwrap();
register_sync_scheme(&socket, "audiohw", &mut device)
.expect("ac97d: failed to register audiohw scheme to namespace");
daemon.ready();
libredox::call::setrens(0, 0).expect("ac97d: failed to enter null namespace");
let all = [Source::Irq, Source::Scheme];
for event in all
.into_iter()
.chain(event_queue.map(|e| e.expect("ac97d: failed to get next event").user_data))
{
match event {
Source::Irq => {
let mut irq = [0; 8];
irq_file.read(&mut irq).unwrap();
if !device.irq() {
continue;
}
irq_file.write(&mut irq).unwrap();
readiness_based
.poll_all_requests(&mut device)
.expect("ac97d: failed to poll requests");
readiness_based
.write_responses()
.expect("ac97d: failed to write to socket");
/*
let next_read = device_irq.next_read();
if next_read > 0 {
return Ok(Some(next_read));
}
*/
}
Source::Scheme => {
readiness_based
.read_and_process_requests(&mut device)
.expect("ac97d: failed to read from socket");
readiness_based
.write_responses()
.expect("ac97d: failed to write to socket");
/*
let next_read = device.borrow().next_read();
if next_read > 0 {
return Ok(Some(next_read));
}
*/
}
}
}
std::process::exit(0);
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
unimplemented!()
}
@@ -0,0 +1,22 @@
[package]
name = "ihdad"
description = "Intel HD Audio chipset driver"
version = "0.1.0"
edition = "2021"
[dependencies]
bitflags.workspace = true
libredox.workspace = true
log.workspace = true
redox_event.workspace = true
redox_syscall.workspace = true
spin.workspace = true
common = { path = "../../common" }
daemon = { path = "../../../daemon" }
pcid = { path = "../../pcid" }
redox-scheme.workspace = true
scheme-utils = { path = "../../../scheme-utils" }
[lints]
workspace = true
@@ -0,0 +1,5 @@
[[drivers]]
name = "Intel HD Audio"
class = 0x04
subclass = 0x03
command = ["ihdad"]
@@ -0,0 +1,501 @@
use common::dma::Dma;
use common::io::{Io, Mmio};
use common::timeout::Timeout;
use syscall::error::{Error, Result, EIO};
use super::common::*;
// CORBCTL
const CMEIE: u8 = 1 << 0; // 1 bit
const CORBRUN: u8 = 1 << 1; // 1 bit
// CORBSIZE
const CORBSZCAP: (u8, u8) = (4, 4);
const CORBSIZE: (u8, u8) = (0, 2);
// CORBRP
const CORBRPRST: u16 = 1 << 15;
// RIRBWP
const RIRBWPRST: u16 = 1 << 15;
// RIRBCTL
const RINTCTL: u8 = 1 << 0; // 1 bit
const RIRBDMAEN: u8 = 1 << 1; // 1 bit
const CORB_OFFSET: usize = 0x00;
const RIRB_OFFSET: usize = 0x10;
const ICMD_OFFSET: usize = 0x20;
// ICS
const ICB: u16 = 1 << 0;
const IRV: u16 = 1 << 1;
// CORB and RIRB offset
const COMMAND_BUFFER_OFFSET: usize = 0x40;
const CORB_BUFF_MAX_SIZE: usize = 1024;
struct CommandBufferRegs {
corblbase: Mmio<u32>,
corbubase: Mmio<u32>,
corbwp: Mmio<u16>,
corbrp: Mmio<u16>,
corbctl: Mmio<u8>,
corbsts: Mmio<u8>,
corbsize: Mmio<u8>,
rsvd5: Mmio<u8>,
rirblbase: Mmio<u32>,
rirbubase: Mmio<u32>,
rirbwp: Mmio<u16>,
rintcnt: Mmio<u16>,
rirbctl: Mmio<u8>,
rirbsts: Mmio<u8>,
rirbsize: Mmio<u8>,
rsvd6: Mmio<u8>,
}
struct CorbRegs {
corblbase: Mmio<u32>,
corbubase: Mmio<u32>,
corbwp: Mmio<u16>,
corbrp: Mmio<u16>,
corbctl: Mmio<u8>,
corbsts: Mmio<u8>,
corbsize: Mmio<u8>,
rsvd5: Mmio<u8>,
}
struct Corb {
regs: &'static mut CorbRegs,
corb_base: *mut u32,
corb_base_phys: usize,
corb_count: usize,
}
impl Corb {
pub fn new(regs_addr: usize, corb_buff_phys: usize, corb_buff_virt: *mut u32) -> Corb {
unsafe {
Corb {
regs: &mut *(regs_addr as *mut CorbRegs),
corb_base: corb_buff_virt,
corb_base_phys: corb_buff_phys,
corb_count: 0,
}
}
}
//Intel 4.4.1.3
pub fn init(&mut self) -> Result<()> {
self.stop()?;
//Determine CORB and RIRB size and allocate buffer
//3.3.24
let corbsize_reg = self.regs.corbsize.read();
let corbszcap = (corbsize_reg >> 4) & 0xF;
let mut corbsize_bytes: usize = 0;
let mut corbsize: u8 = 0;
if (corbszcap & 4) == 4 {
corbsize = 2;
corbsize_bytes = 1024;
self.corb_count = 256;
} else if (corbszcap & 2) == 2 {
corbsize = 1;
corbsize_bytes = 64;
self.corb_count = 16;
} else if (corbszcap & 1) == 1 {
corbsize = 0;
corbsize_bytes = 8;
self.corb_count = 2;
}
assert!(self.corb_count != 0);
let addr = self.corb_base_phys;
self.set_address(addr);
self.regs.corbsize.write((corbsize_reg & 0xFC) | corbsize);
self.reset_read_pointer()?;
let old_wp = self.regs.corbwp.read();
self.regs.corbwp.write(old_wp & 0xFF00);
Ok(())
}
pub fn start(&mut self) {
self.regs.corbctl.writef(CORBRUN, true);
}
#[inline(never)]
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) {
self.regs.corblbase.write((addr & 0xFFFFFFFF) as u32);
self.regs.corbubase.write(((addr as u64) >> 32) as u32);
}
pub fn reset_read_pointer(&mut self) -> Result<()> {
// 3.3.21
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
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.
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)
})?;
}
}
Ok(())
}
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;
}
self.regs.corbwp.write(write_pos as u16);
log::trace!("Corb: {:08X}", cmd);
Ok(())
}
}
struct RirbRegs {
rirblbase: Mmio<u32>,
rirbubase: Mmio<u32>,
rirbwp: Mmio<u16>,
rintcnt: Mmio<u16>,
rirbctl: Mmio<u8>,
rirbsts: Mmio<u8>,
rirbsize: Mmio<u8>,
rsvd6: Mmio<u8>,
}
struct Rirb {
regs: &'static mut RirbRegs,
rirb_base: *mut u64,
rirb_base_phys: usize,
rirb_rp: u16,
rirb_count: usize,
}
impl Rirb {
pub fn new(regs_addr: usize, rirb_buff_phys: usize, rirb_buff_virt: *mut u64) -> Rirb {
unsafe {
Rirb {
regs: &mut *(regs_addr as *mut RirbRegs),
rirb_base: rirb_buff_virt,
rirb_rp: 0,
rirb_base_phys: rirb_buff_phys,
rirb_count: 0,
}
}
}
//Intel 4.4.1.3
pub fn init(&mut self) -> Result<()> {
self.stop()?;
let rirbsize_reg = self.regs.rirbsize.read();
let rirbszcap = (rirbsize_reg >> 4) & 0xF;
let mut rirbsize_bytes: usize = 0;
let mut rirbsize: u8 = 0;
if (rirbszcap & 4) == 4 {
rirbsize = 2;
rirbsize_bytes = 2048;
self.rirb_count = 256;
} else if (rirbszcap & 2) == 2 {
rirbsize = 1;
rirbsize_bytes = 128;
self.rirb_count = 8;
} else if (rirbszcap & 1) == 1 {
rirbsize = 0;
rirbsize_bytes = 16;
self.rirb_count = 2;
}
assert!(self.rirb_count != 0);
let addr = self.rirb_base_phys;
self.set_address(addr);
self.reset_write_pointer();
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) -> 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) {
self.regs.rirblbase.write((addr & 0xFFFFFFFF) as u32);
self.regs.rirbubase.write(((addr as u64) >> 32) as u32);
}
pub fn reset_write_pointer(&mut self) {
self.regs.rirbwp.writef(RIRBWPRST, true);
}
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;
unsafe {
res = *self.rirb_base.offset(read_pos as isize);
}
self.rirb_rp = read_pos;
log::trace!("Rirb: {:08X}", res);
Ok(res)
}
}
struct ImmediateCommandRegs {
icoi: Mmio<u32>,
irii: Mmio<u32>,
ics: Mmio<u16>,
rsvd7: [Mmio<u8>; 6],
}
pub struct ImmediateCommand {
regs: &'static mut ImmediateCommandRegs,
}
impl ImmediateCommand {
pub fn new(regs_addr: usize) -> ImmediateCommand {
unsafe {
ImmediateCommand {
regs: &mut *(regs_addr as *mut ImmediateCommandRegs),
}
}
}
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);
// 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
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)
let mut res: u64 = self.regs.irii.read() as u64;
res |= (self.regs.irii.read() as u64) << 32;
// clear the bit so we know when the next response comes
self.regs.ics.writef(IRV, false);
Ok(res)
}
}
pub struct CommandBuffer {
// regs: &'static mut CommandBufferRegs,
corb: Corb,
rirb: Rirb,
icmd: ImmediateCommand,
use_immediate_cmd: bool,
mem: Dma<[u8; 0x1000]>,
}
impl CommandBuffer {
pub fn new(regs_addr: usize, mut cmd_buff: Dma<[u8; 0x1000]>) -> CommandBuffer {
let corb = Corb::new(
regs_addr + CORB_OFFSET,
cmd_buff.physical(),
cmd_buff.as_mut_ptr().cast(),
);
let rirb = Rirb::new(
regs_addr + RIRB_OFFSET,
cmd_buff.physical() + CORB_BUFF_MAX_SIZE,
cmd_buff
.as_mut_ptr()
.cast::<u8>()
.wrapping_add(CORB_BUFF_MAX_SIZE)
.cast(),
);
let icmd = ImmediateCommand::new(regs_addr + ICMD_OFFSET);
let cmdbuff = CommandBuffer {
corb,
rirb,
icmd,
use_immediate_cmd: false,
mem: cmd_buff,
};
cmdbuff
}
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) -> Result<()> {
self.corb.stop()?;
self.rirb.stop()?;
Ok(())
}
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;
ncmd |= (addr.1 as u32 & 0x0FF) << 20;
ncmd |= (command & 0xFFF) << 8;
ncmd |= (data as u32 & 0x0FF) << 0;
self.cmd(ncmd)
}
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;
ncmd |= (addr.1 as u32 & 0x00FF) << 20;
ncmd |= (command & 0x000F) << 16;
ncmd |= (data as u32 & 0xFFFF) << 0;
self.cmd(ncmd)
}
pub fn cmd(&mut self, cmd: u32) -> Result<u64> {
if self.use_immediate_cmd {
self.cmd_imm(cmd)
} else {
self.cmd_buff(cmd)
}
}
pub fn cmd_imm(&mut self, cmd: u32) -> Result<u64> {
self.icmd.cmd(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) -> Result<()> {
self.use_immediate_cmd = use_imm;
if self.use_immediate_cmd {
self.corb.stop()?;
self.rirb.stop()?;
} else {
self.corb.start();
self.rirb.start();
}
Ok(())
}
}
@@ -0,0 +1,195 @@
use std::fmt;
use std::mem::transmute;
pub type HDANodeAddr = u16;
pub type HDACodecAddr = u8;
pub type NodeAddr = u16;
pub type CodecAddr = u8;
pub type WidgetAddr = (CodecAddr, NodeAddr);
/*
impl fmt::Display for WidgetAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:01X}:{:02X}\n", self.0, self.1)
}
}*/
#[derive(Debug, PartialEq)]
#[repr(u8)]
pub enum HDAWidgetType {
AudioOutput = 0x0,
AudioInput = 0x1,
AudioMixer = 0x2,
AudioSelector = 0x3,
PinComplex = 0x4,
Power = 0x5,
VolumeKnob = 0x6,
BeepGenerator = 0x7,
VendorDefined = 0xf,
}
impl fmt::Display for HDAWidgetType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug, PartialEq)]
#[repr(u8)]
pub enum DefaultDevice {
LineOut = 0x0,
Speaker = 0x1,
HPOut = 0x2,
CD = 0x3,
SPDIF = 0x4,
DigitalOtherOut = 0x5,
ModemLineSide = 0x6,
ModemHandsetSide = 0x7,
LineIn = 0x8,
AUX = 0x9,
MicIn = 0xA,
Telephony = 0xB,
SPDIFIn = 0xC,
DigitalOtherIn = 0xD,
Reserved = 0xE,
Other = 0xF,
}
#[derive(Debug)]
#[repr(u8)]
pub enum PortConnectivity {
ConnectedToJack = 0x0,
NoPhysicalConnection = 0x1,
FixedFunction = 0x2,
JackAndInternal = 0x3,
}
#[derive(Debug)]
#[repr(u8)]
pub enum GrossLocation {
ExternalOnPrimary = 0x0,
Internal = 0x1,
SeperateChasis = 0x2,
Other = 0x3,
}
#[derive(Debug)]
#[repr(u8)]
pub enum GeometricLocation {
NA = 0x0,
Rear = 0x1,
Front = 0x2,
Left = 0x3,
Right = 0x4,
Top = 0x5,
Bottom = 0x6,
Special1 = 0x7,
Special2 = 0x8,
Special3 = 0x9,
Resvd1 = 0xA,
Resvd2 = 0xB,
Resvd3 = 0xC,
Resvd4 = 0xD,
Resvd5 = 0xE,
Resvd6 = 0xF,
}
#[derive(Debug)]
#[repr(u8)]
pub enum Color {
Unknown = 0x0,
Black = 0x1,
Grey = 0x2,
Blue = 0x3,
Green = 0x4,
Red = 0x5,
Orange = 0x6,
Yellow = 0x7,
Purple = 0x8,
Pink = 0x9,
Resvd1 = 0xA,
Resvd2 = 0xB,
Resvd3 = 0xC,
Resvd4 = 0xD,
White = 0xE,
Other = 0xF,
}
pub struct ConfigurationDefault {
value: u32,
}
impl ConfigurationDefault {
pub fn from_u32(value: u32) -> ConfigurationDefault {
ConfigurationDefault { value: value }
}
pub fn color(&self) -> Color {
unsafe { transmute(((self.value >> 12) & 0xF) as u8) }
}
pub fn default_device(&self) -> DefaultDevice {
unsafe { transmute(((self.value >> 20) & 0xF) as u8) }
}
pub fn port_connectivity(&self) -> PortConnectivity {
unsafe { transmute(((self.value >> 30) & 0x3) as u8) }
}
pub fn gross_location(&self) -> GrossLocation {
unsafe { transmute(((self.value >> 28) & 0x3) as u8) }
}
pub fn geometric_location(&self) -> GeometricLocation {
unsafe { transmute(((self.value >> 24) & 0x7) as u8) }
}
pub fn is_output(&self) -> bool {
match self.default_device() {
DefaultDevice::LineOut
| DefaultDevice::Speaker
| DefaultDevice::HPOut
| DefaultDevice::CD
| DefaultDevice::SPDIF
| DefaultDevice::DigitalOtherOut
| DefaultDevice::ModemLineSide => true,
_ => false,
}
}
pub fn is_input(&self) -> bool {
match self.default_device() {
DefaultDevice::ModemHandsetSide
| DefaultDevice::LineIn
| DefaultDevice::AUX
| DefaultDevice::MicIn
| DefaultDevice::Telephony
| DefaultDevice::SPDIFIn
| DefaultDevice::DigitalOtherIn => true,
_ => false,
}
}
pub fn sequence(&self) -> u8 {
(self.value & 0xF) as u8
}
pub fn default_association(&self) -> u8 {
((self.value >> 4) & 0xF) as u8
}
}
impl fmt::Display for ConfigurationDefault {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{:?} {:?} {:?} {:?}",
self.default_device(),
self.color(),
self.gross_location(),
self.geometric_location()
)
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
#![allow(dead_code)]
pub mod cmdbuff;
pub mod common;
pub mod device;
pub mod node;
pub mod stream;
pub use self::node::*;
pub use self::stream::*;
pub use self::cmdbuff::*;
pub use self::device::IntelHDA;
pub use self::stream::BitsPerSample;
pub use self::stream::BufferDescriptorListEntry;
pub use self::stream::StreamBuffer;
pub use self::stream::StreamDescriptorRegs;
@@ -0,0 +1,108 @@
use super::common::*;
use std::{fmt, mem};
#[derive(Clone)]
pub struct HDANode {
pub addr: WidgetAddr,
// 0x4
pub subnode_count: u16,
pub subnode_start: u16,
// 0x5
pub function_group_type: u8,
// 0x9
pub capabilities: u32,
// 0xE
pub conn_list_len: u8,
pub connections: Vec<WidgetAddr>,
pub connection_default: u8,
pub is_widget: bool,
pub config_default: u32,
}
impl HDANode {
pub fn new() -> HDANode {
HDANode {
addr: (0, 0),
subnode_count: 0,
subnode_start: 0,
function_group_type: 0,
capabilities: 0,
conn_list_len: 0,
config_default: 0,
is_widget: false,
connections: Vec::<WidgetAddr>::new(),
connection_default: 0,
}
}
pub fn widget_type(&self) -> HDAWidgetType {
unsafe { mem::transmute(((self.capabilities >> 20) & 0xF) as u8) }
}
pub fn device_default(&self) -> Option<DefaultDevice> {
if self.widget_type() != HDAWidgetType::PinComplex {
None
} else {
Some(unsafe { mem::transmute(((self.config_default >> 20) & 0xF) as u8) })
}
}
pub fn configuration_default(&self) -> ConfigurationDefault {
ConfigurationDefault::from_u32(self.config_default)
}
pub fn addr(&self) -> WidgetAddr {
self.addr
}
}
impl fmt::Display for HDANode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.addr == (0, 0) {
write!(
f,
"Addr: {:02X}:{:02X}, Root Node.",
self.addr.0, self.addr.1
)
} else if self.is_widget {
match self.widget_type() {
HDAWidgetType::PinComplex => write!(
f,
"Addr: {:02X}:{:02X}, Type: {:?}: {:?}, Inputs: {}/{}: {:X?}.",
self.addr.0,
self.addr.1,
self.widget_type(),
self.device_default().unwrap(),
self.connection_default,
self.conn_list_len,
self.connections
),
_ => write!(
f,
"Addr: {:02X}:{:02X}, Type: {:?}, Inputs: {}/{}: {:X?}.",
self.addr.0,
self.addr.1,
self.widget_type(),
self.connection_default,
self.conn_list_len,
self.connections
),
}
} else {
write!(
f,
"Addr: {:02X}:{:02X}, AFG: {}, Widget count {}.",
self.addr.0, self.addr.1, self.function_group_type, self.subnode_count
)
}
}
}
@@ -0,0 +1,387 @@
use common::dma::Dma;
use common::io::{Io, Mmio};
use std::cmp::min;
use std::ptr::copy_nonoverlapping;
use std::result;
use syscall::error::{Error, Result, EIO};
use syscall::PAGE_SIZE;
extern crate syscall;
pub enum BaseRate {
BR44_1,
BR48,
}
pub struct SampleRate {
base: BaseRate,
mult: u16,
div: u16,
}
use self::BaseRate::{BR44_1, BR48};
pub const SR_8: SampleRate = SampleRate {
base: BR48,
mult: 1,
div: 6,
};
pub const SR_11_025: SampleRate = SampleRate {
base: BR44_1,
mult: 1,
div: 4,
};
pub const SR_16: SampleRate = SampleRate {
base: BR48,
mult: 1,
div: 3,
};
pub const SR_22_05: SampleRate = SampleRate {
base: BR44_1,
mult: 1,
div: 2,
};
pub const SR_32: SampleRate = SampleRate {
base: BR48,
mult: 2,
div: 3,
};
pub const SR_44_1: SampleRate = SampleRate {
base: BR44_1,
mult: 1,
div: 1,
};
pub const SR_48: SampleRate = SampleRate {
base: BR48,
mult: 1,
div: 1,
};
pub const SR_88_1: SampleRate = SampleRate {
base: BR44_1,
mult: 2,
div: 1,
};
pub const SR_96: SampleRate = SampleRate {
base: BR48,
mult: 2,
div: 1,
};
pub const SR_176_4: SampleRate = SampleRate {
base: BR44_1,
mult: 4,
div: 1,
};
pub const SR_192: SampleRate = SampleRate {
base: BR48,
mult: 4,
div: 1,
};
#[repr(u8)]
pub enum BitsPerSample {
Bits8 = 0,
Bits16 = 1,
Bits20 = 2,
Bits24 = 3,
Bits32 = 4,
}
pub fn format_to_u16(sr: &SampleRate, bps: BitsPerSample, channels: u8) -> u16 {
// 3.3.41
let base: u16 = match sr.base {
BaseRate::BR44_1 => 1 << 14,
BaseRate::BR48 => 0,
};
let mult = ((sr.mult - 1) & 0x7) << 11;
let div = ((sr.div - 1) & 0x7) << 8;
let bits = (bps as u16) << 4;
let chan = ((channels - 1) & 0xF) as u16;
let val: u16 = base | mult | div | bits | chan;
val
}
#[repr(C, packed)]
pub struct StreamDescriptorRegs {
ctrl_lo: Mmio<u16>,
ctrl_hi: Mmio<u8>,
status: Mmio<u8>,
link_pos: Mmio<u32>,
buff_length: Mmio<u32>,
last_valid_index: Mmio<u16>,
resv1: Mmio<u16>,
fifo_size_: Mmio<u16>,
format: Mmio<u16>,
resv2: Mmio<u32>,
buff_desc_list_lo: Mmio<u32>,
buff_desc_list_hi: Mmio<u32>,
}
impl StreamDescriptorRegs {
pub fn status(&self) -> u8 {
self.status.read()
}
pub fn set_status(&mut self, status: u8) {
self.status.write(status);
}
pub fn control(&self) -> u32 {
let mut ctrl = self.ctrl_lo.read() as u32;
ctrl |= (self.ctrl_hi.read() as u32) << 16;
ctrl
}
pub fn set_control(&mut self, control: u32) {
self.ctrl_lo.write((control & 0xFFFF) as u16);
self.ctrl_hi.write(((control >> 16) & 0xFF) as u8);
}
pub fn set_pcm_format(&mut self, sr: &SampleRate, bps: BitsPerSample, channels: u8) {
// 3.3.41
let val = format_to_u16(sr, bps, channels);
self.format.write(val);
}
pub fn fifo_size(&self) -> u16 {
self.fifo_size_.read()
}
pub fn set_cyclic_buffer_length(&mut self, length: u32) {
self.buff_length.write(length);
}
pub fn cyclic_buffer_length(&self) -> u32 {
self.buff_length.read()
}
pub fn run(&mut self) {
let val = self.control() | (1 << 1);
self.set_control(val);
}
pub fn stop(&mut self) {
let val = self.control() & !(1 << 1);
self.set_control(val);
}
pub fn stream_number(&self) -> u8 {
((self.control() >> 20) & 0xF) as u8
}
pub fn set_stream_number(&mut self, stream_number: u8) {
let val = (self.control() & 0x00FFFF) | (((stream_number & 0xF) as u32) << 20);
self.set_control(val);
}
pub fn set_address(&mut self, addr: usize) {
self.buff_desc_list_lo.write((addr & 0xFFFFFFFF) as u32);
self.buff_desc_list_hi
.write((((addr as u64) >> 32) & 0xFFFFFFFF) as u32);
}
pub fn set_last_valid_index(&mut self, index: u16) {
self.last_valid_index.write(index);
}
pub fn link_position(&self) -> u32 {
self.link_pos.read()
}
pub fn set_interrupt_on_completion(&mut self, enable: bool) {
let mut ctrl = self.control();
if enable {
ctrl |= 1 << 2;
} else {
ctrl &= !(1 << 2);
}
self.set_control(ctrl);
}
pub fn buffer_complete(&self) -> bool {
self.status.readf(1 << 2)
}
pub fn clear_interrupts(&mut self) {
self.status.write(0x7 << 2);
}
// get sample size in bytes
pub fn sample_size(&self) -> usize {
let format = self.format.read();
let chan = (format & 0xF) as usize;
let bits = ((format >> 4) & 0xF) as usize;
match bits {
0 => 1 * (chan + 1),
1 => 2 * (chan + 1),
_ => 4 * (chan + 1),
}
}
}
pub struct OutputStream {
buff: StreamBuffer,
desc_regs: &'static mut StreamDescriptorRegs,
}
impl OutputStream {
pub fn new(
block_count: usize,
block_length: usize,
regs: &'static mut StreamDescriptorRegs,
) -> OutputStream {
OutputStream {
buff: StreamBuffer::new(block_length, block_count).unwrap(),
desc_regs: regs,
}
}
pub fn write_block(&mut self, buf: &[u8]) -> Result<usize> {
self.buff.write_block(buf)
}
pub fn block_size(&self) -> usize {
self.buff.block_size()
}
pub fn block_count(&self) -> usize {
self.buff.block_count()
}
pub fn current_block(&self) -> usize {
self.buff.current_block()
}
pub fn addr(&self) -> usize {
self.buff.addr()
}
pub fn phys(&self) -> usize {
self.buff.phys()
}
}
#[repr(C, packed)]
pub struct BufferDescriptorListEntry {
addr_low: Mmio<u32>,
addr_high: Mmio<u32>,
len: Mmio<u32>,
ioc_resv: Mmio<u32>,
}
impl BufferDescriptorListEntry {
pub fn address(&self) -> u64 {
(self.addr_low.read() as u64) | ((self.addr_high.read() as u64) << 32)
}
pub fn set_address(&mut self, addr: u64) {
self.addr_low.write(addr as u32);
self.addr_high.write((addr >> 32) as u32);
}
pub fn length(&self) -> u32 {
self.len.read()
}
pub fn set_length(&mut self, length: u32) {
self.len.write(length)
}
pub fn interrupt_on_completion(&self) -> bool {
(self.ioc_resv.read() & 0x1) == 0x1
}
pub fn set_interrupt_on_complete(&mut self, ioc: bool) {
self.ioc_resv.writef(1, ioc);
}
}
pub struct StreamBuffer {
mem: Dma<[u8]>,
block_cnt: usize,
block_len: usize,
cur_pos: usize,
}
impl StreamBuffer {
pub fn new(
block_length: usize,
block_count: usize,
) -> result::Result<StreamBuffer, &'static str> {
let page_aligned_size = (block_length * block_count).next_multiple_of(PAGE_SIZE);
let mem = unsafe {
Dma::zeroed_slice(page_aligned_size)
.map_err(|_| "Could not allocate physical memory for buffer.")?
.assume_init()
};
Ok(StreamBuffer {
mem,
block_len: block_length,
block_cnt: block_count,
cur_pos: 0,
})
}
pub fn length(&self) -> usize {
self.block_len * self.block_cnt
}
pub fn addr(&self) -> usize {
self.mem.as_ptr() as usize
}
pub fn phys(&self) -> usize {
self.mem.physical()
}
pub fn block_size(&self) -> usize {
self.block_len
}
pub fn block_count(&self) -> usize {
self.block_cnt
}
pub fn current_block(&self) -> usize {
self.cur_pos
}
pub fn write_block(&mut self, buf: &[u8]) -> Result<usize> {
if buf.len() != self.block_size() {
return Err(Error::new(EIO));
}
let len = min(self.block_size(), buf.len());
//log::trace!("Phys: {:X} Virt: {:X} Offset: {:X} Len: {:X}", self.phys(), self.addr(), self.current_block() * self.block_size(), len);
unsafe {
copy_nonoverlapping(
buf.as_ptr(),
(self.addr() + self.current_block() * self.block_size()) as *mut u8,
len,
);
}
self.cur_pos += 1;
self.cur_pos %= self.block_count();
Ok(len)
}
}
impl Drop for StreamBuffer {
fn drop(&mut self) {
log::debug!("IHDA: Deallocating buffer.");
}
}
+135
View File
@@ -0,0 +1,135 @@
use redox_scheme::scheme::register_sync_scheme;
use redox_scheme::Socket;
use scheme_utils::ReadinessBased;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::usize;
use event::{user_data, EventQueue};
use pcid_interface::irq_helpers::pci_allocate_interrupt_vector;
use pcid_interface::PciFunctionHandle;
pub mod hda;
/*
VEND:PROD
Virtualbox 8086:2668
QEMU ICH9 8086:293E
82801H ICH8 8086:284B
*/
fn main() {
pcid_interface::pci_daemon(daemon);
}
fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
let pci_config = pcid_handle.config();
let mut name = pci_config.func.name();
name.push_str("_ihda");
common::setup_logging(
"audio",
"pci",
&name,
common::output_level(),
common::file_level(),
);
log::info!("IHDA {}", pci_config.func.display());
let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize;
let irq_file = pci_allocate_interrupt_vector(&mut pcid_handle, "ihdad");
{
let vend_prod: u32 = ((pci_config.func.full_device_id.vendor_id as u32) << 16)
| (pci_config.func.full_device_id.device_id as u32);
user_data! {
enum Source {
Irq,
Scheme,
}
}
let event_queue =
EventQueue::<Source>::new().expect("ihdad: Could not create event queue.");
let socket = Socket::nonblock().expect("ihdad: failed to create socket");
let mut device = unsafe {
hda::IntelHDA::new(address, vend_prod).expect("ihdad: failed to allocate device")
};
let mut readiness_based = ReadinessBased::new(&socket, 16);
register_sync_scheme(&socket, "audiohw", &mut device)
.expect("ihdad: failed to register audiohw scheme to namespace");
daemon.ready();
event_queue
.subscribe(
socket.inner().raw(),
Source::Scheme,
event::EventFlags::READ,
)
.unwrap();
event_queue
.subscribe(
irq_file.irq_handle().as_raw_fd() as usize,
Source::Irq,
event::EventFlags::READ,
)
.unwrap();
libredox::call::setrens(0, 0).expect("ihdad: failed to enter null namespace");
let all = [Source::Irq, Source::Scheme];
for event in all
.into_iter()
.chain(event_queue.map(|e| e.expect("failed to get next event").user_data))
{
match event {
Source::Irq => {
let mut irq = [0; 8];
irq_file.irq_handle().read(&mut irq).unwrap();
if !device.irq() {
continue;
}
irq_file.irq_handle().write(&mut irq).unwrap();
readiness_based
.poll_all_requests(&mut device)
.expect("ihdad: failed to poll requests");
readiness_based
.write_responses()
.expect("ihdad: failed to write to socket");
/*
let next_read = device_irq.next_read();
if next_read > 0 {
return Ok(Some(next_read));
}
*/
}
Source::Scheme => {
readiness_based
.read_and_process_requests(&mut device)
.expect("ihdad: failed to read from socket");
readiness_based
.write_responses()
.expect("ihdad: failed to write to socket");
/*
let next_read = device.borrow().next_read();
if next_read > 0 {
return Ok(Some(next_read));
}
*/
}
}
}
std::process::exit(0);
}
}
@@ -0,0 +1,20 @@
[package]
name = "sb16d"
description = "Sound Blaster sound card driver"
version = "0.1.0"
edition = "2021"
[dependencies]
bitflags.workspace = true
common = { path = "../../common" }
libredox.workspace = true
log.workspace = true
daemon = { path = "../../../daemon" }
redox_event.workspace = true
redox_syscall.workspace = true
spin.workspace = true
redox-scheme.workspace = true
scheme-utils = { path = "../../../scheme-utils" }
[lints]
workspace = true
@@ -0,0 +1,232 @@
use std::{thread, time};
use common::io::{Io, Pio, ReadOnly, WriteOnly};
use redox_scheme::scheme::SchemeSync;
use redox_scheme::CallerCtx;
use redox_scheme::OpenResult;
use scheme_utils::{FpathWriter, HandleMap};
use syscall::error::{Error, Result, EACCES, EBADF, ENODEV};
use syscall::schemev2::NewFdFlags;
use spin::Mutex;
const NUM_SUB_BUFFS: usize = 32;
const SUB_BUFF_SIZE: usize = 2048;
enum Handle {
Todo,
SchemeRoot,
}
#[allow(dead_code)]
pub struct Sb16 {
handles: Mutex<HandleMap<Handle>>,
pub(crate) irqs: Vec<u8>,
dmas: Vec<u8>,
// Regs
/* 0x04 */ mixer_addr: WriteOnly<Pio<u8>>,
/* 0x05 */ mixer_data: Pio<u8>,
/* 0x06 */ dsp_reset: WriteOnly<Pio<u8>>,
/* 0x0A */ dsp_read_data: ReadOnly<Pio<u8>>,
/* 0x0C */ dsp_write_data: WriteOnly<Pio<u8>>,
/* 0x0C */ dsp_write_status: ReadOnly<Pio<u8>>,
/* 0x0E */ dsp_read_status: ReadOnly<Pio<u8>>,
}
impl Sb16 {
pub unsafe fn new(addr: u16) -> Result<Self> {
let mut module = Sb16 {
handles: Mutex::new(HandleMap::new()),
irqs: Vec::new(),
dmas: Vec::new(),
// Regs
mixer_addr: WriteOnly::new(Pio::new(addr + 0x04)),
mixer_data: Pio::new(addr + 0x05),
dsp_reset: WriteOnly::new(Pio::new(addr + 0x06)),
dsp_read_data: ReadOnly::new(Pio::new(addr + 0x0A)),
dsp_write_data: WriteOnly::new(Pio::new(addr + 0x0C)),
dsp_write_status: ReadOnly::new(Pio::new(addr + 0x0C)),
dsp_read_status: ReadOnly::new(Pio::new(addr + 0x0E)),
};
module.init()?;
Ok(module)
}
fn mixer_read(&mut self, index: u8) -> u8 {
self.mixer_addr.write(index);
self.mixer_data.read()
}
fn mixer_write(&mut self, index: u8, value: u8) {
self.mixer_addr.write(index);
self.mixer_data.write(value);
}
fn dsp_read(&mut self) -> Result<u8> {
// Bit 7 must be 1 before data can be sent
while !self.dsp_read_status.readf(1 << 7) {
//TODO: timeout!
std::thread::yield_now();
}
Ok(self.dsp_read_data.read())
}
fn dsp_write(&mut self, value: u8) -> Result<()> {
// Bit 7 must be 0 before data can be sent
while self.dsp_write_status.readf(1 << 7) {
//TODO: timeout!
std::thread::yield_now();
}
self.dsp_write_data.write(value);
Ok(())
}
fn init(&mut self) -> Result<()> {
// Perform DSP reset
{
// Write 1 to reset port
self.dsp_reset.write(1);
// Wait 3us
thread::sleep(time::Duration::from_micros(3));
// Write 0 to reset port
self.dsp_reset.write(0);
//TODO: Wait for ready byte (0xAA) using read status
thread::sleep(time::Duration::from_micros(100));
let ready = self.dsp_read()?;
if ready != 0xAA {
log::error!("ready byte was 0x{:02X} instead of 0xAA", ready);
return Err(Error::new(ENODEV));
}
}
// Read DSP version
{
self.dsp_write(0xE1)?;
let major = self.dsp_read()?;
let minor = self.dsp_read()?;
log::info!("DSP version {}.{:02}", major, minor);
if major != 4 {
log::error!("Unsupported DSP major version {}", major);
return Err(Error::new(ENODEV));
}
}
// Get available IRQs and DMAs
{
self.irqs.clear();
let irq_mask = self.mixer_read(0x80);
if (irq_mask & (1 << 0)) != 0 {
self.irqs.push(2);
}
if (irq_mask & (1 << 1)) != 0 {
self.irqs.push(5);
}
if (irq_mask & (1 << 2)) != 0 {
self.irqs.push(7);
}
if (irq_mask & (1 << 3)) != 0 {
self.irqs.push(10);
}
self.dmas.clear();
let dma_mask = self.mixer_read(0x81);
if (dma_mask & (1 << 0)) != 0 {
self.dmas.push(0);
}
if (dma_mask & (1 << 1)) != 0 {
self.dmas.push(1);
}
if (dma_mask & (1 << 3)) != 0 {
self.dmas.push(3);
}
if (dma_mask & (1 << 5)) != 0 {
self.dmas.push(5);
}
if (dma_mask & (1 << 6)) != 0 {
self.dmas.push(6);
}
if (dma_mask & (1 << 7)) != 0 {
self.dmas.push(7);
}
log::info!("IRQs {:02X?} DMAs {:02X?}", self.irqs, self.dmas);
}
// Set output sample rate to 44100 Hz (Redox OS standard)
{
let rate = 44100u16;
self.dsp_write(0x41)?;
self.dsp_write((rate >> 8) as u8)?;
self.dsp_write(rate as u8)?;
}
Ok(())
}
pub fn irq(&mut self) -> bool {
//TODO
false
}
}
impl SchemeSync for Sb16 {
fn scheme_root(&mut self) -> Result<usize> {
Ok(self.handles.lock().insert(Handle::SchemeRoot))
}
fn openat(
&mut self,
dirfd: usize,
_path: &str,
_flags: usize,
_fcntl_flags: u32,
ctx: &CallerCtx,
) -> Result<OpenResult> {
{
let handles = self.handles.lock();
let handle = handles.get(dirfd)?;
if !matches!(handle, Handle::SchemeRoot) {
return Err(Error::new(EACCES));
}
}
if ctx.uid == 0 {
let id = self.handles.lock().insert(Handle::Todo);
Ok(OpenResult::ThisScheme {
number: id,
flags: NewFdFlags::empty(),
})
} else {
Err(Error::new(EACCES))
}
}
fn write(
&mut self,
_id: usize,
_buf: &[u8],
_offset: u64,
_flags: u32,
_ctx: &CallerCtx,
) -> Result<usize> {
//TODO
Err(Error::new(EBADF))
}
fn fpath(&mut self, _id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
FpathWriter::with(buf, "audiohw", |_| Ok(()))
}
fn on_close(&mut self, id: usize) {
self.handles.lock().remove(id);
}
}
@@ -0,0 +1,118 @@
use libredox::{flag, Fd};
use redox_scheme::scheme::register_sync_scheme;
use redox_scheme::Socket;
use scheme_utils::ReadinessBased;
use std::{env, usize};
use event::{user_data, EventQueue};
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub mod device;
fn main() {
daemon::Daemon::new(daemon);
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn daemon(daemon: daemon::Daemon) -> ! {
let mut args = env::args().skip(1);
let addr_str = args.next().unwrap_or("220".to_string());
let addr = u16::from_str_radix(&addr_str, 16).expect("sb16: failed to parse address");
println!(" + sb16 at 0x{:X}\n", addr);
common::setup_logging(
"audio",
"pci",
"sb16",
common::output_level(),
common::file_level(),
);
common::acquire_port_io_rights().expect("sb16d: failed to acquire port IO rights");
let socket = Socket::nonblock().expect("sb16d: failed to create socket");
let mut device = unsafe { device::Sb16::new(addr).expect("sb16d: failed to allocate device") };
let mut readiness_based = ReadinessBased::new(&socket, 16);
//TODO: error on multiple IRQs?
let irq_file = match device.irqs.first() {
Some(irq) => Fd::open(&format!("/scheme/irq/{}", irq), flag::O_RDWR, 0)
.expect("sb16d: failed to open IRQ file"),
None => panic!("sb16d: no IRQs found"),
};
user_data! {
enum Source {
Irq,
Scheme,
}
}
let event_queue = EventQueue::<Source>::new().expect("sb16d: Could not create event queue.");
event_queue
.subscribe(irq_file.raw(), Source::Irq, event::EventFlags::READ)
.unwrap();
event_queue
.subscribe(
socket.inner().raw(),
Source::Scheme,
event::EventFlags::READ,
)
.unwrap();
register_sync_scheme(&socket, "sb16d", &mut device)
.expect("sb16d: failed to register audiohw scheme to namespace");
daemon.ready();
libredox::call::setrens(0, 0).expect("sb16d: failed to enter null namespace");
let all = [Source::Irq, Source::Scheme];
for event in all
.into_iter()
.chain(event_queue.map(|e| e.expect("sb16d: failed to get next event").user_data))
{
match event {
Source::Irq => {
let mut irq = [0; 8];
irq_file.read(&mut irq).unwrap();
if !device.irq() {
continue;
}
irq_file.write(&mut irq).unwrap();
readiness_based
.poll_all_requests(&mut device)
.expect("sb16d: failed to poll requests");
readiness_based
.write_responses()
.expect("sb16d: failed to write to socket");
/*
let next_read = device_irq.next_read();
if next_read > 0 {
return Ok(Some(next_read));
}
*/
}
Source::Scheme => {
readiness_based
.read_and_process_requests(&mut device)
.expect("sb16d: failed to read from socket");
readiness_based
.write_responses()
.expect("sb16d: failed to write to socket");
}
}
}
std::process::exit(0);
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
fn daemon(daemon: daemon::Daemon) -> ! {
unimplemented!()
}