2016-04-30 6 views
4

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.

+1

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

+0

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

+0

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

Antwort

1

So habe ich strace wie Shepmaster empfohlen, und fand das Problem: das offset Feld der CMapDumb Struktur sollte ein u64 gewesen sein statt einem u32 (ich war zu schnell Kopieren von dem C Original). Der tatsächliche Offset, der von der DRM_IOCTL_MODE_MAP_DUMB IOCTL zurückgegeben wird, ist 33 Bits lang, und ich verlor die bedeutendste. Es funktioniert jetzt gut.

+0

Das ist großartig! Wenn Sie eine funktionierende Lösung bekommen, die aber noch klein ist, möchten Sie vielleicht auf [Code Review ] (http://codereview.stackexchange.com/questions/tagged/rust), da es einen weniger idiomatischen Rust-Code gibt, der aufgeräumt werden könnte ;-). – Shepmaster

+0

Danke für Ihren Vorschlag.Ich behalte es im Hinterkopf, aber der komplette Code ist schon 1400 Zeilen lang, also schätze ich, dass er nicht mehr als "immer noch klein" gilt ... – Karamazov