一、环境资源要求
下载libdrm
Index of /libdrm
这边使用的是2.4.114版本,版本太高对meson版本要求也很高,为了省事用apt安装meson就不用太高版本了,1.x版本虽然使用makefile编译方便但是太老,对应用支持不太好。
https://dri.freedesktop.org/libdrm/libdrm-2.4.114.tar.xz
由于libdrm项目是基于meson构建的,所以需要安装meson
sudo apt install meson
二、代码编译
1、解压并cd到代码目录下
cd libdrm-2.4.114
2、创建目录
#建立安装目录
mkdir install
#创建编译目录
mkdir build
3、创建cross_file.txt文件
#创建并编写cross_file.txt文件,用于配置交叉编译环境
vi cross_file.txt
在cross_file.txt添加如下配置项
[binaries]
c = 'arm-linux-gnueabihf-gcc'
cpp = 'arm-linux-gnueabihf-g++'
ar = 'arm-linux-gnueabihf-ar'
strip = 'arm-linux-gnueabihf-strip'
[host_machine]
system = 'linux'
cpu_family = 'arm'
cpu = 'armv7'
endian = 'little'
[build_machine]
system = 'linux'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'
4、配置编译选项
# cd到build目录
cd build
# 配置编译选项,根据自己平台将对应平台的false替换为true
meson --prefix=$(pwd)/../install \
--cross-file=../cross_file.txt \
-D amdgpu=false \ #amd集显平台
-D etnaviv=false \ #vivante图芯gpu支持
-D exynos=false \ #三星平台
-D freedreno=false \ #高通平台
-D freedreno-kgsl=false \ #高通平台
-D intel=false \ #intel集显平台
-D nouveau=false \ #nvdia平台
-D omap=false \ #ti平台
-D radeon=false \ #amd独显平台
-D tegra=false \ #nvdia tegra(switch)平台
-D vc4=false \ #博通VC4平台
-D libkms=false \ #drm kms库
-D man-pages=false \ #man手册
-D udev=false \ #udev支持
-D valgrind=false \ #内存测试
-D cairo-tests=false \ #cairo语言测试
-D vmwgfx=false #VMWare图形驱动支持
-D install-test-programs=true \ #安装测试程序,建议安装,便于检测排查问题。
5、编译安装
#编译并安装
ninja && ninja install
#完成后在../install目录可以能得到对应的文件
三、环境测试
1、modetest测试
#modetest 参数
modetest -h #帮助
Query options:#用于查询的参数选项
-c list connectors #列举出所有的connectors
-e list encoders #列举出所有的encoders
-f list framebuffers #列举出所有的framebuffers
-p list CRTCs and planes (pipes) #列举出所有的CRTCs和planes
Test options:#用于测试的参数选项
#-P给CRTC指定plane
-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>] set a plane
#-s 设置输出模式,选择connector和crtc
-s <connector_id>[,<connector_id>][@<crtc_id>]:[#<mode index>]<mode>[-<vrefresh>][@<format>] set a mode
-C test hw cursor
-v test vsynced page flipping
-r set the preferred mode for all connectors
-w <obj_id>:<prop_name>:<value> set property
-a use atomic API
-F pattern1,pattern2 specify fill patterns
Generic options:#指定打开设备、驱动
-d drop master after mode set
-M module use the given driver
-D device use the given device
Default is to dump all info.
#例子
#-s <connector_id>[,<connector_id>][@<crtc_id>]:[#<mode index>]<mode>[-<vrefresh>][@<format>]
#-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>] set a plane
modetest -s 37@35:1024x768 -P 33@35:1024x768 #运行成功会有屏幕彩块显示
#37是连接器号,通过modetest -c查询
#两个35都是crtc的号,通过modetest -p可以查询到带分辨率的接口crtc号,以及显示分辨率。
2、代码测试
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#define WIDTH 800 //修改为屏幕对应分辨率宽度
#define HEIGHT 600 //修改为屏幕对应分辨率高度
int main(int argc, char **argv) {
int fd;
drmModeRes *resources;
drmModeConnector *connector;
drmModeEncoder *encoder;
drmModeCrtc *crtc;
uint32_t *framebuffer;
uint32_t handle;
uint32_t stride;
uint32_t size;
int ret;
// 打开DRM设备
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
if (fd < 0) {
perror("Failed to open DRM device");
return 1;
}
// 获取资源
resources = drmModeGetResources(fd);
if (!resources) {
perror("Failed to get DRM resources");
close(fd);
return 1;
}
// 查找连接
for (int i = 0; i < resources->count_connectors; i++) {
connector = drmModeGetConnector(fd, resources->connectors[i]);
if (connector->connection == DRM_MODE_CONNECTED && connector->count_modes > 0) {
break;
}
drmModeFreeConnector(connector);
}
if (!connector) {
fprintf(stderr, "No connected connector found\n");
drmModeFreeResources(resources);
close(fd);
return 1;
}
// 查找编码器
encoder = drmModeGetEncoder(fd, connector->encoder_id);
if (!encoder) {
perror("Failed to get encoder");
drmModeFreeConnector(connector);
drmModeFreeResources(resources);
close(fd);
return 1;
}
// 获取CRTC
crtc = drmModeGetCrtc(fd, encoder->crtc_id);
if (!crtc) {
perror("Failed to get CRTC");
drmModeFreeEncoder(encoder);
drmModeFreeConnector(connector);
drmModeFreeResources(resources);
close(fd);
return 1;
}
// 计算帧缓冲区大小
stride = WIDTH * 4; // 假设每个像素4字节(32位颜色)
size = stride * HEIGHT;
// 创建帧缓冲区
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMMY, &handle);
if (ret < 0) {
perror("Failed to create dummy buffer");
drmModeFreeCrtc(crtc);
drmModeFreeEncoder(encoder);
drmModeFreeConnector(connector);
drmModeFreeResources(resources);
close(fd);
return 1;
}
// 映射帧缓冲区到内存
framebuffer = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, handle);
if (framebuffer == MAP_FAILED) {
perror("Failed to map framebuffer");
drmModeFreeCrtc(crtc);
drmModeFreeEncoder(encoder);
drmModeFreeConnector(connector);
drmModeFreeResources(resources);
close(fd);
return 1;
}
// 填充帧缓冲区
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
uint32_t color = (x * 255 / WIDTH) << 16 | (y * 255 / HEIGHT) << 8 | 255;
framebuffer[y * WIDTH + x] = color;
}
}
// 设置CRTC
ret = drmModeSetCrtc(fd, crtc->crtc_id, handle, 0, 0, &connector->connector_id, 1, &connector->modes[0]);
if (ret < 0) {
perror("Failed to set CRTC");
munmap(framebuffer, size);
drmModeFreeCrtc(crtc);
drmModeFreeEncoder(encoder);
drmModeFreeConnector(connector);
drmModeFreeResources(resources);
close(fd);
return 1;
}
// 等待用户输入
printf("Press Enter to exit...\n");
getchar();
// 恢复原来的CRTC
drmModeSetCrtc(fd, crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, &connector->connector_id, 1, &crtc->mode);
// 清理资源
munmap(framebuffer, size);
drmModeFreeCrtc(crtc);
drmModeFreeEncoder(encoder);
drmModeFreeConnector(connector);
drmModeFreeResources(resources);
close(fd);
return 0;
}
编译代码
gcc -o drm_draw main.c -ldrm
# 交叉编译 gcc可替换arm-linux-gnueabihf-gcc, -ldrm前加上drm库路径 -L /path/libdrm/lib/ 以及加上include路径 -I /path/libdrm/include/
运行代码
sudo ./drm_draw
四、参考文章
DRM框架与libdrm移植-CSDN博客