|
| 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 | +} |
0 commit comments