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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
//! The old/historical PIT "Programmable Interval Timer (PIT)"

use core::hint;
use core::mem::transmute;

use bitfield_struct::bitfield;

use super::io::{Port, PortValue};

/// We only use PIT channel 2
///
/// Channel 2 is not able to send interrupts
const CHANNEL: u8 = 2;
const MODE: Port<Mode> = Port::new(0x43);
const CTRL: Port<Ctrl> = Port::new(0x61);
const DATA: Port<u8> = Port::new(0x40 + CHANNEL as u16);
/// Standard frequency in Hz
const BASE_FREQUENCY: u64 = 1_193_182;

/// Historically, PCs had a Timer component of type 8253 or 8254,
/// modern systems come with a compatible chip.
/// Each of these chips provides three 16-bit wide counters ("channel"),
/// each running at a frequency of 1.19318 MHz.
/// The timer's counting speed is thereby independent from the CPU frequency.
///
/// Traditionally, the first counter (channel 0) was used for triggering interrupts,
/// the second one (channel 1) controlled
/// the memory refresh, and the third counter (channel 2) was assigned to the PC speaker.
///
/// As the PIT's frequency is fixed to a constant value of 1.19318 MHz,
/// the PIT can be used for calibration.
/// For this purpose, we use channel 2 only.
pub struct Timer;

impl Timer {
    /// Initialize the timer for `us` micro seconds.
    pub fn new(us: u16) -> Self {
        let counter = BASE_FREQUENCY * us as u64 / 1_000_000;

        assert!(counter < 0xffff);

        unsafe {
            CTRL.update(|v: Ctrl| {
                v.with_enable_speaker_data(false)
                    .with_enable_timer_counter2(true)
            });

            MODE.write(
                Mode::new()
                    .with_access(Access::LowAndHighByte)
                    .with_operation(Operation::InterruptOnTerminalCount)
                    .with_format(false)
                    .with_channel(CHANNEL),
            );

            DATA.write(counter as u8);
            DATA.write((counter >> 8) as u8);
        }

        Self
    }

    /// Start the timer and actively wait for it to finish.
    pub fn wait(&self) -> bool {
        loop {
            let ctrl = unsafe { CTRL.read() };
            if !ctrl.enable_timer_counter2() {
                return false;
            }
            if ctrl.status_timer_counter2() {
                return true;
            }
            hint::spin_loop();
        }
    }

    /// Beep with a certain frequency.
    pub fn beep(frequency: u16) {
        if frequency == 0 {
            unsafe {
                CTRL.update(|v: Ctrl| {
                    v.with_enable_speaker_data(false)
                        .with_enable_timer_counter2(false)
                });
            }
            return;
        }

        unsafe {
            // Check if already configured
            if !CTRL.read().enable_speaker_data() {
                MODE.write(
                    Mode::new()
                        .with_access(Access::LowAndHighByte)
                        .with_operation(Operation::SquareWaveGenerator)
                        .with_format(false)
                        .with_channel(CHANNEL),
                );
            }

            // Set the frequency
            let counter = (BASE_FREQUENCY / frequency as u64).min(0xffff);
            DATA.write(counter as u8);
            DATA.write((counter >> 8) as u8);

            CTRL.update(|v: Ctrl| {
                // Already configured?
                if !v.enable_speaker_data() {
                    v.with_enable_speaker_data(true)
                        .with_enable_timer_counter2(true)
                } else {
                    v
                }
            });
        }
    }
}

impl Drop for Timer {
    fn drop(&mut self) {
        // Disable
        unsafe {
            CTRL.update(|c| {
                c.with_enable_speaker_data(false)
                    .with_enable_timer_counter2(false)
            });
        }
    }
}

/// Mode register (only writable)
#[bitfield(u8)]
struct Mode {
    format: bool,
    #[bits(3)]
    operation: Operation,
    #[bits(2)]
    access: Access,
    #[bits(2)]
    channel: u8,
}
impl PortValue for Mode {
    type I = u8;
}

/// Operating mode
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[allow(unused)]
enum Operation {
    InterruptOnTerminalCount = 0,
    ProgrammableOneShot = 1,
    RateGenerator = 2,
    /// useful for the PC speaker
    SquareWaveGenerator = 3,
    SoftwareTriggeredStrobe = 4,
    HardwareTriggeredStrobe = 5,
}
impl Operation {
    const fn from_bits(value: u8) -> Self {
        unsafe { transmute(value) }
    }
    const fn into_bits(self) -> u8 {
        self as _
    }
}

/// Access mode
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[allow(unused)]
enum Access {
    LatchCountValue = 0,
    LowByteOnly = 1,
    HighByteOnly = 2,
    LowAndHighByte = 3,
}
impl Access {
    const fn from_bits(value: u8) -> Self {
        unsafe { transmute(value) }
    }
    const fn into_bits(self) -> u8 {
        self as _
    }
}

/// Timer control register
#[bitfield(u8)]
struct Ctrl {
    /// Enable the timer
    enable_timer_counter2: bool,
    /// If set, speaker output is equal to status_timer_counter2
    enable_speaker_data: bool,
    _enable_pci_serr: bool,
    _enable_nmi_iochk: bool,
    /// Must be zero on write
    refresh_cycle_toggle: bool,
    /// Will be set on timer expiration; must be 0 on write
    status_timer_counter2: bool,
    /// Not important, must be 0 on write
    status_iochk_nmi_source: bool,
    /// Not important, must be 0 on write
    status_serr_nmi_source: bool,
}
impl PortValue for Ctrl {
    type I = u8;
}