From eb7b811c368c51104ad50e6ebaa725ebc23be57a Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sat, 21 Jan 2023 14:24:40 -0700 Subject: [PATCH] Add initial sb16 driver --- Cargo.lock | 18 ++++ Cargo.toml | 1 + sb16d/Cargo.toml | 13 +++ sb16d/src/device.rs | 129 +++++++++++++++++++++++++++ sb16d/src/main.rs | 207 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 368 insertions(+) create mode 100644 sb16d/Cargo.toml create mode 100644 sb16d/src/device.rs create mode 100644 sb16d/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index e91290c178..f3bbc00687 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,6 +1211,19 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +[[package]] +name = "sb16d" +version = "0.1.0" +dependencies = [ + "bitflags", + "log", + "redox-daemon", + "redox-log", + "redox_event", + "redox_syscall 0.3.4", + "spin", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -1753,3 +1766,8 @@ dependencies = [ "thiserror", "toml", ] + +[[patch.unused]] +name = "libc" +version = "0.2.137" +source = "git+https://gitlab.redox-os.org/redox-os/liblibc.git?branch=rust-2022-03-18#6ba4d1a527c2b53bdd5554cb219a1cd7d315d518" diff --git a/Cargo.toml b/Cargo.toml index 8533a87637..e0d659d3ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "pcspkrd", "ps2d", "rtl8168d", + "sb16d", "vboxd", "vesad", "xhcid", diff --git a/sb16d/Cargo.toml b/sb16d/Cargo.toml new file mode 100644 index 0000000000..bee3594214 --- /dev/null +++ b/sb16d/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "sb16d" +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/sb16d/src/device.rs b/sb16d/src/device.rs new file mode 100644 index 0000000000..cb77b075a9 --- /dev/null +++ b/sb16d/src/device.rs @@ -0,0 +1,129 @@ +#![allow(dead_code)] + +use std::{mem, thread, time}; +use std::collections::BTreeMap; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use syscall::error::{Error, EACCES, EBADF, Result, EINVAL, ENODEV, ENOENT}; +use syscall::io::{Dma, PhysBox, Mmio, Pio, Io, ReadOnly, WriteOnly}; +use syscall::scheme::SchemeBlockMut; + +use spin::Mutex; + +const NUM_SUB_BUFFS: usize = 32; +const SUB_BUFF_SIZE: usize = 2048; + +enum Handle { + Todo, +} + +#[allow(dead_code)] +struct DspRegs { + /* 0x06 */ reset: WriteOnly>, + /* 0x0A */ read_data: ReadOnly>, + /* 0x0C */ write_data: WriteOnly>, + /* 0x0C */ write_status: ReadOnly>, + /* 0x0E */ read_status: ReadOnly>, +} + +impl DspRegs { + fn new(addr: u16) -> Self { + Self { + reset: WriteOnly::new(Pio::new(addr + 0x06)), + read_data: ReadOnly::new(Pio::new(addr + 0x0A)), + write_data: WriteOnly::new(Pio::new(addr + 0x0C)), + write_status: ReadOnly::new(Pio::new(addr + 0x0C)), + read_status: ReadOnly::new(Pio::new(addr + 0x0E)), + } + } +} + +pub struct Sb16 { + dsp: DspRegs, + handles: Mutex>, + next_id: AtomicUsize, +} + +impl Sb16 { + pub unsafe fn new(addr: u16) -> Result { + let mut module = Sb16 { + dsp: DspRegs::new(addr), + handles: Mutex::new(BTreeMap::new()), + next_id: AtomicUsize::new(0), + }; + + module.init()?; + + Ok(module) + } + + 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_data.read(); + if ready != 0xAA { + log::error!("ready byte was 0x{:02X} instead of 0xAA", ready); + return Err(Error::new(ENODEV)); + } + } + + // Read DSP version + { + //TODO + } + + Ok(()) + } + + pub fn irq(&mut self) -> bool { + //TODO + false + } +} + +impl SchemeBlockMut for Sb16 { + 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> { + //TODO + Err(Error::new(EBADF)) + } + + 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/sb16d/src/main.rs b/sb16d/src/main.rs new file mode 100644 index 0000000000..027eac1a11 --- /dev/null +++ b/sb16d/src/main.rs @@ -0,0 +1,207 @@ +//#![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::{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", "sb16.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!("sb16d: failed to create sb16.log: {}", error), + } + + #[cfg(target_os = "redox")] + match OutputBuilder::in_redox_logging_scheme("audio", "pcie", "sb16.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!("sb16d: failed to create sb16.ansi.log: {}", error), + } + + match logger.enable() { + Ok(logger_ref) => { + eprintln!("sb16d: enabled logger"); + Some(logger_ref) + } + Err(error) => { + eprintln!("sb16d: failed to set default logger: {}", error); + None + } + } +} + +fn main() { + let mut args = env::args().skip(1); + + //TODO: add addr to name? + let name = "sb16".to_string(); + + 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"); + + let irq_str = args.next().unwrap_or("5".to_string()); + let irq = irq_str.parse::().expect("sb16: failed to parse irq"); + + let dma_str = args.next().unwrap_or("1".to_string()); + let dma = dma_str.parse::().expect("sb16: failed to parse dma"); + + print!("{}", format!(" + sb16 at: ADDR 0x{:X}, IRQ {}, DMA {}\n", addr, irq, dma)); + + // Daemonize + redox_daemon::Daemon::new(move |daemon| { + let _logger_ref = setup_logging(); + + unsafe { syscall::iopl(3) }.expect("sb16d: failed to set I/O privilege level to Ring 3"); + + let mut irq_file = File::open(format!("irq:{}", irq)).expect("sb16d: failed to open IRQ file"); + + let device = Arc::new(RefCell::new(unsafe { device::Sb16::new(addr).expect("sb16d: failed to allocate device") })); + let socket_fd = syscall::open(":audiohw", syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("sb16d: failed to create hda scheme"); + let socket = Arc::new(RefCell::new(unsafe { File::from_raw_fd(socket_fd as RawFd) })); + + daemon.ready().expect("sb16d: failed to signal readiness"); + + let mut event_queue = EventQueue::::new().expect("sb16d: Could not create event queue."); + + syscall::setrens(0, 0).expect("sb16d: 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("sb16d: 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("sb16d: failed to catch events on IRQ file"); + + for event_count in event_queue.trigger_all(event::Event { + fd: 0, + flags: EventFlags::empty(), + }).expect("sb16d: 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("sb16d: failed to write event"); + } + + loop { + { + //device_loop.borrow_mut().handle_interrupts(); + } + let event_count = event_queue.run().expect("sb16d: 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("sb16d: failed to write event"); + } + + std::process::exit(0); + }).expect("sb16d: failed to daemonize"); +}