Skip to main content

virtio_drivers/
config.rs

1//! Types and macros for VirtIO device configuration space.
2
3use crate::{Error, transport::Transport};
4use zerocopy::{FromBytes, Immutable, IntoBytes};
5
6/// A configuration space register from which the driver can only read.
7#[derive(Default, FromBytes, Immutable, IntoBytes)]
8#[repr(transparent)]
9pub struct ReadOnly<T: Copy + FromBytes>(pub T);
10
11impl<T: Copy + FromBytes> ReadOnly<T> {
12    /// Constructs a new instance for testing.
13    pub const fn new(value: T) -> Self {
14        Self(value)
15    }
16}
17
18/// A configuration space register to which the driver can only write.
19#[derive(Default, FromBytes, Immutable, IntoBytes)]
20#[repr(transparent)]
21pub struct WriteOnly<T: Copy + Immutable + IntoBytes>(pub T);
22
23impl<T: Copy + Immutable + IntoBytes> WriteOnly<T> {
24    /// Constructs a new instance for testing.
25    pub const fn new(value: T) -> Self {
26        Self(value)
27    }
28}
29
30/// A configuration space register which the driver may both read and write.
31#[derive(Default, FromBytes, Immutable, IntoBytes)]
32#[repr(transparent)]
33pub struct ReadWrite<T: Copy + FromBytes + Immutable + IntoBytes>(pub T);
34
35impl<T: Copy + FromBytes + Immutable + IntoBytes> ReadWrite<T> {
36    /// Constructs a new instance for testing.
37    pub const fn new(value: T) -> Self {
38        Self(value)
39    }
40}
41
42/// Marker trait for configuration space registers from which the driver may read.
43pub trait ConfigReadable<T> {}
44
45/// Marker trait for configuration space registers to which the driver may write.
46pub trait ConfigWritable<T> {}
47
48impl<T: Copy + FromBytes> ConfigReadable<T> for ReadOnly<T> {}
49impl<T: Copy + FromBytes + Immutable + IntoBytes> ConfigReadable<T> for ReadWrite<T> {}
50impl<T: Copy + FromBytes + Immutable + IntoBytes> ConfigWritable<T> for ReadWrite<T> {}
51impl<T: Copy + Immutable + IntoBytes> ConfigWritable<T> for WriteOnly<T> {}
52
53/// Wrapper for [`Transport::read_config_space`] with an extra dummy parameter to force the correct
54/// type to be inferred.
55#[inline(always)]
56#[doc(hidden)]
57pub fn read_help<T, V, R>(transport: &T, offset: usize, _dummy_r: Option<R>) -> Result<V, Error>
58where
59    T: Transport,
60    V: FromBytes + IntoBytes,
61    R: ConfigReadable<V>,
62{
63    transport.read_config_space(offset)
64}
65
66/// Wrapper for [`Transport::write_config_space`] with an extra dummy parameter to force the correct
67/// type to be inferred.
68#[inline(always)]
69#[doc(hidden)]
70pub fn write_help<T, V, W>(
71    transport: &mut T,
72    offset: usize,
73    value: V,
74    _dummy_w: Option<W>,
75) -> Result<(), Error>
76where
77    T: Transport,
78    V: Immutable + IntoBytes,
79    W: ConfigWritable<V>,
80{
81    transport.write_config_space(offset, value)
82}
83
84/// Reads the given field of the given struct from the device config space via the given transport.
85#[macro_export]
86macro_rules! read_config {
87    ($transport:expr, $struct:ty, $field:ident) => {{
88        let dummy_struct: Option<$struct> = None;
89        let dummy_field = dummy_struct.map(|s| s.$field);
90        $crate::config::read_help(
91            &$transport,
92            core::mem::offset_of!($struct, $field),
93            dummy_field,
94        )
95    }};
96}
97
98/// Writes the given field of the given struct from the device config space via the given transport.
99#[macro_export]
100macro_rules! write_config {
101    ($transport:expr, $struct:ty, $field:ident, $value:expr) => {{
102        let dummy_struct: Option<$struct> = None;
103        let dummy_field = dummy_struct.map(|s| s.$field);
104        $crate::config::write_help(
105            &mut $transport,
106            core::mem::offset_of!($struct, $field),
107            $value,
108            dummy_field,
109        )
110    }};
111}
112
113pub use read_config;
114pub use write_config;