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; 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(&mut 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(&mut 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 assert!(!dev.is_null()); 59 let state = dev.cast::<T>(); 60 T::REALIZE.unwrap()(unsafe { &mut *state }); 61 } 62 63 /// # Safety 64 /// 65 /// We expect the FFI user of this function to pass a valid pointer that 66 /// can be downcasted to type `T`. We also expect the device is 67 /// readable/writeable from one thread at any time. 68 unsafe extern "C" fn rust_reset_fn<T: DeviceImpl>(dev: *mut DeviceState) { 69 assert!(!dev.is_null()); 70 let state = dev.cast::<T>(); 71 T::RESET.unwrap()(unsafe { &mut *state }); 72 } 73 74 impl<T> ClassInitImpl<DeviceClass> for T 75 where 76 T: ClassInitImpl<ObjectClass> + DeviceImpl, 77 { 78 fn class_init(dc: &mut DeviceClass) { 79 if <T as DeviceImpl>::REALIZE.is_some() { 80 dc.realize = Some(rust_realize_fn::<T>); 81 } 82 if <T as DeviceImpl>::RESET.is_some() { 83 unsafe { 84 bindings::device_class_set_legacy_reset(dc, Some(rust_reset_fn::<T>)); 85 } 86 } 87 if let Some(vmsd) = <T as DeviceImpl>::vmsd() { 88 dc.vmsd = vmsd; 89 } 90 let prop = <T as DeviceImpl>::properties(); 91 if !prop.is_empty() { 92 unsafe { 93 bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); 94 } 95 } 96 97 <T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class); 98 } 99 } 100 101 #[macro_export] 102 macro_rules! define_property { 103 ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty, default = $defval:expr$(,)*) => { 104 $crate::bindings::Property { 105 // use associated function syntax for type checking 106 name: ::std::ffi::CStr::as_ptr($name), 107 info: $prop, 108 offset: $crate::offset_of!($state, $field) as isize, 109 set_default: true, 110 defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, 111 ..$crate::zeroable::Zeroable::ZERO 112 } 113 }; 114 ($name:expr, $state:ty, $field:ident, $prop:expr, $type:ty$(,)*) => { 115 $crate::bindings::Property { 116 // use associated function syntax for type checking 117 name: ::std::ffi::CStr::as_ptr($name), 118 info: $prop, 119 offset: $crate::offset_of!($state, $field) as isize, 120 set_default: false, 121 ..$crate::zeroable::Zeroable::ZERO 122 } 123 }; 124 } 125 126 #[macro_export] 127 macro_rules! declare_properties { 128 ($ident:ident, $($prop:expr),*$(,)*) => { 129 pub static $ident: [$crate::bindings::Property; { 130 let mut len = 0; 131 $({ 132 _ = stringify!($prop); 133 len += 1; 134 })* 135 len 136 }] = [ 137 $($prop),*, 138 ]; 139 }; 140 } 141 142 unsafe impl ObjectType for DeviceState { 143 type Class = DeviceClass; 144 const TYPE_NAME: &'static CStr = 145 unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; 146 } 147