Исходный код драйвера V4L2 находится в каталоге driver/media/video. Основные основные коды:
Если мы посмотрим непосредственно на исходный код драйвера, у нас все еще нет понимания структуры драйвера, особенно платформы V4L2, которая очень сложна. Давайте сначала проанализируем ее на основе виртуального видеодрайвера vivi.c, включенного в ядро. исходный код, версия ядра 3.4.2.
static int __init vivi_init(void)
{
const struct font_desc *font = find_font("VGA8x16");
int ret = 0, i;
if (font == NULL) {
printk(KERN_ERR "vivi: could not find font\n");
return -ENODEV;
}
font8x16 = font->data;
if (n_devs <= 0)
n_devs = 1;
for (i = 0; i < n_devs; i++) {
ret = vivi_create_instance(i);
if (ret) {
/* If some instantiations succeeded, keep driver */
if (i)
ret = 0;
break;
}
}
if (ret < 0) {
printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
return ret;
}
printk(KERN_INFO "Video Technology Magazine Virtual Video "
"Capture Board ver %s successfully loaded.\n",
VIVI_VERSION);
/* n_devs will reflect the actual number of allocated devices */
n_devs = i;
return ret;
}
static void __exit vivi_exit(void)
{
vivi_release();
}
module_init(vivi_init);
module_exit(vivi_exit);
Определение n_devs идет первым, как показано ниже:
static unsigned n_devs = 1;
module_param(n_devs, uint, 0644);
MODULE_PARAM_DESC(n_devs, "numbers of video devices to create");
Написано очень ясно: n_devs представляет количество видеоустройств, которые вы хотите создать.
Примечание. Обычно в пользовательском режиме параметры передаются через основную функцию. Первый параметр представляет количество аргументов, а второй параметр представляет конкретные параметры. В состоянии ядра параметры не могут передаваться таким образом. Обычно используется метод Module_param. Шаги следующие:
Удалив другие утверждения, мы обнаружили, что важной функцией является только одна функция vivi_create_instance(i). Давайте проанализируем эту функцию ниже.
static int __init vivi_create_instance(int inst)
{
struct vivi_dev *dev;
struct video_device *vfd;
struct v4l2_ctrl_handler *hdl;
struct vb2_queue *q;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
"%s-%03d", VIVI_MODULE_NAME, inst);
ret = v4l2_device_register(NULL, &dev->v4l2_dev);
if (ret)
goto free_dev;
dev->fmt = &formats[0];
dev->width = 640;
dev->height = 480;
hdl = &dev->ctrl_handler;
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);
dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_SATURATION, 0, 255, 1, 127);
dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_GAIN, 0, 255, 1, 100);
dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
if (hdl->error) {
ret = hdl->error;
goto unreg_dev;
}
v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
dev->v4l2_dev.ctrl_handler = hdl;
/* initialize locks */
spin_lock_init(&dev->slock);
/* initialize queue */
q = &dev->vb_vidq;
memset(q, 0, sizeof(dev->vb_vidq));
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivi_buffer);
q->ops = &vivi_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
vb2_queue_init(q);
mutex_init(&dev->mutex);
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
init_waitqueue_head(&dev->vidq.wq);
ret = -ENOMEM;
vfd = video_device_alloc();
if (!vfd)
goto unreg_dev;
*vfd = vivi_template;
vfd->debug = debug;
vfd->v4l2_dev = &dev->v4l2_dev;
set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
/*
* Provide a mutex to v4l2 core. It will be used to protect
* all fops and v4l2 ioctls.
*/
vfd->lock = &dev->mutex;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
if (ret < 0)
goto rel_vdev;
video_set_drvdata(vfd, dev);
/* Now that everything is fine, let's add it to device list */
list_add_tail(&dev->vivi_devlist, &vivi_devlist);
if (video_nr != -1)
video_nr++;
dev->vfd = vfd;
v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
video_device_node_name(vfd));
return 0;
rel_vdev:
video_device_release(vfd);
unreg_dev:
v4l2_ctrl_handler_free(hdl);
v4l2_device_unregister(&dev->v4l2_dev);
free_dev:
kfree(dev);
return ret;
}
1), функция сначала struct vivi_dev *dev выделить память, а затем dev->v4l2_dev.name Имя установлено на “vivi-i” форма, а затем позвоните v4l2_device_register Эта функция используется для регистрации dev->v4l2_dev Эта структура, структура v4l2_device Как показано ниже, его имя называется V4L2 устройство, оно должно быть V4L2 Основная структура устройства:
struct v4l2_device {
/* dev->driver_data points to this struct.
Note: dev might be NULL if there is no parent device
as is the case with e.g. ISA devices.*/
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_device *mdev;
#endif
/* used to keep track of the registered subdevs */
struct list_head subdevs;
/* lock this struct, can be used by the drivers as well if this
struct is embedded into a larger struct.*/
spinlock_t lock;
/* unique device name, by default the driver name + bus ID */
char name[V4L2_DEVICE_NAME_SIZE];
/* notify callback called by some sub-devices. */
void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg);
/* The control handler. May be NULL. */
struct v4l2_ctrl_handler *ctrl_handler;
/* Device's priority state */
struct v4l2_prio_state prio;
/* BKL replacement mutex.Temporary solution only. */
struct kref ref;
/* Release function that is called when the ref count goes to 0. */
void (*release)(struct v4l2_devcie *v4l2_dev);
};
Вы можете видеть, что эта структура содержит элемент родительского устройства устройства, заголовок списка дочерних устройств subdevs, блокировку вращения, указатель функции уведомления, дескриптор управления v4l2_ctrl_handler, приоритет prio, счетчик ссылок и указатель функции выпуска. Мы пока не будем проводить детальный анализ этой структуры. Мы проанализируем ее позже, когда будем анализировать фреймворк V4L2.
2), проходит v4l2_devie_register(NULL, &dev->v4l2_dev) функцию завершения регистрации структуры, видно, что в “vivi.c” , его родительское устройство NULL,v4l2_device_register функционировать в v4l2-device.c середина:
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
if (v4l2_dev == NULL)
return -EINVAL;
INIT_LIST_HEAD(&v4l2_dev->subdevs);
spin_lock_init(&v4l2_dev->lock);
mutex_init(&v4l2_dev->ioctl_lock);
v4l2_prio_init(&v4l2_dev->prio);
kref_init(&v4l2_dev->ref);
get_device(dev);
v4l2_dev->dev = dev;
if (dev == NULL) {
/* If dev == NULL, then name must be filled in by the caller */
WARN_ON(!v4l2_dev->name[0]);
return 0;
}
/* Set name to driver name + device name if it is empty. */
if (!v4l2_dev->name[0])
snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
dev->driver->name, dev_name(dev));
if (!dev_get_drvdata(dev))
dev_set_drvdata(dev, v4l2_dev);
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_device_register);
Эта функция завершает инициализацию связанного списка подустройств, блокировки вращения, мьютекса, приоритета и счетчика ссылок. Другая работа не выполняется.
3), продолжайте возвращаться к vivi_create_instance(i) Анализ по функциисередина,Внизодинпредложение:dev->fmt = &formats[0];
Путем прямого поиска обнаружено, что vivi.c поддерживает массив форматов, который представляет форматы данных, поддерживаемые vivi.c. Мы представили формат видео в среде V4L2. Благодаря этой строке кода мы знаем, что vivi.c поддерживает следующий формат: V4L2_PIX_FMT_YUYV.
struct vivi_fmt {
char *name;
u32 fourcc; /* v4l2 format id */
int depth;
};
static struct vivi_fmt formats[] = {
{
.name = "4:2:2, packed, YUYV",
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
},
{
.name = "4:2:2, packed, UYVY",
.fourcc = V4L2_PIX_FMT_UYVY,
.depth = 16,
},
{
.name = "RGB565 (LE)",
.fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
.depth = 16,
},
{
.name = "RGB565 (BE)",
.fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
.depth = 16,
},
{
.name = "RGB555 (LE)",
.fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
.depth = 16,
},
{
.name = "RGB555 (BE)",
.fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
.depth = 16,
},
};
4) Продолжайте анализировать в функции vivi_create_instance(i):
hdl = &dev->ctrl_handler;
v4l2_ctrl_handler_init(hdl, 11);
Структура v4l2_ctrl_handler определена в v4l2-ctrls.h следующим образом:
struct v4l2_ctrl_handler {
struct mutex lock;
struct list_head ctrls;
struct list_head ctrl_refs;
struct v4l2_ctrl_ref *cached;
struct v4l2_ctrl_ref ** buckets;
u16 nr_of_buckets;
int error;
};
v4l2_ctrl_handler — это структура, используемая для сохранения набора методов управления суб-устройствами. Для видеоустройств эти элементы управления включают настройку яркости, насыщенности, контрастности и резкости и т. д. Элементы управления сохраняются в связанном списке. Вы можете добавить элементы управления в связанный. list через функцию v4l2_ctrl_new_std. Эта функция используется в коде ниже.
/* Initialize the handler */
int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl, unsigned nr_of_controls_hint)
{
mutex_init(&hdl->lock);
INIT_LIST_HEAD(&hdl->ctrls);
INIT_LIST_HEAD(&hdl->ctrl_refs);
hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
hdl->buckets = kcalloc(hdl->nr_of_buckets, sizeof(hdl->buckets[0]), GFP_KERNEL);
hdl->error = hdl->buckets ? 0 : -ENOMEM;
return hdl->error;
}
Вычислите nr_of_buckets по размеру переменной nr_of_control_hint, вычислите nr_of_buckets, подайте заявку на пространство для бакетов и сохраните результат приложения в переменной ошибки.
5) Продолжайте анализировать в функции vivi_create_instance(i) и продолжайте устанавливать некоторые другие параметры в структуре dev. При настройке таких параметров, как громкость, яркость, контрастность, насыщенность и т. д., вызывается функция v4l2_ctrl_new_std, а для кнопки int32, устанавливается меню, битовая маска и другие параметры и вызывается функция v4l2_ctrl_new_custom. С первого взгляда можно сказать, что это две функции. Функции интерфейса, предоставляемые платформой V4L2.
struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s32 min, s32 max, u32 step, s32 def);
Пользовательское пространство может вызывать v4l2_ctrl_handler с помощью инструкции VIDIOC_S_CTRL ioctl, а идентификатор передается через параметр arg.
Выполните настройку яркости, насыщенности и т.д. в видео через несколько неясных вопросов.
6) Далее происходит работа буферной очереди, установка некоторых параметров очереди vb2_queue q, наиболее важными из которых являются следующие два параметра:
q->ops = &vivi_qops;
q->mem_ops = &vb2_vmalloc_memops;
Можетк Видеть:q->ops = &vivi_video_qops середина vivi_video_qops Это необходимость vivi.c Реализован набор рабочих функций, который vivi.c середина определяется следующим образом:
static struct vb2_ops vivi_video_qops = {
.queue_setup = queue_setup,
.buf_init = buffer_init,
.buf_prepare = buffer_prepare,
.buf_finish = buffer_finish,
.buf_cleanup = buffer_cleanup,
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
.wait_prepare = vivi_unlock,
.wait_finish = vivi_lock,
};
Эти немногиеиндивидуальныйфункция Это Необходимость Мы реализуем это сами при написании драйвера из.
Чтосередина vb2_ops Структура находится в videobuf2-core.h серединаопределение,Как показано ниже:
struct vb2_ops {
int (*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[]);
void (*wait_prepare)(struct vb2_queue *q);
void (*wait_finish)(struct vb2_queue *q);
int (*buf_init)(struct vb2_buffer *vb);
int (*buf_prepare)(struct vb2_buffer *vb);
int (*buf_finish)(struct vb2_buffer *vb);
void (*buf_cleanup)(struct vb2_buffer *vb);
int (*start_streaming)(struct vb2_queue *q, unsigned int count);
int (*stop_streaming)(struct vb2_queue *q);
void (*buf_queue)(struct vb2_buffer *vb);
};
для vb2_vmalloc_memops структура, которая представляет собой videobuf2-vmalloc.c серединаопределение,Как показано ниже:
const struct vb2_mem_ops vb2_vmalloc_memops = {
.alloc = vb2_vmalloc_alloc,
.put = vb2_vmalloc_put,
.get_userptr = vb2_vmalloc_get_userptr,
.put_userptr = vb2_vmalloc_put_userptr,
.vaddr = vb2_vmalloc_vaddr,
.mmap = vb2_vmalloc_mmap,
.num_users = vb2_vmalloc_num_users,
};
EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
Посмотрите на его название vb2 Вначале эти функции должны быть хорошими функциями, предоставляемыми системой. Изучив исходный код, мы обнаружили, что они существуют. videobuf2-vmalloc.c середина。
Затем вызовите функцию vb2_queue_init(q) для ее инициализации. Функция vb2_queue_init(q) выглядит следующим образом:
/**
* vb2_queue_init() - initialize a videobuf2 queue
* @q: videobuf2 queue; this structure should be allocated in driver
*
* The vb2_queue structure should be allocated by the driver. The driver is
* responsible of clearing it's content and setting initial values for some
* required entries before calling this function.
* q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer
* to the struct vb2_queue description in include/media/videobuf2-core.h
* for more information.
*/
int vb2_queue_init(struct vb2_queue *q)
{
BUG_ON(!q);
BUG_ON(!q->ops);
BUG_ON(!q->mem_ops);
BUG_ON(!q->type);
BUG_ON(!q->io_modes);
BUG_ON(!q->ops->queue_setup);
BUG_ON(!q->ops->buf_queue);
INIT_LIST_HEAD(&q->queued_list);
INIT_LIST_HEAD(&q->done_list);
spin_lock_init(&q->done_lock);
init_waitqueue_head(&q->done_wq);
if (q->buf_struct_size == 0)
q->buf_struct_size = sizeof(struct vb2_buffer);
return 0;
}
EXPORT_SYMBOL_GPL(vb2_queue_init);
Он просто выполняет некоторые операторы проверки и оценки, а также инициализирует некоторые связанные списки, спин-блокировки и т. д.
7)、/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
init_waitqueue_head(&dev->vidq.wq);
8), следующее является правильным video_device издействовать,это имеет значениедаэтотиндивидуальныйфункциясерединаосновнойиздействовать:
struct video_device *vfd;
vfd = video_device_alloc();
*vfd = vivi_template;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
video_set_drvdata(vfd, dev);
8.1) Давайте сначала посмотрим на это video_device структура, которая представляет собой v4l2-dev.h серединаопределение,Отображается следующим образом:
struct video_device {
/* device ops */
const struct v4l2_file_operations *fops;
/* sysfs */
struct device dev; /* v4l device */
struct cdev *cdev; /* character device */
/* Set either parent or v4l2_dev if your driver uses v4l2_device */
struct device *parent; /* device parent */
struct v4l2_device *v4l2_dev; /* v4l2_device parent */
/* Control handler associated with this device node. May be NULL */
struct v4l2_ctrl_handler *ctrl_handler;
/* Priority state. If NULL, then v4l2_dev->prio will be used */
struct v4l2_prio_state *prio;
/* device info */
char name[32];
int vfl_type;
/* 'minor' is set to -1 if the registration failed */
int minor;
u16 num;
/* use bitops to set/clear/test flags */
unsigned long flags;
/* attribute to differentiate multiple indices on one physical device */
int index;
/* V4L2 file handles */
spinlock_t fh_lock; /* Lock for all v4l2_fhs */
struct list_head fh_list; /* List of struct v4l2_fh */
int debug; /* Activates debug level */
/* Video standed vars */
v4l2_std_id tvnorms; /* Supproted tv norms */
v4l2_std_id current_norm; /* Current tv norm */
/* callbacks */
void (*release)(struct video_device *vdev);
/* ioctl callbacks */
const struct v4l2_ioctl_ops *ioctl_ops;
/* serialization lock */
struct mutex *lock;
};
Вы должны быть в состоянии примерно понять значение каждого участника на основе комментариев. Позже будут некоторые функции инициализации и регистрации этой функции, которые должны включать в себя установку и инициализацию членов этой структуры, поэтому мы подробно разберем эти члены позже.
8.2), см. ниже video_device_alloc функция. это в v4l2-dev.c серединаопределение:
struct video_device *video_device_alloc(void)
{
return kzalloc(sizeof(struct video_device), GFP_KERNEL);
}
EXPORT_SYMBOL(video_device_alloc);
Он просто выделяет часть памяти и устанавливает для нее значение 0, не устанавливая элементы в структуре video_device.
8.3), тогда vivi.c середина Следующее предложение да *vfd = vivi_template существование vivi.c Поиск показал, что существование было определено ранее:
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_device Члены структуры серединаиз, могут быть определены естьсуществовать Это выполняет задание из. Ему назначены только некоторые члены его середины.
8.3.1)、video_device Структура середина firstda .fops = &vivi_fops,существовать vivi.c поиск середина выглядит так:
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
.mmap = vivi_mmap,
};
Давайте сначала посмотрим на имена этих индивидуальных функций. open функция сумма unlocked_ioctl Названия функций отличаются от других. Интуиция мне подсказывает, что они предусмотрены системой, а названия других функций такие. vivi Начиная с того, что этот отдельный файл находится внутри реализации функции, мы существуем. vivi.c Вы можете найти его, выполнив поиск по середина, но мы пока не будем их подробно анализировать.
8.3.2)、video_device Структурасерединавторойиндивидуальныйда .ioctl_ops = &vivi_ioctl_ops , посмотри название тоже дасуществовать vivi.c серединаопределениеиз:
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
Как видите, это множество вызовов ioctl, которые условно можно разделить на следующие категории:
Выше ioctl Позвони обоим необходимостьнаша собственная реализация из, позади 3 индивидуальный ioctl имя v4l2 Начиная с из, он должен быть реализован в системе да. Найти существующие можно поиском. v4l2-ctrls.c и v4l2-event.c серединаопределение。
8.3.3)、video_device Структурасерединатретийиндивидуальныйда .release = video_device_release,этосуществовать v4l2-dev.c серединаопределение,Как показано ниже:
void video_device_release(struct video_device *vdev)
{
kfree(vdev);
}
EXPORT_SYMBOL(video_device_release);
8.3.4)、video_device Структурасерединачетвертый,пятьиндивидуальныйда:
.tvnorms = V4L2_STD_525_60,
.current_norm = V4L2_STD_NTSC_M,
глядя на video_device Структурасерединаиз Комментарий:
/* Video standard vars */
v4l2_std_id tvnorms; /* Supported tv norms */
v4l2_std_id current_norm; /* Current tvnorm */
Относится к поддерживаемым ТВ-форматам и текущему ТВ-формату.
8.3.5), анализ завершен vivi_template члены серединаиз, т.е. video_device Члены структуры серединаиз, также немного сбивающие с толку, существуют video_device Структурасередина,иметьодининдивидуальный struct v4l2_file_operation член, который, в свою очередь, включает члена индивидуального unlocked_ioctl, в то же время video_device Структурасередина还иметьодининдивидуальный struct v4l2_ioctl_ops Участник, почему здесь два участника? ioctl Функция-член? Давайте сначала проведем приблизительный анализ,struct v4l2_file_operations vivi_fops середина .unlocked_ioctl = video_ioctl2,этотиндивидуальный video_ioctl2 существовать v4l2-ioctl.c середина:
long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg)
{
return video_usercopy(file, cmd, arg, __video_do_ioctl);
}
EXPORT_SYMBOL(video_ioctl2);
оно звонит снова __video_do_ioctl функция,такжесуществовать v4l2-ioctl.c середина,этотиндивидуальный __video_do_ioctl функциядаодининдивидуальныйбольшойиз switch,case заявление, согласно разным случай, вызывая разные функции для VIDIOC_QUERYCAP Например:
struct video_device *vfd = video_devdata(file);
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
case VIDIOC_QUERYCAP:
ret = ops->vidioc_querycap(file, fh, cap);
использоватьсуществовать vivi.c этотиндивидуальныйпримерто есть:vfd этотиндивидуальный Структурато есть vivi_template,Ops то есть vivi_template серединаиз ioctl_ops член, а также то есть vivi_ioctl_ops,для VIDIOC_QUERCAP Макрос, то, что на самом деле называется vivi_ioctl_ops серединаиз .vidiioc_querycap члены, то есть мы существуем vivi.c середина пойми это сама из static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) функция. Это немного сбивает с толку, у меня уже кружится голова@__@!~~ Подробно мы проанализируем его позже.
8.4)、продолжатьсуществовать vivi_create_instance серединаанализировать:
vfd->debug = debug;
vfd->v4l2_dev = &dev->v4l2_dev;
Обратите внимание на это предложение индивидуальное, отсюда вы можете увидеть регистрацию существования. video_device Вы должны зарегистрироваться заранее v4l2_device.
set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
vfd->lock = &dev->mutex;
Сделал некоторые настройки, а затем естьбольшой boss Получил:
ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
Его существование v4l2-dev.h середина определяется следующим образом:
static inline int __must_check video_register_device_no_warm(struct video_device *vdev, int type, int nr)
{
return __video_register_device(vdev, type, nr, 0, vdev->fops->owner);
}
__video_register_device существовать v4l2-dev.c серединаопределение:(Просто прямосуществоватькодсередина Комментарий Понятно)
/**
* __video_register_device - register video4linux devices
* @vdev: video device structure we want to register
* @type: type of device to register
* @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...
* -1 == first free)
* @warn_if_nr_in_use: warn if the desired device node number
* was already in use and another number was chosen instead.
* @owner: module that owns the video device node
*
* The registration code assigns minor numbers and device node numbers
* based on the requested type and registers the new device node with
* the kernel.
*
* This function assumes that struct video_device was zeroed when it
* was allocated and does not contain any stale date.
*
* An error is returned if no free minor or device node number could be
* found, or if the registration of the device node failed.
*
* Zero is returned on success.
*
* Valid types are
*
* %VFL_TYPE_GRABBER - A frame grabber
*
* %VFL_TYPE_VBI - Vertical blank data (undecoded)
*
* %VFL_TYPE_RADIO - A radio card
*
* %VFL_TYPE_SUBDEV - A subdevice
*/
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
/* A minor value of -1 marks this video device as never
having been registered */
vdev->minor = -1;
/* the release callback MUST be present */
if (WARN_ON(!vdev->release))
return -EINVAL;
/* Если эта индивидуальная функция выпуска не указана, ошибка будет возвращена напрямую, и она предоставляется существующим vivi_templateсередина. */
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
INIT_LIST_HEAD(&vdev->fh_list);
/* Part 1: check device type */
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
break;
case VFL_TYPE_VBI:
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
name_base = "radio";
break;
case VFL_TYPE_SUBDEV:
name_base = "v4l-subdev";
break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type);
return -EINVAL;
}
/* В соответствии с переданным параметром изtype определите имя из в каталоге существования устройства/dev. */
vdev->vfl_type = type;
vdev->cdev = NULL;
if (vdev->v4l2_dev) {
if (vdev->v4l2_dev->dev)
vdev->parent = vdev->v4l2_dev->dev;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
/* If the prio state pointer is NULL, then use the v4l2_device
prio state. */
if (vdev->prio == NULL)
vdev->prio = &vdev->v4l2_dev->prio;
}
/* Выполните обработку родительского устройства vdevseедина иctrl. Функция инициализации. */
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
* Newer devices (not yet in place) should use the range
* of 128-191 and just pick the first free minor there
* (new style). */
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64;
break;
case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
default:
minor_offset = 128;
minor_cnt = 64;
break;
}
#endif
/* Pick a device node number */
mutex_lock(&videodev_lock);
nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
if (nr == minor_cnt)
nr = devnode_find(vdev, 0, minor_cnt);
if (nr == minor_cnt) {
printk(KERN_ERR "could not get a free device node number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of device node number to minor number */
i = nr;
#else
/* The device node number and minor numbers are independent, so
we just find the first free minor number. */
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_device[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
printk(KERN_ERR "could not get a free minor\n");
return -ENFILE;
}
#endif
vdev->minor = i + minor_offset;
vdev->num = nr;
devnode_set(vdev);
/* Should not happen since we thought this minor was free */
WARN_ON(video_device[vdev->minor] != NULL);
vdev->index = get_index(vdev);
mutex_unlock(&videodev_lock);
/* Выше изpart2to есть определение устройства по номеру субустройства */
/* Part 3: Initialize the character device */
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL) {
ret = -ENOMEM;
goto cleanup;
}
/* Чтобы зарегистрировать устройство, используйте функцию cdev_alloc. Отсюда мы видим, что это обычный драйвер символьного устройства, а затем задайте для него некоторые параметры. Как это есть Драйвер символьного устройства? ? ? Давайте поговорим об этом позже. */
vdev->cdev->ops = &v4l2_fops;
/* cdevвнутри структурыopsзаостренныйv4l2_fopsэтотиндивидуальный Структура,этотиндивидуальныйv4l2_fopsСтруктуратакжедасуществоватьv4l2-dev.cэтотиндивидуальныйдокументсередина。сноваодининдивидуальныйfile_operationsдействоватьфункциянабор,существоватьvivi.cсерединаиметьодининдивидуальныйv4l2_file_operations vivi_fops, какая между ними связь? */
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL;
goto cleanup;
}
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
if (vdev->parent)
vdev->dev.parent = vdev->parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
goto cleanup;
}
/* Register the release callback that will be called when the last
reference to the device goes away. */
vdev->dev.release = v4l2_device_release;
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev));
/* Increase v4l2_device refcount */
if (vdev->v4l2_dev)
v4l2_device_get(vdev->v4l2_dev);
/* существующегоsysfsсередина создает класс и создает узел устройства в рамках существующего класса. */
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
vdev->vfl_type != VFL_TYPE_SUBDEV) {
vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
vdev->entity.name = vdev->name;
vdev->entity.info.v4l.major = VIDEO_MAJOR;
vdev->entity.info.v4l.minor = vdev->minor;
ret = media_device_register_entity(vdev->v4l2_dev->mdev,
&vdev->entity);
if (ret < 0)
printk(KERN_WARNING
"%s: media_device_register_entity failed\n",
__func__);
}
#endif
/* Создайте объект сущности. Этот шаг не является обязательным. Этот шаг необходимо настроить после настройки параметра CONFIG_MEDIA_CONTROLLER. Этот шаг содержит структуру объекта индивидуальныйmedia_entity, которая будет проанализирована позже. */
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
/* Установить бит флага */
mutex_lock(&videodev_lock);
video_device[vdev->minor] = vdev;
/* Сохраните набор структуры vdev изvideo_device в массив video_device середина по младшему номеру устройства. Этот отдельный массив дасуществовать переднюю статику struct video_device *video_device[VIDEO_NUM_DEVICES];Определение из. */
mutex_unlock(&videodev_lock);
return 0;
cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
vdev->minor = -1;
return ret;
}
EXPORT_SYMBOL(__video_register_device);
8.5), регистрация завершена video_device Продолжить после структуры vivi_create_instance серединаосуществлять:
/* Воля vivi_dev dev добавить в video_device vfd середина, зачем ты это делаешь? да для интерфейса драйвера символьного устройства после кизиспользовать */
video_set_drvdata(vfd, dev);
/* добавить в device list связанный списоксередина */
list_add_tail(&dev->vivi_devlist, &vivi_devlist);
/* для подсчета */
if(video_nr != -1)
video_nr ++;
/* ассоциация video_device и vivi_dev */
dev->vfd = vfd;
На этом мы закончили анализ vivi_init и vivi_create_instance Функция, vivi.c середина, оставшаяся из кода, в основном естьк Вниз 3 Мы не будем сейчас анализировать конкретный код реализации индивидуальной структуры.
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,
};
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
.mmap = vivi_mmap,
};
Давайте сначала проанализируем этого человека vivi.c Цель состоит в том, чтобы бросить приблизительный взгляд на некоторые из v4l2 Код драйвера, оставьте несколько вопросов для последующего анализа v4l2 из рамки кода и некоторых концепций иногда, также для этого человека vivi.c Проиллюстрирую примерами. Проанализировав общую структуру, продолжим ее детальный анализ. vivi.c середина Эти конкретные коды реализованы.
Пример описания:
к read Например, приложение существует, вызывает read когда, в соответствии с драйвером file_operations v4l2_fops серединаиз v4l2_read функция,существоватьфункциявпроходить ret = vdev->fops->read(filp, buf, sz, off) Последний звонок нам существует vivi.c середина Заявка на регистрациюиз video_device vivi_template внутри структуры fops->read функция, то есть vivi_read функция. Прямо сейчас V4L2 Только рамка обеспечивает индивидуальную середину передачи эффекта. Посмотрите еще раз vivi_read Внутри функции: возврат vb2_read(&dev->vb_vidq, data, count, ppos, file->f_flags & O_NONBLOCK);оно звонит снова videobuf2-core.c серединаиз vb2_read функция. Это действительно показывает v4l2 рамкаизсерединаэффект поворота.
К таким подобным функциям относятся чтение, запись, опрос, mmap, выпуск Подождите, что еще более особенное ioctl Функция существует, проанализируем ее позже. Все это вызовы приложений, V4L2 рамкасередина Перейти к верному следует издрайвера середина, а затем водитель вызывается в соответствии с разными из, выберите вызов videobuf или ioctl серединаизфункция.
для const struct v4l2_ioctl_ops *ioctl_ops,существовать vivi.c серединато есть static const struct v4l2_ioctl_ops vivi_ioctl_ops;этотиндивидуальный ioctl Более хлопотно, во-первых, как драйвер символьного устройства, когда приложение вызывает ioctl когда это называется file_operations v4l2_fops серединаиз .unlocked_ioctl = v4l2_ioctl,этотиндивидуальный v4l2_ ioctl Также прошло ret = vdev->fops->ioctl(filp, cmd, arg) Только что позвонил vivi.c середина Заявка на регистрациюиз struct video_device vivi_template внутри структуры fops->unlocked_ioctl функция, то есть v4l2_file_operatios vivi_fops внутри video_ioctl2 функция,этотиндивидуальный video_ioctl2 Функция вызывается снова __video_do_ioctl функция(к Последние дваиндивидуальныйфункция Всесуществовать v4l2-ioctl.c середина), в зависимости от cmd Макро, к VIDIOC_QUERYCAP Например:
проходить ret = ops->vidioc_querycap(file, fh, cap);Чтосередина struct video_device *vfd = video_devdata(file);const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;Можетквидеть,__video_do_ioctl Функция вызывается снова Понятно struct video_device vivi_template Структура Лимана .ioctl_ops = &vivi_ioctl_ops,Затем выберите структуру v4l2_ioctl_ios vivi_ioctl_ops на основе имени макроса.