rust: add bindings for gpio_{in|out} initialization

Wrap qdev_init_gpio_{in|out} as methods in DeviceMethods. And for
qdev_init_gpio_in, based on FnCall, it can support idiomatic Rust
callback without the need for C style wrapper.

Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
Link: https://lore.kernel.org/r/20250210030051.2562726-5-zhao1.liu@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Zhao Liu 2025-02-10 11:00:45 +08:00 committed by Paolo Bonzini
parent e6f1195f55
commit 9a96d41007
2 changed files with 43 additions and 5 deletions

View file

@ -6,17 +6,18 @@
use std::{
ffi::{CStr, CString},
os::raw::c_void,
os::raw::{c_int, c_void},
ptr::NonNull,
};
pub use bindings::{Clock, ClockEvent, DeviceClass, DeviceState, Property, ResetType};
use crate::{
bindings::{self, Error, ResettableClass},
bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass},
callbacks::FnCall,
cell::bql_locked,
chardev::Chardev,
irq::InterruptSource,
prelude::*,
qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned},
vmstate::VMStateDescription,
@ -28,8 +29,8 @@ pub trait ResettablePhasesImpl {
/// If not None, this is called when the object enters reset. It
/// can reset local state of the object, but it must not do anything that
/// has a side-effect on other objects, such as raising or lowering an
/// [`InterruptSource`](crate::irq::InterruptSource), or reading or
/// writing guest memory. It takes the reset's type as argument.
/// [`InterruptSource`], or reading or writing guest memory. It takes the
/// reset's type as argument.
const ENTER: Option<fn(&Self, ResetType)> = None;
/// If not None, this is called when the object for entry into reset, once
@ -318,6 +319,44 @@ where
bindings::qdev_prop_set_chr(self.as_mut_ptr(), c_propname.as_ptr(), chr.as_mut_ptr());
}
}
fn init_gpio_in<F: for<'a> FnCall<(&'a Self::Target, u32, u32)>>(
&self,
num_lines: u32,
_cb: F,
) {
let _: () = F::ASSERT_IS_SOME;
unsafe extern "C" fn rust_irq_handler<T, F: for<'a> FnCall<(&'a T, u32, u32)>>(
opaque: *mut c_void,
line: c_int,
level: c_int,
) {
// SAFETY: the opaque was passed as a reference to `T`
F::call((unsafe { &*(opaque.cast::<T>()) }, line as u32, level as u32))
}
let gpio_in_cb: unsafe extern "C" fn(*mut c_void, c_int, c_int) =
rust_irq_handler::<Self::Target, F>;
unsafe {
qdev_init_gpio_in(
self.as_mut_ptr::<DeviceState>(),
Some(gpio_in_cb),
num_lines as c_int,
);
}
}
fn init_gpio_out(&self, pins: &[InterruptSource]) {
unsafe {
qdev_init_gpio_out(
self.as_mut_ptr::<DeviceState>(),
InterruptSource::slice_as_ptr(pins),
pins.len() as c_int,
);
}
}
}
impl<R: ObjectDeref> DeviceMethods for R where R::Target: IsA<DeviceState> {}