pci_driver/regions/
bit_field_macros.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3/* ---------------------------------------------------------------------------------------------- */
4
5/// TODO: Document.
6#[macro_export]
7macro_rules! pci_bit_field {
8    (
9        $(
10            $(#[$attr:meta])*
11            $vis:vis struct $name:ident<$lifetime:lifetime> : $mode:ident $type:ty {
12                $(
13                    $(#[$elem_attr:meta])*
14                    $elem_name:ident @ $elem_first_bit:literal$(--$elem_last_bit:literal)? :
15                    $elem_mode:ident $($elem_type:ty)?
16                ),* $(,)?
17            }
18        )*
19    ) => {
20        $(
21            $(#[$attr])*
22            #[derive(Clone, Copy)]
23            $vis struct $name<$lifetime> {
24                region: &$lifetime dyn $crate::regions::PciRegion,
25                offset: u64,
26            }
27
28            impl<'a> $crate::regions::BackedByPciSubregion<'a> for $name<'a> {
29                fn backed_by(as_subregion: impl $crate::regions::AsPciSubregion<'a>) -> Self {
30                    let subregion = $crate::regions::AsPciSubregion::as_subregion(&as_subregion);
31                    $name {
32                        region: subregion.underlying_region(),
33                        offset: subregion.offset_in_underlying_region(),
34                    }
35                }
36            }
37
38            impl<'a> $crate::regions::AsPciSubregion<'a> for $name<'a> {
39                fn as_subregion(&self) -> $crate::regions::PciSubregion<'a> {
40                    self.region
41                        .subregion(self.offset..self.offset + ::std::mem::size_of::<$type>() as u64)
42                }
43            }
44
45            impl $crate::regions::structured::PciBitFieldReadable for $name<'_> {
46                type Type = $type;
47
48                fn read(&self) -> ::std::io::Result<$type> {
49                    $crate::regions::structured::PciRegisterValue::read(
50                        self.region,
51                        self.offset,
52                    )
53                }
54            }
55
56            impl ::std::fmt::Debug for $name<'_> {
57                fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
58                    let mut debug_struct = f.debug_struct(::std::stringify!($name));
59                    $(
60                        $crate::_pci_bit_field_debug_elem!(
61                            self, debug_struct, $elem_name : $elem_mode $($elem_type)?
62                        );
63                    )*
64                    debug_struct.finish()
65                }
66            }
67
68            impl<$lifetime> $name<$lifetime> {
69                $(
70                    $crate::_pci_bit_field_elem! {
71                        $lifetime $type :
72                        $(#[$elem_attr])*
73                        $elem_name @ $elem_first_bit$(--$elem_last_bit)? :
74                        $elem_mode $($elem_type)?
75                    }
76                )*
77            }
78
79            $crate::_pci_bit_field_impl_writeable_part! {
80                impl $name<$lifetime> : $mode $type {
81                    $(
82                        $(#[$elem_attr])*
83                        $elem_name @ $elem_first_bit$(--$elem_last_bit)? :
84                        $elem_mode $($elem_type)?
85                    ),*
86                }
87            }
88        )*
89    };
90}
91
92/// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate.
93#[doc(hidden)]
94#[macro_export]
95macro_rules! _pci_bit_field_debug_elem {
96    ( $self:ident, $debug_struct:ident, $elem_name:ident : RsvdP ) => {};
97    ( $self:ident, $debug_struct:ident, $elem_name:ident : RsvdZ ) => {};
98    ( $self:ident, $debug_struct:ident, $elem_name:ident : $elem_mode:ident $($elem_type:ty)? ) => {
99        $debug_struct.field(::std::stringify!($elem_name), &$self.$elem_name())
100    };
101}
102
103/// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate.
104#[doc(hidden)]
105#[macro_export]
106macro_rules! _pci_bit_field_impl_writeable_part {
107    (
108        impl $name:ident<$lifetime:lifetime> : RO $type:ty {
109            $(
110                $(#[$elem_attr:meta])*
111                $elem_name:ident @ $elem_first_bit:literal$(--$elem_last_bit:literal)? :
112                $elem_mode:ident $($elem_type:ty)?
113            ),* $(,)?
114        }
115    ) => {};
116
117    (
118        impl $name:ident<$lifetime:lifetime> : RW $type:ty {
119            $(
120                $(#[$elem_attr:meta])*
121                $elem_name:ident @ $elem_first_bit:literal$(--$elem_last_bit:literal)? :
122                $elem_mode:ident $($elem_type:ty)?
123            ),* $(,)?
124        }
125    ) => {
126        impl $crate::regions::structured::PciBitFieldWriteable for $name<'_> {
127            const WRITE_MASK: $type = $crate::_pci_bit_field_write_mask!(
128                $type,
129                $(
130                    @ $elem_first_bit$(--$elem_last_bit)? :
131                    $elem_mode $($elem_type)?
132                ),*
133            );
134
135            fn write(&self, value: $type) -> ::std::io::Result<()> {
136                $crate::regions::structured::PciRegisterValue::write(
137                    value,
138                    self.region,
139                    self.offset,
140                )
141            }
142        }
143    }
144}
145
146/// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate.
147#[doc(hidden)]
148#[macro_export]
149macro_rules! _pci_bit_field_elem {
150    (
151        $lifetime:lifetime $field_type:ty :
152        $(#[$elem_attr:meta])*
153        $elem_name:ident @ $elem_bit:literal : RO
154    ) => {
155        $(#[$elem_attr])*
156        pub fn $elem_name(&self) -> $crate::regions::structured::PciBitReadOnly<$lifetime, $field_type> {
157            $crate::regions::structured::PciBitReadOnly::backed_by(
158                self.region,
159                self.offset,
160                1 << $elem_bit, // mask
161            )
162        }
163    };
164
165    (
166        $lifetime:lifetime $field_type:ty :
167        $(#[$elem_attr:meta])*
168        $elem_name:ident @ $elem_first_bit:literal--$elem_last_bit:literal : RO $elem_type:ty
169    ) => {
170        $(#[$elem_attr])*
171        pub fn $elem_name(&self) -> $crate::regions::structured::PciBitsReadOnly<$lifetime, $field_type, $elem_type> {
172            const MASK: $field_type = $crate::_bit_range!($field_type, $elem_first_bit, $elem_last_bit);
173            $crate::regions::structured::PciBitsReadOnly::backed_by(
174                self.region,
175                self.offset,
176                MASK,
177                $elem_first_bit, // shift
178            )
179        }
180    };
181
182    (
183        $lifetime:lifetime $field_type:ty :
184        $(#[$elem_attr:meta])*
185        $elem_name:ident @ $elem_bit:literal : RW
186    ) => {
187        $(#[$elem_attr])*
188        pub fn $elem_name(&self) -> $crate::regions::structured::PciBitReadWrite<$lifetime, $field_type> {
189            $crate::regions::structured::PciBitReadWrite::backed_by(
190                self.region,
191                self.offset,
192                1 << $elem_bit, // mask
193                <Self as $crate::regions::structured::PciBitFieldWriteable>::WRITE_MASK,
194            )
195        }
196    };
197
198    (
199        $lifetime:lifetime $field_type:ty :
200        $(#[$elem_attr:meta])*
201        $elem_name:ident @ $elem_first_bit:literal--$elem_last_bit:literal : RW $elem_type:ty
202    ) => {
203        $(#[$elem_attr])*
204        pub fn $elem_name(&self) -> $crate::regions::structured::PciBitsReadWrite<$lifetime, $field_type, $elem_type> {
205            const MASK: $field_type = $crate::_bit_range!($field_type, $elem_first_bit, $elem_last_bit);
206            $crate::regions::structured::PciBitsReadWrite::backed_by(
207                self.region,
208                self.offset,
209                MASK,
210                $elem_first_bit, // shift
211                <Self as $crate::regions::structured::PciBitFieldWriteable>::WRITE_MASK
212            )
213        }
214    };
215
216    (
217        $lifetime:lifetime $field_type:ty :
218        $(#[$elem_attr:meta])*
219        $elem_name:ident @ $elem_bit:literal : RW1C
220    ) => {
221        $(#[$elem_attr])*
222        pub fn $elem_name(&self) -> $crate::regions::structured::PciBitReadClear<$lifetime, $field_type> {
223            $crate::regions::structured::PciBitReadClear::backed_by(
224                self.region,
225                self.offset,
226                1 << $elem_bit, // mask
227                <Self as $crate::regions::structured::PciBitFieldWriteable>::WRITE_MASK,
228            )
229        }
230    };
231
232    (
233        $lifetime:lifetime $field_type:ty :
234        $elem_name:ident @ $elem_bit:literal : RsvdP
235    ) => {};
236
237    (
238        $lifetime:lifetime $field_type:ty :
239        $elem_name:ident @ $elem_first_bit:literal--$elem_last_bit:literal : RsvdP
240    ) => {};
241
242    (
243        $lifetime:lifetime $field_type:ty :
244        $elem_name:ident @ $elem_bit:literal : RsvdZ
245    ) => {};
246
247    (
248        $lifetime:lifetime $field_type:ty :
249        $elem_name:ident @ $elem_first_bit:literal--$elem_last_bit:literal : RsvdZ
250    ) => {};
251}
252
253/// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate.
254#[doc(hidden)]
255#[macro_export]
256macro_rules! _pci_bit_field_write_mask {
257    (
258        $field_type:ty,
259        $(
260            @ $elem_first_bit:literal$(--$elem_last_bit:literal)? :
261            $elem_mode:ident $($elem_type:ty)?
262        ),* $(,)?
263    ) => {
264        $(
265            $crate::_pci_bit_field_write_mask_elem!(
266                $field_type,
267                @ $elem_first_bit$(--$elem_last_bit)? :
268                $elem_mode $($elem_type)?
269            ) &
270        )* !0
271    };
272}
273
274/// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate.
275#[doc(hidden)]
276#[macro_export]
277macro_rules! _pci_bit_field_write_mask_elem {
278    ($field_type:ty, @ $elem_bit:literal : RW1C) => {{
279        !(1 << $elem_bit)
280    }};
281
282    ($field_type:ty, @ $elem_bit:literal : RsvdZ) => {{
283        !(1 << $elem_bit)
284    }};
285
286    ($field_type:ty, @ $elem_first_bit:literal--$elem_last_bit:literal : RsvdZ) => {{
287        !$crate::_bit_range!($field_type, $elem_first_bit, $elem_last_bit)
288    }};
289
290    (
291        $field_type:ty,
292        @ $elem_first_bit:literal$(--$elem_last_bit:literal)? :
293        $elem_mode:ident $($elem_type:ty)?
294    ) => {{
295        !0
296    }};
297}
298
299/// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate.
300#[doc(hidden)]
301#[macro_export]
302macro_rules! _bit_range {
303    ($field_type:ty, $elem_first_bit:literal, $elem_last_bit:literal) => {{
304        let one: $field_type = 1;
305        let mask_1 = match one.checked_shl($elem_last_bit + 1) {
306            ::std::option::Option::Some(v) => v - 1,
307            ::std::option::Option::None => !0,
308        };
309        let mask_2 = (1 << $elem_first_bit) - 1;
310        mask_1 & !mask_2
311    }};
312}
313
314/* ---------------------------------------------------------------------------------------------- */
315
316#[cfg(test)]
317mod tests {
318    #[test]
319    fn test_pci_bit_field_write_mask() {
320        assert_eq!(
321            _pci_bit_field_write_mask!(
322                u8,
323                @    3 : RsvdZ,
324                @ 6--7 : RsvdZ,
325            ),
326            0b_0011_0111_u8
327        );
328    }
329
330    #[test]
331    fn test_pci_bit_field_write_mask_elem() {
332        assert_eq!(
333            _pci_bit_field_write_mask_elem!(u8, @ 0 : RsvdZ),
334            0b_1111_1110_u8
335        );
336        assert_eq!(
337            _pci_bit_field_write_mask_elem!(u8, @ 3 : RsvdZ),
338            0b_1111_0111_u8
339        );
340        assert_eq!(
341            _pci_bit_field_write_mask_elem!(u8, @ 7 : RsvdZ),
342            0b_0111_1111_u8
343        );
344
345        assert_eq!(
346            _pci_bit_field_write_mask_elem!(u8, @ 0--1 : RsvdZ),
347            0b_1111_1100_u8
348        );
349        assert_eq!(
350            _pci_bit_field_write_mask_elem!(u8, @ 3--5 : RsvdZ),
351            0b_1100_0111_u8
352        );
353        assert_eq!(
354            _pci_bit_field_write_mask_elem!(u8, @ 6--7 : RsvdZ),
355            0b_0011_1111_u8
356        );
357    }
358}
359
360/* ---------------------------------------------------------------------------------------------- */