From d7ca6eeeedcd438d1d2a0768aed41fbd3c244a90 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 15 Nov 2022 13:37:31 -0700 Subject: [PATCH] ac97: add stub driver for AC97 --- Cargo.lock | 13 +++ Cargo.toml | 1 + ac97d/Cargo.toml | 13 +++ ac97d/config.toml | 5 ++ ac97d/src/device.rs | 196 ++++++++++++++++++++++++++++++++++++++++++ ac97d/src/main.rs | 205 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 433 insertions(+) create mode 100644 ac97d/Cargo.toml create mode 100644 ac97d/config.toml create mode 100644 ac97d/src/device.rs create mode 100644 ac97d/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 13813dde0d..e448276b87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,19 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ac97d" +version = "0.1.0" +dependencies = [ + "bitflags", + "log", + "redox-daemon", + "redox-log", + "redox_event", + "redox_syscall 0.3.4", + "spin", +] + [[package]] name = "acpid" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 9ddcd94a99..8533a87637 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "ac97d", "acpid", "ahcid", "alxd", diff --git a/ac97d/Cargo.toml b/ac97d/Cargo.toml new file mode 100644 index 0000000000..249ea0ed03 --- /dev/null +++ b/ac97d/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "ac97d" +version = "0.1.0" +edition = "2018" + +[dependencies] +bitflags = "1" +log = "0.4" +redox-daemon = "0.1" +redox-log = "0.1" +redox_event = { git = "https://gitlab.redox-os.org/redox-os/event.git" } +redox_syscall = "0.3" +spin = "0.9" diff --git a/ac97d/config.toml b/ac97d/config.toml new file mode 100644 index 0000000000..7bafbb4975 --- /dev/null +++ b/ac97d/config.toml @@ -0,0 +1,5 @@ +[[drivers]] +name = "AC97 Audio" +class = 4 +subclass = 1 +command = ["ac97d", "$NAME", "$BAR0", "$BAR1", "$IRQ"] diff --git a/ac97d/src/device.rs b/ac97d/src/device.rs new file mode 100644 index 0000000000..0ba70e9ca9 --- /dev/null +++ b/ac97d/src/device.rs @@ -0,0 +1,196 @@ +#![allow(dead_code)] + +use std::cmp; +use std::collections::HashMap; +use std::str; +use std::collections::BTreeMap; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use syscall::{PHYSMAP_NO_CACHE, PHYSMAP_WRITE}; +use syscall::error::{Error, EACCES, EBADF, Result, EINVAL}; +use syscall::flag::{SEEK_SET, SEEK_CUR, SEEK_END}; +use syscall::io::{Pio, Io}; +use syscall::scheme::SchemeBlockMut; + +use spin::Mutex; + +const NUM_SUB_BUFFS: usize = 4; +const SUB_BUFF_SIZE: usize = 2048; + +enum Handle { + Todo, +} + +#[repr(packed)] +#[allow(dead_code)] +struct MixerRegs { + /* 0x00 */ reset: Pio, + /* 0x02 */ master_volume: Pio, + /* 0x04 */ aux_out_volume: Pio, + /* 0x06 */ mono_volume: Pio, + /* 0x08 */ master_tone: Pio, + /* 0x0A */ pc_beep_volume: Pio, + /* 0x0C */ phone_volume: Pio, + /* 0x0E */ mic_volume: Pio, + /* 0x10 */ line_in_volume: Pio, + /* 0x12 */ cd_volume: Pio, + /* 0x14 */ video_volume: Pio, + /* 0x16 */ aux_in_volume: Pio, + /* 0x18 */ pcm_out_volume: Pio, + /* 0x1A */ record_select: Pio, + /* 0x1C */ record_gain: Pio, + /* 0x1E */ record_gain_mic: Pio, + /* 0x20 */ general_purpose: Pio, + /* 0x22 */ control_3d: Pio, + /* 0x24 */ audio_int_paging: Pio, + /* 0x26 */ powerdown: Pio, + //TODO: extended registers +} + +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), + } + } +} + +#[repr(packed)] +#[allow(dead_code)] +struct BusBoxRegs { + /// Buffer descriptor list base address + /* 0x00 */ bdbar: Pio, + /// Current index value + /* 0x04 */ civ: Pio, + /// Last valid index + /* 0x05 */ lvi: Pio, + /// Status + /* 0x06 */ sr: Pio, + /// Position in current buffer + /* 0x08 */ picb: Pio, + /// Prefetched index value + /* 0x0A */ piv: Pio, + /// Control + /* 0x0B */ cr: Pio, +} + +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), + } + } +} + +#[repr(packed)] +#[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), + } + } +} + +pub struct Ac97 { + mixer: MixerRegs, + bus: BusRegs, + handles: Mutex>, + next_id: AtomicUsize, +} + +impl Ac97 { + pub unsafe fn new(bar0: u16, bar1: u16) -> Result { + let mut module = Ac97 { + mixer: MixerRegs::new(bar0), + bus: BusRegs::new(bar1), + handles: Mutex::new(BTreeMap::new()), + next_id: AtomicUsize::new(0), + }; + + //TODO: init + + Ok(module) + } + + pub fn irq(&mut self) -> bool { + //TODO + false + } +} + +impl SchemeBlockMut for Ac97 { + fn open(&mut self, _path: &str, _flags: usize, uid: u32, _gid: u32) -> Result> { + if uid == 0 { + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + self.handles.lock().insert(id, Handle::Todo); + Ok(Some(id)) + } else { + Err(Error::new(EACCES)) + } + } + + fn write(&mut self, id: usize, buf: &[u8]) -> Result> { + let index = { + let mut handles = self.handles.lock(); + let _handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + 0 + }; + + Ok(Some(buf.len())) + } + + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result> { + let mut handles = self.handles.lock(); + let _handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + let mut i = 0; + let scheme_path = b"audiohw:"; + while i < buf.len() && i < scheme_path.len() { + buf[i] = scheme_path[i]; + i += 1; + } + Ok(Some(i)) + } + + fn close(&mut self, id: usize) -> Result> { + let mut handles = self.handles.lock(); + handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(Some(0))) + } +} diff --git a/ac97d/src/main.rs b/ac97d/src/main.rs new file mode 100644 index 0000000000..1be4ff1423 --- /dev/null +++ b/ac97d/src/main.rs @@ -0,0 +1,205 @@ +//#![deny(warnings)] + +extern crate bitflags; +extern crate spin; +extern crate syscall; +extern crate event; + +use std::{env, usize}; +use std::fs::File; +use std::io::{ErrorKind, Read, Write, Result}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use syscall::{PHYSMAP_NO_CACHE, PHYSMAP_WRITE, Packet, SchemeBlockMut, EventFlags}; +use std::cell::RefCell; +use std::sync::Arc; + +use event::EventQueue; +use redox_log::{OutputBuilder, RedoxLogger}; + +pub mod device; + +fn setup_logging() -> Option<&'static RedoxLogger> { + let mut logger = RedoxLogger::new() + .with_output( + OutputBuilder::stderr() + .with_filter(log::LevelFilter::Info) // limit global output to important info + .with_ansi_escape_codes() + .flush_on_newline(true) + .build() + ); + + #[cfg(target_os = "redox")] + match OutputBuilder::in_redox_logging_scheme("audio", "pcie", "ac97.log") { + Ok(b) => logger = logger.with_output( + // TODO: Add a configuration file for this + b.with_filter(log::LevelFilter::Info) + .flush_on_newline(true) + .build() + ), + Err(error) => eprintln!("ac97d: failed to create ac97.log: {}", error), + } + + #[cfg(target_os = "redox")] + match OutputBuilder::in_redox_logging_scheme("audio", "pcie", "ac97.ansi.log") { + Ok(b) => logger = logger.with_output( + b.with_filter(log::LevelFilter::Info) + .with_ansi_escape_codes() + .flush_on_newline(true) + .build() + ), + Err(error) => eprintln!("ac97d: failed to create ac97.ansi.log: {}", error), + } + + match logger.enable() { + Ok(logger_ref) => { + eprintln!("ac97d: enabled logger"); + Some(logger_ref) + } + Err(error) => { + eprintln!("ac97d: failed to set default logger: {}", error); + None + } + } +} + +fn main() { + let mut args = env::args().skip(1); + + let mut name = args.next().expect("ac97: no name provided"); + name.push_str("_ac97"); + + let bar0_str = args.next().expect("ac97: no address provided"); + let bar0 = u16::from_str_radix(&bar0_str, 16).expect("ac97: failed to parse address"); + + let bar1_str = args.next().expect("ac97: no address provided"); + let bar1 = u16::from_str_radix(&bar1_str, 16).expect("ac97: failed to parse address"); + + let irq_str = args.next().expect("ac97: no irq provided"); + let irq = irq_str.parse::().expect("ac97: failed to parse irq"); + + print!("{}", format!(" + ac97 {} on: {:X}, {:X}, IRQ {}\n", name, bar0, bar1, irq)); + + // Daemonize + redox_daemon::Daemon::new(move |daemon| { + let _logger_ref = setup_logging(); + + let mut irq_file = File::open(format!("irq:{}", irq)).expect("ac97d: failed to open IRQ file"); + + let device = Arc::new(RefCell::new(unsafe { device::Ac97::new(bar0, bar1).expect("ac97d: failed to allocate device") })); + let socket_fd = syscall::open(":audiohw", syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("ac97d: failed to create hda scheme"); + let socket = Arc::new(RefCell::new(unsafe { File::from_raw_fd(socket_fd as RawFd) })); + + daemon.ready().expect("ac97d: failed to signal readiness"); + + let mut event_queue = EventQueue::::new().expect("ac97d: Could not create event queue."); + + syscall::setrens(0, 0).expect("ac97d: failed to enter null namespace"); + + let todo = Arc::new(RefCell::new(Vec::::new())); + + let todo_irq = todo.clone(); + let device_irq = device.clone(); + let socket_irq = socket.clone(); + + event_queue.add(irq_file.as_raw_fd(), move |_event| -> Result> { + let mut irq = [0; 8]; + irq_file.read(&mut irq)?; + + if device_irq.borrow_mut().irq() { + irq_file.write(&mut irq)?; + + let mut todo = todo_irq.borrow_mut(); + let mut i = 0; + while i < todo.len() { + if let Some(a) = device_irq.borrow_mut().handle(&mut todo[i]) { + let mut packet = todo.remove(i); + packet.a = a; + socket_irq.borrow_mut().write(&packet)?; + } else { + i += 1; + } + } + + /* + let next_read = device_irq.next_read(); + if next_read > 0 { + return Ok(Some(next_read)); + } + */ + } + Ok(None) + }).expect("ac97d: failed to catch events on IRQ file"); + let socket_fd = socket.borrow().as_raw_fd(); + let socket_packet = socket.clone(); + event_queue.add(socket_fd, move |_event| -> Result> { + loop { + let mut packet = Packet::default(); + match socket_packet.borrow_mut().read(&mut packet) { + Ok(0) => return Ok(Some(0)), + Ok(_) => (), + Err(err) => if err.kind() == ErrorKind::WouldBlock { + break; + } else { + return Err(err); + } + } + + if let Some(a) = device.borrow_mut().handle(&mut packet) { + packet.a = a; + socket_packet.borrow_mut().write(&packet)?; + } else { + todo.borrow_mut().push(packet); + } + } + + /* + let next_read = device.borrow().next_read(); + if next_read > 0 { + return Ok(Some(next_read)); + } + */ + + Ok(None) + }).expect("ac97d: failed to catch events on IRQ file"); + + for event_count in event_queue.trigger_all(event::Event { + fd: 0, + flags: EventFlags::empty(), + }).expect("ac97d: failed to trigger events") { + socket.borrow_mut().write(&Packet { + id: 0, + pid: 0, + uid: 0, + gid: 0, + a: syscall::number::SYS_FEVENT, + b: 0, + c: syscall::flag::EVENT_READ.bits(), + d: event_count + }).expect("ac97d: failed to write event"); + } + + loop { + { + //device_loop.borrow_mut().handle_interrupts(); + } + let event_count = event_queue.run().expect("ac97d: failed to handle events"); + if event_count == 0 { + //TODO: Handle todo + break; + } + + socket.borrow_mut().write(&Packet { + id: 0, + pid: 0, + uid: 0, + gid: 0, + a: syscall::number::SYS_FEVENT, + b: 0, + c: syscall::flag::EVENT_READ.bits(), + d: event_count + }).expect("ac97d: failed to write event"); + } + + std::process::exit(0); + }).expect("ac97d: failed to daemonize"); +}