ihdad: cleanup, fix blocking code, decrease buffer size to decrease latency
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
Executable
+944
@@ -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)))
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user