Skip to content

Commit 9452b05

Browse files
wedsonafDanilo Krummrich
authored andcommitted
rust: implement IdArray, IdTable and RawDeviceId
Most subsystems use some kind of ID to match devices and drivers. Hence, we have to provide Rust drivers an abstraction to register an ID table for the driver to match. Generally, those IDs are subsystem specific and hence need to be implemented by the corresponding subsystem. However, the `IdArray`, `IdTable` and `RawDeviceId` types provide a generalized implementation that makes the life of subsystems easier to do so. Co-developed-by: Asahi Lina <[email protected]> Signed-off-by: Asahi Lina <[email protected]> Signed-off-by: Wedson Almeida Filho <[email protected]> Co-developed-by: Danilo Krummrich <[email protected]> Signed-off-by: Danilo Krummrich <[email protected]>
1 parent 55443b4 commit 9452b05

File tree

2 files changed

+338
-0
lines changed

2 files changed

+338
-0
lines changed

rust/kernel/device_id.rs

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Generic implementation of device IDs.
4+
//!
5+
//! Each bus / subsystem that matches device and driver through a bus / subsystem specific ID is
6+
//! expected to implement [`RawDeviceId`].
7+
8+
use core::marker::PhantomData;
9+
10+
/// Conversion from a device id to a raw device id.
11+
///
12+
/// This is meant to be implemented by buses/subsystems so that they can use [`IdTable`] to
13+
/// guarantee (at compile-time) zero-termination of device id tables provided by drivers.
14+
///
15+
/// Originally, RawDeviceId was implemented as a const trait. However, this unstable feature is
16+
/// broken/gone in 1.73. To work around this, turn IdArray::new() into a macro such that it can use
17+
/// concrete types (which can still have const associated functions) instead of a trait.
18+
///
19+
/// # Safety
20+
///
21+
/// Implementers must ensure that:
22+
/// - [`RawDeviceId::ZERO`] is actually a zeroed-out version of the raw device id.
23+
/// - `to_rawid` is implemented and stores `offset` in the context/data field of the raw device
24+
/// id so that buses can recover the pointer to the data. (This should actually be a trait
25+
/// function, however, this requires `const_trait_impl`, and hence has to changed once the
26+
/// feature is stabilized.)
27+
pub unsafe trait RawDeviceId {
28+
/// The raw type that holds the device id.
29+
///
30+
/// Id tables created from [`Self`] are going to hold this type in its zero-terminated array.
31+
type RawType: Copy;
32+
33+
/// A zeroed-out representation of the raw device id.
34+
///
35+
/// Id tables created from [`Self`] use [`Self::ZERO`] as the sentinel to indicate the end of
36+
/// the table.
37+
const ZERO: Self::RawType;
38+
}
39+
40+
/// A zero-terminated device id array, followed by context data.
41+
#[repr(C)]
42+
pub struct IdArray<T: RawDeviceId, U, const N: usize> {
43+
ids: [T::RawType; N],
44+
sentinel: T::RawType,
45+
id_infos: [Option<U>; N],
46+
}
47+
48+
impl<T: RawDeviceId, U, const N: usize> IdArray<T, U, N> {
49+
const U_NONE: Option<U> = None;
50+
51+
/// Returns an `IdTable` backed by `self`.
52+
///
53+
/// This is used to essentially erase the array size.
54+
pub const fn as_table(&self) -> IdTable<'_, T, U> {
55+
IdTable {
56+
first: &self.ids[0],
57+
_p: PhantomData,
58+
}
59+
}
60+
61+
/// Creates a new instance of the array.
62+
///
63+
/// The contents are derived from the given identifiers and context information.
64+
#[doc(hidden)]
65+
pub const unsafe fn new(raw_ids: [T::RawType; N], infos: [Option<U>; N]) -> Self
66+
where
67+
T: RawDeviceId + Copy,
68+
T::RawType: Copy + Clone,
69+
{
70+
Self {
71+
ids: raw_ids,
72+
sentinel: T::ZERO,
73+
id_infos: infos,
74+
}
75+
}
76+
77+
#[doc(hidden)]
78+
pub const fn get_offset(idx: usize) -> isize
79+
where
80+
T: RawDeviceId + Copy,
81+
T::RawType: Copy + Clone,
82+
{
83+
// SAFETY: We are only using this dummy value to get offsets.
84+
let array = unsafe { Self::new([T::ZERO; N], [Self::U_NONE; N]) };
85+
// SAFETY: Both pointers are within `array` (or one byte beyond), consequently they are
86+
// derived from the same allocated object. We are using a `u8` pointer, whose size 1,
87+
// so the pointers are necessarily 1-byte aligned.
88+
let ret = unsafe {
89+
(&array.id_infos[idx] as *const _ as *const u8)
90+
.offset_from(&array.ids[idx] as *const _ as _)
91+
};
92+
core::mem::forget(array);
93+
ret
94+
}
95+
}
96+
97+
// Creates a new ID array. This is a macro so it can take the concrete ID type as a parameter in
98+
// order to call to_rawid() on it, and still remain const. This is necessary until a new
99+
// const_trait_impl implementation lands, since the existing implementation was removed in Rust
100+
// 1.73.
101+
#[macro_export]
102+
#[doc(hidden)]
103+
macro_rules! _new_id_array {
104+
(($($args:tt)*), $id_type:ty) => {{
105+
/// Creates a new instance of the array.
106+
///
107+
/// The contents are derived from the given identifiers and context information.
108+
const fn new< U, const N: usize>(ids: [$id_type; N], infos: [Option<U>; N])
109+
-> $crate::device_id::IdArray<$id_type, U, N>
110+
where
111+
$id_type: $crate::device_id::RawDeviceId + Copy,
112+
<$id_type as $crate::device_id::RawDeviceId>::RawType: Copy + Clone,
113+
{
114+
let mut raw_ids =
115+
[<$id_type as $crate::device_id::RawDeviceId>::ZERO; N];
116+
let mut i = 0usize;
117+
while i < N {
118+
let offset: isize = $crate::device_id::IdArray::<$id_type, U, N>::get_offset(i);
119+
raw_ids[i] = ids[i].to_rawid(offset);
120+
i += 1;
121+
}
122+
123+
// SAFETY: We are passing valid arguments computed with the correct offsets.
124+
unsafe {
125+
$crate::device_id::IdArray::<$id_type, U, N>::new(raw_ids, infos)
126+
}
127+
}
128+
129+
new($($args)*)
130+
}}
131+
}
132+
133+
/// A device id table.
134+
///
135+
/// The table is guaranteed to be zero-terminated and to be followed by an array of context data of
136+
/// type `Option<U>`.
137+
pub struct IdTable<'a, T: RawDeviceId, U> {
138+
first: &'a T::RawType,
139+
_p: PhantomData<&'a U>,
140+
}
141+
142+
impl<T: RawDeviceId, U> AsRef<T::RawType> for IdTable<'_, T, U> {
143+
fn as_ref(&self) -> &T::RawType {
144+
self.first
145+
}
146+
}
147+
148+
/// Counts the number of parenthesis-delimited, comma-separated items.
149+
///
150+
/// # Examples
151+
///
152+
/// ```
153+
/// # use kernel::count_paren_items;
154+
///
155+
/// assert_eq!(0, count_paren_items!());
156+
/// assert_eq!(1, count_paren_items!((A)));
157+
/// assert_eq!(1, count_paren_items!((A),));
158+
/// assert_eq!(2, count_paren_items!((A), (B)));
159+
/// assert_eq!(2, count_paren_items!((A), (B),));
160+
/// assert_eq!(3, count_paren_items!((A), (B), (C)));
161+
/// assert_eq!(3, count_paren_items!((A), (B), (C),));
162+
/// ```
163+
#[macro_export]
164+
macro_rules! count_paren_items {
165+
(($($item:tt)*), $($remaining:tt)*) => { 1 + $crate::count_paren_items!($($remaining)*) };
166+
(($($item:tt)*)) => { 1 };
167+
() => { 0 };
168+
}
169+
170+
/// Converts a comma-separated list of pairs into an array with the first element. That is, it
171+
/// discards the second element of the pair.
172+
///
173+
/// Additionally, it automatically introduces a type if the first element is warpped in curly
174+
/// braces, for example, if it's `{v: 10}`, it becomes `X { v: 10 }`; this is to avoid repeating
175+
/// the type.
176+
///
177+
/// # Examples
178+
///
179+
/// ```
180+
/// # use kernel::first_item;
181+
///
182+
/// #[derive(PartialEq, Debug)]
183+
/// struct X {
184+
/// v: u32,
185+
/// }
186+
///
187+
/// assert_eq!([] as [X; 0], first_item!(X, ));
188+
/// assert_eq!([X { v: 10 }], first_item!(X, ({ v: 10 }, Y)));
189+
/// assert_eq!([X { v: 10 }], first_item!(X, ({ v: 10 }, Y),));
190+
/// assert_eq!([X { v: 10 }], first_item!(X, (X { v: 10 }, Y)));
191+
/// assert_eq!([X { v: 10 }], first_item!(X, (X { v: 10 }, Y),));
192+
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y)));
193+
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y),));
194+
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y)));
195+
/// assert_eq!([X { v: 10 }, X { v: 20 }], first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y),));
196+
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
197+
/// first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y), ({v: 30}, Y)));
198+
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
199+
/// first_item!(X, ({ v: 10 }, Y), ({ v: 20 }, Y), ({v: 30}, Y),));
200+
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
201+
/// first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y), (X {v: 30}, Y)));
202+
/// assert_eq!([X { v: 10 }, X { v: 20 }, X { v: 30 }],
203+
/// first_item!(X, (X { v: 10 }, Y), (X { v: 20 }, Y), (X {v: 30}, Y),));
204+
/// ```
205+
#[macro_export]
206+
macro_rules! first_item {
207+
($id_type:ty, $(({$($first:tt)*}, $second:expr)),* $(,)?) => {
208+
{
209+
type IdType = $id_type;
210+
[$(IdType{$($first)*},)*]
211+
}
212+
};
213+
($id_type:ty, $(($first:expr, $second:expr)),* $(,)?) => { [$($first,)*] };
214+
}
215+
216+
/// Converts a comma-separated list of pairs into an array with the second element. That is, it
217+
/// discards the first element of the pair.
218+
///
219+
/// # Examples
220+
///
221+
/// ```
222+
/// # use kernel::second_item;
223+
///
224+
/// assert_eq!([] as [u32; 0], second_item!());
225+
/// assert_eq!([10u32], second_item!((X, 10u32)));
226+
/// assert_eq!([10u32], second_item!((X, 10u32),));
227+
/// assert_eq!([10u32], second_item!(({ X }, 10u32)));
228+
/// assert_eq!([10u32], second_item!(({ X }, 10u32),));
229+
/// assert_eq!([10u32, 20], second_item!((X, 10u32), (X, 20)));
230+
/// assert_eq!([10u32, 20], second_item!((X, 10u32), (X, 20),));
231+
/// assert_eq!([10u32, 20], second_item!(({ X }, 10u32), ({ X }, 20)));
232+
/// assert_eq!([10u32, 20], second_item!(({ X }, 10u32), ({ X }, 20),));
233+
/// assert_eq!([10u32, 20, 30], second_item!((X, 10u32), (X, 20), (X, 30)));
234+
/// assert_eq!([10u32, 20, 30], second_item!((X, 10u32), (X, 20), (X, 30),));
235+
/// assert_eq!([10u32, 20, 30], second_item!(({ X }, 10u32), ({ X }, 20), ({ X }, 30)));
236+
/// assert_eq!([10u32, 20, 30], second_item!(({ X }, 10u32), ({ X }, 20), ({ X }, 30),));
237+
/// ```
238+
#[macro_export]
239+
macro_rules! second_item {
240+
($(({$($first:tt)*}, $second:expr)),* $(,)?) => { [$($second,)*] };
241+
($(($first:expr, $second:expr)),* $(,)?) => { [$($second,)*] };
242+
}
243+
244+
/// Defines a new constant [`IdArray`] with a concise syntax.
245+
///
246+
/// It is meant to be used by buses and subsystems to create a similar macro with their device id
247+
/// type already specified, i.e., with fewer parameters to the end user.
248+
///
249+
/// # Examples
250+
///
251+
/// ```
252+
/// # use kernel::{define_id_array, device_id::RawDeviceId};
253+
///
254+
/// #[derive(Copy, Clone)]
255+
/// struct Id(u32);
256+
///
257+
/// // SAFETY: `ZERO` is all zeroes and `to_rawid` stores `offset` as the second element of the raw
258+
/// // device id pair.
259+
/// unsafe impl RawDeviceId for Id {
260+
/// type RawType = (u64, isize);
261+
/// const ZERO: Self::RawType = (0, 0);
262+
/// }
263+
///
264+
/// impl Id {
265+
/// #[allow(clippy::wrong_self_convention)]
266+
/// const fn to_rawid(&self, offset: isize) -> <Id as RawDeviceId>::RawType {
267+
/// (self.0 as u64 + 1, offset)
268+
/// }
269+
/// }
270+
///
271+
/// define_id_array!(A1, Id, (), []);
272+
/// define_id_array!(A2, Id, &'static [u8], [(Id(10), None)]);
273+
/// define_id_array!(A3, Id, &'static [u8], [(Id(10), Some(b"id1")), ]);
274+
/// define_id_array!(A4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2"))]);
275+
/// define_id_array!(A5, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2")), ]);
276+
/// define_id_array!(A6, Id, &'static [u8], [(Id(10), None), (Id(20), Some(b"id2")), ]);
277+
/// define_id_array!(A7, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), None), ]);
278+
/// define_id_array!(A8, Id, &'static [u8], [(Id(10), None), (Id(20), None), ]);
279+
/// ```
280+
#[macro_export]
281+
macro_rules! define_id_array {
282+
($table_name:ident, $id_type:ty, $data_type:ty, [ $($t:tt)* ]) => {
283+
const $table_name: $crate::device_id::IdArray<$id_type,
284+
$data_type, {
285+
$crate::count_paren_items!($($t)*)
286+
}> = $crate::_new_id_array!(
287+
($crate::first_item!($id_type, $($t)*),
288+
$crate::second_item!($($t)*)),
289+
$id_type);
290+
};
291+
}
292+
293+
/// Defines a new constant [`IdTable`] with a concise syntax.
294+
///
295+
/// It is meant to be used by buses and subsystems to create a similar macro with their device id
296+
/// type already specified, i.e., with fewer parameters to the end user.
297+
///
298+
/// # Examples
299+
///
300+
/// ```
301+
/// # use kernel::{define_id_table, device_id::RawDeviceId};
302+
///
303+
/// #[derive(Copy, Clone)]
304+
/// struct Id(u32);
305+
///
306+
/// // SAFETY: `ZERO` is all zeroes and `to_rawid` stores `offset` as the second element of the raw
307+
/// // device id pair.
308+
/// unsafe impl RawDeviceId for Id {
309+
/// type RawType = (u64, isize);
310+
/// const ZERO: Self::RawType = (0, 0);
311+
/// }
312+
///
313+
/// impl Id {
314+
/// #[allow(clippy::wrong_self_convention)]
315+
/// const fn to_rawid(&self, offset: isize) -> <Id as RawDeviceId>::RawType {
316+
/// (self.0 as u64 + 1, offset)
317+
/// }
318+
/// }
319+
///
320+
/// define_id_table!(T1, Id, &'static [u8], [(Id(10), None)]);
321+
/// define_id_table!(T2, Id, &'static [u8], [(Id(10), Some(b"id1")), ]);
322+
/// define_id_table!(T3, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2"))]);
323+
/// define_id_table!(T4, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), Some(b"id2")), ]);
324+
/// define_id_table!(T5, Id, &'static [u8], [(Id(10), None), (Id(20), Some(b"id2")), ]);
325+
/// define_id_table!(T6, Id, &'static [u8], [(Id(10), Some(b"id1")), (Id(20), None), ]);
326+
/// define_id_table!(T7, Id, &'static [u8], [(Id(10), None), (Id(20), None), ]);
327+
/// ```
328+
#[macro_export]
329+
macro_rules! define_id_table {
330+
($table_name:ident, $id_type:ty, $data_type:ty, [ $($t:tt)* ]) => {
331+
const $table_name: Option<$crate::device_id::IdTable<'static, $id_type, $data_type>> = {
332+
$crate::define_id_array!(ARRAY, $id_type, $data_type, [ $($t)* ]);
333+
Some(ARRAY.as_table())
334+
};
335+
};
336+
}

rust/kernel/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
1414
#![no_std]
1515
#![feature(coerce_unsized)]
16+
#![feature(const_refs_to_cell)]
1617
#![feature(dispatch_from_dyn)]
1718
#![feature(new_uninit)]
1819
#![feature(receiver_trait)]
@@ -31,6 +32,7 @@ pub mod alloc;
3132
pub mod block;
3233
mod build_assert;
3334
pub mod device;
35+
pub mod device_id;
3436
pub mod driver;
3537
pub mod error;
3638
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]

0 commit comments

Comments
 (0)