use std::io::Error as IoError;
use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;
use anyhow::Context;
use base::error;
use base::warn;
use base::Descriptor;
use base::Error as SysError;
use base::EventToken;
use base::WaitContext;
use bit_field::BitField1;
use bit_field::*;
use hypervisor::PitChannelState;
use hypervisor::PitRWMode;
use hypervisor::PitRWState;
use hypervisor::PitState;
use remain::sorted;
use sync::Mutex;
use thiserror::Error;
cfg_if::cfg_if! {
if #[cfg(test)] {
use base::FakeClock as Clock;
use base::FakeTimer as Timer;
} else {
use base::Clock;
use base::Timer;
}
}
use base::TimerTrait;
use base::WorkerThread;
use crate::bus::BusAccessInfo;
use crate::pci::CrosvmDeviceId;
use crate::BusDevice;
use crate::DeviceId;
use crate::IrqEdgeEvent;
use crate::Suspendable;
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
enum CommandBit {
CommandBCD = 0x01, CommandMode = 0x0e, CommandRW = 0x30, CommandSC = 0xc0, }
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
enum CommandCounter {
CommandCounter0 = 0x00, CommandCounter1 = 0x40, CommandCounter2 = 0x80, CommandReadBack = 0xc0, }
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
enum CommandAccess {
CommandLatch = 0x00, CommandRWLeast = 0x10, CommandRWMost = 0x20, CommandRWBoth = 0x30, }
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
enum CommandMode {
CommandInterrupt = 0x00, CommandHWOneShot = 0x02, CommandRateGen = 0x04, CommandSquareWaveGen = 0x06, CommandSWStrobe = 0x08, CommandHWStrobe = 0x0a, }
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
#[rustfmt::skip] enum CommandReadBackLatch {
CommandRBLatchBits = 0x30, CommandRBLatchBoth = 0x00, CommandRBLatchCount = 0x10, CommandRBLatchStatus = 0x20, }
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
enum CommandReadBackCounters {
CommandRBCounter2 = 0x08,
CommandRBCounter1 = 0x04,
CommandRBCounter0 = 0x02,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
#[rustfmt::skip] enum ReadBackData {
ReadBackOutput = 0x80, ReadBackNullCount = 0x40, }
#[derive(Debug, Clone, Copy, PartialEq, Eq, enumn::N)]
enum PortIOSpace {
PortCounter0Data = 0x40, PortCounter1Data = 0x41, PortCounter2Data = 0x42, PortCommand = 0x43, PortSpeaker = 0x61, }
#[bitfield]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct SpeakerPortFields {
gate: BitField1,
speaker_on: BitField1,
pic_serr: BitField1,
iochk_enable: BitField1,
refresh_clock: BitField1,
output: BitField1,
iochk_nmi: BitField1,
serr_nmi: BitField1,
}
const FREQUENCY_HZ: u64 = 1193182;
const NUM_OF_COUNTERS: usize = 3;
const NANOS_PER_SEC: u64 = 1_000_000_000;
const MAX_TIMER_FREQ: u32 = 65536;
#[derive(EventToken)]
enum Token {
TimerExpire,
Kill,
}
#[sorted]
#[derive(Error, Debug)]
pub enum PitError {
#[error("failed to clone event: {0}")]
CloneEvent(SysError),
#[error("failed to create event: {0}")]
CreateEvent(SysError),
#[error("failed to create poll context: {0}")]
CreateWaitContext(SysError),
#[error("failed to spawn thread: {0}")]
SpawnThread(IoError),
#[error("failed to create pit counter due to timer fd: {0}")]
TimerCreateError(SysError),
#[error("failed to wait for events: {0}")]
WaitError(SysError),
}
type PitResult<T> = std::result::Result<T, PitError>;
pub struct Pit {
counters: Vec<Arc<Mutex<PitCounter>>>,
worker_thread: Option<WorkerThread<PitResult<()>>>,
activated: bool,
}
impl BusDevice for Pit {
fn debug_label(&self) -> String {
"userspace PIT".to_string()
}
fn device_id(&self) -> DeviceId {
CrosvmDeviceId::Pit.into()
}
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
self.ensure_started();
if data.len() != 1 {
warn!("Bad write size for Pit: {}", data.len());
return;
}
match PortIOSpace::n(info.address as i64) {
Some(PortIOSpace::PortCounter0Data) => self.counters[0].lock().write_counter(data[0]),
Some(PortIOSpace::PortCounter1Data) => self.counters[1].lock().write_counter(data[0]),
Some(PortIOSpace::PortCounter2Data) => self.counters[2].lock().write_counter(data[0]),
Some(PortIOSpace::PortCommand) => self.command_write(data[0]),
Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().write_speaker(data[0]),
None => warn!("PIT: bad write to {}", info),
}
}
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
self.ensure_started();
if data.len() != 1 {
warn!("Bad read size for Pit: {}", data.len());
return;
}
data[0] = match PortIOSpace::n(info.address as i64) {
Some(PortIOSpace::PortCounter0Data) => self.counters[0].lock().read_counter(),
Some(PortIOSpace::PortCounter1Data) => self.counters[1].lock().read_counter(),
Some(PortIOSpace::PortCounter2Data) => self.counters[2].lock().read_counter(),
Some(PortIOSpace::PortCommand) => {
warn!("Ignoring read to command reg");
0
}
Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().read_speaker(),
None => {
warn!("PIT: bad read from {}", info);
return;
}
};
}
}
impl Pit {
pub fn new(interrupt_evt: IrqEdgeEvent, clock: Arc<Mutex<Clock>>) -> PitResult<Pit> {
let mut counters = Vec::new();
let mut interrupt = Some(interrupt_evt);
for i in 0..NUM_OF_COUNTERS {
let pit_counter = PitCounter::new(i, interrupt, clock.clone())?;
counters.push(Arc::new(Mutex::new(pit_counter)));
interrupt = None;
}
assert_eq!(counters.len(), NUM_OF_COUNTERS);
Ok(Pit {
counters,
worker_thread: None,
activated: false,
})
}
fn ensure_started(&mut self) {
if self.worker_thread.is_some() {
return;
}
if let Err(e) = self.start() {
error!("failed to start PIT: {}", e);
}
}
fn start(&mut self) -> PitResult<()> {
let pit_counter = self.counters[0].clone();
self.worker_thread = Some(WorkerThread::start("pit counter worker", move |kill_evt| {
let timer_descriptor = Descriptor(pit_counter.lock().timer.as_raw_descriptor());
let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
(&timer_descriptor, Token::TimerExpire),
(&kill_evt, Token::Kill),
])
.map_err(PitError::CreateWaitContext)?;
let mut worker = Worker {
pit_counter,
wait_ctx,
};
worker.run()
}));
self.activated = true;
Ok(())
}
fn command_write(&mut self, control_word: u8) {
let command: u16 = (control_word & CommandBit::CommandSC as u8).into();
let counter_index: usize = (command >> 6).into();
if command == (CommandCounter::CommandReadBack as u16) {
if (control_word & (CommandReadBackCounters::CommandRBCounter0 as u8)) != 0 {
self.counters[0].lock().read_back_command(control_word);
}
if (control_word & (CommandReadBackCounters::CommandRBCounter1 as u8)) != 0 {
self.counters[1].lock().read_back_command(control_word);
}
if (control_word & (CommandReadBackCounters::CommandRBCounter2 as u8)) != 0 {
self.counters[2].lock().read_back_command(control_word);
}
} else if (control_word & (CommandBit::CommandRW as u8))
== (CommandAccess::CommandLatch as u8)
{
self.counters[counter_index].lock().latch_counter();
} else {
self.counters[counter_index]
.lock()
.store_command(control_word);
}
}
pub fn get_pit_state(&self) -> PitState {
PitState {
channels: [
self.counters[0].lock().get_channel_state(),
self.counters[1].lock().get_channel_state(),
self.counters[2].lock().get_channel_state(),
],
flags: 0,
}
}
pub fn set_pit_state(&mut self, state: &PitState) {
self.counters[0]
.lock()
.set_channel_state(&state.channels[0]);
self.counters[1]
.lock()
.set_channel_state(&state.channels[1]);
self.counters[2]
.lock()
.set_channel_state(&state.channels[2]);
}
}
impl Suspendable for Pit {
fn sleep(&mut self) -> anyhow::Result<()> {
if let Some(thread) = self.worker_thread.take() {
thread
.stop()
.context("pit worker thread exited with error")?;
}
Ok(())
}
fn wake(&mut self) -> anyhow::Result<()> {
if self.activated {
if let Err(e) = self.start() {
error!("failed to start PIT: {}", e);
}
}
Ok(())
}
fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
Ok(serde_json::Value::Null)
}
fn restore(&mut self, _data: serde_json::Value) -> anyhow::Result<()> {
Ok(())
}
}
struct PitCounter {
interrupt_evt: Option<IrqEdgeEvent>,
reload_value: u16,
latched_value: u16,
command: u8,
status: u8,
start: Option<Instant>,
clock: Arc<Mutex<Clock>>,
creation_time: Instant,
counter_id: usize,
wrote_low_byte: bool,
read_low_byte: bool,
latched: bool,
status_latched: bool,
gate: bool,
speaker_on: bool,
count: u32,
timer_valid: bool,
timer: Box<dyn TimerTrait>,
}
impl Drop for PitCounter {
fn drop(&mut self) {
if self.timer_valid {
self.timer.clear().unwrap();
}
}
}
fn adjust_count(count: u32) -> u32 {
if count == 0 {
MAX_TIMER_FREQ
} else {
count
}
}
impl PitCounter {
fn new(
counter_id: usize,
interrupt_evt: Option<IrqEdgeEvent>,
clock: Arc<Mutex<Clock>>,
) -> PitResult<PitCounter> {
#[cfg(not(test))]
let timer = Timer::new().map_err(PitError::TimerCreateError)?;
#[cfg(test)]
let timer = Timer::new(clock.clone());
Ok(PitCounter {
interrupt_evt,
reload_value: 0,
latched_value: 0,
command: 0,
status: 0,
start: None,
clock: clock.clone(),
creation_time: clock.lock().now(),
counter_id,
wrote_low_byte: false,
read_low_byte: false,
latched: false,
status_latched: false,
gate: false,
speaker_on: false,
count: MAX_TIMER_FREQ,
timer_valid: false,
timer: Box::new(timer),
})
}
fn get_channel_state(&self) -> PitChannelState {
let load_time = match &self.start {
Some(t) => t.saturating_duration_since(self.creation_time).as_nanos() as u64,
None => 0,
};
let mut state = PitChannelState {
count: self.count,
latched_count: self.latched_value,
status_latched: self.status_latched,
status: self.status,
reload_value: self.reload_value,
mode: (self.command & CommandBit::CommandMode as u8) >> 1,
bcd: false,
gate: self.gate,
count_load_time: load_time,
rw_mode: PitRWMode::None,
read_state: PitRWState::None,
write_state: PitRWState::None,
count_latched: PitRWState::None,
};
match self.get_access_mode() {
Some(CommandAccess::CommandRWLeast) => {
state.rw_mode = PitRWMode::Least;
state.read_state = PitRWState::LSB;
state.write_state = PitRWState::LSB;
}
Some(CommandAccess::CommandRWMost) => {
state.rw_mode = PitRWMode::Most;
state.read_state = PitRWState::MSB;
state.write_state = PitRWState::MSB;
}
Some(CommandAccess::CommandRWBoth) => {
state.rw_mode = PitRWMode::Both;
state.read_state = if self.read_low_byte {
PitRWState::Word1
} else {
PitRWState::Word0
};
state.write_state = if self.wrote_low_byte {
PitRWState::Word1
} else {
PitRWState::Word0
};
}
_ => {}
};
if self.latched {
state.count_latched = state.read_state;
}
state
}
fn set_channel_state(&mut self, state: &PitChannelState) {
self.count = state.count;
self.latched_value = state.latched_count;
self.status_latched = state.status_latched;
self.status = state.status;
self.reload_value = state.reload_value;
self.command = (state.mode << 1) | ((state.rw_mode as u8) << 4);
self.gate = state.gate;
self.latched = state.count_latched != PitRWState::None;
self.read_low_byte = state.read_state == PitRWState::Word1;
self.wrote_low_byte = state.write_state == PitRWState::Word1;
self.start = self
.creation_time
.checked_add(Duration::from_nanos(state.count_load_time));
}
fn get_access_mode(&self) -> Option<CommandAccess> {
CommandAccess::n(self.command & (CommandBit::CommandRW as u8))
}
fn get_command_mode(&self) -> Option<CommandMode> {
CommandMode::n(self.command & CommandBit::CommandMode as u8)
}
fn read_counter(&mut self) -> u8 {
if self.status_latched {
self.status_latched = false;
return self.status;
};
let data_value: u16 = if self.latched {
self.latched_value
} else {
self.get_read_value()
};
let access_mode = self.get_access_mode();
match (access_mode, self.read_low_byte) {
(Some(CommandAccess::CommandRWLeast), _) => {
self.latched = false; (data_value & 0xff) as u8
}
(Some(CommandAccess::CommandRWBoth), false) => {
self.read_low_byte = true;
(data_value & 0xff) as u8
}
(Some(CommandAccess::CommandRWBoth), true)
| (Some(CommandAccess::CommandRWMost), _) => {
self.read_low_byte = false; self.latched = false;
(data_value >> 8) as u8
}
(_, _) => 0, }
}
fn write_counter(&mut self, written_datum: u8) {
let access_mode = self.get_access_mode();
let datum: u16 = written_datum.into();
let mut should_start_timer = true;
self.reload_value = match access_mode {
Some(CommandAccess::CommandRWLeast) => datum,
Some(CommandAccess::CommandRWMost) => datum << 8,
Some(CommandAccess::CommandRWBoth) => {
if self.wrote_low_byte {
self.wrote_low_byte = false;
self.reload_value | (datum << 8)
} else {
self.wrote_low_byte = true;
should_start_timer = false; datum
}
}
_ => {
should_start_timer = false;
self.reload_value
}
};
if should_start_timer {
let reload: u32 = self.reload_value.into();
self.load_and_start_timer(reload);
}
}
fn get_output(&self) -> bool {
let ticks_passed = self.get_ticks_passed();
let count: u64 = self.count.into();
match self.get_command_mode() {
Some(CommandMode::CommandInterrupt) => ticks_passed >= count,
Some(CommandMode::CommandHWOneShot) => ticks_passed < count,
Some(CommandMode::CommandRateGen) => ticks_passed != 0 && ticks_passed % count == 0,
Some(CommandMode::CommandSquareWaveGen) => ticks_passed < (count + 1) / 2,
Some(CommandMode::CommandSWStrobe) | Some(CommandMode::CommandHWStrobe) => {
ticks_passed == count
}
None => {
warn!("Invalid command mode based on command: {:#x}", self.command);
false
}
}
}
fn read_speaker(&self) -> u8 {
let us = self
.clock
.lock()
.now()
.duration_since(self.creation_time)
.subsec_micros();
let refresh_clock = us % 15 == 0;
let mut speaker = SpeakerPortFields::new();
speaker.set_gate(self.gate.into());
speaker.set_speaker_on(self.speaker_on.into());
speaker.set_iochk_enable(0);
speaker.set_refresh_clock(refresh_clock.into());
speaker.set_output(self.get_output().into());
speaker.set_iochk_nmi(0);
speaker.set_serr_nmi(0);
speaker.get(0, 8) as u8
}
fn write_speaker(&mut self, datum: u8) {
let mut speaker = SpeakerPortFields::new();
speaker.set(0, 8, datum.into());
let new_gate = speaker.get_gate() != 0;
match self.get_command_mode() {
Some(CommandMode::CommandInterrupt) | Some(CommandMode::CommandSWStrobe) => (),
Some(_) => {
if new_gate && !self.gate {
self.start = Some(self.clock.lock().now());
}
}
None => {
warn!("Invalid command mode based on command {:#x}", self.command);
return;
}
}
self.speaker_on = speaker.get_speaker_on() != 0;
self.gate = new_gate;
}
fn load_and_start_timer(&mut self, initial_count: u32) {
self.count = adjust_count(initial_count);
self.start_timer();
}
fn start_timer(&mut self) {
self.start = Some(self.clock.lock().now());
if self.counter_id != 0 {
return;
}
let timer_len = Duration::from_nanos(u64::from(self.count) * NANOS_PER_SEC / FREQUENCY_HZ);
let safe_timer_len = if timer_len == Duration::new(0, 0) {
Duration::from_nanos(1)
} else {
timer_len
};
match self.get_command_mode() {
Some(CommandMode::CommandInterrupt)
| Some(CommandMode::CommandHWOneShot)
| Some(CommandMode::CommandSWStrobe)
| Some(CommandMode::CommandHWStrobe) => {
if let Err(e) = self.timer.reset_oneshot(safe_timer_len) {
error!("failed to reset oneshot timer: {}", e);
}
}
Some(CommandMode::CommandRateGen) | Some(CommandMode::CommandSquareWaveGen) => {
if let Err(e) = self.timer.reset_repeating(safe_timer_len) {
error!("failed to reset repeating timer: {}", e);
}
}
None => {
warn!("Invalid command mode based on command {:#x}", self.command);
return;
}
}
self.timer_valid = true;
}
fn read_back_command(&mut self, control_word: u8) {
let latch_cmd =
CommandReadBackLatch::n(control_word & CommandReadBackLatch::CommandRBLatchBits as u8);
match latch_cmd {
Some(CommandReadBackLatch::CommandRBLatchCount) => {
self.latch_counter();
}
Some(CommandReadBackLatch::CommandRBLatchStatus) => {
self.latch_status();
}
_ => warn!(
"Unexpected ReadBackLatch. control_word: {:#x}",
control_word
),
};
}
fn latch_counter(&mut self) {
if self.latched {
return;
}
self.latched_value = self.get_read_value();
self.latched = true;
self.read_low_byte = false;
}
fn latch_status(&mut self) {
self.status = self.command
& (CommandBit::CommandRW as u8
| CommandBit::CommandMode as u8
| CommandBit::CommandBCD as u8);
if self.start.is_none() {
self.status |= ReadBackData::ReadBackNullCount as u8;
}
if self.get_output() {
self.status |= ReadBackData::ReadBackOutput as u8;
}
self.status_latched = true;
}
fn store_command(&mut self, datum: u8) {
self.command = datum;
self.latched = false;
if self.timer_valid {
self.start = None;
self.timer_valid = false;
self.timer.clear().unwrap();
}
self.wrote_low_byte = false;
self.read_low_byte = false;
}
fn timer_handler(&mut self) {
if let Err(e) = self.timer.mark_waited() {
error!("pit: timer wait unexpectedly failed: {}", e);
return;
}
let mode = self.get_command_mode();
if mode == Some(CommandMode::CommandRateGen)
|| mode == Some(CommandMode::CommandSquareWaveGen)
{
self.start = Some(self.clock.lock().now());
}
if let Some(interrupt) = &mut self.interrupt_evt {
interrupt.trigger().unwrap();
}
}
fn get_ticks_passed(&self) -> u64 {
match self.start {
None => 0,
Some(t) => {
let dur = self.clock.lock().now().duration_since(t);
let dur_ns: u64 = dur.as_secs() * NANOS_PER_SEC + u64::from(dur.subsec_nanos());
dur_ns * FREQUENCY_HZ / NANOS_PER_SEC
}
}
}
fn get_read_value(&self) -> u16 {
match self.start {
None => 0,
Some(_) => {
let count: u64 = adjust_count(self.reload_value.into()).into();
let ticks_passed = self.get_ticks_passed();
match self.get_command_mode() {
Some(CommandMode::CommandInterrupt)
| Some(CommandMode::CommandHWOneShot)
| Some(CommandMode::CommandSWStrobe)
| Some(CommandMode::CommandHWStrobe) => {
if ticks_passed > count {
0
} else {
((count - ticks_passed) & 0xFFFF) as u16
}
}
Some(CommandMode::CommandRateGen) => (count - (ticks_passed % count)) as u16,
Some(CommandMode::CommandSquareWaveGen) => {
(count - ((ticks_passed * 2) % count)) as u16
}
None => {
warn!("Invalid command mode: command = {:#x}", self.command);
0
}
}
}
}
}
}
struct Worker {
pit_counter: Arc<Mutex<PitCounter>>,
wait_ctx: WaitContext<Token>,
}
impl Worker {
fn run(&mut self) -> PitResult<()> {
loop {
let events = self.wait_ctx.wait().map_err(PitError::WaitError)?;
for event in events.iter().filter(|e| e.is_readable) {
match event.token {
Token::TimerExpire => {
let mut pit = self.pit_counter.lock();
pit.timer_handler();
}
Token::Kill => return Ok(()),
}
}
}
}
}
#[cfg(test)]
mod tests {
use base::Event;
use super::*;
struct TestData {
pit: Pit,
irqfd: Event,
clock: Arc<Mutex<Clock>>,
}
fn pit_bus_address(address: PortIOSpace) -> BusAccessInfo {
let offset = match address as u64 {
x if x >= PortIOSpace::PortCounter0Data as u64
&& x < PortIOSpace::PortCounter0Data as u64 + 0x8 =>
{
address as u64 - PortIOSpace::PortCounter0Data as u64
}
x if x == PortIOSpace::PortSpeaker as u64 => 0,
_ => panic!("invalid PIT address: {:#x}", address as u64),
};
BusAccessInfo {
offset,
address: address as u64,
id: 0,
}
}
fn write_command(pit: &mut Pit, command: u8) {
pit.write(pit_bus_address(PortIOSpace::PortCommand), &[command])
}
fn write_speaker(pit: &mut Pit, command: u8) {
pit.write(pit_bus_address(PortIOSpace::PortSpeaker), &[command])
}
fn write_counter(pit: &mut Pit, counter_idx: usize, data: u16, access_mode: CommandAccess) {
let port = match counter_idx {
0 => PortIOSpace::PortCounter0Data,
1 => PortIOSpace::PortCounter1Data,
2 => PortIOSpace::PortCounter2Data,
_ => panic!("Invalid counter_idx: {}", counter_idx),
};
if access_mode == CommandAccess::CommandRWLeast
|| access_mode == CommandAccess::CommandRWBoth
{
pit.write(pit_bus_address(port), &[(data & 0xff) as u8]);
}
if access_mode == CommandAccess::CommandRWMost
|| access_mode == CommandAccess::CommandRWBoth
{
pit.write(pit_bus_address(port), &[(data >> 8) as u8]);
}
}
fn read_counter(pit: &mut Pit, counter_idx: usize, expected: u16, access_mode: CommandAccess) {
let port = match counter_idx {
0 => PortIOSpace::PortCounter0Data,
1 => PortIOSpace::PortCounter1Data,
2 => PortIOSpace::PortCounter2Data,
_ => panic!("Invalid counter_idx: {}", counter_idx),
};
let mut result: u16 = 0;
if access_mode == CommandAccess::CommandRWLeast
|| access_mode == CommandAccess::CommandRWBoth
{
let mut buffer = [0];
pit.read(pit_bus_address(port), &mut buffer);
result = buffer[0].into();
}
if access_mode == CommandAccess::CommandRWMost
|| access_mode == CommandAccess::CommandRWBoth
{
let mut buffer = [0];
pit.read(pit_bus_address(port), &mut buffer);
result |= u16::from(buffer[0]) << 8;
}
assert_eq!(result, expected);
}
fn set_up() -> TestData {
let evt = IrqEdgeEvent::new().unwrap();
let clock = Arc::new(Mutex::new(Clock::new()));
TestData {
irqfd: evt.get_trigger().try_clone().unwrap(),
pit: Pit::new(evt, clock.clone()).unwrap(),
clock,
}
}
fn advance_by_tick(data: &mut TestData) {
advance_by_ticks(data, 1);
}
fn advance_by_ticks(data: &mut TestData, ticks: u64) {
println!(
"Advancing by {:#x} ticks ({} ns)",
ticks,
(NANOS_PER_SEC * ticks) / FREQUENCY_HZ + 1
);
let mut lock = data.clock.lock();
lock.add_ns((NANOS_PER_SEC * ticks) / FREQUENCY_HZ + 1);
}
#[test]
fn write_and_latch() {
let mut data = set_up();
let both_interrupt =
CommandAccess::CommandRWBoth as u8 | CommandMode::CommandInterrupt as u8;
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8 | both_interrupt,
);
write_counter(&mut data.pit, 0, 24, CommandAccess::CommandRWBoth);
advance_by_tick(&mut data);
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
);
advance_by_tick(&mut data);
read_counter(&mut data.pit, 0, 23, CommandAccess::CommandRWBoth);
write_command(
&mut data.pit,
CommandCounter::CommandCounter1 as u8 | both_interrupt,
);
write_counter(&mut data.pit, 1, 314, CommandAccess::CommandRWBoth);
advance_by_tick(&mut data);
write_command(
&mut data.pit,
CommandCounter::CommandCounter1 as u8 | CommandAccess::CommandLatch as u8,
);
advance_by_tick(&mut data);
read_counter(&mut data.pit, 1, 313, CommandAccess::CommandRWBoth);
write_command(
&mut data.pit,
CommandCounter::CommandCounter2 as u8 | both_interrupt,
);
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
advance_by_tick(&mut data);
write_command(
&mut data.pit,
CommandCounter::CommandCounter2 as u8 | CommandAccess::CommandLatch as u8,
);
advance_by_tick(&mut data);
read_counter(&mut data.pit, 2, 0xfffe, CommandAccess::CommandRWBoth);
}
#[test]
fn write_and_read_least() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWLeast as u8
| CommandMode::CommandInterrupt as u8,
);
write_counter(&mut data.pit, 0, 0x3424, CommandAccess::CommandRWLeast);
read_counter(&mut data.pit, 0, 0x0024, CommandAccess::CommandRWLeast);
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
);
advance_by_tick(&mut data);
read_counter(&mut data.pit, 0, 0x0024, CommandAccess::CommandRWLeast);
}
#[test]
fn write_and_read_most() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWMost as u8
| CommandMode::CommandInterrupt as u8,
);
write_counter(&mut data.pit, 0, 0x3424, CommandAccess::CommandRWMost);
read_counter(&mut data.pit, 0, 0x3400, CommandAccess::CommandRWMost);
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
);
advance_by_tick(&mut data);
read_counter(&mut data.pit, 0, 0x3400, CommandAccess::CommandRWMost);
}
#[test]
fn read_command() {
let mut data = set_up();
let mut buf = [0];
data.pit
.read(pit_bus_address(PortIOSpace::PortCommand), &mut buf);
assert_eq!(buf, [0]);
}
#[test]
fn test_timed_latch() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWBoth as u8
| CommandMode::CommandInterrupt as u8,
);
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
);
data.clock.lock().add_ns(25_000_000);
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
);
read_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8 | CommandAccess::CommandLatch as u8,
);
read_counter(
&mut data.pit,
0,
0xffff - ((25_000_000 * FREQUENCY_HZ) / NANOS_PER_SEC) as u16,
CommandAccess::CommandRWBoth,
);
}
#[test]
fn interrupt_mode() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWBoth as u8
| CommandMode::CommandInterrupt as u8,
);
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
advance_by_ticks(&mut data, 0xffff);
data.irqfd.wait().unwrap();
}
#[test]
fn rate_gen_mode() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWBoth as u8
| CommandMode::CommandRateGen as u8,
);
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
advance_by_ticks(&mut data, 0xffff);
data.irqfd.wait().unwrap();
advance_by_ticks(&mut data, 0xffff);
data.irqfd.wait().unwrap();
advance_by_ticks(&mut data, 0xffff);
data.irqfd.wait().unwrap();
}
#[test]
fn square_wave_counter_read() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWBoth as u8
| CommandMode::CommandSquareWaveGen as u8,
);
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
advance_by_ticks(&mut data, 10_000);
read_counter(
&mut data.pit,
0,
0xffff - 10_000 * 2,
CommandAccess::CommandRWBoth,
);
}
#[test]
fn rate_gen_counter_read() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWBoth as u8
| CommandMode::CommandRateGen as u8,
);
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
advance_by_ticks(&mut data, 10_000);
read_counter(
&mut data.pit,
0,
0xffff - 10_000,
CommandAccess::CommandRWBoth,
);
}
#[test]
fn interrupt_counter_read() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWBoth as u8
| CommandMode::CommandInterrupt as u8,
);
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
advance_by_ticks(&mut data, 10_000);
read_counter(
&mut data.pit,
0,
0xffff - 10_000,
CommandAccess::CommandRWBoth,
);
advance_by_ticks(&mut data, 3 * FREQUENCY_HZ);
read_counter(&mut data.pit, 0, 0, CommandAccess::CommandRWBoth);
}
#[test]
fn read_back_count_access_low() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWLeast as u8
| CommandMode::CommandInterrupt as u8,
);
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWLeast);
write_command(
&mut data.pit,
CommandCounter::CommandReadBack as u8
| CommandReadBackLatch::CommandRBLatchCount as u8
| CommandReadBackCounters::CommandRBCounter0 as u8,
);
advance_by_ticks(&mut data, 100);
write_command(
&mut data.pit,
CommandCounter::CommandReadBack as u8
| CommandReadBackLatch::CommandRBLatchCount as u8
| CommandReadBackCounters::CommandRBCounter0 as u8,
);
read_counter(&mut data.pit, 0, 0x00ff, CommandAccess::CommandRWLeast);
write_command(
&mut data.pit,
CommandCounter::CommandReadBack as u8
| CommandReadBackLatch::CommandRBLatchCount as u8
| CommandReadBackCounters::CommandRBCounter0 as u8,
);
read_counter(
&mut data.pit,
0,
(0xffff - 100) & 0x00ff,
CommandAccess::CommandRWLeast,
);
}
#[test]
fn read_back_count_access_high() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWMost as u8
| CommandMode::CommandInterrupt as u8,
);
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWLeast);
write_command(
&mut data.pit,
CommandCounter::CommandReadBack as u8
| CommandReadBackLatch::CommandRBLatchCount as u8
| CommandReadBackCounters::CommandRBCounter0 as u8,
);
advance_by_ticks(&mut data, 512);
write_command(
&mut data.pit,
CommandCounter::CommandReadBack as u8
| CommandReadBackLatch::CommandRBLatchCount as u8
| CommandReadBackCounters::CommandRBCounter0 as u8,
);
read_counter(&mut data.pit, 0, 0xff00, CommandAccess::CommandRWMost);
write_command(
&mut data.pit,
CommandCounter::CommandReadBack as u8
| CommandReadBackLatch::CommandRBLatchCount as u8
| CommandReadBackCounters::CommandRBCounter0 as u8,
);
read_counter(
&mut data.pit,
0,
(0xffff - 512) & 0xff00,
CommandAccess::CommandRWMost,
);
}
#[test]
fn read_back_status() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter0 as u8
| CommandAccess::CommandRWBoth as u8
| CommandMode::CommandSWStrobe as u8,
);
write_counter(&mut data.pit, 0, 0xffff, CommandAccess::CommandRWBoth);
write_command(
&mut data.pit,
CommandCounter::CommandReadBack as u8
| CommandReadBackLatch::CommandRBLatchStatus as u8
| CommandReadBackCounters::CommandRBCounter0 as u8,
);
read_counter(
&mut data.pit,
0,
CommandAccess::CommandRWBoth as u16 | CommandMode::CommandSWStrobe as u16,
CommandAccess::CommandRWLeast,
);
}
#[test]
fn speaker_square_wave() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter2 as u8
| CommandAccess::CommandRWBoth as u8
| CommandMode::CommandSquareWaveGen as u8,
);
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
advance_by_ticks(&mut data, 128);
read_counter(
&mut data.pit,
2,
0xffff - 128 * 2,
CommandAccess::CommandRWBoth,
);
}
#[test]
fn speaker_rate_gen() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter2 as u8
| CommandAccess::CommandRWBoth as u8
| CommandMode::CommandRateGen as u8,
);
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
advance_by_ticks(&mut data, 128);
read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
write_speaker(&mut data.pit, 0x1);
advance_by_ticks(&mut data, 128);
read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
}
#[test]
fn speaker_interrupt() {
let mut data = set_up();
write_command(
&mut data.pit,
CommandCounter::CommandCounter2 as u8
| CommandAccess::CommandRWBoth as u8
| CommandMode::CommandInterrupt as u8,
);
write_counter(&mut data.pit, 2, 0xffff, CommandAccess::CommandRWBoth);
advance_by_ticks(&mut data, 128);
read_counter(&mut data.pit, 2, 0xffff - 128, CommandAccess::CommandRWBoth);
write_speaker(&mut data.pit, 0x1);
advance_by_ticks(&mut data, 128);
read_counter(&mut data.pit, 2, 0xffff - 256, CommandAccess::CommandRWBoth);
}
#[test]
fn invalid_write_and_read() {
let mut data = set_up();
data.pit.write(
BusAccessInfo {
address: 0x44,
offset: 0x4,
id: 0,
},
&[0],
);
data.pit.read(
BusAccessInfo {
address: 0x55,
offset: 0x15,
id: 0,
},
&mut [0],
);
}
}