1、数据及数据结构:
Connector:代表显卡上的插口,有几个Connector表示会有几个输出。
CRTC:crt controller,负责将帧缓存中的数据传送到Connector,数据在传送到Connector之前会经过Encoder。 帧缓存 --> CRTC --> Encoder --> Connector --> 显示器。
FrameBuffer:在这里帧缓存并不是指的显存上的某一块区域,而是Linux DRM抽象出来的一个概念,用fb_id表示。
drm_mode_create_dumb:drmIoctl的参数,意思是请求内核创建缓存。 这个才是真正的内存块,可以使用mmap映射到程序虚拟内存中。Framebuffer的创建中必须指定一个缓存的id。
2、函数:
drmModeSetCrtc:最核心的函数之一,它负责将建立从帧缓存到Connector的关联。只有调用它,显示器才能显示缓存中的数据。
drmModePageFlip:直译就是翻页,笔者的理解是 drm_mode_create_dumb类型的缓存其实是双缓存,只有调用此函数之后,crtc才能将之前写入的数据传送给显示器。
3、代码:
找到处于连接状态的Connector
drmModeConnector* FindConnector(int fd)
{
drmModeRes *resources = drmModeGetResources(fd); //drmModeRes描述了计算机所有的显卡信息:connector,encoder,crtc,modes等。
if (!resources)
{
return NULL;
}
drmModeConnector* conn = NULL;
int i = 0;
for (i = 0; i < resources->count_connectors; i++)
{
conn = drmModeGetConnector(fd, resources->connectors[i]);
if (conn != NULL)
{
//找到处于连接状态的Connector。
if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0 && conn == NULL)
{
break;
}
else
{
drmModeFreeConnector(conn);
}
}
}
drmModeFreeResources(resources);
return conn;
}
int FindCrtc(int fd, drmModeConnector *conn)
{
drmModeRes *resources = drmModeGetResources(fd);
if (!resources)
{
fprintf(stderr, "drmModeGetResources failed\n");
return -1;
}
unsigned int i, j;
for (i = 0; i < conn->count_encoders; ++i)
{
drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]);
if (NULL != enc)
{
for (j = 0; j < resources->count_crtcs; ++j)
{
// connector下连接若干encoder,每个encoder支持若干crtc,possible_crtcs的某一位为1代表相应次序(不是id哦)的crtc可用。
if ((enc->possible_crtcs & (1 << j)))
{
int id = resources->crtcs[j];
drmModeFreeEncoder(enc);
drmModeFreeResources(resources);
return id;
}
}
drmModeFreeEncoder(enc);
}
}
drmModeFreeResources(resources);
return -1;
}
void SetColor(unsigned char *dest, int stride, int w, int h)
{
struct color {
unsigned r, g, b;
};
struct color ccs[] = {
{ 255, 0, 0 },
{ 0, 255, 0 },
{ 0, 0, 255 },
{ 255, 255, 0 },
{ 0, 255, 255 },
{ 255, 0, 255 }
};
static int i = 0;
unsigned int j, k, off;
unsigned int r = 255;
unsigned int g = 1;
unsigned int b = 1;
for (j = 0; j < h; ++j)
{
for (k = 0; k < w; ++k)
{
off = stride * j + k * 4;
*(uint32_t*)&(dest[off]) = (ccs[i].r << 16) | (ccs[i].g << 8) | ccs[i].b;
}
}
i++;
printf("draw picture\n");
}
#define _FILE_OFFSET_BITS 64
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int ret, fd;
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
{
/* Probably permissions error */
fprintf(stderr, "couldn't open %s, skipping\n", "");
return -1;
}
drmSetMaster(fd);
drmModeConnectorPtr connector = FindConnector(fd);
int width = connector->modes[0].hdisplay;
int height = connector->modes[0].vdisplay;
printf("display is %d*%d.\n", width, height);
int crtcid = FindCrtc(fd, connector);
struct drm_mode_create_dumb creq;
memset(&creq, 0, sizeof(creq));
creq.width = width;
creq.height = height;
creq.bpp = 32;
creq.flags = 0;
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
if (ret)
{
printf("create dumb failed!\n");
}
uint32_t framebuffer = -1;
uint32_t stride = creq.pitch;
//使用缓存的handel创建一个FB,返回fb的id:framebuffer。
ret = drmModeAddFB(fd, width, height, 24, 32, creq.pitch, creq.handle, &framebuffer);
if (ret)
{
printf("failed to create fb\n");
return -1;
}
struct drm_mode_map_dumb mreq; //请求映射缓存到内存。
mreq.handle = creq.handle;
ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
if (ret)
{
printf("map dumb failed!\n");
}
// 猜测:创建的缓存位于显存上,在使用之前先使用drm_mode_map_dumb将其映射到内存空间。
// 但是映射后缓存位于内核内存空间,还需要一次mmap才能被程序使用。
unsigned char* buf = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset);
if (buf == MAP_FAILED)
{
printf("mmap failed!\n");
}
memset(buf, 255, creq.size);
//一切准备完毕,只差连接在一起了!
ret = drmModeSetCrtc(fd, crtcid, framebuffer, 0, 0, &connector->connector_id, 1, connector->modes);
if (ret)
{
fprintf(stderr, "failed to set mode: %m\n");
return -1;
}
int cc = 0;
while (cc < 5)
{
SetColor(buf, stride, width, height);
drmModePageFlip(fd, crtcid, framebuffer, DRM_MODE_PAGE_FLIP_EVENT, 0);
cc++;
sleep(2);
}
printf("over\n");
getchar();
close(fd);
exit(0);
return ret;
}