Created
February 15, 2026 13:15
-
-
Save bombless/cd9a0ad3fac149f65e18ed9e9bbf01d2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 文件名: drm_demo.c | |
| // 编译: gcc drm_demo.c -o drm_demo -ldrm -lxf86drm | |
| // 运行: 需要在纯控制台(tty)下,并以root权限执行 (sudo ./drm_demo) | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <fcntl.h> | |
| #include <unistd.h> | |
| #include <sys/mman.h> | |
| #include <xf86drm.h> | |
| #include <xf86drmMode.h> | |
| #define WIDTH 640 | |
| #define HEIGHT 480 | |
| int main() { | |
| int drm_fd; | |
| drmModeRes *res; | |
| drmModeConnector *conn = NULL; | |
| drmModeEncoder *enc = NULL; | |
| uint32_t crtc_id; | |
| uint32_t connector_id; | |
| drmModeModeInfo mode; | |
| struct drm_mode_create_dumb creq; | |
| struct drm_mode_map_dumb mreq; | |
| uint32_t fb_id, handle; | |
| uint32_t *map; | |
| int ret; | |
| // 1. 打开DRM设备 | |
| drm_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC); | |
| if (drm_fd < 0) { | |
| perror("无法打开 /dev/dri/card0"); | |
| return -1; | |
| } | |
| printf("成功打开DRM设备\n"); | |
| // 2. 获取Master权限 (需要root) | |
| ret = drmSetMaster(drm_fd); | |
| if (ret) { | |
| perror("无法成为DRM Master,请确保以root运行且在纯控制台"); | |
| close(drm_fd); | |
| return -1; | |
| } | |
| // 3. 获取资源并找到第一个可用的连接器和CRTC | |
| res = drmModeGetResources(drm_fd); | |
| if (!res) { | |
| perror("无法获取DRM资源"); | |
| close(drm_fd); | |
| return -1; | |
| } | |
| // 找到第一个状态为"已连接"的连接器 | |
| for (int i = 0; i < res->count_connectors; i++) { | |
| conn = drmModeGetConnector(drm_fd, res->connectors[i]); | |
| if (conn->connection == DRM_MODE_CONNECTED) { | |
| printf("找到已连接的连接器 ID: %d\n", conn->connector_id); | |
| break; | |
| } | |
| drmModeFreeConnector(conn); | |
| conn = NULL; | |
| } | |
| if (!conn) { | |
| fprintf(stderr, "未找到已连接的连接器\n"); | |
| goto out_free_res; | |
| } | |
| // 使用连接器的第一个显示模式 | |
| mode = conn->modes[0]; | |
| printf("选定显示模式: %s (%dx%d)\n", mode.name, mode.hdisplay, mode.vdisplay); | |
| // 找到可用的编码器和CRTC | |
| for (int i = 0; i < res->count_encoders; i++) { | |
| enc = drmModeGetEncoder(drm_fd, res->encoders[i]); | |
| for (int j = 0; j < res->count_crtcs; j++) { | |
| // 检查编码器是否可能连接到这个CRTC | |
| if (enc->possible_crtcs & (1 << j)) { | |
| crtc_id = res->crtcs[j]; | |
| printf("选定CRTC ID: %d\n", crtc_id); | |
| break; | |
| } | |
| } | |
| if (crtc_id) break; // 找到了就跳出外层循环 | |
| drmModeFreeEncoder(enc); | |
| enc = NULL; | |
| } | |
| if (!crtc_id) { | |
| fprintf(stderr, "未找到可用的CRTC\n"); | |
| goto out_free_conn; | |
| } | |
| connector_id = conn->connector_id; | |
| // 4. 创建 dumb buffer | |
| memset(&creq, 0, sizeof(creq)); | |
| creq.width = WIDTH; | |
| creq.height = HEIGHT; | |
| creq.bpp = 32; // 32位色,每像素4字节 | |
| ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); | |
| if (ret) { | |
| perror("无法创建 dumb buffer"); | |
| goto out_free_enc; | |
| } | |
| handle = creq.handle; | |
| printf("创建 dumb buffer, handle: %d, size: %ld, pitch: %d\n", handle, creq.size, creq.pitch); | |
| // 5. 添加帧缓冲 (framebuffer),关联这个 buffer | |
| ret = drmModeAddFB(drm_fd, WIDTH, HEIGHT, 24, creq.bpp, | |
| creq.pitch, handle, &fb_id); | |
| if (ret) { | |
| perror("无法添加帧缓冲 (drmModeAddFB)"); | |
| goto out_destroy_dumb; | |
| } | |
| printf("创建帧缓冲, fb_id: %d\n", fb_id); | |
| // 6. 准备内存映射 | |
| memset(&mreq, 0, sizeof(mreq)); | |
| mreq.handle = handle; | |
| ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); | |
| if (ret) { | |
| perror("无法获取 map offset"); | |
| goto out_rm_fb; | |
| } | |
| // 7. 将 buffer 映射到用户空间 | |
| map = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, | |
| drm_fd, mreq.offset); | |
| if (map == MAP_FAILED) { | |
| perror("mmap 失败"); | |
| goto out_rm_fb; | |
| } | |
| // 8. 绘制:将整个屏幕填充为红色 (ARGB格式,A为0) | |
| // 注意:以 uint32_t 指针操作,循环赋值 | |
| for (int j = 0; j < (creq.size / 4); j++) { | |
| map[j] = 0x00FF0000; // 在ARGB中,这是红色 (A=0, R=255, G=0, B=0) | |
| } | |
| printf("已绘制红色到缓冲区\n"); | |
| // 9. 关键一步:使用 drmModeSetCrtc 激活显示 | |
| ret = drmModeSetCrtc(drm_fd, crtc_id, fb_id, | |
| 0, 0, &connector_id, 1, &mode); | |
| if (ret) { | |
| perror("drmModeSetCrtc 失败,无法开启显示"); | |
| goto out_munmap; | |
| } | |
| printf("CRTC设置成功,画面应该已经显示。程序将暂停10秒...\n"); | |
| // 保持画面10秒,以便观察 | |
| sleep(10); | |
| // 10. 清理工作 | |
| out_munmap: | |
| munmap(map, creq.size); | |
| out_rm_fb: | |
| drmModeRmFB(drm_fd, fb_id); | |
| out_destroy_dumb: | |
| // 销毁 dumb buffer | |
| struct drm_mode_destroy_dumb dreq = {.handle = handle}; | |
| drmIoctl(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); | |
| out_free_enc: | |
| if (enc) drmModeFreeEncoder(enc); | |
| out_free_conn: | |
| drmModeFreeConnector(conn); | |
| out_free_res: | |
| drmModeFreeResources(res); | |
| close(drm_fd); | |
| printf("程序退出。\n"); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment