Implement most functions
This commit is contained in:
@@ -2,5 +2,7 @@
|
||||
name = "audiod"
|
||||
version = "0.1.0"
|
||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
redox_syscall = "0.1"
|
||||
|
||||
+121
-1
@@ -1 +1,121 @@
|
||||
fn main() {}
|
||||
extern crate syscall;
|
||||
|
||||
use std::{fs, io, mem, process, slice, thread};
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use syscall::data::{Packet, SigAction};
|
||||
use syscall::flag::{O_CLOEXEC, SIGUSR1};
|
||||
use syscall::scheme::SchemeBlockMut;
|
||||
|
||||
use self::scheme::AudioScheme;
|
||||
|
||||
mod scheme;
|
||||
|
||||
fn from_syscall_error(error: syscall::Error) -> io::Error {
|
||||
io::Error::from_raw_os_error(error.errno as i32)
|
||||
}
|
||||
|
||||
extern "C" fn sigusr_handler(_sig: usize) {}
|
||||
|
||||
fn thread(scheme: Arc<Mutex<AudioScheme>>, pid: usize, mut hda_file: fs::File) -> io::Result<()> {
|
||||
loop {
|
||||
let buffer = scheme.lock().unwrap().buffer();
|
||||
let buffer_u8 = unsafe {
|
||||
slice::from_raw_parts(
|
||||
buffer.as_ptr() as *const u8,
|
||||
mem::size_of_val(&buffer)
|
||||
)
|
||||
};
|
||||
|
||||
// Wake up the scheme thread
|
||||
syscall::kill(pid, SIGUSR1).map_err(from_syscall_error)?;
|
||||
|
||||
hda_file.write(&buffer_u8)?;
|
||||
}
|
||||
}
|
||||
|
||||
fn daemon(pipe_fd: usize) -> io::Result<()> {
|
||||
// Handle signals from the hda thread
|
||||
syscall::sigaction(SIGUSR1, Some(&SigAction {
|
||||
sa_handler: sigusr_handler,
|
||||
sa_mask: [0; 2],
|
||||
sa_flags: 0,
|
||||
}), None).map_err(from_syscall_error)?;
|
||||
|
||||
let pid = syscall::getpid().map_err(from_syscall_error)?;
|
||||
|
||||
let hda_file = fs::OpenOptions::new().write(true).open("hda:")?;
|
||||
|
||||
let mut scheme_file = fs::OpenOptions::new().create(true).read(true).write(true).open(":audio")?;
|
||||
|
||||
// The scheme is now ready to accept requests, notify the original process
|
||||
syscall::write(pipe_fd, &[1]).map_err(from_syscall_error)?;
|
||||
let _ = syscall::close(pipe_fd);
|
||||
|
||||
// Enter the null namespace
|
||||
syscall::setrens(0, 0).map_err(from_syscall_error)?;
|
||||
|
||||
let scheme = Arc::new(Mutex::new(AudioScheme::new()));
|
||||
|
||||
// Spawn a thread to mix and send audio data
|
||||
let scheme_thread = scheme.clone();
|
||||
let _thread = thread::spawn(move || thread(scheme_thread, pid, hda_file));
|
||||
|
||||
let mut todo = Vec::new();
|
||||
loop {
|
||||
let mut packet = Packet::default();
|
||||
let count = match scheme_file.read(&mut packet) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => if err.kind() == io::ErrorKind::Interrupted {
|
||||
0
|
||||
} else {
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
if count > 0 {
|
||||
if let Some(a) = scheme.lock().unwrap().handle(&mut packet) {
|
||||
packet.a = a;
|
||||
scheme_file.write(&packet)?;
|
||||
} else {
|
||||
todo.push(packet);
|
||||
}
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
if let Some(a) = scheme.lock().unwrap().handle(&mut todo[i]) {
|
||||
let mut packet = todo.remove(i);
|
||||
packet.a = a;
|
||||
scheme_file.write(&packet)?;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn audiod() -> io::Result<()> {
|
||||
let mut pipe = [0; 2];
|
||||
syscall::pipe2(&mut pipe, O_CLOEXEC).map_err(from_syscall_error)?;
|
||||
|
||||
// Daemonize
|
||||
if unsafe { syscall::clone(0) }.map_err(from_syscall_error)? == 0 {
|
||||
let _ = syscall::close(pipe[0]);
|
||||
return daemon(pipe[1]);
|
||||
} else {
|
||||
let _ = syscall::close(pipe[1]);
|
||||
|
||||
syscall::read(pipe[0], &mut [0]).map_err(from_syscall_error)?;
|
||||
let _ = syscall::close(pipe[0]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = audiod() {
|
||||
eprintln!("audiod: {}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use syscall::error::{EBADF, Error, Result};
|
||||
use syscall::scheme::SchemeBlockMut;
|
||||
|
||||
// The strict buffer size of the hda: driver
|
||||
const HDA_BUFFER_SIZE: usize = 512;
|
||||
// The desired buffer size of each handle
|
||||
const HANDLE_BUFFER_SIZE: usize = 4096;
|
||||
|
||||
struct Handle {
|
||||
flags: usize,
|
||||
buffer: VecDeque<(u16, u16)>,
|
||||
}
|
||||
|
||||
pub struct AudioScheme {
|
||||
next_id: usize,
|
||||
handles: BTreeMap<usize, Handle>
|
||||
}
|
||||
|
||||
impl AudioScheme {
|
||||
pub fn new() -> Self {
|
||||
AudioScheme {
|
||||
next_id: 0,
|
||||
handles: BTreeMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buffer(&mut self) -> [(u16, u16); HDA_BUFFER_SIZE] {
|
||||
let mut buffer = [(0, 0); HDA_BUFFER_SIZE];
|
||||
|
||||
for (_id, handle) in self.handles.iter_mut() {
|
||||
let mut i = 0;
|
||||
while i < buffer.len() {
|
||||
if let Some(sample) = handle.buffer.pop_front() {
|
||||
buffer[i].0 += sample.0;
|
||||
buffer[i].1 += sample.1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeBlockMut for AudioScheme {
|
||||
fn open(&mut self, _path: &[u8], flags: usize, _uid: u32, _gid: u32) -> Result<Option<usize>> {
|
||||
self.next_id += 1;
|
||||
let id = self.next_id;
|
||||
|
||||
self.handles.insert(id, Handle {
|
||||
flags,
|
||||
buffer: VecDeque::new()
|
||||
});
|
||||
|
||||
Ok(Some(id))
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
|
||||
let mut handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if handle.buffer.len() >= HANDLE_BUFFER_SIZE {
|
||||
Ok(None)
|
||||
} else {
|
||||
let mut i = 0;
|
||||
while i + 4 <= buf.len() {
|
||||
handle.buffer.push_back((
|
||||
(buf[i] as u16) | ((buf[i + 1] as u16) << 8),
|
||||
(buf[i + 2] as u16) | ((buf[i + 3] as u16) << 8)
|
||||
));
|
||||
|
||||
i += 4;
|
||||
}
|
||||
|
||||
Ok(Some(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user