1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//! Basic support for waiting using the Programmable Interval Timer (PIT) clock.
//! 
//! Use the `pit_clock` crate for a more fully-featured PIT interface,
//! including enabling interrupts.

#![no_std]

extern crate spin;
#[macro_use] extern crate log;
extern crate port_io;

use port_io::Port;
use spin::Mutex;

/// Port for Channel 0; used for PIT interrupts.
pub const CHANNEL0: u16 = 0x40;
/// Port for Channel 1, which does not exist and should NOT be used.
const _CHANNEL1: u16 = 0x41;
/// Port for Channel 2; technically the speaker for beeps,
/// but is also used for waiting in `pit_wait()`.
const CHANNEL2: u16 = 0x42;
/// Port for the PIT command register. 
const COMMAND_REGISTER: u16 = 0x43;

/// the timer's default frequency is 1.19 MHz
pub const PIT_DEFAULT_DIVIDEND_HZ: u32 = 1193182;
pub const PIT_MINIMUM_FREQ:        u32 = 19;

pub static PIT_COMMAND:   Mutex<Port<u8>> = Mutex::new( Port::new(COMMAND_REGISTER) );
pub static PIT_CHANNEL_0: Mutex<Port<u8>> = Mutex::new( Port::new(CHANNEL0) );
static PIT_CHANNEL_2: Mutex<Port<u8>> = Mutex::new( Port::new(CHANNEL2) );


/// Waits (blocking) for the given number of `microseconds` using the PIT Channel 2.
/// 
/// This uses a separate PIT clock channel so it doesn't affect PIT interrupts.
/// 
/// ## Arguments
/// * `microseconds`: the number of microseconds to wait, max value 55555.
pub fn pit_wait(microseconds: u32) -> Result<(), &'static str> {
    let divisor = PIT_DEFAULT_DIVIDEND_HZ / (1000000/microseconds); 
    if divisor > (u16::max_value() as u32) {
        error!("pit_wait(): the chosen wait time {}us is too large, max value is {}!", microseconds, 1000000/PIT_MINIMUM_FREQ);
        return Err("microsecond value was too large");
    }

    // SAFE because we're simply configuring the PIT clock, and the code below is correct.
    unsafe {
        let port_60 = Port::<u8>::new(0x60);
        let port_61 = Port::<u8>::new(0x61);

        // see code example: https://wiki.osdev.org/APIC_timer
        let port_61_val = port_61.read(); 
        port_61.write(port_61_val & 0xFD | 0x1); // sets the speaker channel 2 to be controlled by PIT hardware
        PIT_COMMAND.lock().write(0b10110010); // channel 2, access mode: lobyte/hibyte, hardware-retriggerable one shot mode, 16-bit binary (not BCD)

        // set frequency; must write the low byte first and then the high byte
        PIT_CHANNEL_2.lock().write(divisor as u8);
        // read from PS/2 port 0x60, which acts as a short delay and acknowledges the status register
        let _ignore: u8 = port_60.read();
        PIT_CHANNEL_2.lock().write((divisor >> 8) as u8);
        
        // reset PIT one-shot counter
        let port_61_val = port_61.read() & 0xFE;
        port_61.write(port_61_val); // clear bit 0
        port_61.write(port_61_val | 0x1); // set bit 0
        // here, PIT channel 2 timer has started counting
        
        // wait for PIT timer to reach 0, which is tested by checking bit 5
        while port_61.read() & 0x20 != 0 { }
        Ok(())
    }
}