初始化相机后,我想从内核模块访问 /dev/video0 。为此,我想在 ueventd 守护进程启动之前创建 /dev/video0 节点。
1 回答
更深入地查看 /dev/video0 节点的内核处理,每当应用程序试图打开这个文件时,它会得到一个 FILE *fp 指针,linux 内核虚拟文件系统检查这是一个常规文件还是设备文件,如果它是一个设备文件它检查它的主要编号以跟踪注册它的驱动程序并将次要编号保存在 struct inode *inode 的 i_rdev 字段中,该字段再次嵌入到 struct file *fp 并传递给该驱动程序。
因此,对于应用程序打开的每个 FILE *fp,在已注册的驱动程序(即我们的例子中的 v4l2 驱动程序)中都有结构文件 *fp。这个文件指针被传递给内核 ioctl API v4l2_ioctl。
现在在内部 v4l2 驱动程序维护一个指向所有已注册视频设备的指针数组,如下所示: static struct video_device *video_device[VIDEO_NUM_DEVICES];
现在,如果我们看到 main ioctl 调用的实现。
static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(filp);
...
}
这个视频设备结构是从文件指针中提取的,文件指针是我们可以控制视频设备的关键,即我们从内核中的相机,因为它包含指向所有已注册的 v4l2 ioctl 的函数指针。所以我们的目标是从内核中访问视频设备结构。
现在再次查看内核在收到应用程序请求时如何访问视频设备。
struct video_device *video_devdata(struct file *file)
{
return video_device[iminor(file->f_path.dentry->d_inode)];
}
EXPORT_SYMBOL(video_devdata);
static inline unsigned iminor(const struct inode *inode)
{
return MINOR(inode->i_rdev);
}
如上所示,它使用 i_rdev 字段来获取从 struct file *fp 通过 VFS 传递的次要编号。
总结一下,如果我们想从内核中访问 ioctl,我们需要在 file->f_path.dentry->d_inode.i_rdev 字段中填充一个包含次要编号的虚拟文件 *fp 指针,v4l2 子系统将使用该字段获取 video_device 结构并将能够通过使用 video_device->ioctl_ops 字段从 video_device 结构进一步驱动 ioctl 操作,如下所示。
struct video_device
{
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_entity entity;
#endif
/* device ops */
const struct v4l2_file_operations *fops;
const struct v4l2_ioctl_ops *ioctl_ops;
...
}
要设置 file->f_path.dentry->d_inode.i_rdev 我们需要按照以下伪代码在文件结构中添加对 inode 和 dentry 结构的引用:
static int enumerate_camera()
{
inode.i_rdev = cam_minor_number ;// Saved when camera device registered;
dentry.d_inode = inode;
file.f_path.dentry = dentry;
file.f_dentry->d_inode = inode;
....
}