Libdrm full analysis twenty-two - source code full analysis (19)

Continued from the previous article: Full analysis of libdrm 21 - Full analysis of source code (18)

This article refers to the following blog posts:

DRM driver development (VKMS)

Thank you very much!

The previous article introduced the general process of DRM and gave sample codes. Starting from this chapter, we will formally analyze the functions used in the previous article and the macro definitions involved.

60. DRM_IOCTL_MODE_GETRESOURCES

The 60th macro is DRM_IOCTL_MODE_GETRESOURCES, the corresponding code is as follows:

#define DRM_IOCTL_MODE_GETRESOURCES	DRM_IOWR(0xA0, struct drm_mode_card_res)

Combined with the final definition of _IOWR(type,nr,size) in the previous article, the following code is obtained:

#define DRM_IOCTL_MODE_GETRESOURCES        ( ((3)  << 30) | (('d') << 8) | ((0xA0)   << 0) | ((sizeof(struct drm_mode_card_res)) << 16) )

struct drm_mode_card_res is defined in the same file (include/drm/drm.h), the code is as follows:

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;
	__u32 max_width;
	__u32 min_height;
	__u32 max_height;
};

The Userspace APIs corresponding to DRM_IOCTL_MODE_GETRESOURCES are: drmIsKMS() and drmModeGetResources(). This function is also in xf86drm.c, the codes are as follows:

drm_public int drmIsKMS(int fd)
{
	struct drm_mode_card_res res = {0};

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) != 0)
		return 0;

	return res.count_crtcs > 0 && res.count_connectors > 0 && res.count_encoders > 0;
}

The drmIsKMS function obtains resources (struct drm_mode_card_res res) through drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res), and judges according to the number of crtc, connector, and encoders. If all three are greater than 0, it returns true (true), which means it is KMS; Otherwise return false (false), representing not KMS. 

drm_public drmModeResPtr drmModeGetResources(int fd)
{
	struct drm_mode_card_res res, counts;
	drmModeResPtr r = 0;

retry:
	memclear(res);
	if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
		return 0;

	counts = res;

	if (res.count_fbs) {
		res.fb_id_ptr = VOID2U64(drmMalloc(res.count_fbs*sizeof(uint32_t)));
		if (!res.fb_id_ptr)
			goto err_allocs;
	}
	if (res.count_crtcs) {
		res.crtc_id_ptr = VOID2U64(drmMalloc(res.count_crtcs*sizeof(uint32_t)));
		if (!res.crtc_id_ptr)
			goto err_allocs;
	}
	if (res.count_connectors) {
		res.connector_id_ptr = VOID2U64(drmMalloc(res.count_connectors*sizeof(uint32_t)));
		if (!res.connector_id_ptr)
			goto err_allocs;
	}
	if (res.count_encoders) {
		res.encoder_id_ptr = VOID2U64(drmMalloc(res.count_encoders*sizeof(uint32_t)));
		if (!res.encoder_id_ptr)
			goto err_allocs;
	}

	if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
		goto err_allocs;

	/* The number of available connectors and etc may have changed with a
	 * hotplug event in between the ioctls, in which case the field is
	 * silently ignored by the kernel.
	 */
	if (counts.count_fbs < res.count_fbs ||
	    counts.count_crtcs < res.count_crtcs ||
	    counts.count_connectors < res.count_connectors ||
	    counts.count_encoders < res.count_encoders)
	{
		drmFree(U642VOID(res.fb_id_ptr));
		drmFree(U642VOID(res.crtc_id_ptr));
		drmFree(U642VOID(res.connector_id_ptr));
		drmFree(U642VOID(res.encoder_id_ptr));

		goto retry;
	}

	/*
	 * return
	 */
	if (!(r = drmMalloc(sizeof(*r))))
		goto err_allocs;

	r->min_width     = res.min_width;
	r->max_width     = res.max_width;
	r->min_height    = res.min_height;
	r->max_height    = res.max_height;
	r->count_fbs     = res.count_fbs;
	r->count_crtcs   = res.count_crtcs;
	r->count_connectors = res.count_connectors;
	r->count_encoders = res.count_encoders;

	r->fbs        = drmAllocCpy(U642VOID(res.fb_id_ptr), res.count_fbs, sizeof(uint32_t));
	r->crtcs      = drmAllocCpy(U642VOID(res.crtc_id_ptr), res.count_crtcs, sizeof(uint32_t));
	r->connectors = drmAllocCpy(U642VOID(res.connector_id_ptr), res.count_connectors, sizeof(uint32_t));
	r->encoders   = drmAllocCpy(U642VOID(res.encoder_id_ptr), res.count_encoders, sizeof(uint32_t));
	if ((res.count_fbs && !r->fbs) ||
	    (res.count_crtcs && !r->crtcs) ||
	    (res.count_connectors && !r->connectors) ||
	    (res.count_encoders && !r->encoders))
	{
		drmFree(r->fbs);
		drmFree(r->crtcs);
		drmFree(r->connectors);
		drmFree(r->encoders);
		drmFree(r);
		r = 0;
	}

err_allocs:
	drmFree(U642VOID(res.fb_id_ptr));
	drmFree(U642VOID(res.crtc_id_ptr));
	drmFree(U642VOID(res.connector_id_ptr));
	drmFree(U642VOID(res.encoder_id_ptr));

	return r;
}

The drmModeGetResources function is worth explaining in depth, it is crucial in the whole process. As can be seen from the two routines and the general process in the previous article, it is the function called in the first step, so it is worth writing about.

For the analysis of this function, put it in the next article. Here, an equally important basic function is explained clearly. This function is: drmIoctl.

In fact, I have encountered this function too many times before, but I have not formally explained it. The drmIoctl function is also in xf86drm.c, the code is as follows:

/**
 * Call ioctl, restarting if it is interrupted
 */
drm_public int
drmIoctl(int fd, unsigned long request, void *arg)
{
    int ret;

    do {
        ret = ioctl(fd, request, arg);
    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
    return ret;
}

As you can see from the code, it is actually a layer of encapsulation of the ioctl system call. When the ioctl system call returns an error (-1 due to EINTR and EAGIN), the ioctl system call is repeated until it is correct or other errors occur.

Take drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) as an example, what is actually called is:

ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)

For other drmIoctl, it has a similar meaning.

Guess you like

Origin blog.csdn.net/phmatthaus/article/details/132494774