DRM全解析 —— CREATE_DUMB(1)

本文参考以下博文:

DRM驱动(三)之CREATE_DUMB

特此致谢!

在笔者之前的libdrm全解析系列文章中,讲到了drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create)以及其封装函数drmModeCreateDumbBuffer。对应的文章链接为:

libdrm全解析二十九 —— 源码全解析(26)

libdrm全解析三十 —— 源码全解析(27)

文章中讲到drmIoctl函数实际上最终调用了ioctl系统调用,相关代码在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;
}

可见,传入的request值就是DRM_IOCTL_MODE_CREATE_DUMB。

众所周知,ioctl系统调用会从用户空间进入到内核空间进行相关操作和处理,之后将结果返回用户空间。因此,要以DRM_IOCTL_MODE_CREATE_DUMB为线索(关键字)进入到Linux Kernel源码中,看一下与libdrm中此项对应的DRM的相关代码。

笔者的内核版本为6.1。在内核源码中搜索“DRM_IOCTL_MODE_CREATE_DUMB”,虽然有几处,但是我们想要的是drivers/gpu/drm/drm_ioctl.c中的这一行代码:

DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, 0),

DRM_IOCTL_MODE_CREATE_DUMB是一个宏,之前在libdrm中讲过。此定义不单在libdrm源码中有,在Linux Kernel代码中同样存在,因为libdrm和DRM要上下对应起来。在DRM即内核源码中对应的文件是include/uapi/drm/drm.h,代码如下:

#define DRM_IOCTL_MODE_CREATE_DUMB DRM_IOWR(0xB2, struct drm_mode_create_dumb)

宏最终展开后的定义为:

#define DRM_IOCTL_MODE_CREATE_DUMB        ( ((3)  << 30) | (('d') << 8) | ((0xB2)   << 0) | ((sizeof(struct drm_mode_create_dumb)) << 16) )

DRM_IOCTL_DEF是一个宏,在同文件中定义,如下:

#define DRM_IOCTL_DEF(ioctl, _func, _flags)	\
	[DRM_IOCTL_NR(ioctl)] = {		\
		.cmd = ioctl,			\
		.func = _func,			\
		.flags = _flags,		\
		.name = #ioctl			\
	}

DRM_IOCTL_NR也是一个宏,在include/drm/drm_ioctl.h中定义,如下:

#define DRM_IOCTL_NR(n)                _IOC_NR(n)

_IOC_NR的定义在include/uapi/asm-generic/ioctl.h中,如下:

/* used to decode ioctl numbers.. */
#define _IOC_DIR(nr)		(((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)		(((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)		(((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)		(((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

_IOC_NRSHIFT、_IOC_NRMASK等宏定义就在上边,如下:

#define _IOC_NRBITS	8
#define _IOC_TYPEBITS	8

/*
 * Let any architecture override either of the following before
 * including this file.
 */

#ifndef _IOC_SIZEBITS
# define _IOC_SIZEBITS	14
#endif

#ifndef _IOC_DIRBITS
# define _IOC_DIRBITS	2
#endif

#define _IOC_NRMASK	((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK	((1 << _IOC_TYPEBITS)-1)
#define _IOC_SIZEMASK	((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK	((1 << _IOC_DIRBITS)-1)

#define _IOC_NRSHIFT	0
#define _IOC_TYPESHIFT	(_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_SIZESHIFT	(_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT	(_IOC_SIZESHIFT+_IOC_SIZEBITS)

看过libdrm全解析系列文章的读者会有印象,我在其中详细讲过_IOC相关的宏定义,当时讲的是在/usr/include/asm-generic/ioctl.h(注意,此处是系统路径,而非内核源码相对路径)中的以下代码:

#define _IO(type,nr)		_IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)	_IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)	_IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size)	_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

此处的_IOC_NR实际上就是_IO、_IOR、_IOW、_IOWR宏的“反宏”。这些是将4部分功能组成一个整体,而_IOC_DIR、_IOC_TYPE、_IOC_NR、_IOC_SIZE宏则是将整体中的4个部分一一拆分出来。

综上,DRM_IOCTL_DEF宏定义逐层展开为:

​#define DRM_IOCTL_DEF(ioctl, _func, _flags)	\
	[_IOC_NR(ioctl)] = {		\
		.cmd = ioctl,			\
		.func = _func,			\
		.flags = _flags,		\
		.name = #ioctl			\
	}

---> 

​#define DRM_IOCTL_DEF(ioctl, _func, _flags)	\
	[(((ioctl) >> _IOC_NRSHIFT) & _IOC_NRMASK)] = {		\
		.cmd = ioctl,			\
		.func = _func,			\
		.flags = _flags,		\
		.name = #ioctl			\
	}

--->

​#define DRM_IOCTL_DEF(ioctl, _func, _flags)	\
	[(((ioctl) >> 0) & 0xFF)] = {		\
		.cmd = ioctl,			\
		.func = _func,			\
		.flags = _flags,		\
		.name = #ioctl			\
	}

代入以下实际值:

DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, 0),

最终得到:

​#define DRM_IOCTL_DEF(ioctl, _func, _flags)	\
	[0xB2] = {		\
		.cmd = DRM_IOCTL_MODE_CREATE_DUMB,			\
		.func = drm_mode_create_dumb_ioctl,			\
		.flags = 0,		\
		.name = "DRM_IOCTL_MODE_CREATE_DUMB"		\
	}

加入drivers/gpu/drm/drm_ioctl.c中上下文后,完整的定义是这样:

/* Ioctl table */
static const struct drm_ioctl_desc drm_ioctls[] = {
    ……
    [0xB2] = {
        .cmd = DRM_IOCTL_MODE_CREATE_DUMB,
		.func = drm_mode_create_dumb_ioctl,
		.flags = 0,
		.name = "DRM_IOCTL_MODE_CREATE_DUMB"
	},
    ……
},

关于内核中的DRM_IOCTL_MODE_CREATE_DUMB以及对应的函数drm_mode_create_dumb_ioctl,余下的部分在后续文章中讲解。

你可能感兴趣的:(DRM,DRM,Linux内核)