V4L2はVideo for linux2の略で、Linuxのビデオ機器のカーネルドライバーです。v4L2は、uvc(USBビデオクラス)ドライブフリーUSBデバイス用のプログラミングフレームワークで、主にUSBカメラなどの収集に使用されます。
次の図はV4L2のフレームワークです。最初に、システムコアレイヤーの割り当て設定は、cdev構造(cdev構造はvideo_device構造の一部です)という名前の変数を登録し、cdev-> ops = v4l2_fopsを設定します。ハードウェアレイヤーで、設定登録を割り当てますvfd構造(video_device構造)という名前の変数、およびset vfd-> fops =&vivi_fops、vfd-> ioctl_ops =&vivi_ioctl_ops;アプリケーション(APP)が読み取りやオープンなどの関数を呼び出すと、v4l2_fopsで呼び出されます読み取り関数とオープン関数、およびv4l2_fopsの読み取り関数とオープン関数は、ハードウェアレイヤーに関連するvfd-> fopsの読み取り関数とオープン関数を呼び出します。ioctl関数も同様です。
V4L2フレームワークを分析するプログラムから始めましょう。この記事では、Linuxカーネルディレクトリのdrivers \ medio \ videoにある仮想ビデオドライバーvivi.cを使用して(このコードは、v4l2 apiを使用して実際のビデオデバイスをシミュレートします)、V4L2フレームワークを分析します。 。その全体的なフレームワークは次のとおりです。
vivi_init
vivi_create_instance
v4l2_device_register // 不是主要, 只是用于初始化一些东西,比如自旋锁、引用计数
video_device_alloc
// 设置
1. vfd:
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.release = video_device_release,
2.
vfd->v4l2_dev = &dev->v4l2_dev;
3. 设置"ctrl属性"(用于APP的ioctl):
v4l2_ctrl_handler_init(hdl, 11);
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 16);
video_register_device(video_device, type:VFL_TYPE_GRABBER, nr)
__video_register_device
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
cdev_add
video_device[vdev->minor] = vdev;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
①vivi.cのvivi_init関数から開始したところ、v4l2_device_registerが呼び出され、スピンロックや参照カウントなど、初期化に必要ないものを使用していることがわかりました。②video_device_allocを呼び出して、video_device構造体を割り当て、たとえば、適切な設定を行います
.fops =&vivi_fops、
.ioctl_ops =&vivi_ioctl_ops、
.release = video_device_release、
設定を待ってから、構造体をvideo_register_deviceに登録します。
③video_register_device関数は__video_register_deviceを呼び出して次の操作を実行します。
vdev-> cdev = cdev_alloc();
vdev-> cdev-> ops =&v4l2_fops;
cdev_add
video_device [vdev-> minor] = vdev;
if(vdev-> ctrl_handler == NULL)
vdev-> ctrl_handler = vdev-> v4l2_dev-> ctrl_handler;
上の画像はvivi_create_instance関数の一部です。最初にvideo_device構造の変数vfdを割り当て、次に* vfd = vivi_templateを設定します。ここで、vivi_templateはvideo_deviceの構造変数であり、それ自体が.fopsなどの情報を設定します(以下を参照) )、この操作は設定と同等です
1. vfd:
.fops =&vivi_fops、
.ioctl_ops =&vivi_ioctl_ops、
.release = video_device_release、
static struct video_device vivi_template = {
.name = "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.release = video_device_release,
.tvnorms = V4L2_STD_525_60,
.current_norm = V4L2_STD_NTSC_M,
};
次に、video_register_device関数を入力します。以下は、video_register_deviceのソースコードの一部です。最初にcdev構造体を割り当てます
次に、cdev-> ops =&v4l2_fops;を設定します。v4l2_fops自体がいくつかの関数を指します(下図を参照)。cdevもこれらの関数を指します。APPが読み取り関数を呼び出すと、cdevの読み取り関数が呼び出されます。
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};
cdevのread関数を下図に示しますが、まずfilpに従ってvideo_device構造体を取得し、次にvideo_device構造体のread関数を判定し、存在する場合はそれを呼び出します。関数を読み取ります。
open関数やread関数と比較して、ioctlの呼び出しプロセスはより複雑なので、以下を見てみましょう(例としてVIDIOC_QUERYCAPを取り上げます)。次の図は、v4l2_fopsの.unlocked_ioctlが指すv4l2_ioctl関数です。
以前のvivi_templateのfopでioctlを呼び出します。
次の図では、vivi_templateのfopsのioctlが__video_do_ioctl関数に呼び出され、最終的にvfdのioctl_opsメンバーの関数、つまりvivi_ioctl_opsの関数を呼び出します
たとえば、VIDIOC_QUERYCAPが呼び出されると、最終的に次の関数が呼び出されます。
/* ------------------------------------------------------------------
IOCTL vidioc handling
------------------------------------------------------------------*/
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct vivi_dev *dev = video_drvdata(file);
strcpy(cap->driver, "vivi");
strcpy(cap->card, "vivi");
strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
cap->version = VIVI_VERSION;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \
V4L2_CAP_READWRITE;
return 0;
}
概要:ioctlの呼び出しはopenおよびreadよりも1層多くなります。APPがioctl関数を呼び出すと、cdevでioctl関数が呼び出され、先ほど設定したvfd.fopsでioctl関数が呼び出されます(つまり、読み取り、開く)。 V4l2_ioctlを関数の同じ構造にして、最後にとを呼び出します
.fops =&vivi_fops、同じ構造の.ioctl_ops =&vivi_ioctl_opsの
対応する関数。
関連記事:
https://blog.csdn.net/qingkongyeyue/article/details/53447331
https://blog.csdn.net/qingkongyeyue/article/details/52372960