驱动会注册一个支持KMS的DRM设备时,会在/dev/drm/下创建一个card%d文件,用户态可以通过打开该文件,并对文件描述符做相应的操作实现相应的功能。该文件描述符对应的文件操作回调函数(filesystem_operations)位于drm_driver中,并由驱动程序填充。典型如下:
static const struct file_operations rockchip_drm_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.mmap = rockchip_gem_mmap,
.poll = drm_poll,
.read = drm_read,
.unlocked_ioctl = drm_ioctl,
.compat_ioctl = drm_compat_ioctl,
.release = drm_release,
};
long drm_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct drm_file *file_priv = filp->private_data;
struct drm_device *dev;
const struct drm_ioctl_desc *ioctl = NULL;
drm_ioctl_t *func;
unsigned int nr = DRM_IOCTL_NR(cmd);
int retcode = -EINVAL;
char stack_kdata[128];
char *kdata = NULL;
unsigned int in_size, out_size, drv_size, ksize;
bool is_driver_ioctl;
dev = file_priv->minor->dev;
if (drm_dev_is_unplugged(dev))
return -ENODEV;
is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END;
if (is_driver_ioctl) {
/* driver ioctl */
unsigned int index = nr - DRM_COMMAND_BASE;
if (index >= dev->driver->num_ioctls)
goto err_i1;
index = array_index_nospec(index, dev->driver->num_ioctls);
ioctl = &dev->driver->ioctls[index];
} else {
/* core ioctl */
if (nr >= DRM_CORE_IOCTL_COUNT)
goto err_i1;
nr = array_index_nospec(nr, DRM_CORE_IOCTL_COUNT);
ioctl = &drm_ioctls[nr];
}
drv_size = _IOC_SIZE(ioctl->cmd);
out_size = in_size = _IOC_SIZE(cmd);
if ((cmd & ioctl->cmd & IOC_IN) == 0)
in_size = 0;
if ((cmd & ioctl->cmd & IOC_OUT) == 0)
out_size = 0;
ksize = max(max(in_size, out_size), drv_size);
DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n",
task_pid_nr(current),
(long)old_encode_dev(file_priv->minor->kdev->devt),
file_priv->authenticated, ioctl->name);
/* Do not trust userspace, use our own definition */
func = ioctl->func;
if (unlikely(!func)) {
DRM_DEBUG("no function\n");
retcode = -EINVAL;
goto err_i1;
}
if (ksize <= sizeof(stack_kdata)) {
kdata = stack_kdata;
} else {
kdata = kmalloc(ksize, GFP_KERNEL);
if (!kdata) {
retcode = -ENOMEM;
goto err_i1;
}
}
if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) {
retcode = -EFAULT;
goto err_i1;
}
if (ksize > in_size)
memset(kdata + in_size, 0, ksize - in_size);
retcode = drm_ioctl_kernel(filp, func, kdata, ioctl->flags);
if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
retcode = -EFAULT;
err_i1:
if (!ioctl)
DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
task_pid_nr(current),
(long)old_encode_dev(file_priv->minor->kdev->devt),
file_priv->authenticated, cmd, nr);
if (kdata != stack_kdata)
kfree(kdata);
if (retcode)
DRM_DEBUG("pid=%d, ret = %d\n", task_pid_nr(current), retcode);
return retcode;
}
drm上层是如何调用的呢,我们使用以下命令跟踪。
strace -o 1.txt ./modetest -M mali-dp -P 27@34:1280x720@RG16
其结果摘抄了一部分使用如下。
ioctl(3, DRM_IOCTL_VERSION, 0x4213f0) = 0
ioctl(3, DRM_IOCTL_VERSION, 0x4213f0) = 0
ioctl(3, DRM_IOCTL_GET_UNIQUE, 0x7fd60bb870) = 0
ioctl(3, DRM_IOCTL_GET_UNIQUE, 0x7fd60bb870) = 0
ioctl(3, DRM_IOCTL_SET_CLIENT_CAP, 0x7fd60bbb40) = 0
ioctl(3, DRM_IOCTL_MODE_GETRESOURCES, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETRESOURCES, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETCRTC, 0x7fd60bbae8) = 0
ioctl(3, DRM_IOCTL_MODE_GETENCODER, 0x7fd60bbb38) = 0
ioctl(3, DRM_IOCTL_MODE_GETCONNECTOR, 0x7fd60bbb00) = 0
ioctl(3, DRM_IOCTL_MODE_GETCONNECTOR, 0x7fd60bbb00) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPLANERESOURCES, 0x7fd60bbb40) = 0
ioctl(3, DRM_IOCTL_MODE_GETPLANERESOURCES, 0x7fd60bbb40) = 0
ioctl(3, DRM_IOCTL_MODE_GETPLANE, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPLANE, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPLANE, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPLANE, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPLANE, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPLANE, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPLANE, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPLANE, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 0x7fd60bbb30) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_MODE_GETPROPERTY, 0x7fd60bbb10) = 0
ioctl(3, DRM_IOCTL_GET_CAP, 0x7fd60bbb40) = 0
ioctl(3, DRM_IOCTL_MODE_CREATE_DUMB, 0x7fd60bb980) = 0
ioctl(3, DRM_IOCTL_MODE_MAP_DUMB, 0x7fd60bb980) = 0
mmap(NULL, 8294400, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0x100e10000) = 0x7f9d4c0000
munmap(0x7f9d4c0000, 8294400) = 0
ioctl(3, DRM_IOCTL_MODE_ADDFB2, 0x7fd60bb918) = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(0x5, 0x1), ...}) = 0
ioctl(1, TCGETS, {B115200 opost isig icanon echo ...}) = 0
write(1, "setting mode 1920x1080-75.00Hz o"..., 57) = 57
ioctl(3, DRM_IOCTL_MODE_SETCRTC, 0x7fd60bb9d8) = 0
ioctl(3, DRM_IOCTL_MODE_DIRTYFB, 0x7fd60bba28) = -1 ENOSYS (Function not implemented)
ioctl(3, DRM_IOCTL_MODE_OBJ_SETPROPERTY, 0x7fd60bab88) = 0
ioctl(3, DRM_IOCTL_MODE_OBJ_SETPROPERTY, 0x7fd60bab88) = 0
fstat(0, {st_mode=S_IFCHR|0600, st_rdev=makedev(0x5, 0x1), ...}) = 0
ioctl(0, TCGETS, {B115200 opost isig icanon echo ...}) = 0
read(0, "\n", 4096) = 1
ioctl(3, DRM_IOCTL_MODE_RMFB, 0x7fd60bbb4c) = 0
ioctl(3, DRM_IOCTL_MODE_DESTROY_DUMB, 0x7fd60bbb48) = 0
fstat(3, {st_mode=S_IFCHR|0660, st_rdev=makedev(0xe2, 0), ...}) = 0
fstat(3, {st_mode=S_IFCHR|0660, st_rdev=makedev(0xe2, 0), ...}) = 0
close(3) = 0
exit_group(0) = ?
+++ exited with 0 +++
我们从上面简单分析,基本上按以下的ioctl调用顺序,进行调用的。
DRM_IOCTL_MODE_GETRESOURCES // 获得资源
DRM_IOCTL_MODE_GETCRTC // 获得crtc
DRM_IOCTL_MODE_GETENCODER // 获得encoder
DRM_IOCTL_MODE_GETCONNECTOR // 获得connector
DRM_IOCTL_MODE_OBJ_GETPROPERTIES // 获得参数变量
DRM_IOCTL_MODE_GETPROPERTY // 获得参数变量
DRM_IOCTL_MODE_GETPLANERESOURCES // 获得plane资源
DRM_IOCTL_MODE_GETPLANE // 获得plane
DRM_IOCTL_MODE_CREATE_DUMB // 获得gem
DRM_IOCTL_MODE_MAP_DUMB // map
mmap(NULL, 8294400, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0x100e10000) = 0x7f9d4c0000
DRM_IOCTL_MODE_ADDFB2 //填充fb
DRM_IOCTL_MODE_SETCRTC // 设置crtc
DRM_IOCTL_MODE_DIRTYFB
DRM_IOCTL_MODE_RMFB
后面我们会挑选一部分进行分析。