use crate::{
alloc::allocator::Kmalloc,
bindings, device, drm,
drm::driver::AllocImpl,
error::from_err_ptr,
error::Result,
prelude::*,
sync::aref::{ARef, AlwaysRefCounted},
types::Opaque,
};
use core::{alloc::Layout, mem, ops::Deref, ptr, ptr::NonNull};
#[cfg(CONFIG_DRM_LEGACY)]
macro_rules! drm_legacy_fields {
( $($field:ident: $val:expr),* $(,)? ) => {
bindings::drm_driver {
$( $field: $val ),*,
firstopen: None,
preclose: None,
dma_ioctl: None,
dma_quiescent: None,
context_dtor: None,
irq_handler: None,
irq_preinstall: None,
irq_postinstall: None,
irq_uninstall: None,
get_vblank_counter: None,
enable_vblank: None,
disable_vblank: None,
dev_priv_size: 0,
}
}
}
#[cfg(not(CONFIG_DRM_LEGACY))]
macro_rules! drm_legacy_fields {
( $($field:ident: $val:expr),* $(,)? ) => {
bindings::drm_driver {
$( $field: $val ),*
}
}
}
#[repr(C)]
pub struct Device<T: drm::Driver> {
dev: Opaque<bindings::drm_device>,
data: T::Data,
}
impl<T: drm::Driver> Device<T> {
const VTABLE: bindings::drm_driver = drm_legacy_fields! {
load: None,
open: Some(drm::File::<T::File>::open_callback),
postclose: Some(drm::File::<T::File>::postclose_callback),
unload: None,
release: Some(Self::release),
master_set: None,
master_drop: None,
debugfs_init: None,
gem_create_object: T::Object::ALLOC_OPS.gem_create_object,
prime_handle_to_fd: T::Object::ALLOC_OPS.prime_handle_to_fd,
prime_fd_to_handle: T::Object::ALLOC_OPS.prime_fd_to_handle,
gem_prime_import: T::Object::ALLOC_OPS.gem_prime_import,
gem_prime_import_sg_table: T::Object::ALLOC_OPS.gem_prime_import_sg_table,
dumb_create: T::Object::ALLOC_OPS.dumb_create,
dumb_map_offset: T::Object::ALLOC_OPS.dumb_map_offset,
show_fdinfo: None,
fbdev_probe: None,
major: T::INFO.major,
minor: T::INFO.minor,
patchlevel: T::INFO.patchlevel,
name: crate::str::as_char_ptr_in_const_context(T::INFO.name).cast_mut(),
desc: crate::str::as_char_ptr_in_const_context(T::INFO.desc).cast_mut(),
driver_features: drm::driver::FEAT_GEM,
ioctls: T::IOCTLS.as_ptr(),
num_ioctls: T::IOCTLS.len() as i32,
fops: &Self::GEM_FOPS,
};
const GEM_FOPS: bindings::file_operations = drm::gem::create_fops();
pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<ARef<Self>> {
let layout = Kmalloc::aligned_layout(Layout::new::<Self>());
let raw_drm: *mut Self = unsafe {
bindings::__drm_dev_alloc(
dev.as_raw(),
&Self::VTABLE,
layout.size(),
mem::offset_of!(Self, dev),
)
}
.cast();
let raw_drm = NonNull::new(from_err_ptr(raw_drm)?).ok_or(ENOMEM)?;
let raw_data = unsafe { ptr::addr_of_mut!((*raw_drm.as_ptr()).data) };
unsafe { data.__pinned_init(raw_data) }.inspect_err(|_| {
let drm_dev = unsafe { Self::into_drm_device(raw_drm) };
unsafe { bindings::drm_dev_put(drm_dev) };
})?;
Ok(unsafe { ARef::from_raw(raw_drm) })
}
pub(crate) fn as_raw(&self) -> *mut bindings::drm_device {
self.dev.get()
}
unsafe fn from_drm_device(ptr: *const bindings::drm_device) -> *mut Self {
unsafe { crate::container_of!(Opaque::cast_from(ptr), Self, dev) }.cast_mut()
}
unsafe fn into_drm_device(ptr: NonNull<Self>) -> *mut bindings::drm_device {
unsafe { &raw mut (*ptr.as_ptr()).dev }.cast()
}
#[doc(hidden)]
pub unsafe fn from_raw<'a>(ptr: *const bindings::drm_device) -> &'a Self {
let ptr = unsafe { Self::from_drm_device(ptr) };
unsafe { &*ptr.cast() }
}
extern "C" fn release(ptr: *mut bindings::drm_device) {
let this = unsafe { Self::from_drm_device(ptr) };
unsafe { core::ptr::drop_in_place(this) };
}
}
impl<T: drm::Driver> Deref for Device<T> {
type Target = T::Data;
fn deref(&self) -> &Self::Target {
&self.data
}
}
unsafe impl<T: drm::Driver> AlwaysRefCounted for Device<T> {
fn inc_ref(&self) {
unsafe { bindings::drm_dev_get(self.as_raw()) };
}
unsafe fn dec_ref(obj: NonNull<Self>) {
let drm_dev = unsafe { Self::into_drm_device(obj) };
unsafe { bindings::drm_dev_put(drm_dev) };
}
}
impl<T: drm::Driver> AsRef<device::Device> for Device<T> {
fn as_ref(&self) -> &device::Device {
unsafe { device::Device::from_raw((*self.as_raw()).dev) }
}
}
unsafe impl<T: drm::Driver> Send for Device<T> {}
unsafe impl<T: drm::Driver> Sync for Device<T> {}