linux ModeSetting学习

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;
}



查找与Connector匹配的Crtc

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;
}





你可能感兴趣的:(linux ModeSetting学习)