DRM实例教程
DRM是一个显示驱动框架,也就是把功能封装成 open/close/ioctl 等标准接口,应用程序调用这些接口来驱动设备,显示数据。我们这里将从使用的角度来看看,怎么验证和使用DRM驱动。
DRM设备节点
DRM驱动会在/dev/dri下创建3个设备节点:
card0
controlD64
renderD128
libdrm库
DRM驱动,对用户空间,提供了专门的的调用库libdrm.so,用户空间通过该库可以间接的调用和使用驱动。
打开设备
int fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "cannot open '%s': %m\n", node);
return ret;
}
打开设备有专门的接口:drmOpen
检查DRM的能力
DRM的能力通过drmGetCap接口获取,用drm_get_cap结构描述:
/** DRM_IOCTL_GET_CAP ioctl argument type */
struct drm_get_cap {
__u64 capability;
__u64 value;
};
int drmGetCap(int fd, uint64_t capability, uint64_t *value)
{
struct drm_get_cap cap;
int ret;
memclear(cap);
cap.capability = capability;
ret = drmIoctl(fd, DRM_IOCTL_GET_CAP, &cap);
if (ret)
return ret;
*value = cap.value;
return 0;
}
使用示例:
uint64_t has_dumb;
if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 ||
!has_dumb) {
fprintf(stderr, "drm device '%s' does not support dumb buffers\n",
node);
close(fd);
return -EOPNOTSUPP;
}
检索Resource
Resource的获取需要两次,第一次,获取数量大小,第二次才真正获取具体的Resource。具体看这个函数:
drmModeResPtr drmModeGetResources(int fd)
Resource结构封装:
struct drm_mode_card_res {
__u64 fb_id_ptr;
__u64 crtc_id_ptr;
__u64 connector_id_ptr;
__u64 encoder_id_ptr;
__u32 count_fbs;
__u32 count_crtcs;
__u32 count_connectors;
__u32 count_encoders;
__u32 min_width, max_width;
__u32 min_height, max_height;
};
typedef struct _drmModeRes {
int count_fbs;
uint32_t *fbs;
int count_crtcs;
uint32_t *crtcs;
int count_connectors;
uint32_t *connectors;
int count_encoders;
uint32_t *encoders;
uint32_t min_width, max_width;
uint32_t min_height, max_height;
} drmModeRes, *drmModeResPtr;
实例
/* retrieve resources */
int ret = drmModeGetResources(fd);
if (!res) {
fprintf(stderr, "cannot retrieve DRM resources (%d): %m\n",
errno);
return -errno;
}
获取Connector
_drmModeConnector描述结构:
typedef struct _drmModeConnector {
uint32_t connector_id;
uint32_t encoder_id; /**< Encoder currently connected to */
uint32_t connector_type;
uint32_t connector_type_id;
drmModeConnection connection;
uint32_t mmWidth, mmHeight; /**< HxW in millimeters */
drmModeSubPixel subpixel;
int count_modes;
drmModeModeInfoPtr modes;
int count_props;
uint32_t *props; /**< List of property ids */
uint64_t *prop_values; /**< List of property values */
int count_encoders;
uint32_t *encoders; /**< List of encoder ids */
} drmModeConnector, *drmModeConnectorPtr;
示例:
drmModeConnector *conn = drmModeGetConnector(fd, res->connectors[i]);
if (!conn) {
fprintf(stderr, "cannot retrieve DRM connector %u:%u (%d): %m\n",
i, res->connectors[i], errno);
continue;
}
Encoder
Encoder的结构描述:
typedef struct _drmModeEncoder {
uint32_t encoder_id;
uint32_t encoder_type;
uint32_t crtc_id;
uint32_t possible_crtcs;
uint32_t possible_clones;
} drmModeEncoder, *drmModeEncoderPtr;
示例:
if (conn->encoder_id)
drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoder_id);
}
drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
{
struct drm_mode_get_encoder enc;
drmModeEncoderPtr r = NULL;
memclear(enc);
enc.encoder_id = encoder_id;
if (drmIoctl(fd, DRM_IOCTL_MODE_GETENCODER, &enc))
return 0;
if (!(r = drmMalloc(sizeof(*r))))
return 0;
r->encoder_id = enc.encoder_id;
r->crtc_id = enc.crtc_id;
r->encoder_type = enc.encoder_type;
r->possible_crtcs = enc.possible_crtcs;
r->possible_clones = enc.possible_clones;
return r;
}
crtc
CRTC结构描述:
struct crtc {
drmModeCrtc *crtc;
drmModeObjectProperties *props;
drmModePropertyRes **props_info;
drmModeModeInfo *mode;
};
typedef struct _drmModeCrtc {
uint32_t crtc_id;
uint32_t buffer_id; /**< FB id to connect to 0 = disconnect */
uint32_t x, y; /**< Position on the framebuffer */
uint32_t width, height;
int mode_valid;
drmModeModeInfo mode;
int gamma_size; /**< Number of gamma stops */
} drmModeCrtc, *drmModeCrtcPtr;
FrameBuffer
创建DUMB Buffer
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
if (ret < 0) {
fprintf(stderr, "cannot create dumb buffer (%d): %m\n",
errno);
return -errno;
}
添加FB
/* create framebuffer object for the dumb-buffer */
ret = drmModeAddFB(fd, dev->width, dev->height, 24, 32, dev->stride,
dev->handle, &dev->fb);
if (ret) {
fprintf(stderr, "cannot create framebuffer (%d): %m\n",
errno);
ret = -errno;
goto err_destroy;
}
准备map
/* prepare buffer for memory mapping */
memset(&mreq, 0, sizeof(mreq));
mreq.handle = dev->handle;
ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
if (ret) {
fprintf(stderr, "cannot map dumb buffer (%d): %m\n",
errno);
ret = -errno;
goto err_fb;
}
做map操作:
/* perform actual memory mapping */
dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, mreq.offset);
if (dev->map == MAP_FAILED) {
fprintf(stderr, "cannot mmap dumb buffer (%d): %m\n",
errno);
ret = -errno;
goto err_fb;
}
CRTC的准备
drmModeGetCrtc
drmModeSetCrtc
drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId)
{
struct drm_mode_crtc crtc;
drmModeCrtcPtr r;
memclear(crtc);
crtc.crtc_id = crtcId;
if (drmIoctl(fd, DRM_IOCTL_MODE_GETCRTC, &crtc))
return 0;
/*
* return
*/
if (!(r = drmMalloc(sizeof(*r))))
return 0;
r->crtc_id = crtc.crtc_id;
r->x = crtc.x;
r->y = crtc.y;
r->mode_valid = crtc.mode_valid;
if (r->mode_valid) {
memcpy(&r->mode, &crtc.mode, sizeof(struct drm_mode_modeinfo));
r->width = crtc.mode.hdisplay;
r->height = crtc.mode.vdisplay;
}
r->buffer_id = crtc.fb_id;
r->gamma_size = crtc.gamma_size;
return r;
}
int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
uint32_t x, uint32_t y, uint32_t *connectors, int count,
drmModeModeInfoPtr mode)
{
struct drm_mode_crtc crtc;
memclear(crtc);
crtc.x = x;
crtc.y = y;
crtc.crtc_id = crtcId;
crtc.fb_id = bufferId;
crtc.set_connectors_ptr = VOID2U64(connectors);
crtc.count_connectors = count;
if (mode) {
memcpy(&crtc.mode, mode, sizeof(struct drm_mode_modeinfo));
crtc.mode_valid = 1;
}
return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETCRTC, &crtc);
}
绘制
static void modeset_draw(void)
{
uint8_t r, g, b;
bool r_up, g_up, b_up;
unsigned int i, j, k, off;
struct modeset_dev *iter;
srand(time(NULL));
r = rand() % 0xff;
g = rand() % 0xff;
b = rand() % 0xff;
r_up = g_up = b_up = true;
for (i = 0; i < 50; ++i) {
r = next_color(&r_up, r, 20);
g = next_color(&g_up, g, 10);
b = next_color(&b_up, b, 5);
for (iter = modeset_list; iter; iter = iter->next) {
for (j = 0; j < iter->height; ++j) {
for (k = 0; k < iter->width; ++k) {
off = iter->stride * j + k * 4;
*(uint32_t*)&iter->map[off] =
(r << 16) | (g << 8) | b;
}
}
}
usleep(100000);
}
}
具体的代码,可以参考how-to实例:
how-to代码实例