Seit einiger Zeit habe ich mit Linux 'Direct Rendering Manager gespielt, die eine sehr sehr Low-Level-Grafikverwaltung ermöglicht. Dies geschieht normalerweise in C mit Hilfe von libdrm oder direkt unter Verwendung der DRM headers.Aufruf von mmap auf Dummbuffer mit Linux Direct Rendering Manager in Rust scheitert bei der Arbeit in C
Ich versuche, ein Äquivalent zu libdrm in Rust zu erstellen, das wäre nicht nur eine Bindung an die C-Bibliothek, sondern würde direkt die syscalls verwenden. Dies ist keine leichte Aufgabe, da es dort draußen fast keine Dokumentation für DRM gibt, aber ich folge this example in C, um Hinweise zu bekommen, wo ich anfangen soll.
Ich bin jetzt an dem Punkt angekommen, wo ich einen dummen Puffer erstellen und im Speicher abbilden soll, damit ich Pixel pro Pixel ändern kann, was auf dem Bildschirm erscheint. Dafür muss ich mmap
verwenden, aber ich bekomme einen wirklich seltsamen Fehler.
ist hier ein minimaler Arbeits Code in C:
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <drm/drm.h>
#include <drm/drm_mode.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
int main() {
// STEP 1: GET ACCESS TO DRM
int fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
if (fd < 0) {
printf("Error in function open(): %s\n", strerror(errno));
return 1;
}
// STEP 2: CREATE DUMBBUFFER
struct drm_mode_create_dumb dreq;
dreq.height = 1080,
dreq.width = 1920,
dreq.bpp = 32,
dreq.flags = 0,
dreq.handle = 0,
dreq.pitch = 0,
dreq.size = 0;
int ret = ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dreq);
if (ret == -1) {
printf("Call to DRM_IOCTL_MODE_CREATE_DUMB failed: %s\n",
strerror(errno));
return 1;
}
// STEP 3: ADD FRAMEBUFFER
struct drm_mode_fb_cmd creq;
creq.fb_id = 0;
creq.width = dreq.width;
creq.height = dreq.height;
creq.pitch = dreq.pitch;
creq.bpp = dreq.bpp;
creq.depth = 24;
creq.handle = dreq.handle;
ret = ioctl(fd, DRM_IOCTL_MODE_ADDFB, &creq);
if (ret == -1) {
printf("Call to DRM_IOCTL_MODE_ADDFB failed: %s\n", strerror(errno));
return 1;
}
// STEP 4: PREPARE FOR MAPPING
struct drm_mode_map_dumb mreq;
mreq.handle = dreq.handle;
mreq.pad = 0;
mreq.offset = 0;
ret = ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
if (ret == -1) {
printf("Call to DRM_IOCTL_MODE_MAP_DUMB failed: %s\n", strerror(errno));
return 1;
}
// STEP 5: MAPPING PROPER
void *map = mmap(0, dreq.size, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, mreq.offset);
if (map == MAP_FAILED) {
printf("Error in function mmap(): %s\n", strerror(errno));
return 1;
} else {
printf("Address of mapped data: 0x%x\n", map);
}
return 0;
}
Dies ist genau die gleiche Code in Rust. Natürlich hat mein echter Code viel mehr Dinge drin, aber diese minimal ist genug, um den Fehler zu erhalten:
#![feature(libc)]
extern crate libc;
use self::libc::{c_char, c_int, c_ulong, c_void, off_t, size_t};
extern {
pub fn ioctl(fd : c_int, request : c_ulong, arg : *mut c_void) -> c_int;
}
fn errno() -> c_int {
unsafe { *libc::__errno_location() }
}
fn get_c_error() -> String {
unsafe {
let strerr = libc::strerror(errno()) as *mut u8;
let length = libc::strlen(strerr as *const c_char) as usize;
let mut string = String::with_capacity(length);
for i in 0..length {
let car = *strerr.offset(i as isize) as char;
if car == (0 as char) { break; }
string.push(car);
}
string
}
}
#[repr(C)]
struct CCreateDumb {
height : u32,
width : u32,
bpp : u32,
_flags : u32,
handle : u32,
pitch : u32,
size : u64,
}
#[repr(C)]
struct CFrameBuffer {
_fb_id : u32,
_width : u32,
_height : u32,
_pitch : u32,
_bpp : u32,
_depth : u32,
_handle : u32,
}
#[repr(C)]
struct CMapDumb {
_handle : u32,
_pad : u32,
offset : u32,
}
fn main() {
// STEP 1: GET ACCESS TO DRM
let pathname = "/dev/dri/card0".to_string();
let fd : c_int = unsafe {
libc::open(pathname.as_ptr() as *const c_char,
libc::O_RDWR | libc::O_CLOEXEC)
};
if fd < 0 {
panic!("Error in call of C function open(): {}", get_c_error());
}
// STEP 2: CREATE DUMBBUFFER
let mut dreq = CCreateDumb {
height : 1080,
width : 1920,
bpp : 32,
_flags : 0,
handle : 0,
pitch : 0,
size : 0,
};
// NB : 0xc02064b2 = DRM_IOCTL_MODE_CREATE_DUMB
let mut ret = unsafe {
ioctl(fd, 0xc02064b2 as c_ulong, &mut dreq as *mut _ as *mut c_void)
};
if ret == -1 {
panic!("Call to DRM_IOCTL_MODE_CREATE_DUMB failed: {}", get_c_error());
}
// STEP 3: ADD FRAMEBUFFER
let mut creq = CFrameBuffer {
_fb_id : 0,
_width : dreq.width,
_height : dreq.height,
_pitch : dreq.pitch,
_bpp : dreq.bpp,
_depth : 24,
_handle : dreq.handle,
};
// NB : 0xc01c64ae = DRM_IOCTL_MODE_ADDFB
ret = unsafe {
ioctl(fd, 0xc01c64ae as c_ulong, &mut creq as *mut _ as *mut c_void)
};
if ret == -1 {
panic!("Call to DRM_IOCTL_MODE_ADDFB failed: {}", get_c_error());
}
// STEP 4: PREPARE FOR MAPPING
let mut mreq = CMapDumb {
_handle : dreq.handle,
_pad : 0,
offset : 0,
};
// NB : 0xc01064b3 = DRM_IOCTL_MODE_MAP_DUMB
ret = unsafe {
ioctl(fd, 0xc01064b3 as c_ulong, &mut mreq as *mut _ as *mut c_void)
};
if ret == -1 {
panic!("Call to DRM_IOCTL_MODE_MAP_DUMB failed: {}", get_c_error());
}
// STEP 5: MAPPING PROPER
let map = unsafe {
libc::mmap(
0 as *mut c_void,
dreq.size as size_t,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
mreq.offset as off_t
)
};
if map == libc::MAP_FAILED {
panic!("Error in call of C function mmap(): {}", get_c_error());
} else {
println!("Address of mapped data: 0x{:p}", map);
}
}
Es gerade fein kompiliert, aber wenn ich es ausführen, bekomme ich diesen Fehler.
thread '' panicked at 'Error in call of C function mmap(): Invalid argument', memmapping.rs:139 note: Run with
RUST_BACKTRACE=1
for a backtrace.
einen extern
Block Verwendung direkt gegenüber der ursprünglichen C mmap
Funktion statt dem Rost eine der Kiste zu verbinden libc
nichts ändert.
nahm ich einen Blick darauf, wie this projectmmap
genannt, und versucht, das gleiche zu tun, um sicher zu sein, dass size
und offset
sind seitenausgerichtet ist, aber es hat nichts ändern, da sie bereits Seite ausgerichtet waren.
This SO question verwendet eine Einrichtung der Stdlib namens std::os::MemoryMap
, aber es existiert nicht mehr.
Können Sie die Werte hinzufügen, die in jedem Fall an 'mmap' übergeben werden? Fügen Sie möglicherweise auch den "strace" -Ausgang jedes "mmap" -Aufrufs hinzu, nur um zu sehen, wie unterschiedlich sie sind? – Shepmaster
Ich bin auch überrascht, dass Sie beim Öffnen der Datei keine Fehler bekommen. Rust-Strings sind nicht NUL-terminiert, so dass 'libc :: open (Pfadname.as_ptr()' eine Fußpistole ist, die darauf wartet, zu passieren. Ich würde empfehlen, etwas wie ['AsRawFd'] zu verwenden (http: //doc.rust- lang.org/std/os/unix/io/trait.AsRawFd.html) auf eine 'Datei' Sie interessieren sich wahrscheinlich auch für [' CStr'] (http://doc.rust-lang.org/std/ ffi/struct.CStr.html) (und 'CString'). – Shepmaster
Die Verwendung von' fs :: File' ist sicherlich besser für die eigentliche lib, aber in meinem Beispiel hier wollte ich so nah wie möglich an dem C-Gegenstück bleiben, um die möglichen Unterschiede einzugrenzen. Was "strace" anbelangt, hat es den Zweck. Weitere Informationen in meiner Antwort unten. – Karamazov