Files
RedBear-OS/ihdad/src/hda/stream.rs
T

381 lines
7.7 KiB
Rust

use syscall::PHYSMAP_WRITE;
use syscall::error::{Error, EIO, Result};
use syscall::io::{Mmio, Io};
use std::result;
use std::cmp::min;
use std::ptr::copy_nonoverlapping;
use std::ptr;
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(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 >> 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 {
unsafe {
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(packed)]
pub struct BufferDescriptorListEntry {
addr: Mmio<u64>,
len: Mmio<u32>,
ioc_resv: Mmio<u32>,
}
impl BufferDescriptorListEntry {
pub fn address(&self) -> usize {
self.addr.read() as usize
}
pub fn set_address(&mut self, addr:usize) {
self.addr.write(addr as u64);
}
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 {
phys: usize,
addr: usize,
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 phys = match unsafe {
syscall::physalloc(block_length * block_count)
} {
Ok(phys) => phys,
Err(err) => {
return Err("Could not allocate physical memory for buffer.");
}
};
let addr = match unsafe {
syscall::physmap(phys, block_length * block_count, PHYSMAP_WRITE)
} {
Ok(addr) => addr,
Err(err) => {
unsafe {
syscall::physfree(phys, block_length * block_count);
}
return Err("Could not map physical memory for buffer.");
}
};
unsafe {
ptr::write_bytes(addr as *mut u8, 0, block_length * block_count);
}
Ok(StreamBuffer {
phys: phys,
addr: addr,
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.addr
}
pub fn phys(&self) -> usize {
self.phys
}
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());
//print!("Phys: {:X} Virt: {:X} Offset: {:X} Len: {:X}\n", 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) {
unsafe {
print!("IHDA: Deallocating buffer.\n");
if syscall::physunmap(self.addr).is_ok() {
let _ = syscall::physfree(self.phys, self.block_len * self.block_cnt);
}
}
}
}