1. 程式人生 > >最簡單的DRM應用程式 (single-buffer)

最簡單的DRM應用程式 (single-buffer)

在上一篇DRM (Direct Rendering Manager) 學習簡介 中,我們學習了DRM的基本概念以及基本組成元素。從本篇開始,我將以示例程式碼的形式,給大家分享學習DRM驅動開發的整個學習過程。

在學習DRM驅動之前,應該首先了解如何使用DRM驅動。以下使用虛擬碼的方式,簡單介紹如何編寫一個最簡單的DRM應用程式。

虛擬碼:

int main(int argc, char **argv)
{
	/* open the drm device */
	open("/dev/dri/card0");

	/* get crtc/encoder/connector id */
	drmModeGetResources
(...); /* get connector for display mode */ drmModeGetConnector(...); /* create a dumb-buffer */ drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB); /* bind the dumb-buffer to an FB object */ drmModeAddFB(...); /* map the dumb buffer for userspace drawing */ drmIoctl(DRM_IOCTL_MODE_MAP_DUMB); mmap(...); /* start display */
drmModeSetCrtc(crtc_id, fb_id, connector_id, mode); }

當執行完mmap之後,我們就可以直接在應用層對framebuffer進行繪圖操作了。

詳細參考程式碼如下:

modeset-single-buffer.c

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <sys/mman.h> #include <time.h> #include <unistd.h> #include <xf86drm.h> #include <xf86drmMode.h> struct buffer_object { uint32_t width; uint32_t height; uint32_t pitch; uint32_t handle; uint32_t size; uint8_t *vaddr; uint32_t fb_id; }; struct buffer_object buf; static int modeset_create_fb(int fd, struct buffer_object *bo) { struct drm_mode_create_dumb create = {}; struct drm_mode_map_dumb map = {}; /* create a dumb-buffer, the pixel format is XRGB888 */ create.width = bo->width; create.height = bo->height; create.bpp = 32; drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); /* bind the dumb-buffer to an FB object */ bo->pitch = create.pitch; bo->size = create.size; bo->handle = create.handle; drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch, bo->handle, &bo->fb_id); /* map the dumb-buffer to userspace */ map.handle = create.handle; drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map); bo->vaddr = mmap(0, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, map.offset); /* initialize the dumb-buffer with white-color */ memset(bo->vaddr, 0xff, bo->size); return 0; } static void modeset_destroy_fb(int fd, struct buffer_object *bo) { struct drm_mode_destroy_dumb destroy = {}; drmModeRmFB(fd, bo->fb_id); munmap(bo->vaddr, bo->size); destroy.handle = bo->handle; drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy); } int main(int argc, char **argv) { int fd; drmModeConnector *conn; drmModeRes *res; uint32_t conn_id; uint32_t crtc_id; fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC); res = drmModeGetResources(fd); crtc_id = res->crtcs[0]; conn_id = res->connectors[0]; conn = drmModeGetConnector(fd, conn_id); buf.width = conn->modes[0].hdisplay; buf.height = conn->modes[0].vdisplay; modeset_create_fb(fd, &buf); drmModeSetCrtc(fd, crtc_id, buf.fb_id, 0, 0, &conn_id, 1, &conn->modes[0]); getchar(); modeset_destroy_fb(fd, &buf); drmModeFreeConnector(conn); drmModeFreeResources(res); close(fd); return 0; }

上面程式碼有一個關鍵的函式,它就是drmModeSetCrtc(),該函式需要crtc_idconnector_idfb_iddrm_mode 這幾個引數。可以看到,幾乎所有的程式碼都是為了該函式能夠順利傳參而編寫的:

為了獲取 crtc_idconnector_id,需要呼叫 drmModeGetResources()
為了獲取 fb_id,需要呼叫 drmModeAddFB()
為了獲取 drm_mode,需要呼叫 drmModeGetConnector

通過呼叫drmModeSetCrtc(),整個底層顯示pipeline硬體就都初始化好了,並且還在螢幕上顯示出了FB的內容,非常簡單。

以上程式碼其實是基於kernel DRM maintainer David Herrmann 所寫的drm-howto/modeset.c 檔案修改的,需要注意的是,以上參考程式碼刪除了許多異常錯誤處理,且只有在以下條件都滿足時,才能正常執行:

  1. DRM驅動支援MODESET;
  2. DRM驅動支援dumb-buffer(即連續實體記憶體);
  3. DRM驅動至少支援1個CRTC,1個Encoder,1個Connector;
  4. DRM驅動的Connector至少包含1個有效的drm_display_mode。

執行結果:(模擬效果)
modeset-single-buffer.gif-46.9kB
描述:程式執行後,顯示全屏白色,等待使用者輸入按鍵;當用戶按下任意按鍵後,程式退出,顯示黑屏。