ihdad: cleanup, fix blocking code, decrease buffer size to decrease latency

This commit is contained in:
Jeremy Soller
2018-12-29 12:21:12 -07:00
parent c47c68f44c
commit 939b294eae
9 changed files with 825 additions and 997 deletions
+430
View File
@@ -0,0 +1,430 @@
use syscall::io::{Io, Mmio};
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: usize) -> Corb {
unsafe {
Corb {
regs: &mut *(regs_addr as *mut CorbRegs),
corb_base: (corb_buff_virt) as *mut u32,
corb_base_phys: corb_buff_phys,
corb_count: 0,
}
}
}
//Intel 4.4.1.3
pub fn init(&mut self) {
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.corbwp.write(0);
self.reset_read_pointer();
}
pub fn start(&mut self) {
self.regs.corbctl.writef(CORBRUN, true);
}
pub fn stop(&mut self) {
while self.regs.corbctl.readf(CORBRUN) {
self.regs.corbctl.write(0);
}
}
pub fn set_address(&mut self, addr: usize) {
self.regs.corblbase.write((addr & 0xFFFFFFFF) as u32);
self.regs.corbubase.write((addr >> 32) as u32);
}
pub fn reset_read_pointer(&mut self) {
/*
* FIRST ISSUE/PATCH
* This will loop forever in virtualbox
* So maybe just resetting the read pointer
* and leaving for the specific model?
*/
if true {
self.regs.corbrp.writef(CORBRPRST, true);
} else {
// 3.3.21
self.stop();
// Set CORBRPRST to 1
print!("CORBRP {:X}\n", self.regs.corbrp.read());
self.regs.corbrp.writef(CORBRPRST, true);
print!("CORBRP {:X}\n", self.regs.corbrp.read());
print!("Here!\n");
// Wait for it to become 1
while !self.regs.corbrp.readf(CORBRPRST) {
self.regs.corbrp.writef(CORBRPRST, true);
}
print!("Here!!\n");
// Clear the bit again
self.regs.corbrp.write(0);
// Read back the bit until zero to verify that it is cleared.
loop {
if !self.regs.corbrp.readf(CORBRPRST) {
break;
}
self.regs.corbrp.write(0);
}
print!("Here!!!\n");
}
}
fn send_command(&mut self, cmd: u32) {
// wait for the commands to finish
while (self.regs.corbwp.read() & 0xff) != (self.regs.corbrp.read() & 0xff) {}
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);
print!("Corb: {:08X}\n", cmd);
}
}
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: usize) -> Rirb {
unsafe {
Rirb {
regs: &mut *(regs_addr as *mut RirbRegs),
rirb_base: (rirb_buff_virt) as *mut u64,
rirb_rp: 0,
rirb_base_phys: rirb_buff_phys,
rirb_count: 0,
}
}
}
//Intel 4.4.1.3
pub fn init(&mut self) {
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);
}
pub fn start(&mut self) {
self.regs.rirbctl.writef(RIRBDMAEN | RINTCTL, true);
}
pub fn stop(&mut self) {
let mut val = self.regs.rirbctl.read();
val &= !(RIRBDMAEN);
self.regs.rirbctl.write(val);
}
pub fn set_address(&mut self, addr: usize) {
self.regs.rirblbase.write((addr & 0xFFFFFFFF) as u32);
self.regs.rirbubase.write((addr >> 32) as u32);
}
pub fn reset_write_pointer(&mut self) {
self.regs.rirbwp.writef(RIRBWPRST, true);
}
fn read_response(&mut self) -> u64 {
// wait for response
while (self.regs.rirbwp.read() & 0xff) == (self.rirb_rp & 0xff) {}
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;
print!("Rirb: {:08X}\n", res);
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) -> u64 {
// wait for ready
while self.regs.ics.readf(ICB) {}
// 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
while !self.regs.ics.readf(IRV) {}
// 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);
res
}
}
pub struct CommandBuffer {
// regs: &'static mut CommandBufferRegs,
corb: Corb,
rirb: Rirb,
icmd: ImmediateCommand,
corb_rirb_base_phys: usize,
use_immediate_cmd: bool,
}
impl CommandBuffer {
pub fn new(
regs_addr: usize,
cmd_buff_frame_phys: usize,
cmd_buff_frame: usize,
) -> CommandBuffer {
let corb = Corb::new(regs_addr + CORB_OFFSET, cmd_buff_frame_phys, cmd_buff_frame);
let rirb = Rirb::new(
regs_addr + RIRB_OFFSET,
cmd_buff_frame_phys + CORB_BUFF_MAX_SIZE,
cmd_buff_frame + CORB_BUFF_MAX_SIZE,
);
let icmd = ImmediateCommand::new(regs_addr + ICMD_OFFSET);
let cmdbuff = CommandBuffer {
corb: corb,
rirb: rirb,
icmd: icmd,
corb_rirb_base_phys: cmd_buff_frame_phys,
use_immediate_cmd: false,
};
cmdbuff
}
pub fn init(&mut self, use_imm_cmds: bool) {
self.corb.init();
self.rirb.init();
self.set_use_imm_cmds(use_imm_cmds);
}
pub fn cmd12(&mut self, addr: WidgetAddr, command: u32, data: u8) -> 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) -> 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) -> u64 {
if self.use_immediate_cmd {
self.cmd_imm(cmd)
} else {
self.cmd_buff(cmd)
}
}
pub fn cmd_imm(&mut self, cmd: u32) -> u64 {
self.icmd.cmd(cmd)
}
pub fn cmd_buff(&mut self, cmd: u32) -> u64 {
self.corb.send_command(cmd);
self.rirb.read_response()
}
pub fn set_use_imm_cmds(&mut self, use_imm: bool) {
self.use_immediate_cmd = use_imm;
if self.use_immediate_cmd {
self.corb.stop();
self.rirb.stop();
} else {
self.corb.start();
self.rirb.start();
}
}
}
+195
View File
@@ -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()
)
}
}
+944
View File
@@ -0,0 +1,944 @@
#![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_WRITE;
use syscall::error::{Error, EACCES, EBADF, Result, EINVAL};
use syscall::flag::{SEEK_SET, SEEK_CUR, SEEK_END};
use syscall::io::{Mmio, Io};
use syscall::scheme::SchemeBlockMut;
use spin::Mutex;
use super::BufferDescriptorListEntry;
use super::common::*;
use super::StreamDescriptorRegs;
use super::StreamBuffer;
use super::BitsPerSample;
use super::CommandBuffer;
use super::HDANode;
use super::OutputStream;
// GCTL - Global Control
const CRST: u32 = 1 << 0; // 1 bit
const FNCTRL: u32 = 1 << 1; // 1 bit
const UNSOL: u32 = 1 << 8; // 1 bit
// 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
// ICS
const ICB: u16 = 1 << 0;
const IRV: u16 = 1 << 1;
// CORB and RIRB offset
const COMMAND_BUFFER_OFFSET: usize = 0x40;
const NUM_SUB_BUFFS: usize = 2;
const SUB_BUFF_SIZE: usize = 2048;
enum Handle {
Todo,
Pcmout(usize, usize, usize), // Card, index, block_ptr
Pcmin(usize, usize, usize), // Card, index, block_ptr
StrBuf(Vec<u8>,usize),
}
#[repr(packed)]
#[allow(dead_code)]
struct Regs {
gcap: Mmio<u16>,
vmin: Mmio<u8>,
vmaj: Mmio<u8>,
outpay: Mmio<u16>,
inpay: Mmio<u16>,
gctl: Mmio<u32>,
wakeen: Mmio<u16>,
statests: Mmio<u16>,
gsts: Mmio<u16>,
rsvd0: [Mmio<u8>; 6],
outstrmpay: Mmio<u16>,
instrmpay: Mmio<u16>,
rsvd1: [Mmio<u8>; 4],
intctl: Mmio<u32>,
intsts: Mmio<u32>,
rsvd2: [Mmio<u8>; 8],
walclk: Mmio<u32>,
rsvd3: Mmio<u32>,
ssync: Mmio<u32>,
rsvd4: Mmio<u32>,
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>,
icoi: Mmio<u32>,
irii: Mmio<u32>,
ics: Mmio<u16>,
rsvd7: [Mmio<u8>; 6],
dplbase: Mmio<u32>, // 0x70
dpubase: Mmio<u32>, // 0x74
}
pub struct IntelHDA {
vend_prod: u32,
base: usize,
regs: &'static mut Regs,
//corb_rirb_base_phys: usize,
cmd: CommandBuffer,
codecs: Vec<CodecAddr>,
outputs: Vec<WidgetAddr>,
inputs: Vec<WidgetAddr>,
widget_map: HashMap<WidgetAddr, HDANode>,
output_pins: Vec<WidgetAddr>,
input_pins: Vec<WidgetAddr>,
beep_addr: WidgetAddr,
buff_desc: &'static mut [BufferDescriptorListEntry; 256],
buff_desc_phys: usize,
output_streams: Vec<OutputStream>,
buffs: Vec<Vec<StreamBuffer>>,
int_counter: usize,
handles: Mutex<BTreeMap<usize, Handle>>,
next_id: AtomicUsize,
}
impl IntelHDA {
pub unsafe fn new(base: usize, vend_prod:u32) -> Result<Self> {
let regs = &mut *(base as *mut Regs);
let buff_desc_phys = unsafe {
syscall::physalloc(0x1000)
.expect("Could not allocate physical memory for buffer descriptor list.")
};
let buff_desc_virt = unsafe {
syscall::physmap(buff_desc_phys, 0x1000, PHYSMAP_WRITE)
.expect("ihdad: failed to map address for buffer descriptor list.")
};
print!("Virt: {:016X}, Phys: {:016X}\n", buff_desc_virt, buff_desc_phys);
let buff_desc = &mut *(buff_desc_virt as *mut [BufferDescriptorListEntry;256]);
let cmd_buff_address = unsafe {
syscall::physalloc(0x1000)
.expect("Could not allocate physical memory for CORB and RIRB.")
};
let cmd_buff_virt = unsafe { syscall::physmap(cmd_buff_address, 0x1000, PHYSMAP_WRITE).expect("ihdad: failed to map address for CORB/RIRB buff") };
print!("Virt: {:016X}, Phys: {:016X}\n", cmd_buff_virt, cmd_buff_address);
let mut module = IntelHDA {
vend_prod: vend_prod,
base: base,
regs: regs,
cmd: CommandBuffer::new(base + COMMAND_BUFFER_OFFSET, cmd_buff_address, cmd_buff_virt),
beep_addr: (0,0),
widget_map: HashMap::<WidgetAddr, HDANode>::new(),
codecs: Vec::<CodecAddr>::new(),
outputs: Vec::<WidgetAddr>::new(),
inputs: Vec::<WidgetAddr>::new(),
output_pins: Vec::<WidgetAddr>::new(),
input_pins: Vec::<WidgetAddr>::new(),
buff_desc: buff_desc,
buff_desc_phys: buff_desc_phys,
output_streams: Vec::<OutputStream>::new(),
buffs: Vec::<Vec<StreamBuffer>>::new(),
int_counter: 0,
handles: Mutex::new(BTreeMap::new()),
next_id: AtomicUsize::new(0),
};
module.init();
// module.info();
module.enumerate();
module.configure();
print!("IHDA: Initialization finished.\n");
Ok(module)
}
pub fn init(&mut self) -> bool {
self.reset_controller();
let use_immediate_command_interface = match self.vend_prod {
0x8086_2668 => false,
_ => true,
};
self.cmd.init(use_immediate_command_interface);
self.init_interrupts();
true
}
pub fn init_interrupts(&mut self) {
// TODO: provide a function to enable certain interrupts
// This just enables the first output stream interupt and the global interrupt
// TODO: No magic numbers! Bad Schemm.
self.regs.intctl.write((1 << 31) | /* (1 << 30) |*/ (1 << 4));
}
pub fn irq(&mut self) -> bool {
self.int_counter += 1;
self.handle_interrupts()
}
pub fn int_count(&self) -> usize {
self.int_counter
}
pub fn read_node(&mut self, addr: WidgetAddr) -> HDANode {
let mut node = HDANode::new();
let mut temp:u64;
node.addr = addr;
temp = self.cmd.cmd12( addr, 0xF00, 0x04);
node.subnode_count = (temp & 0xff) as u16;
node.subnode_start = ((temp >> 16) & 0xff) as u16;
if addr == (0,0) {
return node;
}
temp = self.cmd.cmd12(addr, 0xF00, 0x04);
node.function_group_type = (temp & 0xff) as u8;
temp = self.cmd.cmd12(addr, 0xF00, 0x09);
node.capabilities = temp as u32;
temp = self.cmd.cmd12(addr, 0xF00, 0x0E);
node.conn_list_len = (temp & 0xFF) as u8;
node.connections = self.node_get_connection_list(&node);
node.config_default = self.cmd.cmd12(addr, 0xF1C, 0x00) as u32;
node
}
pub fn node_get_connection_list(&mut self, node: &HDANode) -> Vec<WidgetAddr> {
let len_field: u8 = (self.cmd.cmd12(node.addr, 0xF00, 0x0E) & 0xFF) as u8;
// Highest bit is if addresses are represented in longer notation
// lower 7 is actual count
let count:u8 = len_field & 0x7F;
let use_long_addr: bool = (len_field >> 7) & 0x1 == 1;
let mut current: u8 = 0;
let mut list = Vec::<WidgetAddr>::new();
while current < count {
let response: u32 = (self.cmd.cmd12(node.addr, 0xF02, current) & 0xFFFFFFFF) as u32;
if use_long_addr {
for i in 0..2 {
let addr_field = ((response >> (16 * i)) & 0xFFFF) as u16;
let addr = addr_field & 0x7FFF;
if addr == 0 { break; }
if (addr_field >> 15) & 0x1 == 0x1 {
for i in list.pop().unwrap().1 .. (addr + 1) {
list.push((node.addr.0, i));
}
} else {
list.push((node.addr.0, addr));
}
}
} else {
for i in 0..4 {
let addr_field = ((response >> (8 * i)) & 0xff) as u16;
let addr = addr_field & 0x7F;
if addr == 0 { break; }
if (addr_field >> 7) & 0x1 == 0x1 {
for i in list.pop().unwrap().1 .. (addr + 1) {
list.push((node.addr.0, i));
}
} else {
list.push((node.addr.0, addr));
}
}
}
current = list.len() as u8;
}
list
}
pub fn enumerate(&mut self) {
self.output_pins.clear();
self.input_pins.clear();
let codec:u8 = 0;
let root = self.read_node((codec,0));
// print!("{}\n", root);
let root_count = root.subnode_count;
let root_start = root.subnode_start;
//FIXME: So basically the way this is set up is to only support one codec and hopes the first one is an audio
for i in 0..root_count {
let afg = self.read_node((codec, root_start + i));
// print!("{}\n", afg);
let afg_count = afg.subnode_count;
let afg_start = afg.subnode_start;
for j in 0..afg_count {
let mut widget = self.read_node((codec, afg_start + j));
widget.is_widget = true;
match widget.widget_type() {
HDAWidgetType::AudioOutput => {self.outputs.push(widget.addr)},
HDAWidgetType::AudioInput => {self.inputs.push(widget.addr)},
HDAWidgetType::BeepGenerator => {self.beep_addr = widget.addr },
HDAWidgetType::PinComplex => {
let config = widget.configuration_default();
if config.is_output() {
self.output_pins.push(widget.addr);
} else if config.is_input() {
self.input_pins.push(widget.addr);
}
print!("{:02X}{:02X} {}\n", widget.addr().0, widget.addr().1, config);
},
_ => {},
}
print!("{}\n", widget);
self.widget_map.insert(widget.addr(), widget);
}
}
}
pub fn find_best_output_pin(&self) -> Option<WidgetAddr>{
let outs = &self.output_pins;
if outs.len() == 0 {
None
} else if outs.len() == 1 {
Some(outs[0])
} else {
// TODO: Somehow find the best.
// Slightly okay is find the speaker with the lowest sequence number.
for &out in outs {
let widget = self.widget_map.get(&out).unwrap();
let cd = widget.configuration_default();
if cd.sequence() == 0 && cd.default_device() == DefaultDevice::Speaker {
return Some(out);
}
}
None
}
}
pub fn find_path_to_dac(&self, addr: WidgetAddr) -> Option<Vec<WidgetAddr>>{
let widget = self.widget_map.get(&addr).unwrap();
if widget.widget_type() == HDAWidgetType::AudioOutput {
return Some(vec![addr]);
}else{
if widget.connections.len() == 0 {
return None;
}else{
// TODO: do more than just first widget
let res = self.find_path_to_dac(widget.connections[0]);
match res {
Some(p) => {
let mut ret = p.clone();
ret.insert(0, addr);
Some(ret)
},
None => {None},
}
}
}
}
/*
Here we update the buffers and split them into 128 byte sub chunks
because each BufferDescriptorList needs to be 128 byte aligned,
this makes it so each of the streams can have up to 128/16 (8) buffer descriptors
*/
/*
Vec of a Vec was doing something weird and causing the driver to hang.
So now we have a set of variables instead.
Fixed?
*/
pub fn update_sound_buffers(&mut self) {
/*
for i in 0..self.buffs.len(){
for j in 0.. min(self.buffs[i].len(), 128/16 ) {
self.buff_desc[i * 128/16 + j].set_address(self.buffs[i][j].phys());
self.buff_desc[i * 128/16 + j].set_length(self.buffs[i][j].length() as u32);
self.buff_desc[i * 128/16 + j].set_interrupt_on_complete(true);
}
}*/
let r = self.get_output_stream_descriptor(0).unwrap();
self.output_streams.push(OutputStream::new(NUM_SUB_BUFFS, SUB_BUFF_SIZE, r));
let o = self.output_streams.get_mut(0).unwrap();
self.buff_desc[0].set_address(o.phys());
self.buff_desc[0].set_length(o.block_size() as u32);
self.buff_desc[0].set_interrupt_on_complete(true);
self.buff_desc[1].set_address(o.phys() + o.block_size());
self.buff_desc[1].set_length(o.block_size() as u32);
self.buff_desc[1].set_interrupt_on_complete(true);
}
pub fn configure(&mut self) {
let outpin = self.find_best_output_pin().expect("IHDA: No output pins?!");
//print!("Best pin: {:01X}:{:02X}\n", outpin.0, outpin.1);
let path = self.find_path_to_dac(outpin).unwrap();
let dac = *path.last().unwrap();
let pin = *path.first().unwrap();
//print!("Path to DAC: {:?}\n", path);
// Pin enable
self.cmd.cmd12(pin, 0x707, 0x40);
// EAPD enable
self.cmd.cmd12(pin, 0x70C, 2);
self.set_stream_channel(dac, 1, 0);
self.update_sound_buffers();
//print!("Supported Formats: {:08X}\n", self.get_supported_formats((0,0x1)));
//print!("Capabilities: {:08X}\n", self.get_capabilities(path[0]));
let output = self.get_output_stream_descriptor(0).unwrap();
output.set_address(self.buff_desc_phys);
output.set_pcm_format(&super::SR_44_1, BitsPerSample::Bits16, 2);
output.set_cyclic_buffer_length(0x8000); // number of bytes
output.set_stream_number(1);
output.set_last_valid_index(1);
output.set_interrupt_on_completion(true);
self.set_power_state(dac, 0); // Power state 0 is fully on
self.set_converter_format(dac, &super::SR_44_1, BitsPerSample::Bits16, 2);
self.cmd.cmd12(dac, 0xA00, 0);
// Unmute and set gain for pin complex and DAC
self.set_amplifier_gain_mute(dac, true, true, true, true, 0, false, 0x7f);
self.set_amplifier_gain_mute(pin, true, true, true, true, 0, false, 0x7f);
output.run();
}
/*
pub fn configure_vbox(&mut self) {
let outpin = self.find_best_output_pin().expect("IHDA: No output pins?!");
print!("Best pin: {:01X}:{:02X}\n", outpin.0, outpin.1);
let path = self.find_path_to_dac(outpin).unwrap();
print!("Path to DAC: {:?}\n", path);
// Pin enable
self.cmd.cmd12((0,0xC), 0x707, 0x40);
// EAPD enable
self.cmd.cmd12((0,0xC), 0x70C, 2);
self.set_stream_channel((0,0x3), 1, 0);
self.update_sound_buffers();
print!("Supported Formats: {:08X}\n", self.get_supported_formats((0,0x1)));
print!("Capabilities: {:08X}\n", self.get_capabilities((0,0x1)));
let output = self.get_output_stream_descriptor(0).unwrap();
output.set_address(self.buff_desc_phys);
output.set_pcm_format(&super::SR_44_1, BitsPerSample::Bits16, 2);
output.set_cyclic_buffer_length(0x8000);
output.set_stream_number(1);
output.set_last_valid_index(1);
output.set_interrupt_on_completion(true);
self.set_power_state((0,0x3), 0); // Power state 0 is fully on
self.set_converter_format((0,0x3), &super::SR_44_1, BitsPerSample::Bits16, 2);
self.cmd.cmd12((0,0x3), 0xA00, 0);
// Unmute and set gain for pin complex and DAC
self.set_amplifier_gain_mute((0,0x3), true, true, true, true, 0, false, 0x7f);
self.set_amplifier_gain_mute((0,0xC), true, true, true, true, 0, false, 0x7f);
output.run();
self.beep(1);
}
*/
// BEEP!!
pub fn beep(&mut self, div:u8) {
let addr = self.beep_addr;
if addr != (0,0) {
let _ = self.cmd.cmd12(addr, 0xF0A, div);
}
}
pub fn read_beep(&mut self) -> u8 {
let addr = self.beep_addr;
if addr != (0,0) {
self.cmd.cmd12(addr, 0x70A, 0) as u8
}else{
0
}
}
pub fn reset_controller(&mut self) -> bool {
self.regs.statests.write(0xFFFF);
// 3.3.7
self.regs.gctl.writef(CRST, false);
loop {
if ! self.regs.gctl.readf(CRST) {
break;
}
}
self.regs.gctl.writef(CRST, true);
loop {
if self.regs.gctl.readf(CRST) {
break;
}
}
let mut ticks:u32 = 0;
while self.regs.statests.read() == 0 {
ticks += 1;
if ticks > 10000 { break;}
}
let statests = self.regs.statests.read();
print!("Statests: {:04X}\n", statests);
for i in 0..15 {
if (statests >> i) & 0x1 == 1 {
self.codecs.push(i as CodecAddr);
}
}
true
}
pub fn num_output_streams(&self) -> usize{
let gcap = self.regs.gcap.read();
((gcap >> 12) & 0xF) as usize
}
pub fn num_input_streams(&self) -> usize{
let gcap = self.regs.gcap.read();
((gcap >> 8) & 0xF) as usize
}
pub fn num_bidirectional_streams(&self) -> usize{
let gcap = self.regs.gcap.read();
((gcap >> 3) & 0xF) as usize
}
pub fn num_serial_data_out(&self) -> usize{
let gcap = self.regs.gcap.read();
((gcap >> 1) & 0x3) as usize
}
pub fn info(&self) {
print!("Intel HD Audio Version {}.{}\n", self.regs.vmaj.read(), self.regs.vmin.read());
print!("IHDA: Input Streams: {}\n", self.num_input_streams());
print!("IHDA: Output Streams: {}\n", self.num_output_streams());
print!("IHDA: Bidirectional Streams: {}\n", self.num_bidirectional_streams());
print!("IHDA: Serial Data Outputs: {}\n", self.num_serial_data_out());
print!("IHDA: 64-Bit: {}\n", self.regs.gcap.read() & 1 == 1);
}
fn get_input_stream_descriptor(&self, index: usize) -> Option<&'static mut StreamDescriptorRegs> {
unsafe {
if index < self.num_input_streams() {
Some(&mut *((self.base + 0x80 + index * 0x20) as *mut StreamDescriptorRegs))
}else{
None
}
}
}
fn get_output_stream_descriptor(&self, index: usize) -> Option<&'static mut StreamDescriptorRegs> {
unsafe {
if index < self.num_output_streams() {
Some(&mut *((self.base + 0x80 +
self.num_input_streams() * 0x20 +
index * 0x20) as *mut StreamDescriptorRegs))
}else{
None
}
}
}
fn get_bidirectional_stream_descriptor(&self, index: usize) -> Option<&'static mut StreamDescriptorRegs> {
unsafe {
if index < self.num_bidirectional_streams() {
Some(&mut *((self.base + 0x80 +
self.num_input_streams() * 0x20 +
self.num_output_streams() * 0x20 +
index * 0x20) as *mut StreamDescriptorRegs))
}else{
None
}
}
}
fn set_dma_position_buff_addr(&mut self, addr: usize) {
let addr_val = addr & !0x7F;
self.regs.dplbase.write((addr_val & 0xFFFFFFFF) as u32);
self.regs.dpubase.write((addr_val >> 32) as u32);
}
fn set_stream_channel(&mut self, addr: WidgetAddr, stream: u8, channel:u8) {
let val = ((stream & 0xF) << 4) | (channel & 0xF);
self.cmd.cmd12(addr, 0x706, val);
}
fn set_power_state(&mut self, addr:WidgetAddr, state:u8) {
self.cmd.cmd12(addr, 0x705, state & 0xF) as u32;
}
fn get_supported_formats(&mut self, addr: WidgetAddr) -> u32 {
self.cmd.cmd12(addr, 0xF00, 0x0A) as u32
}
fn get_capabilities(&mut self, addr: WidgetAddr) -> u32 {
self.cmd.cmd12(addr, 0xF00, 0x09) as u32
}
fn set_converter_format(&mut self, addr:WidgetAddr, sr: &super::SampleRate, bps: BitsPerSample, channels:u8) {
let fmt = super::format_to_u16(sr, bps, channels);
self.cmd.cmd4(addr, 0x2, fmt);
}
fn set_amplifier_gain_mute(&mut self, addr: WidgetAddr, output:bool, input:bool, left:bool, right:bool, index:u8, mute:bool, gain: u8) {
let mut payload: u16 = 0;
if output { payload |= 1 << 15; }
if input { payload |= 1 << 14; }
if left { payload |= 1 << 13; }
if right { payload |= 1 << 12; }
if mute { payload |= 1 << 7; }
payload |= ((index as u16) & 0x0F) << 8;
payload |= (gain as u16) & 0x7F;
self.cmd.cmd4(addr, 0x3, payload);
}
pub fn write_to_output(&mut self, index:u8, buf: &[u8]) -> Result<Option<usize>> {
let output = self.get_output_stream_descriptor(index as usize).unwrap();
let os = self.output_streams.get_mut(index as usize).unwrap();
//let sample_size:usize = output.sample_size();
let mut open_block = (output.link_position() as usize) / os.block_size();
if open_block == 0 {
open_block = 1;
} else {
open_block = open_block - 1;
}
//print!("Status: {:02X} Pos: {:08X} Output CTL: {:06X}\n", output.status(), output.link_position(), output.control());
if open_block == os.current_block() {
Ok(None)
} else {
os.write_block(buf).map(|count| Some(count))
}
}
pub fn handle_interrupts(&mut self) -> bool {
let intsts = self.regs.intsts.read();
let sis = intsts & 0x3FFFFFFF;
//print!("IHDA INTSTS: {:08X}\n", intsts);
if ((intsts >> 31) & 1) == 1 { // Global Interrupt Status
if ((intsts >> 30) & 1) == 1 { // Controller Interrupt Status
self.handle_controller_interrupt();
}
if sis != 0 {
self.handle_stream_interrupts(sis);
}
}
intsts != 0
}
pub fn handle_controller_interrupt(&mut self) {
}
pub fn handle_stream_interrupts(&mut self, sis: u32) {
let oss = self.num_output_streams();
let iss = self.num_input_streams();
let bss = self.num_bidirectional_streams();
for i in 0..iss {
if ((sis >> i) & 1 ) == 1 {
let mut input = self.get_input_stream_descriptor(i).unwrap();
input.clear_interrupts();
}
}
for i in 0..oss {
if ((sis >> (i + iss)) & 1 ) == 1 {
let mut output = self.get_output_stream_descriptor(i).unwrap();
output.clear_interrupts();
}
}
for i in 0..bss {
if ((sis >> (i + iss + oss)) & 1 ) == 1 {
let mut bid = self.get_bidirectional_stream_descriptor(i).unwrap();
bid.clear_interrupts();
}
}
}
fn validate_path(&mut self, path: &Vec<&str>) -> bool {
print!("Path: {:?}\n", path);
let mut it = path.iter();
match it.next() {
Some(card_str) if (*card_str).starts_with("card") => {
match usize::from_str_radix(&(*card_str)[4..], 10) {
Ok(card_num) => {
print!("Card# {}\n", card_num);
match it.next() {
Some(codec_str) if (*codec_str).starts_with("codec#") => {
match usize::from_str_radix(&(*codec_str)[6..], 10) {
Ok(_codec_num) => {
//let id = self.next_id.fetch_add(1, Ordering::SeqCst);
//self.handles.lock().insert(id, Handle::Disk(disk.clone(), 0));
true
},
_ => false,
}
},
Some(pcmout_str) if (*pcmout_str).starts_with("pcmout") => {
match usize::from_str_radix(&(*pcmout_str)[6..], 10) {
Ok(pcmout_num) => {
print!("pcmout {}\n", pcmout_num);
true
},
_ => false,
}
},
Some(pcmin_str) if (*pcmin_str).starts_with("pcmin") => {
match usize::from_str_radix(&(*pcmin_str)[6..], 10) {
Ok(pcmin_num) => {
print!("pcmin {}\n", pcmin_num);
true
},
_ => false,
}
},
_ => false,
}
},
_ => false,
}
},
Some(cards_str) if *cards_str == "cards" => {
true
},
_ => false,
}
}
}
impl Drop for IntelHDA {
fn drop(&mut self) {
print!("IHDA: Deallocating IHDA driver.\n");
}
}
impl SchemeBlockMut for IntelHDA {
fn open(&mut self, _path: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result<Option<usize>> {
//let path: Vec<&str>;
/*
match str::from_utf8(_path) {
Ok(p) => {
path = p.split("/").collect();
if !self.validate_path(&path) {
return Err(Error::new(EINVAL));
},
Err(_) => {return Err(Error::new(EINVAL));},
}*/
// TODO:
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<Option<usize>> {
let index = {
let mut handles = self.handles.lock();
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
0
};
//print!("Int count: {}\n", self.int_counter);
self.write_to_output(index, buf)
}
fn seek(&mut self, id: usize, pos: usize, whence: usize) -> Result<Option<usize>> {
let mut handles = self.handles.lock();
match *handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::StrBuf(ref mut strbuf, ref mut size) => {
let len = strbuf.len() as usize;
*size = match whence {
SEEK_SET => cmp::min(len, pos),
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
_ => return Err(Error::new(EINVAL))
};
Ok(Some(*size))
},
_ => Err(Error::new(EINVAL)),
}
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
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"hda:";
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<Option<usize>> {
let mut handles = self.handles.lock();
handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(Some(0)))
}
}
+17
View File
@@ -0,0 +1,17 @@
#![allow(dead_code)]
pub mod device;
pub mod stream;
pub mod common;
pub mod node;
pub mod cmdbuff;
pub use self::stream::*;
pub use self::node::*;
pub use self::cmdbuff::*;
pub use self::stream::StreamDescriptorRegs;
pub use self::stream::BufferDescriptorListEntry;
pub use self::stream::BitsPerSample;
pub use self::stream::StreamBuffer;
pub use self::device::IntelHDA;
+104
View File
@@ -0,0 +1,104 @@
use std::{mem, fmt};
use super::common::*;
#[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,
// 0xC
pub pin_caps: u32,
// 0xD
pub in_amp: u32,
// 0xE
pub conn_list_len: u8,
// 0x12
pub out_amp: u32,
// 0x13
pub vol_knob: u8,
pub connections: Vec<WidgetAddr>,
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,
pin_caps: 0,
in_amp: 0,
out_amp: 0,
vol_knob: 0,
conn_list_len: 0,
config_default: 0,
is_widget: false,
connections: Vec::<WidgetAddr>::new(),
}
}
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.conn_list_len,
self.connections
),
_ => write!(f, "Addr: {:02X}:{:02X}, Type: {:?}, Inputs: {:X}: {:?}.", self.addr.0, self.addr.1, self.widget_type(), 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)
}
}
}
+380
View File
@@ -0,0 +1,380 @@
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);
}
}
}
}