autocommit 2022-01-28 03:35:35 CET
This commit is contained in:
parent
c340df0d08
commit
a5573b8a3a
36 changed files with 3046 additions and 114 deletions
16
src/drm/dma.rs
Normal file
16
src/drm/dma.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use crate::format::Format;
|
||||
use uapi::OwnedFd;
|
||||
|
||||
pub struct DmaBufPlane {
|
||||
pub offset: u32,
|
||||
pub stride: u32,
|
||||
pub fd: OwnedFd,
|
||||
}
|
||||
|
||||
pub struct DmaBuf {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub format: &'static Format,
|
||||
pub modifier: u64,
|
||||
pub planes: Vec<DmaBufPlane>,
|
||||
}
|
||||
395
src/drm/drm.rs
Normal file
395
src/drm/drm.rs
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
use crate::utils::bitflags::BitflagsExt;
|
||||
use crate::utils::debug_fn::debug_fn;
|
||||
use crate::utils::ptr_ext::PtrExt;
|
||||
use bstr::ByteSlice;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::ptr;
|
||||
use thiserror::Error;
|
||||
use uapi::c::c_char;
|
||||
use uapi::{c, Errno, OwnedFd, Ustring};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DrmError {
|
||||
#[error("Could not create a lease")]
|
||||
CreateLeaseError(#[source] std::io::Error),
|
||||
#[error("Could not reopen a node")]
|
||||
ReopenNode(#[source] std::io::Error),
|
||||
#[error("Could not retrieve the render node name")]
|
||||
RenderNodeName,
|
||||
#[error("Could not retrieve the device node name")]
|
||||
DeviceNodeName,
|
||||
#[error("Could not retrieve device")]
|
||||
GetDevice(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
const DRM_NODE_PRIMARY: c::c_int = 0;
|
||||
const DRM_NODE_CONTROL: c::c_int = 1;
|
||||
const DRM_NODE_RENDER: c::c_int = 2;
|
||||
const DRM_NODE_MAX: c::c_int = 3;
|
||||
|
||||
const DRM_BUS_PCI: c::c_int = 0;
|
||||
const DRM_BUS_USB: c::c_int = 1;
|
||||
const DRM_BUS_PLATFORM: c::c_int = 2;
|
||||
const DRM_BUS_HOST1X: c::c_int = 3;
|
||||
|
||||
#[link(name = "drm")]
|
||||
extern "C" {
|
||||
fn drmIsMaster(fd: c::c_int) -> c::c_int;
|
||||
fn drmModeCreateLease(
|
||||
fd: c::c_int,
|
||||
o: *const u32,
|
||||
num_objects: c::c_int,
|
||||
flags: c::c_int,
|
||||
lessee_id: *mut u32,
|
||||
) -> c::c_int;
|
||||
fn drmGetNodeTypeFromFd(fd: c::c_int) -> c::c_int;
|
||||
fn drmGetRenderDeviceNameFromFd(fd: c::c_int) -> *mut c::c_char;
|
||||
fn drmGetDeviceNameFromFd2(fd: c::c_int) -> *mut c::c_char;
|
||||
fn drmFreeDevice(device: *mut *mut drmDevice);
|
||||
fn drmGetDevice(fd: c::c_int, device: *mut *mut drmDevice) -> c::c_int;
|
||||
}
|
||||
|
||||
fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
|
||||
unsafe {
|
||||
let name = drmGetRenderDeviceNameFromFd(fd);
|
||||
if name.is_null() {
|
||||
Err(DrmError::RenderNodeName)
|
||||
} else {
|
||||
Ok(CString::from_raw(name).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn device_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
|
||||
unsafe {
|
||||
let name = drmGetDeviceNameFromFd2(fd);
|
||||
if name.is_null() {
|
||||
Err(DrmError::DeviceNodeName)
|
||||
} else {
|
||||
Ok(CString::from_raw(name).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reopen(fd: c::c_int, allow_downgrade: bool) -> Result<OwnedFd, DrmError> {
|
||||
unsafe {
|
||||
if drmIsMaster(fd) != 0 {
|
||||
let mut lessee = 0;
|
||||
let lease_fd = drmModeCreateLease(fd, ptr::null(), 0, c::O_CLOEXEC, &mut lessee);
|
||||
if lease_fd >= 0 {
|
||||
return Ok(OwnedFd::new(lease_fd));
|
||||
}
|
||||
}
|
||||
let path = if drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER {
|
||||
uapi::format_ustr!("/proc/self/fd/{}", fd)
|
||||
} else if allow_downgrade {
|
||||
render_node_name(fd)?
|
||||
} else {
|
||||
device_node_name(fd)?
|
||||
};
|
||||
match uapi::open(&path, c::O_RDWR | c::O_CLOEXEC, 0) {
|
||||
Ok(f) => Ok(f),
|
||||
Err(e) => Err(DrmError::ReopenNode(e.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Drm {
|
||||
fd: OwnedFd,
|
||||
}
|
||||
|
||||
impl Drm {
|
||||
pub fn new(fd: c::c_int, allow_downgrade: bool) -> Result<Self, DrmError> {
|
||||
Ok(Self {
|
||||
fd: reopen(fd, allow_downgrade)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> c::c_int {
|
||||
self.fd.raw()
|
||||
}
|
||||
|
||||
pub fn dup_unprivileged(&self) -> Result<Self, DrmError> {
|
||||
Self::new(self.fd.raw(), true)
|
||||
}
|
||||
|
||||
pub fn get_device(&self) -> Result<DrmDevice, DrmError> {
|
||||
unsafe {
|
||||
let mut dev = ptr::null_mut();
|
||||
if drmGetDevice(self.fd.raw(), &mut dev) < 0 {
|
||||
return Err(DrmError::GetDevice(Errno::default().into()));
|
||||
}
|
||||
Ok(DrmDevice { dev })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmPciBusInfo {
|
||||
domain: u16,
|
||||
bus: u8,
|
||||
dev: u8,
|
||||
func: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmUsbBusInfo {
|
||||
bus: u8,
|
||||
dev: u8,
|
||||
}
|
||||
|
||||
const DRM_PLATFORM_DEVICE_NAME_LEN: usize = 512;
|
||||
|
||||
#[repr(C)]
|
||||
struct drmPlatformBusInfo {
|
||||
fullname: [c::c_char; DRM_PLATFORM_DEVICE_NAME_LEN],
|
||||
}
|
||||
|
||||
const DRM_HOST1X_DEVICE_NAME_LEN: usize = 512;
|
||||
|
||||
#[repr(C)]
|
||||
struct drmHost1xBusInfo {
|
||||
fullname: [c::c_char; DRM_HOST1X_DEVICE_NAME_LEN],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
union businfo {
|
||||
pci: *mut drmPciBusInfo,
|
||||
usb: *mut drmUsbBusInfo,
|
||||
platform: *mut drmPlatformBusInfo,
|
||||
host1x: *mut drmHost1xBusInfo,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmPciDeviceInfo {
|
||||
vendor_id: u16,
|
||||
device_id: u16,
|
||||
subvendor_id: u16,
|
||||
subdevice_id: u16,
|
||||
revision_id: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmUsbDeviceInfo {
|
||||
vendor: u16,
|
||||
product: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmPlatformDeviceInfo {
|
||||
compatible: *mut *mut c::c_char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmHost1xDeviceInfo {
|
||||
compatible: *mut *mut c::c_char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
union deviceinfo {
|
||||
pci: *mut drmPciDeviceInfo,
|
||||
usb: *mut drmUsbDeviceInfo,
|
||||
platform: *mut drmPlatformDeviceInfo,
|
||||
host1x: *mut drmHost1xDeviceInfo,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmDevice {
|
||||
nodes: *mut *mut c::c_char,
|
||||
available_nodes: c::c_int,
|
||||
bustype: c::c_int,
|
||||
businfo: businfo,
|
||||
deviceinfo: deviceinfo,
|
||||
}
|
||||
|
||||
pub struct DrmDevice {
|
||||
dev: *mut drmDevice,
|
||||
}
|
||||
|
||||
impl Drop for DrmDevice {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
drmFreeDevice(&mut self.dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DrmDevice {
|
||||
pub fn nodes<'a>(&'a self) -> impl Iterator<Item = (c::c_int, &'a CStr)> + 'a {
|
||||
struct Iter<'a> {
|
||||
next: usize,
|
||||
dev: &'a DrmDevice,
|
||||
}
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = (c::c_int, &'a CStr);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
let dev = self.dev.dev.deref();
|
||||
while self.next < DRM_NODE_MAX as _ {
|
||||
let idx = self.next;
|
||||
self.next += 1;
|
||||
if dev.available_nodes.contains(1 << idx) {
|
||||
return Some((idx as _, CStr::from_ptr(*dev.nodes.add(idx))));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
Iter { next: 0, dev: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DrmDevice {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
struct StrStr<'a> {
|
||||
v: &'a [*mut c::c_char],
|
||||
}
|
||||
impl Debug for StrStr<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let mut list = f.debug_list();
|
||||
for &v in self.v {
|
||||
if v.is_null() {
|
||||
list.entry(&v);
|
||||
} else {
|
||||
unsafe {
|
||||
list.entry(&CStr::from_ptr(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
list.finish()
|
||||
}
|
||||
}
|
||||
impl<'a> StrStr<'a> {
|
||||
fn from_nt(nt: *const *mut c_char) -> Self {
|
||||
unsafe {
|
||||
let mut num = 0;
|
||||
let mut tmp = nt;
|
||||
while !tmp.deref().is_null() {
|
||||
num += 1;
|
||||
tmp = tmp.add(1);
|
||||
}
|
||||
Self {
|
||||
v: std::slice::from_raw_parts(nt, num),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut ds = f.debug_struct("DrmDevice");
|
||||
unsafe {
|
||||
let dev = self.dev.deref();
|
||||
let nodes = std::slice::from_raw_parts(dev.nodes, DRM_NODE_MAX as _);
|
||||
ds.field(
|
||||
"available_nodes",
|
||||
&debug_fn(|f| write!(f, "0b{:b}", dev.available_nodes)),
|
||||
);
|
||||
ds.field("nodes", &StrStr { v: nodes });
|
||||
ds.field("bustype", &dev.bustype);
|
||||
match dev.bustype {
|
||||
DRM_BUS_PCI => {
|
||||
ds.field(
|
||||
"businfo",
|
||||
&debug_fn(|f| {
|
||||
let pci = dev.businfo.pci.deref();
|
||||
f.debug_struct("drmPciBusInfo")
|
||||
.field("domain", &pci.domain)
|
||||
.field("bus", &pci.bus)
|
||||
.field("dev", &pci.dev)
|
||||
.field("func", &pci.func)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
ds.field(
|
||||
"deviceinfo",
|
||||
&debug_fn(|f| {
|
||||
let pci = dev.deviceinfo.pci.deref();
|
||||
f.debug_struct("drmPciDeviceInfo")
|
||||
.field("vendor_id", &pci.vendor_id)
|
||||
.field("device_id", &pci.device_id)
|
||||
.field("subvendor_id", &pci.subvendor_id)
|
||||
.field("subdevice_id", &pci.subdevice_id)
|
||||
.field("revision_id", &pci.revision_id)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
}
|
||||
DRM_BUS_USB => {
|
||||
ds.field(
|
||||
"businfo",
|
||||
&debug_fn(|f| {
|
||||
let usb = dev.businfo.usb.deref();
|
||||
f.debug_struct("drmUsbBusInfo")
|
||||
.field("bus", &usb.bus)
|
||||
.field("dev", &usb.dev)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
ds.field(
|
||||
"deviceinfo",
|
||||
&debug_fn(|f| {
|
||||
let usb = dev.deviceinfo.usb.deref();
|
||||
f.debug_struct("drmUsbDeviceInfo")
|
||||
.field("vendor", &usb.vendor)
|
||||
.field("product", &usb.product)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
}
|
||||
DRM_BUS_PLATFORM => {
|
||||
ds.field(
|
||||
"businfo",
|
||||
&debug_fn(|f| {
|
||||
let platform = dev.businfo.platform.deref();
|
||||
f.debug_struct("drmPlatformBusInfo")
|
||||
.field(
|
||||
"fullname",
|
||||
&CStr::from_ptr(platform.fullname.as_ptr())
|
||||
.to_bytes()
|
||||
.as_bstr(),
|
||||
)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
ds.field(
|
||||
"deviceinfo",
|
||||
&debug_fn(|f| {
|
||||
let platform = dev.deviceinfo.platform.deref();
|
||||
f.debug_struct("drmPlatformDeviceInfo")
|
||||
.field("compatible", &StrStr::from_nt(platform.compatible))
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
}
|
||||
DRM_BUS_HOST1X => {
|
||||
ds.field(
|
||||
"businfo",
|
||||
&debug_fn(|f| {
|
||||
let host1x = dev.businfo.host1x.deref();
|
||||
f.debug_struct("drmHost1xBusInfo")
|
||||
.field(
|
||||
"fullname",
|
||||
&CStr::from_ptr(host1x.fullname.as_ptr())
|
||||
.to_bytes()
|
||||
.as_bstr(),
|
||||
)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
ds.field(
|
||||
"deviceinfo",
|
||||
&debug_fn(|f| {
|
||||
let host1x = dev.deviceinfo.host1x.deref();
|
||||
f.debug_struct("drmHost1xDeviceInfo")
|
||||
.field("compatible", &StrStr::from_nt(host1x.compatible))
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ds.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
169
src/drm/gbm.rs
Normal file
169
src/drm/gbm.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
use crate::drm::dma::{DmaBuf, DmaBufPlane};
|
||||
use crate::drm::drm::{Drm, DrmError};
|
||||
use crate::drm::{ModifiedFormat, INVALID_MODIFIER};
|
||||
use crate::format::formats;
|
||||
use std::ptr;
|
||||
use thiserror::Error;
|
||||
use uapi::{c, OwnedFd};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GbmError {
|
||||
#[error("The drm subsystem returned an error")]
|
||||
Drm(#[from] DrmError),
|
||||
#[error("Cloud not create a gbm device")]
|
||||
CreateDevice,
|
||||
#[error("Cloud not create a gbm buffer")]
|
||||
CreateBo,
|
||||
#[error("gbm buffer has an unknown format")]
|
||||
UnknownFormat,
|
||||
#[error("Could not retrieve a drm-buf fd")]
|
||||
DrmFd,
|
||||
}
|
||||
|
||||
type Device = u8;
|
||||
type Bo = u8;
|
||||
|
||||
pub const GBM_BO_USE_SCANOUT: u32 = 1 << 0;
|
||||
pub const GBM_BO_USE_CURSOR: u32 = 1 << 1;
|
||||
pub const GBM_BO_USE_RENDERING: u32 = 1 << 2;
|
||||
pub const GBM_BO_USE_WRITE: u32 = 1 << 3;
|
||||
pub const GBM_BO_USE_LINEAR: u32 = 1 << 4;
|
||||
pub const GBM_BO_USE_PROTECTED: u32 = 1 << 5;
|
||||
|
||||
#[link(name = "gbm")]
|
||||
extern "C" {
|
||||
fn gbm_create_device(fd: c::c_int) -> *mut Device;
|
||||
fn gbm_device_destroy(dev: *mut Device);
|
||||
|
||||
fn gbm_bo_create_with_modifiers2(
|
||||
dev: *mut Device,
|
||||
width: u32,
|
||||
height: u32,
|
||||
format: u32,
|
||||
modifiers: *const u64,
|
||||
count: c::c_uint,
|
||||
flags: u32,
|
||||
) -> *mut Bo;
|
||||
fn gbm_bo_destroy(bo: *mut Bo);
|
||||
fn gbm_bo_get_plane_count(bo: *mut Bo) -> c::c_int;
|
||||
fn gbm_bo_get_width(bo: *mut Bo) -> u32;
|
||||
fn gbm_bo_get_height(bo: *mut Bo) -> u32;
|
||||
fn gbm_bo_get_stride(bo: *mut Bo) -> u32;
|
||||
fn gbm_bo_get_modifier(bo: *mut Bo) -> u64;
|
||||
fn gbm_bo_get_stride_for_plane(bo: *mut Bo, plane: c::c_int) -> u32;
|
||||
fn gbm_bo_get_fd_for_plane(bo: *mut Bo, plane: c::c_int) -> c::c_int;
|
||||
fn gbm_bo_get_offset(bo: *mut Bo, plane: c::c_int) -> u32;
|
||||
fn gbm_bo_get_format(bo: *mut Bo) -> u32;
|
||||
fn gbm_bo_get_bpp(bo: *mut Bo) -> u32;
|
||||
}
|
||||
|
||||
pub struct GbmDevice {
|
||||
drm: Drm,
|
||||
dev: *mut Device,
|
||||
}
|
||||
|
||||
struct BoHolder {
|
||||
bo: *mut Bo,
|
||||
}
|
||||
|
||||
pub struct GbmBo {
|
||||
bo: BoHolder,
|
||||
dma: DmaBuf,
|
||||
}
|
||||
|
||||
unsafe fn export_bo(bo: *mut Bo) -> Result<DmaBuf, GbmError> {
|
||||
Ok(DmaBuf {
|
||||
width: gbm_bo_get_width(bo) as _,
|
||||
height: gbm_bo_get_height(bo) as _,
|
||||
modifier: gbm_bo_get_modifier(bo),
|
||||
format: {
|
||||
let format = gbm_bo_get_format(bo);
|
||||
match formats().get(&format).copied() {
|
||||
Some(f) => f,
|
||||
_ => return Err(GbmError::UnknownFormat),
|
||||
}
|
||||
},
|
||||
planes: {
|
||||
let mut planes = vec![];
|
||||
for plane in 0..gbm_bo_get_plane_count(bo) {
|
||||
let offset = gbm_bo_get_offset(bo, plane);
|
||||
let stride = gbm_bo_get_stride_for_plane(bo, plane);
|
||||
let fd = gbm_bo_get_fd_for_plane(bo, plane);
|
||||
if fd < 0 {
|
||||
return Err(GbmError::DrmFd);
|
||||
}
|
||||
planes.push(DmaBufPlane {
|
||||
offset,
|
||||
stride,
|
||||
fd: OwnedFd::new(fd),
|
||||
})
|
||||
}
|
||||
planes
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
impl GbmDevice {
|
||||
pub fn new(drm: &Drm) -> Result<Self, GbmError> {
|
||||
let drm = drm.dup_unprivileged()?;
|
||||
let dev = unsafe { gbm_create_device(drm.raw()) };
|
||||
if dev.is_null() {
|
||||
Err(GbmError::CreateDevice)
|
||||
} else {
|
||||
Ok(Self { drm, dev })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_bo(
|
||||
&self,
|
||||
width: i32,
|
||||
height: i32,
|
||||
format: &ModifiedFormat,
|
||||
usage: u32,
|
||||
) -> Result<GbmBo, GbmError> {
|
||||
unsafe {
|
||||
let (modifiers, n_modifiers) = if format.modifier == INVALID_MODIFIER {
|
||||
(ptr::null(), 0)
|
||||
} else {
|
||||
(&format.modifier as _, 1)
|
||||
};
|
||||
let bo = gbm_bo_create_with_modifiers2(
|
||||
self.dev,
|
||||
width as _,
|
||||
height as _,
|
||||
format.format.drm,
|
||||
modifiers,
|
||||
n_modifiers,
|
||||
usage,
|
||||
);
|
||||
if bo.is_null() {
|
||||
return Err(GbmError::CreateBo);
|
||||
}
|
||||
let bo = BoHolder { bo };
|
||||
let dma = export_bo(bo.bo)?;
|
||||
Ok(GbmBo { bo, dma })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GbmDevice {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gbm_device_destroy(self.dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GbmBo {
|
||||
pub fn dma(&self) -> &DmaBuf {
|
||||
&self.dma
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BoHolder {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gbm_bo_destroy(self.bo);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/drm/mod.rs
Normal file
14
src/drm/mod.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
use crate::format::Format;
|
||||
|
||||
pub mod dma;
|
||||
pub mod drm;
|
||||
pub mod gbm;
|
||||
|
||||
pub type Modifier = u64;
|
||||
|
||||
pub const INVALID_MODIFIER: Modifier = 0x00ff_ffff_ffff_ffff;
|
||||
|
||||
pub struct ModifiedFormat {
|
||||
pub format: &'static Format,
|
||||
pub modifier: Modifier,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue