接前一篇文章:libdrm全解析二十一 —— 源码全解析(18)
本文参考以下博文:
特此致谢!
前一篇文章介绍了DRM的一般流程,并给出了示例代码。从本回开始,正式对于上一篇文章中用到的函数以及涉及到的宏定义进行解析。
60. DRM_IOCTL_MODE_GETRESOURCES
第60个宏是DRM_IOCTL_MODE_GETRESOURCES,相应代码如下:
#define DRM_IOCTL_MODE_GETRESOURCES DRM_IOWR(0xA0, struct drm_mode_card_res)
结合之前文章中的_IOWR(type,nr,size)的最终定义,得到如下代码:
#define DRM_IOCTL_MODE_GETRESOURCES ( ((3) << 30) | (('d') << 8) | ((0xA0) << 0) | ((sizeof(struct drm_mode_card_res)) << 16) )
struct drm_mode_card_res在同文件(include/drm/drm.h)中定义,代码如下:
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;
};
DRM_IOCTL_MODE_GETRESOURCES对应的Userspace API为:drmIsKMS()和drmModeGetResources()。这函数也是在xf86drm.c中,代码分别如下:
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;
}
drmIsKMS函数通过drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)获取到的资源(struct drm_mode_card_res res),根据crtc、connector、encoders的数量进行判断,如果三者都大于0,则返回真(true),代表是KMS;否则返回假(false),代表不是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;
}
drmModeGetResources函数值得深入地进行讲解,其在整个流程中是至关重要的。通过前一篇文章中的两个例程以及一般流程可以看到,它是第一步调用的函数,因此,值得大书特书。
对于这个函数的解析,放在下一篇文章中。这里先把一个同样十分重要的基础函数讲解清楚。这个函数就是:drmIoctl。
其实之前已经太多次遇到这个函数了,只是一直没有正式对其进行讲解。drmIoctl函数也在xf86drm.c中,代码如下:
/**
* 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;
}
通过代码可以看到,实际上就是ioctl系统调用的一层封装。在ioctl系统调用返回错误(由于EINTR和EAGIN而返回-1)的时候,反复进行ioctl系统调用,直到正确或者出现其它错误为止。
以drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)为例,实际上调用的是:
ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)
对于其它的drmIoctl,也是类似的意思。