1
0
Fork 0
forked from wry/wry

autocommit 2022-01-28 03:35:35 CET

This commit is contained in:
Julian Orth 2022-01-28 03:35:35 +01:00
parent c340df0d08
commit a5573b8a3a
36 changed files with 3046 additions and 114 deletions

16
src/drm/dma.rs Normal file
View 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
View 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
View 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
View 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,
}