接前一篇文章:DRM全解析 —— ADD_FB2(1)
本文参考以下博文:
特此致谢!
本回对于DRM_IOCTL_MODE_ADDFB2的ioctl系统调用进行深入分析。
众所周知,ioctl系统调用会从用户空间进入到内核空间进行相关操作和处理,之后将结果返回用户空间。因此,要以DRM_IOCTL_MODE_ADDFB2为线索(关键字)进入到Linux Kernel源码中,看一下与libdrm中此项对应的DRM的相关代码。
笔者的内核版本为6.1。在内核源码中搜索“DRM_IOCTL_MODE_ADDFB2”,虽然有几处,但是我们想要的是drivers/gpu/drm/drm_ioctl.c中的这一行代码:
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2_ioctl, 0),
DRM_IOCTL_MODE_ADDFB2是一个宏,其定义不单在libdrm源码中有,在Linux Kernel代码中同样存在,因为libdrm和DRM要上下对应起来。在DRM即内核源码中对应的文件是include/uapi/drm/drm.h,代码如下:
#define DRM_IOCTL_MODE_ADDFB2 DRM_IOWR(0xB8, struct drm_mode_fb_cmd2)
宏最终展开后的定义为:
#define DRM_IOCTL_MODE_ADDFB2 ( ((3) << 30) | (('d') << 8) | ((0xB8) << 0) | ((sizeof(struct drm_mode_fb_cmd2)) << 16) )
DRM_IOCTL_DEF是一个宏,在同文件(drivers/gpu/drm/drm_ioctl.c)中定义,如下:
#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_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宏的“反宏”。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_ADDFB2, drm_mode_addfb2_ioctl, 0),
最终得到:
#define DRM_IOCTL_DEF(ioctl, _func, _flags) \
[0xAE] = { \
.cmd = DRM_IOCTL_MODE_ADDFB2, \
.func = drm_mode_addfb2_ioctl, \
.flags = 0, \
.name = "DRM_IOCTL_MODE_ADDFB2"
}
关于内核中的DRM_IOCTL_MODE_ADDFB2以及对应的函数drm_mode_addfb2_ioctl,余下的部分在后续文章中讲解。