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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
//! A PCI device's [`Status`] and [`Command`] registers.
use mycelium_bitfield::{bitfield, FromBits};

bitfield! {
    /// A PCI device's `Status` register.
    ///
    /// See <https://wiki.osdev.org/Pci#Status_Register>
    #[derive(Eq, PartialEq)]
    pub struct Status<u16> {
        const _RES0 = 2;
        /// Interrupt Status.
        ///
        /// Represents the state of the device's `INTx#` signal. If set to 1 and
        /// bit 10 of the [`Command`] register ([`INTERRUPT_DISABLE`]) is set to
        /// 0, the signal will be asserted; otherwise, the signal will be ignored.
        ///
        /// [`INTERUPT_DISABLE`]: Command::INTERRUPT_DISABLE
        pub const INTERRUPT_STATUS: bool;

        /// Capabilities List
        ///
        /// If set to 1, the device implements the pointer for a New Capabilities
        /// linked list at offset `0x34`; otherwise, the linked list is not available.
        pub const CAPABILITIES_LIST: bool;

        /// 66 MHz Capable
        ///
        /// If set to 1 the device is capable of running at 66 MHz; otherwise,
        /// the device runs at 33 MHz.
        pub const IS_66MHZ_CAPABLE: bool;

        /// As of revision 3.0 of the PCI Local Bus specification this bit is
        /// reserved. In revision 2.1 of the specification this bit was used to
        /// indicate whether or not a device supported User Definable Features
        const _RES1 = 1;

        /// Fast Back-To-Back Capable
        ///
        /// If set to 1 the device can accept fast back-to-back transactions
        /// that are not from the same agent; otherwise, transactions can only
        /// be accepted from the same agent.
        pub const FAST_BACK_TO_BACK_CAPABLE: bool;

        /// Master Data Parity Error
        ///
        /// This bit is only set when the following conditions are met. The bus
        /// agent asserted `PERR#` on a read or observed an assertion of `PERR#`
        /// on a write, the agent setting the bit  acted as the bus master for
        /// the operation in which the error occurred, and bit 6 of the
        /// [`Command`] register ([`PARITY_ERROR_RESPONSE`] bit) is set to 1.
        ///
        /// [`PARITY_ERROR_RESPONSE`]: Command::PARITY_ERROR_RESPONSE
        pub const MASTER_DATA_PARITY_ERROR: bool;

        /// `DEVSEL#` Timing
        ///
        /// Read-only bits that represent the slowest time that a device will
        /// assert `DEVSEL#` for any bus command except Configuration Space read
        /// and writes. A value of 0x0 represents fast timing, a value of
        /// 0x1 represents medium timing, and a value of 0x2 represents slow
        /// timing.
        pub const DEVSEL_TIMING: DevselTiming;

        /// Signalled Target Abort
        ///
        /// This bit will be set to 1 whenever a target device terminates a
        /// transaction with Target-Abort.
        pub const SIGNALLED_TARGET_ABORT: bool;

        /// Received Target Abort
        ///
        /// This bit will be set to 1, by a master device, whenever its
        /// transaction is terminated with Target-Abort.
        pub const RECEIVED_TARGET_ABORT: bool;

        /// Received Master Abort
        ///
        /// This bit will be set to 1, by a master device, whenever its
        /// transaction (except for Special Cycle transactions) is terminated
        /// with Master-Abort.
        pub const RECEIVED_MASTER_ABORT: bool;

        /// Signalled System Error
        ///
        /// This bit will be set to 1 whenever the device asserts `SERR#`.
        pub const SIGNALLED_SYSTEM_ERROR: bool;

        /// Detected Parity Error
        ///
        /// This bit will be set to 1 whenever the device detects a parity
        /// error, even if parity error handling is disabled.
        pub const DETECTED_PARITY_ERROR: bool;
    }
}

bitfield! {

    /// A PCI device's `Command` register.
    ///
    /// See <https://wiki.osdev.org/Pci#Command_Register>
    #[derive(Eq, PartialEq)]
    pub struct Command<u16> {
        /// I/O Space Enabled
        ///
        /// If set to 1 the device can respond to I/O Space accesses; otherwise,
        /// the device's response is disabled.
        pub const IO_SPACE_ENABLED: bool;

        /// Memory Space Enabled
        ///
        /// If set to 1 the device can respond to Memory Space accesses;
        /// otherwise, the device's response is disabled.
        pub const MEMORY_SPACE_ENABLED: bool;

        /// Bus Master
        ///
        /// If set to 1, the device can behave as a bus master; otherwise, the
        /// device can not generate PCI accesses.
        pub const BUS_MASTER: bool;

        /// Special Cycle Enabled
        ///
        /// If set to 1 the device can monitor Special Cycle operations;
        /// otherwise, the device will ignore them.
        pub const SPECIAL_CYCLE_ENABLE: bool;

        /// Memory Write and Invalidate Enabled
        ///
        /// If set to 1 the device can generate the Memory Write and Invalidate
        /// command; otherwise, the Memory Write command must be used.
        pub const MEMORY_WRITE_AND_INVALIDATE_ENABLED: bool;

        /// VGA Palette Snoop
        ///
        /// If set to 1 the device does not respond to palette register writes
        /// and will snoop the data; otherwise, the device will treat palette
        /// write accesses like all other accesses.
        pub const VGA_PALETTE_SNOOP: bool;

        /// Parity Error Response enabled
        ///
        /// If set to 1 the device will take its normal action when a parity
        /// error is detected; otherwise, when an error is detected, the device
        /// will set bit 15 of the [`Status`] register
        /// ([`DETECTED_PARITY_ERROR`]), but will not assert the PERR# (Parity
        /// Error) pin and will continue operation as normal.
        ///
        /// [`DETECTED_PARITY_ERROR`]: Status::DETECTED_PARITY_ERROR
        pub const PARITY_ERROR_RESPONSE_ENABLED: bool;

        /// As of revision 3.0 of the PCI local bus specification this bit is
        /// hardwired to 0. In earlier versions of the specification this bit
        /// was used by devices and may have been hardwired to 0, 1, or
        /// implemented as a read/write bit.
        const _RES0 = 1;

        /// `SERR#` Enabled
        ///
        /// If set to 1 the `SERR#` driver is enabled; otherwise, the driver is disabled.
        pub const SERR_ENABLED: bool;

        /// Fast Back-To-Back Enabled
        ///
        /// If set to 1, indicates a device is allowed to generate fast
        /// back-to-back transactions; otherwise, fast back-to-back
        /// transactions are only allowed to the same agent.
        pub const FAST_BACK_TO_BACK_ENABLED: bool;

        /// Interrupt Disable
        ///
        /// If set to 1, the assertion of the devices `INTx#` signal is
        /// disabled; otherwise, assertion of the signal is enabled.
        pub const INTERRUPT_DISABLE: bool;
    }
}

bitfield! {
    /// Built-In Self Test (BIST) register.
    #[derive(Eq, PartialEq)]
    pub struct Bist<u8> {
        /// The completion code set by running a built-in self test.
        ///
        /// If the test completed successfully, this should be 0.
        pub const COMPLETION_CODE = 3;

        /// Reserved
        const _RES = 2;

        /// Start BIST
        ///
        /// Set to 1 by the OS to start a BIST. This bit is reset when BIST
        /// completes. If BIST does not complete after 2 seconds the device
        /// should be failed by system software.
        pub const START_BIST: bool;
        /// BIST Capable
        ///
        /// If this is 1, the device supports BIST. If it is 0, this device does
        /// not support a built-in self test.
        pub const BIST_CAPABLE: bool;
    }
}

impl Bist {
    /// Returns the device's BIST completion code, if a BIST has completed.
    ///
    /// If the BIST is still in progress, this method returns `None`.
    pub fn completion_code(self) -> Option<u8> {
        if self.get(Self::START_BIST) {
            return None;
        }

        Some(self.get(Self::COMPLETION_CODE))
    }
}

/// Slowest time that a device will assert `DEVSEL#` for any bus command except
/// Configuration Space reads/writes.
#[derive(Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum DevselTiming {
    Fast = 0x0,
    Medium = 0x1,
    Slow = 0x2,
}

bitfield! {
    pub(crate) struct RegisterWord<u32> {
        pub(crate) const COMMAND: Command;
        pub(crate) const STATUS: Status;
    }
}

// === impl Status ===

impl Status {
    /// Returns `true` if this device received an abort (either a Master Abort
    /// or Target Abort).
    ///
    /// This returns `true` if the [`Status::RECEIVED_MASTER_ABORT`] or
    /// [`Status::RECEIVED_TARGET_ABORT`] bits are set.
    pub fn received_abort(self) -> bool {
        self.get(Self::RECEIVED_MASTER_ABORT) || self.get(Self::RECEIVED_TARGET_ABORT)
    }
}

impl FromBits<u32> for Status {
    const BITS: u32 = 16;
    type Error = core::convert::Infallible;

    fn try_from_bits(bits: u32) -> Result<Self, Self::Error> {
        Ok(Self::from_bits((bits >> 16) as u16))
    }

    fn into_bits(self) -> u32 {
        (self.bits() as u32) << 16
    }
}

// === impl Command ===

impl FromBits<u32> for Command {
    const BITS: u32 = 16;
    type Error = core::convert::Infallible;

    fn try_from_bits(bits: u32) -> Result<Self, Self::Error> {
        Ok(Self::from_bits(bits as u16))
    }

    fn into_bits(self) -> u32 {
        self.bits() as u32
    }
}

// === impl DevselTiming ===

impl FromBits<u16> for DevselTiming {
    const BITS: u32 = 2;
    type Error = crate::error::UnexpectedValue<u16>;

    fn try_from_bits(bits: u16) -> Result<Self, Self::Error> {
        match bits as u8 {
            bits if bits == Self::Fast as u8 => Ok(Self::Fast),
            bits if bits == Self::Medium as u8 => Ok(Self::Medium),
            bits if bits == Self::Slow as u8 => Ok(Self::Slow),
            _ => Err(crate::error::unexpected(bits).named("DEVSEL timing")),
        }
    }

    fn into_bits(self) -> u16 {
        self as u16
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn status_is_valid() {
        Status::assert_valid();
    }

    #[test]
    fn command_is_valid() {
        Status::assert_valid();
    }

    #[test]
    fn register_word_is_valid() {
        RegisterWord::assert_valid();
    }
}