1 // Copyright 2024, Linaro Limited 2 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 //! Bindings to create devices and access device functionality from Rust. 6 7 use std::{ffi::CStr, ptr::NonNull}; 8 9 pub use bindings::{DeviceClass, DeviceState, Property}; 10 11 use crate::{ 12 bindings::{self, Error}, 13 prelude::*, 14 qom::{ClassInitImpl, ObjectClass}, 15 vmstate::VMStateDescription, 16 }; 17 18 /// Trait providing the contents of [`DeviceClass`]. 19 pub trait DeviceImpl { 20 /// _Realization_ is the second stage of device creation. It contains 21 /// all operations that depend on device properties and can fail (note: 22 /// this is not yet supported for Rust devices). 23 /// 24 /// If not `None`, the parent class's `realize` method is overridden 25 /// with the function pointed to by `REALIZE`. 26 const REALIZE: Option<fn(&Self)> = None; 27 28 /// If not `None`, the parent class's `reset` method is overridden 29 /// with the function pointed to by `RESET`. 30 /// 31 /// Rust does not yet support the three-phase reset protocol; this is 32 /// usually okay for leaf classes. 33 const RESET: Option<fn(&Self)> = None; 34 35 /// An array providing the properties that the user can set on the 36 /// device. Not a `const` because referencing statics in constants 37 /// is unstable until Rust 1.83.0. 38 fn properties() -> &'static [Property] { 39 &[] 40 } 41 42 /// A `VMStateDescription` providing the migration format for the device 43 /// Not a `const` because referencing statics in constants is unstable 44 /// until Rust 1.83.0. 45 fn vmsd() -> Option<&'static VMStateDescription> { 46 None 47 } 48 } 49 50 /// # Safety 51 /// 52 /// This function is only called through the QOM machinery and 53 /// used by the `ClassInitImpl<DeviceClass>` trait. 54 /// We expect the FFI user of this function to pass a valid pointer that 55 /// can be downcasted to type `T`. We also expect the device is 56 /// readable/writeable from one thread at any time. 57 unsafe extern "C" fn rust_realize_fn<T: DeviceImpl>(dev: *mut DeviceState, _errp: *mut *mut Error) { 58 let state = NonNull::new(dev).unwrap().cast::<T>(); 59 T::REALIZE.unwrap()(unsafe { state.as_ref() }); 60 } 61 62 /// # Safety 63 /// 64 /// We expect the FFI user of this function to pass a valid pointer that 65 /// can be downcasted to type `T`. We also expect the device is 66 /// readable/writeable from one thread at any time. 67 unsafe extern "C" fn rust_reset_fn<T: DeviceImpl>(dev: *mut DeviceState) { 68 let mut state = NonNull::new(dev).unwrap().cast::<T>(); 69 T::RESET.unwrap()(unsafe { state.as_mut() }); 70 } 71 72 impl<T> ClassInitImpl<DeviceClass> for T 73 where 74 T: ClassInitImpl<ObjectClass> + DeviceImpl, 75 { 76 fn class_init(dc: &mut DeviceClass) { 77 if <T as DeviceImpl>::REALIZE.is_some() { 78 dc.realize = Some(rust_realize_fn::<T>); 79 } 80 if <T as DeviceImpl>::RESET.is_some() { 81 unsafe { 82 bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::<T>)); 83 } 84 } 85 if let Some(vmsd) = <T as DeviceImpl>::vmsd() { 86 dc.vmsd = vmsd; 87 } 88 let prop = <T as DeviceImpl>::properties(); 89 if !prop.is_empty() { 90 unsafe { 91 bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); 92 } 93 } 94 95 <T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class); 96 } 97 } 98 99 #[macro_export] 100 macro_rules! define_property { 101 ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { 102 $crate::bindings::Property { 103 // use associated function syntax for type checking 104 name: ::std::ffi::CStr::as_ptr($name), 105 info: $prop, 106 offset: $crate::offset_of!($state, $field) as isize, 107 set_default: true, 108 defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, 109 ..$crate::zeroable::Zeroable::ZERO 110 } 111 }; 112 ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { 113 $crate::bindings::Property { 114 // use associated function syntax for type checking 115 name: ::std::ffi::CStr::as_ptr($name), 116 info: $prop, 117 offset: $crate::offset_of!($state, $field) as isize, 118 set_default: false, 119 ..$crate::zeroable::Zeroable::ZERO 120 } 121 }; 122 } 123 124 #[macro_export] 125 macro_rules! declare_properties { 126 ($ident:ident, $($prop:expr),*$(,)*) => { 127 pub static $ident: [$crate::bindings::Property; { 128 let mut len = 0; 129 $({ 130 _ = stringify!($prop); 131 len += 1; 132 })* 133 len 134 }] = [ 135 $($prop),*, 136 ]; 137 }; 138 } 139 140 unsafe impl ObjectType for DeviceState { 141 type Class = DeviceClass; 142 const TYPE_NAME: &'static CStr = 143 unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; 144 } 145 qom_isa!(DeviceState: Object); 146