Видеоустройства генерируют много данных, и традиционный механизм буферизации больше не может удовлетворить спрос. С этой целью ядро Linux абстрагирует механизм videobuf2 для управления буфером кадров, в котором хранятся видеоизображения. Уровень абстракции videobuf2 подобен мосту, соединяющему пользовательское пространство и драйвер V4L2. Уровень абстракции videobuf2 обеспечивает стандартные системные вызовы ввода-вывода POSIX в пользовательское пространство, включая чтение, опрос, mmap и т. д., а также обеспечивает большое количество вызовов ioctl V4L2, связанных с потоковым вводом-выводом, включая выделение буфера и постановку буфера в очередь. , удаление буфера из очереди и управление потоком. Хотя использование videobuf2 потребует от драйвера некоторых проектных решений, преимущество его использования состоит в том, что videobuf2 позволяет сократить код драйвера и сохранить согласованность подсистемы V4L2 в API пользовательского пространства. Очевидно, использование videobuf2 более разумно.
Не все видеоустройства используют один и тот же тип videobuf2. В реальном времени в ядре Linux есть три разных типа videobuf2.
Кроме того, имеется буфер оверлея, который находится в видеопамяти системы. Буфер наложения в настоящее время устарел, но его все еще иногда можно увидеть в некоторых драйверах системы на кристалле.
Что касается того, какой videobuf2 использовать, разработчикам драйверов необходимо оценить его на основе фактической среды использования.
Основная структура данных videobuf2 — это очередь буферов, которая используется для управления всеми буферами. Очередь буфера описывается структурой vb2_queue, а буфер — структурой vb2_buffer. Указатели функций в структурах struct vb2_ops и struct vb2_mem_ops должны быть реализованы драйвером. Диаграмма отношений структуры данных показана на рисунке ниже. Структура данных для хранения и управления данными видеокадра описывается структурой vb2_buffer. buf_struct_size определяет размер буфера и должен быть установлен драйвером; num_buffers определяет количество буферов, которое не может быть больше VIDEO_MAX_FRAME и не может быть меньше min_buffers_needed. Размер буфера определяется параметром buf_struct_size. Драйвер может одновременно определить свой собственный буфер и установить buf_struct_size. Если он равен 0, это означает, что драйвер не определяет свою собственную структуру буфера, а затем использует sizeof (struct vb2_buffer). ), чтобы инициализировать buf_struct_size. Тип буфера определяется перечислением v4l2_buf_type, а для получения изображения (камера) используется тип V4L2_BUF_TYPE_VIDEO_CAPTURE.
Взаимосвязь между структурами данных struct vb2_queue и struct vb2_buffer можно описать на рисунке ниже, поэтому динамически выделяемая структура struct vb2_buffer сохраняется в массиве bufs[VIDEO_MAX_FRAME] и единообразно управляется struct vb2_queue. Максимальное количество структур struct vb2_buffer определяется макросом VIDEO_MAX_FRAME; Queue_list сохраняет все буферы, поставленные в очередь из пользовательского пространства; Done_list сохраняет буферы, готовые к исключению из очереди в пользовательское пространство.
Модель ввода-вывода буфера видеоизображения описывается перечислением vb2_io_modes, которое требует здесь специального пояснения. Система Linux разделена на пространство пользователя и пространство ядра. Приложения находятся в пространстве пользователя, а ядро работает в пространстве ядра. Подсистема V4L2 является компонентом ядра и также работает в пространстве ядра, а собранные ею данные также хранятся в памяти пространства ядра. Приложения не могут напрямую обращаться к памяти в пространстве ядра и должны использовать некоторые методы для доступа к ней. Для буферов существует три основных режима ввода-вывода:
(1) VB2_READ и VB2_WRITE:
Используйте традиционные функции интерфейса ввода-вывода для чтения и записи. Хотя этот метод является самым простым, он копирует данные туда и обратно между пространством пользователя и пространством ядра, что неэффективно. Он также занимает память как в пространстве пользователя, так и в пространстве ядра, что является дорогостоящим.
(2)VB2_MMAP:
Метод отображения памяти. mmap сопоставляет память, управляемую videobuf2 в драйвере, с пользовательским пространством, и приложение может напрямую обращаться к памяти, управляемой videobuf2. Этот метод очень эффективен и использует мало памяти. Однако память, управляемая videobuf2, принадлежит пространству ядра и не может быть заменена на раздел SWAP. Если одновременно запускается большое количество процессов, она будет занимать больше памяти, и эту память нельзя будет заменить, что приведет к уменьшению. производительность системы.
(3)VB2_USERPTR:
Режим пользовательского указателя. Память выделяется приложением, и адрес памяти передается драйверу ядра V4L2, который затем заполняет данные в памяти пользовательского пространства.
[include/uapi/linux/videodev2.h]
// перечисление типа videobuf2
enum v4l2_buf_type {
V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, // Получение изображения
V4L2_BUF_TYPE_VIDEO_OUTPUT = 2, // Вывод изображения
V4L2_BUF_TYPE_VIDEO_OVERLAY = 3,
V4L2_BUF_TYPE_VBI_CAPTURE = 4,
V4L2_BUF_TYPE_VBI_OUTPUT = 5,
V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6,
V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7,
#if 1
V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8, /* Experimental */
#endif
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10,
V4L2_BUF_TYPE_SDR_CAPTURE = 11,
};
// тип буфера
enum v4l2_memory {
V4L2_MEMORY_MMAP = 1,// Памятькартографирование V4L2_MEMORY_USERPTR = 2, // указатель пользовательского пространства
V4L2_MEMORY_OVERLAY = 3,// Памятьперекрытие V4L2_MEMORY_DMABUF = 4, // DMAПамять
};
/* Timestamp type */
#define V4L2_BUF_FLAG_TIMESTAMP_MASK 0x0000e000
#define V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN 0x00000000
// Временная метка возрастающего типа, в Ядро monotonic Временная метка, созданная на временной шкале часов.
#define V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC 0x00002000
#define V4L2_BUF_FLAG_TIMESTAMP_COPY 0x00004000
/* Timestamp sources. */
#define V4L2_BUF_FLAG_TSTAMP_SRC_MASK 0x00070000
#define V4L2_BUF_FLAG_TSTAMP_SRC_EOF 0x00000000
#define V4L2_BUF_FLAG_TSTAMP_SRC_SOE 0x00010000
[include/media/videobuf2-core.h]
enum vb2_io_modes {
VB2_MMAP = (1 << 0), // Драйвер поддерживает API потоковой передачи mmap.
VB2_USERPTR = (1 << 1), // Драйвер поддерживает API потоковой передачи в режиме указателя пользователя.
VB2_READ = (1 << 2), // Драйвер поддерживает доступ read().
VB2_WRITE = (1 << 3), // Драйвер поддерживает доступ write().
VB2_DMABUF = (1 << 4), // Драйвер поддерживает API потоковой передачи DMABUF.
};
struct vb2_queue {
enum v4l2_buf_type type; // Тип videobuf2, см. перечисление Перечисление v4l2_buf_type
unsigned int io_modes; // Поддерживаемые режимы ввода-вывода, см. перечисление. Перечисление vb2_io_modes
unsigned fileio_read_once:1; // Сообщить об EOF после чтения первого буфера
unsigned fileio_write_immediately:1; // Данные, записанные посредством записи, добавляются в очередь буфера.
unsigned allow_zero_bytesused:1;
// защитить структуру Блокировка мьютекса vb2_queue сериализует операции буферной очереди. Если драйвер действительно имеет блокировку мьютекса,
// Для него можно установить значение NULL, и API основного уровня videobuf2 не использует эту блокировку.
struct mutex *lock;
const struct vb2_ops *ops; // В драйвере реализована функция обратного вызова
const struct vb2_mem_ops *mem_ops; // Требуется дозатор Память Функция
void *drv_priv; // Управляйте личными данными
// Размер структуры буфера драйвера. Если он равен 0, это означает, что драйвер не определяет свою собственную структуру буфера. Используйте sizeof(struct. vb2_buffer)
unsigned int buf_struct_size;
// Флаг метки времени, см. определения макросов V4L2_BUF_FLAG_TIMESTAMP_XX и V4L2_BUF_FLAG_TSTAMP_SRC_XX.
u32 timestamp_flags;
// Флаг Память при выделении буфера, обычно 0, также используется GFP_DMA или __GFP_DMA32, чтобы принудительно выделить Память явной области Память.
gfp_t gfp_flags;
u32 min_buffers_needed; // Минимальное количество буферов, необходимое перед обработкой потока данных
// Частные блокировки, защищающие выделение, освобождение и сопоставление буферов.
struct mutex mmap_lock;
// В настоящее время используется тип буфера,Значениеenum перечисление v4l2_memory
enum v4l2_memory memory;
struct vb2_buffer *bufs[VIDEO_MAX_FRAME]; // Сохраните адрес выделенного буфера
unsigned int num_buffers; // Количество выделенных буферов
struct list_head queued_list; // Связанный список буферов в пользовательском пространстве
unsigned int queued_count; // Количество готовых буферов в очереди
atomic_t owned_by_drv_count; // Количество буферов, принадлежащих драйверу
struct list_head done_list; // Буфер в этом связанном списке заполнен данными и может быть исключен из очереди и использован пользовательским пространством.
spinlock_t done_lock; // Спин-блокировка, защищающая связанный список Done_list
wait_queue_head_t done_wq; // Очередь ожидания ожидает освобождения буфера из очереди
void *alloc_ctx[VIDEO_MAX_PLANES];
unsigned int plane_sizes[VIDEO_MAX_PLANES];
unsigned int streaming:1; // Текущее состояние потока
unsigned int start_streaming_called:1; // start_streaming() был успешно вызван
unsigned int error:1; // struct Произошла фатальная ошибка в vb2_queue.
unsigned int waiting_for_buffers:1; // Используется в функции опроса, чтобы проверить, ожидают ли данные еще
......
};
Драйвер должен реализовать функции структуры vb2_ops. Конечно, он также может реализовать ее часть или напрямую использовать функции, предоставляемые ядром.
Очередь_setup вызывается командами ioctl VIDIOC_REQBUFS и VIDIOC_CREATE_BUFS. Часто используется и вызывается один раз перед выделением фактической памяти. Если количество выделенных буферов меньше num_buffers, оно будет вызвано снова. Этой функции необходимо сохранить количество выделенных буферов в num_buffers, количество плоскостей буфера в num_planes, а размер каждого плана сохраняется в массиве размеров. Плоскости связаны с форматом пикселей видео. Например, плоскостей формата YUV420SP — 2. Массив alloc_ctxs хранит конкретные данные для каждой плоскости.
Ядра функций wait_prepare и wait_finish предоставляют реализации по умолчанию, которые можно использовать напрямую, что соответствует функциям vb2_ops_wait_prepare и vb2_ops_wait_finish соответственно. Реализация этих двух функций очень проста. vb2_ops_wait_prepare снимает блокировку мьютекса, а vb2_ops_wait_finish получает блокировку мьютекса. Когда пользователь вызывает ioctl и использует команду VIDIOC_QBUF, ядро определяет, является ли это блокирующим вызовом и данные не готовы, ядро вызывает wait_prepare, чтобы снять блокировку и перейти в режим ожидания, пока данные не будут готовы. готов к пробуждению, а затем вызов wait_finish повторно удерживает блокировку.
buf_init вызывается после выделения буфера или после получения нового USERPTR (в случае MMAP). Если инициализация не удалась, будет возвращено число, отличное от 0. В этот момент функцияqueue_setup завершится неудачно. . Обычно не используется.
Буфер buf_prepare необходимо вызывать каждый раз, когда он ставится в очередь из пользовательского пространства или вызывается командой VIDIOC_PREPARE_BUF ioctl. Драйверу необходимо выполнить некоторую работу по инициализации или получить и изменить буфер. Если драйвер поддерживает VIDIOC_CREATE_BUFS, размер буфера. необходимо проверить. В случае возникновения ошибки буфер не будет поставлен в очередь.
После вызова start_streaming поток переходит в открытое состояние. Перед вызовом драйвер должен сначала вызвать buf_queue, чтобы получить буфер. Если происходит аппаратная ошибка, драйвер может вернуть ошибку через этот обратный вызов, и все буферы будут возвращены в videobuf2 (вызов vb2_buffer_done(*vb, VB2_BUF_STATE_QUEUED))。Если вам нужно установитьstart_streamingчасbufferминимальное количество,Тогда вам следует позвонить вфункция Минимальные настройки передbufferзначение количестваvb2_queue->min_buffers_needed,толькоbufferколичество больше, чемvb2_queue->min_buffers_neededчасstart_streamingможно назвать успешно。
После вызова stop_streaming поток переходит в закрытое состояние, и драйверу необходимо остановить передачу DMA или дождаться завершения работы и вернуть все буферы (возврат к videobuf2). Вызовите vb2_buffer_done, чтобы вернуть все буферы, хранящиеся в драйвере, используя параметры VB2_BUF_STATE_DONE (операция завершена) и VB2_BUF_STATE_ERR (ошибка операции). Чтобы дождаться завершения, используйте vb2_wait_for_all_buffers.
// Драйвер требует четко определенной функции для работы vb2_queue.
[include/media/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[]);
// Снимите все блокировки при вызове ioctl, чтобы дождаться нового буфера и избежать взаимоблокировки при блокировке.
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);
// Его необходимо вызывать каждый раз, когда буфер выводится из очереди в пользовательское пространство, и драйвер может получить доступ к буферу или изменить его.
void (*buf_finish)(struct vb2_buffer *vb);
// После вызова буфер освобождается, и драйвер может выполнить некоторую работу по очистке.
void (*buf_cleanup)(struct vb2_buffer *vb);
// После вызова поток переходит в открытое состояние. Перед вызовом драйвер должен сначала вызвать buf_queue, чтобы получить буфер.
int (*start_streaming)(struct vb2_queue *q, unsigned int count);
// После вызова поток переходит в закрытое состояние, и драйверу необходимо остановить передачу DMA или дождаться завершения работы и постановки всех буферов в очередь.
void (*stop_streaming)(struct vb2_queue *q);
// Буфер добавляется в очередь драйвера
void (*buf_queue)(struct vb2_buffer *vb);
};
Struct vb2_buffer — это базовая структура, в которой хранятся видеоданные и информация. Каждый кадр изображения соответствует структуре struct vb2_buffer. В структуре struct v4l2_buffer хранится информация об изображении, такая как метка времени, номер, серийный номер и другая информация. Структура vb2_buffer выделяется, когда приложение запрашивает буфер с помощью команды VIDIOC_REQBUFS команды ioctl. Состояние структуры vb2_buffer описывается определением перечисления enum vb2_buffer_state. Если это тип V4L2_MEMORY_MMAP, будет выделена дополнительная память, данные изображения будут храниться в дополнительно выделенной памяти, а указатель дополнительно выделенной памяти будет сохранен в массиве planes[VIDEO_MAX_PLANES].
[include/media/videobuf2-core.h]
enum vb2_buffer_state { // Перечисление статуса буфера
VB2_BUF_STATE_DEQUEUED, // Удаление буфера из очереди, под контролем пользовательского пространства, состояние по умолчанию
VB2_BUF_STATE_PREPARING, // videobuf2 готовит буфер
VB2_BUF_STATE_PREPARED, // буфер готов
VB2_BUF_STATE_QUEUED, // Буфер помещен в очередь и находится в videobuf2, а не в драйвере.
VB2_BUF_STATE_ACTIVE, // Буфер находится в драйвере
VB2_BUF_STATE_DONE, // Буфер возвращается из драйвера в videobuf2, но еще не исключен из очереди в пространство пользователя.
VB2_BUF_STATE_ERROR, // Возникает ошибка. При исключении из очереди в пространство пользователя будет сообщено об ошибке.
};
struct vb2_buffer { // дескриптор видеобуфера
struct v4l2_buffer v4l2_buf; // Соответствующий буфер может быть прочитан драйвером
// Может быть прочитан и записан драйвером (используется в байтах) даже для одноплоскостных типов, v4l2_planes[0] должен быть
// Используйте byted вместо просто v4l2_buf.
// Драйвер использует vb2_set_plane_payload() для установки байтов.
struct v4l2_plane v4l2_planes[VIDEO_MAX_PLANES];
struct vb2_queue *vb2_queue; // vb2_queue, к которому принадлежит vb2_buffer
unsigned int num_planes; // Сколько плоскостей имеет этот буфер?
enum vb2_buffer_state state; // Текущее состояние буфера
// queued Буферный связанный список, сохраняет все данные из пользовательского пространства. буферы в очереди
struct list_head queued_entry;
// Сохраните все буферы, которые будут исключены из очереди в связанный список пользовательского пространства.
struct list_head done_entry;
// Частная информация для каждого самолета, водителю запрещено ее изменять.
struct vb2_plane planes[VIDEO_MAX_PLANES];
......
};
// выделить структуру Если vb2_buffer имеет тип V4L2_MEMORY_MMAP, будет выделена дополнительная Память.
// Данные изображения хранятся в дополнительно выделенной Память, а дополнительно выделенный указатель Память хранится в структуре.
struct vb2_plane {
void *mem_priv; //Сохранение одного кадра данных изображения (для режима типа MMAP)
struct dma_buf *dbuf; //(для режима типа DMA)
unsigned int dbuf_mapped;
};
// struct v4l2_buffer используется для указания и описания буфера кадра, который может установить приложение.
struct v4l2_buffer {
__u32 index; // номер буфера
__u32 type; // Тип буфера, состоящий из перечисления определение v4l2_buf_type
// Количество байтов, занимаемых данными в буфере (полезная нагрузка), неиспользуемых (установлено в 0) для многоплоскостных буферов.
__u32 bytesused;
// Бит флага, см. определение макроса V4L2_BUF_FLAG_XX, общие значения — V4L2_BUF_FLAG_MAPPED,
// V4L2_BUF_FLAG_QUEUED и V4L2_BUF_FLAG_DONE соответственно означают, что текущий кэш сопоставлен и
// Кэш может собирать данные, кеш может извлекать данные
__u32 flags;
__u32 field;
struct timeval timestamp; // Временная метка видеокадра
struct v4l2_timecode timecode; // временной код
__u32 sequence; // Порядковый номер кадра
__u32 memory; // enum перечисление определение v4l2_memory
union {
// V4L2_MEMORY_MMAP, с устройства, которое необходимо сопоставить смещение от головы памяти к головке данных
__u32 offset;
// V4L2_MEMORY_USERPTR,указатель пользовательского пробел указывает на этот буфер
unsigned long userptr;
// for multiplanar buffers; userspace pointer to the array of plane
// info structs for this buffer
struct v4l2_plane *planes;
// V4L2_MEMORY_DMABUF, с этим дескриптором связан дескриптор пользовательского пространства
__s32 fd;
} m;
// Для одноплоскостного указывает количество байтов в буфере.
// Для многоплоскостности буферы означают плоскости количество элементов в массиве
__u32 length;
__u32 reserved2;
__u32 reserved;
};
Struct vb2_mem_ops — это набор операционных функций для выделения и обработки буферной памяти. Эти функции связаны с типом буфера, то есть типом, определяемым перечислением v4l2_memory. Подробности заключаются в следующем.
Структура struct vb2_mem_ops обычно инициализируется структурой vb2_dma_contig_memops, которая предоставляется ядром и может использоваться напрямую.
[include/linux/dma-direction.h]
enum dma_data_direction { // Направление передачи данных DMA
DMA_BIDIRECTIONAL = 0, // двусторонний
DMA_TO_DEVICE = 1, // Перенос на устройство
DMA_FROM_DEVICE = 2, // Передача с устройства
DMA_NONE = 3,
};
[include/media/videobuf2-core.h]
// Буфер Память, операции обработки и выделения, набор функций
struct vb2_mem_ops {
// Выделить видео Память и распределитель личных данных (необязательно)
void *(*alloc)(void *alloc_ctx, unsigned long size,
enum dma_data_direction dma_dir, gfp_t gfp_flags);
// Сообщите распределителю, что этот буфер больше не используется. Если другой пользователь его не использует, распределитель освободит этот блок.
void (*put)(void *buf_priv);
struct dma_buf *(*get_dmabuf)(void *buf_priv, unsigned long flags);
// получатьуказатель пользовательского пространствазаостренный Память,существоватьV4L2_MEMORY_USERPTRиспользуется в шаблоне
void *(*get_userptr)(void *alloc_ctx, unsigned long vaddr,
unsigned long size, enum dma_data_direction dma_dir);
// Сообщите распределителю, что буфер USERPTR больше не используется.
void (*put_userptr)(void *buf_priv);
// Буфер вызывается каждый раз, когда он добавляется в очередь из пользовательского пространства, что полезно для синхронизации кэша.
void (*prepare)(void *buf_priv);
// Буфер вызывается каждый раз, когда он добавляется в пространство пользователя из очереди Ядро.
void (*finish)(void *buf_priv);
// Добавить общую структуру для аппаратных операций. dma_buf, используется в режиме V4L2_MEMORY_DMABUF
// alloc_ctx — контекст выделения, dbuf — общий dma_buf
void *(*attach_dmabuf)(void *alloc_ctx, struct dma_buf *dbuf,
unsigned long size, enum dma_data_direction dma_dir);
// Уведомить экспортера буфера о том, что текущий DMABUF больше не используется.
void (*detach_dmabuf)(void *buf_priv);
// Запросить доступ к DMABUF у распределителя. Распределитель этого DMABUF уведомит драйвер о том, что DMABUF будет использоваться.
int (*map_dmabuf)(void *buf_priv);
// Освободите контроль доступа к DMABUF. Распределитель этого DMABUF уведомит драйвер о том, что DMABUF был использован.
void (*unmap_dmabuf)(void *buf_priv);
// Возвращает виртуальный адрес Ядро данного буфера, связанного с частной структурой данных.
void *(*vaddr)(void *buf_priv);
// Возвращает файл cookie, определенный распределителем, для данного буфера.
void *(*cookie)(void *buf_priv);
// Возвращает текущего пользователя этого буфера. Если используется только слой videobuf2, верните 1.
unsigned int (*num_users)(void *buf_priv);
// Устанавливает сопоставление пользовательского пространства с областью виртуального адреса данного буфера.
int (*mmap)(void *buf_priv, struct vm_area_struct *vma);
};
Использование videobuf2 сложное и его необходимо объяснять на конкретных примерах драйверов, чтобы его было легче понять. На следующем рисунке показано, как использовать videobuf2 контроллера CSI на платформе imx6ull. Приложения могут получить доступ к видеоустройству, вызывая системные вызовы open, close, ioctl, mmap и read. Ядро использует соответствующие методы для доступа к videobuf2 в соответствии с различными системными вызовами. Начнем с этих системных вызовов и разберем использование videobuf2 в ядре.
Приложение вызывает open, чтобы открыть видеоустройство и получить дескриптор устройства. Ядро сначала вызывает v4l2_open, а затем вызывает функцию mx6s_csi_open, предоставляемую драйвером. Буферная очередь vb2_queue инициализируется в функции mx6s_csi_open. Структура данных буферной очереди vb2_queue обычно встроена в другие структуры и динамически выделяется драйвером. Во-первых, вы должны установить тип типа буфера, модель ввода-вывода io_modes, коллекцию функций операций с буфером, коллекцию функций управления буферной памятью mem_ops, тип метки времени timestamp_flags (обычно устанавливается в V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC), другие элементы могут быть установлены в соответствии с фактической ситуацией. и, наконец, вызов vb2_queue_init завершает инициализацию буферной очереди vb2_queue.
[include/media/videobuf2-core.h]
Структура данных очереди q-буфера указатель vb2_queue
Возвращаемое значение равно -0 в случае успеха; если оно меньше 0, произойдет ошибка.
int vb2_queue_init(struct vb2_queue *q)
{
// Следующие элементы должны быть установлены, иначе будет возвращена ошибка -EINVAL.
if (WARN_ON(!q) || WARN_ON(!q->ops) || WARN_ON(!q->mem_ops) ||
WARN_ON(!q->type) || WARN_ON(!q->io_modes) ||
WARN_ON(!q->ops->queue_setup) || WARN_ON(!q->ops->buf_queue) ||
WARN_ON(q->timestamp_flags & ~(V4L2_BUF_FLAG_TIMESTAMP_MASK |
V4L2_BUF_FLAG_TSTAMP_SRC_MASK)))
return -EINVAL;
// Водитель должен выбрать подходящую временную метку, иначе Ядро выдаст предупреждение.
WARN_ON((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN);
INIT_LIST_HEAD(&q->queued_list); // Инициализировать узел связанного списка Queueed_list
INIT_LIST_HEAD(&q->done_list); // Инициализировать узел связанного списка Done_list
spin_lock_init(&q->done_lock); // Инициализировать спин-блокировку
mutex_init(&q->mmap_lock); // Инициализировать блокировку мьютекса
init_waitqueue_head(&q->done_wq); // Инициализировать заголовок очереди ожидания
// Если драйвер размера буфера не установлен, Ядро инициализируется значением по умолчанию.
if (q->buf_struct_size == 0)
q->buf_struct_size = sizeof(struct vb2_buffer);
return 0;
}
Подсистема V4L2 определяет множество команд ioctl для использования приложениями. Команда VIDIOC_REQBUFS используется для запроса буфера у Ядро.,Команда VIDIOC_QUERYBUF используется для получения информации о буфере.,Команда VIDIOC_QBUF возвращает пустой кэш после чтения данных в очередь кэша драйвера.,Команда VIDIOC_DQBUF возвращает заполненный буфер из драйвера в приложение.,Команда VIDIOC_STREAMOFF используется для закрытия потока.,Остановитесь немедленно Получение изображения,Команда VIDIOC_STREAMON используется для открытия потока.,Сразу открыть Получение изображения。Ядро Процесс вызова вv4l2_ioctl->video_ioctl2->__video_do_ioctl->Вызов разных драйверов на основе разных командфункция->Позвоните в соответствующееvideobuf2иметь дело сфункция,Пожалуйста, обратитесь к рисунку выше для конкретного процесса вызова. Ниже приводится подробный анализ функции обработки videobuf2, вызываемой ioctl.
[include/media/videobuf2-core.h]
// Структура данных очереди q-буфера указатель vb2_queue
// req — структура, в которой хранится информация, необходимая для обращения к буферу. Приложению необходимо установить элементы внутри.
// Возвращаемое значение равно -0 в случае успеха; если оно меньше 0, произойдет ошибка.
int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req);
[include/uapi/linux/videodev2.h]
struct v4l2_requestbuffers {
__u32 count; // Количество буферов, к которым применяется один кадр изображения, соответствует одному буферу.
__u32 type; // тип буфера,Камера V4L2_BUF_TYPE_VIDEO_CAPTURE.
__u32 memory; // Обычно V4L2_MEMORY_MMAP или V4L2_MEMORY_USERPTR.
__u32 reserved[2]; // бронировать
};
Процесс вызова vb2_reqbufs можно резюмировать следующим образом:
(1) Убедитесь, что тип памяти и тип буфера указаны правильно. Функция __verify_memory_type используется для проверки типа буфера и типа буферной памяти. Тип типа буфера в vb2_queue и тип типа буфера в v4l2_requestbuffers должны быть согласованы. Тип буферной памяти должен быть одним из V4L2_MEMORY_MMAP, V4L2_MEMORY_USERPTR и V4L2_MEMORY_DMABUF.
Если это тип V4L2_MEMORY_MMAP,ноq->io_modesДолжно быть установленоVB2_MMAP,mem_ops->alloc、q->mem_ops->putиq->mem_ops->mmapизфункция Указатель также должен быть установлен。 Если это тип V4L2_MEMORY_USERPTR,ноq->io_modesДолжно быть установленоVB2_USERPTR,mem_ops->get_userptrиq->mem_ops->put_userptrизфункция Указатель также должен быть установлен。
Если это тип V4L2_MEMORY_DMABUF,ноq->io_modesДолжно быть установленоVB2_DMABUF,mem_ops->attach_dmabuf、q->mem_ops->detach_dmabuf、q->mem_ops->map_dmabufиq->mem_ops->unmap_dmabufизфункция Указатель также должен быть установлен。
(2) Определите, верны ли параметры буфера. Если нет, необходимо выполнить некоторую обработку. Если количество запрошенных буферов равно 0, или количество буферов в очереди буферов не равно 0, или тип применяемой буферной памяти несовместим с типом буферной памяти в очереди буферов, будет введена дополнительная логика обработки. Если тип памяти — V4L2_MEMORY_MMAP и буфер используется, ошибка будет возвращена напрямую. Очищает буферы в состоянии PREPARED или QUEUED и освобождает буферную память.
(3) Рассчитайте количество буферов, которые необходимо выделить.
(4) Вызовите функцию Functionqueue_setup, реализованную драйвером.,водить машинуфункция Нужно установитьnum_buffers、num_buffers、q->plane_sizesиq->alloc_ctx。
(5) Вызовите __vb2_queue_alloc, чтобы выделить буферную память. Состояние буфера в это время — VB2_BUF_STATE_DEQUEUED. Эта функция подробно описана позже.
(6) Если количество выделенных буферов меньше количества, которое необходимо выделить, вам необходимо снова вызвать функциюqueue_setup, предоставляемую драйвером.
(7) Установите количество выделенных буферов и верните количество выделенных буферов приложению.
vb2_reqbufs
// Проверьте память буфера типибуфер Тип правильный?
->__verify_memory_type
->__reqbufs
// Количество запрошенных буферов равно 0 или количество буферов в очереди буферов не равно 0 или
// Если примененный тип буфера несовместим с типом буфера в очереди буферов, требуется дополнительная обработка.
if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
mutex_lock(&q->mmap_lock);
// Если тип Память — V4L2_MEMORY_MMAP и буфер используется, то ошибка будет возвращена напрямую.
if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
mutex_unlock(&q->mmap_lock);
return -EBUSY;
}
// Если буфер находится в состоянии ПОДГОТОВЛЕНО или В ОЧЕРЕДИ, его необходимо очистить.
// Обычно такая ситуация возникает, когда буфер применяется, но STREAMON не вызывается.
__vb2_queue_cancel(q);
// Освободить выделенный буфер
ret = __vb2_queue_free(q, q->num_buffers);
mutex_unlock(&q->mmap_lock);
}
// Количество буферов — это меньшее из числа, запрошенного приложением, а максимальное количество VIDEO_MAX_FRAME определяется как 32.
num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
// Количество буферов — это большее из num_buffers и минимальное необходимое количество буферов.
num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
// Установите модель буфера «Память» в очереди буферов. Модель «Память» должна соответствовать модели, применяемой приложением.
q->memory = req->memory;
// Обратный вызов функции query_setup, предоставляемой драйвером, функция, предоставляемая imx6ull, — mx6s_videobuf_setup.
// водить машинуфункция Нужно установитьnum_buffers、num_buffers、q->plane_sizesиq->alloc_ctx
call_qop(...queue_setup...)
// Выделить буфер
->__vb2_queue_alloc
// нравиться Количество выделенных буферовменьше, чем требуется для выделенияизколичество,Нужно позвонить еще разводить машинупоставлятьизqueue_setupфункция
if (!ret && allocated_buffers < num_buffers) {
num_buffers = allocated_buffers;
// Если драйвер может справиться с этой ситуацией, он не вернет ошибку. Если он не может ее обработать, он вернет ошибку.
// mx6s_videobuf_setup не имеет этой функции
ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
&num_planes, q->plane_sizes, q->alloc_ctx);
if (!ret && allocated_buffers < num_buffers)
ret = -ENOMEM;
}
->mutex_lock(&q->mmap_lock); // Доступ к общему участнику буфера требует синхронизации.
// настраивать Количество выделенных буферов
q->num_buffers = allocated_buffers;
->mutex_unlock(&q->mmap_lock);
req->count = allocated_buffers // Вернуть количество в приложение выделенных буферов
Функция очереди_setup должна быть предоставлена драйвером. Функция, предоставляемая imx6ull, — mx6s_videobuf_setup.,основнойиз Функциянастраивать__reqbufsфункциясерединаиз缓冲区количествоnum_buffers、Количество самолетов num_planes、planeизразмерq->plane_sizesиq->alloc_ctx。вышенастраиватьиз Переменные называютсяфункцияуказательизформа передана в。
static int mx6s_videobuf_setup(struct vb2_queue *vq,
const struct v4l2_format *fmt,
unsigned int *count, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vq);
alloc_ctxs[0] = csi_dev->alloc_ctx; // установить alloc_ctxs
sizes[0] = csi_dev->pix.sizeimage; // Установите plane_sizes в соответствии с размером изображения.
if (0 == *count) // Если количество переданных буферов равно 0, ему присваивается значение 32.
*count = 32;
// Если num_planes равно 0 и общее количество Память, занимаемое буфером, превышает указанное максимальное значение, количество буферов будет пересчитано.
// Максимальное значение — 64 МБ, MAX_VIDEO_MEM определяется как 64.
if (!*num_planes && sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
// Затем количество буферов рассчитывается по максимальному Память
*count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
*num_planes = 1; // Установите количество самолетов на 1.
return 0;
}
Распределение буфера в основном реализуется функцией __vb2_queue_alloc. Выделите буферы num_buffers, то есть выделите структуры num_buffers struct vb2_buffer. Все адреса буферной памяти сохраняются в массиве bufs в структуре vb2_queue. Если буферная память имеет тип V4L2_MEMORY_MMAP, вам необходимо выделить дополнительный буфер для сохранения изображения. Один буфер выделяет буферы num_planes для сохранения изображения. Этот буфер выделяется с помощью vb2_dc_alloc. Структура буфера после выделения показана на рисунке ниже. Буферу типа V4L2_MEMORY_MMAP необходимо выделить дополнительное пространство памяти для хранения данных изображения, как показано в зеленом поле на рисунке. Предпочтительно сначала выделить управляемую структуру struct vb2_dc_buf, а затем выделить буфер, который фактически хранит данные изображения. физический буфер для хранения изображения. Адрес соответствует виртуальному адресу. Виртуальный адрес сохраняется в элементе vaddr структуры управления, виртуальный адрес сохраняется в элементе dma_addr структуры управления, а размер буфера сохраняется в. размер члена структуры управления.
static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
unsigned int num_buffers, unsigned int num_planes)
{
unsigned int buffer;
struct vb2_buffer *vb;
int ret;
// Всего выделено num_buffers буферов.
for (buffer = 0; buffer < num_buffers; ++buffer) {
// Выделить буфер,Размер буфера — buf_struct_size.,Обычно устанавливается водителем,
// Для платформы imx6ull установлено значение sizeof(struct mx6s_buffer)
vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
// Если оно многоплоскостное буферы, длина буфера хранит количество плоскостей
if (V4L2_TYPE_IS_MULTIPLANAR(q->type))
vb->v4l2_buf.length = num_planes;
vb->state = VB2_BUF_STATE_DEQUEUED; // Установить статус буфера
vb->vb2_queue = q; // Установите очередь буферов для управления буферами
vb->num_planes = num_planes; // Установите количество самолетов
vb->v4l2_buf.index = q->num_buffers + buffer; // Установить номер буфера
vb->v4l2_buf.type = q->type; // настраиватьтип буфера
vb->v4l2_buf.memory = memory; // Установить тип буфера
// Для типа V4L2_MEMORY_MMAP также необходимо выделить дополнительную Память для сохранения данных изображения.
// Затем, отображаясь в пользовательском пространстве, пользователи могут напрямую читать данные в дополнительном Память.
if (memory == V4L2_MEMORY_MMAP) {
// ******Выделение хранилища данных изображения Памятьфункция******
->__vb2_buf_mem_alloc(vb);
// Для каждого буфера выделите num_planes дополнительных блоков Память
for (plane = 0; plane < vb->num_planes; ++plane) {
// Выделенный размер Память выровнен по странице.
unsigned long size = PAGE_ALIGN(q->plane_sizes[plane]);
// Вызовите allocфункцию, предоставленную драйвером, чтобы выделить Память,
// Платформа imx6ull вызывает функцию vb2_dc_alloc
mem_priv = call_ptr_memop(vb, alloc, q->alloc_ctx[plane],
size, dma_dir, q->gfp_flags);
// Сохраните дополнительно выделенную Память в члене mem_priv.
vb->planes[plane].mem_priv = mem_priv;
// сохранить длину
vb->v4l2_planes[plane].length = q->plane_sizes[plane];
}
// Вызов функции buf_init, предоставляемой драйвером для инициализации, которая не предусмотрена imx6ull.
->call_vb_qop(vb, buf_init, vb);
}
// Сохранить адрес буфера
q->bufs[q->num_buffers + buffer] = vb;
}
// Установите длину каждой плоскости всех буферов
->__setup_lengths(q, buffer);
vb->v4l2_planes[plane].length = q->plane_sizes[plane]
// Для типа MMAP также необходимо установить смещение. Каждое смещение плоскости каждого буфера отличается.
if (memory == V4L2_MEMORY_MMAP)
->__setup_offsets(q, buffer);
return buffer;
}
vb2_dc_alloc предоставляется драйвером. Виртуальный адрес и физический адрес выделенного им буфера являются непрерывными, что относится к третьему типу videobuf2.
[drivers\media\v4l2-core\videobuf2-dma-contig.c]
struct vb2_dc_buf {
struct device *dev;
void *vaddr; // Память виртуальный адрес
unsigned long size; // Памятьразмер dma_addr_t dma_addr; // Памятьфизический адрес
enum dma_data_direction dma_dir; // Направление передачи DMA
struct sg_table *dma_sgt; // SG связанные с DMA
/* Переменные, связанные с MMAP */
struct vb2_vmarea_handler handler;
atomic_t refcount;
struct sg_table *sgt_base;
struct vm_area_struct *vma; // Переменные, связанные с USERPTR
struct dma_buf_attachment *db_attach; // Переменные, связанные с DMABUF
};
static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size,
enum dma_data_direction dma_dir, gfp_t gfp_flags)
{
struct vb2_dc_conf *conf = alloc_ctx;
struct device *dev = conf->dev;
struct vb2_dc_buf *buf;
// Сначала выделите структуру Структура vb2_dc_buf
buf = kzalloc(sizeof *buf, GFP_KERNEL);
// Затем выделите буфер для хранения данных изображения. Физический и виртуальный адреса этого буфера являются смежными.
buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr, GFP_KERNEL | gfp_flags);
buf->dev = get_device(dev); // Установить указатель родительского устройства
buf->size = size; // Размер буфера для хранения данных изображения
buf->dma_dir = dma_dir; // Записывать Направление передачи DMA
// установить структуру Структура vb2_vmarea_handler
buf->handler.refcount = &buf->refcount;
buf->handler.put = vb2_dc_put; // перезвонитьфункция buf->handler.arg = buf; // Параметры функции обратного вызова
atomic_inc(&buf->refcount); // Увеличьте счетчик ссылок и освободите буфер, когда счетчик ссылок достигнет 0.
return buf; // Возвращает назначенный адрес vb2_dc_buf
}
[include/media/videobuf2-core.h]
// Структура данных очереди q-буфера указатель vb2_queue
// b-v4l2_buffer указатель на структуру, Ядро хранит в нем информацию о буфере
// Возвращаемое значение равно -0 в случае успеха; если оно меньше 0, произойдет ошибка.
int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b);
[include/media/videobuf2-core.h]
// Структура данных очереди q-буфера указатель vb2_queue
// b-v4l2_buffer указатель структуры
// Возвращаемое значение равно -0 в случае успеха; если оно меньше 0, произойдет ошибка.
int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b);
Основная работа vb2_qbuf заключается в следующем:
(1) Получите адрес буфера соответствующего числа через массив bufs.
(2) Выполните различную обработку в соответствии с разными состояниями буфера. Буферу в состоянии VB2_BUF_STATE_DEQUEUED необходимо вызвать функцию __buf_prepare для выполнения некоторой подготовительной работы. Сначала установите состояние буфера на VB2_BUF_STATE_PREPARING, тип V4L2_MEMORY_MMAP вызывает buf_prepare, платформа imx6ull вызывает mx6s_videobuf_prepare, V4L2_MEMORY_USERPTR вызывает get_userptr, а платформа imx6ull вызывает vb2_dc_get_userptr. Буферы в состоянии VB2_BUF_STATE_PREPARED не требуют подготовки. Буферы в других состояниях напрямую возвращают ошибки и не могут выполнять операции VIDIOC_QBUF.
(3) Повесьте буфер в связанный список очереди_list.
(4) Установите статус буфера на VB2_BUF_STATE_QUEUED.
(5) Если был вызван VIDIOC_STREAMON, это означает, что поток был открыт, и вам необходимо вызвать функцию __enqueue_in_driver, чтобы добавить буфер в очередь драйвера.
vb2_qbuf
->vb2_internal_qbuf
// Получить адрес буфера соответствующего числа
vb = q->bufs[b->index];
// Проверьте состояние буфера, только VB2_BUF_STATE_DEQUEUED и
// Состояние VB2_BUF_STATE_PREPARED можно добавить в очередь кэша драйвера.
switch (vb->state) {
case VB2_BUF_STATE_DEQUEUED:
-> __buf_prepare(vb, b);
->__verify_length; // Проверьте длину данных
vb->state = VB2_BUF_STATE_PREPARING; // Установить статус буферадляPREPARING
vb->v4l2_buf.timestamp.tv_sec = 0; // Очистить временную метку
vb->v4l2_buf.timestamp.tv_usec = 0;
vb->v4l2_buf.sequence = 0; // Установите серийный номер на 0
->__qbuf_mmap(vb, b); // Вызов типа V4L2_MEMORY_MMAP
->__fill_vb2_buffer // Заполните данные и проверьте
// Вызов buf_prepareфункция, платформа imx6ull вызывает mx6s_videobuf_prepareфункция
// Основная функция — установить полезную нагрузку и проверить правильность виртуального адреса буфера.
call_vb_qop(vb, buf_prepare, vb)
->__qbuf_userptr(vb, b); // Вызов типа V4L2_MEMORY_USERPTR
->__fill_vb2_buffer // Заполните данные и проверьте
// Вызовите функцию обратного вызова get_userptr, предоставляемую драйвером,
// Платформа imx6ull вызывает функцию обратного вызова vb2_dc_get_userptr
call_ptr_memop(...get_userptr...);
// Вызов buf_prepareфункция, платформа imx6ull вызывает mx6s_videobuf_prepareфункция
call_vb_qop(vb, buf_prepare, vb);
// dmabuf в настоящее время не представлен и не будет обсуждаться.
->__qbuf_dmabuf(vb, b); // Вызов типа V4L2_MEMORY_DMABUF
break;
case VB2_BUF_STATE_PREPARED:
break;
// videobuf2 готовит буфер,нельзя добавить вводить машинуизочередь кэша
// __buf_prepareфункция установит внутреннее состояние VB2_BUF_STATE_PREPARING
case VB2_BUF_STATE_PREPARING:
return -EINVAL;
default:
return -EINVAL;
}
// Повесьте буфер, переданный приложением, в связанный списокqueued_list.
list_add_tail(&vb->queued_entry, &q->queued_list)
q->queued_count++; // Увеличьте количество буферов в списке очереди_списка на 1.
vb->state = VB2_BUF_STATE_QUEUED;
// Если был вызван VIDIOC_STREAMON, буфер необходимо добавить в очередь буферов драйвера.
if (q->start_streaming_called)
// Подвешивание буфера к imx6ull В связанном списке захвата структуры драйвера устройства CSI.
->__enqueue_in_driver(vb);
__fill_v4l2_buffer(vb, b); // Наполните пользовательское пространство информацией
Функция обратного вызова buf_prepare платформы imx6ull — это mx6s_videobuf_prepare. Ее основная функция — установить полезную нагрузку, проверить, существует ли виртуальный адрес буфера и правильно ли установлена полезная нагрузка.
static int mx6s_videobuf_prepare(struct vb2_buffer *vb)
{
struct mx6s_csi_dev *csi_dev = vb2_get_drv_priv(vb->vb2_queue);
int ret = 0;
// Установите полезную нагрузку, полезная нагрузка — это размер изображения.
vb2_set_plane_payload(vb, 0, csi_dev->pix.sizeimage);
// Эффективное количество байтов в буфере — это размер изображения.
vb->v4l2_planes[plane_no].bytesused = size
// Проверьте, существует ли виртуальный адрес буфера и правильно ли установлена полезная нагрузка.
if (vb2_plane_vaddr(vb, 0) &&
vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
ret = -EINVAL;
goto out;
}
return 0;
out:
return ret;
}
// Получить виртуальный адрес самолета
vb2_plane_vaddr(vb, 0);
// Вызовите функцию vaddr, предоставленную драйвером, и платформа imx6ull вызовет vb2_dc_vaddr.
call_ptr_memop(vb, vaddr, vb->planes[plane_no].mem_priv)
->vb2_dc_vaddr
return buf->vaddr // Возвращает виртуальный адрес, по которому хранится изображение Память.
[include/media/videobuf2-core.h]
// Структура данных очереди q-буфера указатель vb2_queue
// type-тип буфера
// Возвращаемое значение равно -0 в случае успеха; если оно меньше 0, произойдет ошибка.
int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type);
Основная работа vb2_streamon заключается в следующем:
(1) Перейдите по связанному спискуqueued_list, сначала установите статус буфера на VB2_BUF_STATE_ACTIVE, а затем добавьте все буферы, находящиеся в очереди, в очередь драйвера. Платформа imx6ull вызывает mx6s_videobuf_queue, чтобы добавить буфер в связанный список захвата.
(2) Вызовите start_streaming, чтобы начать потоковую передачу видео. Платформа imx6ull вызывает функцию mx6s_start_streaming, чтобы включить устройство и начать сбор изображений.
(3) Флаг открытия потоковой передачи установлен в 1.
vb2_streamon
->vb2_internal_streamon
->vb2_start_streaming
// Добавьте в драйвер все буферы из очереди_list.
list_for_each_entry(vb, &q->queued_list, queued_entry)
__enqueue_in_driver
vb->state = VB2_BUF_STATE_ACTIVE // Установить статус буфера
atomic_inc(&q->owned_by_drv_count) // Увеличьте количество буферов использования драйверов
// Вызов функции подготовки для каждой плоскости буфера
all_void_memop(vb, prepare, vb->planes[plane].mem_priv)
->vb2_dc_prepare // Платформа imx6ull вызывает функцию vb2_dc_prepareфункция.
// буфер синхронизации
->dma_sync_sg_for_device
// вызов функции buf_queue
call_void_vb_qop(vb, buf_queue, vb)
->mx6s_videobuf_queue
->spin_lock_irqsave // Замок
// Подвешивание буфера к imx6ull В связанном списке захвата структуры драйвера устройства CSI.
list_add_tail(&buf->internal.queue, &csi_dev->capture)
->spin_unlock_irqrestore // Разблокировать
q->start_streaming_called = 1 // Установить VIDIOC_STREAMON был вызван флаг
// Обратный вызов start_streamingфункции в драйвере, видеоустройство начинает работать
call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count))
->mx6s_start_streaming // Платформа imx6ull вызывает функцию mx6s_start_streaming.
->mx6s_csi_enable // включить устройство,начинать Получение изображения
q->streaming = 1; // Установить флаг открытия потока
[include/media/videobuf2-core.h]
// Структура данных очереди q-буфера указатель vb2_queue
// b-v4l2_buffer указатель структуры
// флаг неблокирующей блокировки,в соответствии сfile->f_flags & Решение O_NONBLOCK
// Возвращаемое значение равно -0 в случае успеха; если оно меньше 0, произойдет ошибка.
int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking);
Основная работа vb2_dqbuf заключается в следующем:
(1) Проверьте, доступен ли буфер. В режиме получения изображения вам необходимо дождаться заполнения буфера данными изображения, прежде чем его можно будет использовать. Если неблокируется и буфер недоступен, напрямую возвращается ошибка -EAGAIN. Заблокировано и нет доступного буфера, спите и ждите.
(2) Если буфер доступен, получите доступный буфер и удалите его из связанного списка Done_list.
(3) Скопируйте доступную информацию о буфере в пространство пользователя.
(4) Удалите доступный буфер из связанного списка очереди_списка.
(5) Установите статус буфера на VB2_BUF_STATE_DEQUEUED.
vb2_dqbuf
->vb2_internal_dqbuf
// Подождите, пока буфер станет доступным. Если буфер доступен, верните 0.
// Если неблокировка и буфер недоступен, будет возвращена ошибка -EAGAIN.
// Заблокировано и нет доступного буфера, спать и ждать
->__vb2_wait_for_done_vb
// буфер ожидания ожидания
call_void_qop(q, wait_prepare, q)
->vb2_ops_wait_prepare // wait_prepare в конечном итоге вызывает функцию vb2_ops_wait_prepare
->vb2_ops_wait_prepare
->mutex_unlock(vq->lock); // разблокировать замок
->wait_event_interruptible // буфер ожидания ожидания Доступный
// Буфер пробуждается, когда он доступен, выполняется функция wait_finish и, наконец, вызывается функция vb2_ops_wait_finish.
call_void_qop(q, wait_finish, q)
->vb2_ops_wait_finish
->mutex_lock(vq->lock); // Получить блокировку
->spin_lock_irqsave(&q->done_lock, flags) // Получить спин-блокировку
// Получить доступный буфер в списке Done_list.
list_first_entry(&q->done_list, struct vb2_buffer, done_entry)
->__verify_planes_array // Убедитесь, что все плоскости в извлеченном буфере доступны.
list_del(&(*vb)->done_entry) // Если все плоскости в буфере доступны, удалите буфер из списка Done_list.
spin_unlock_irqrestore(&q->done_lock, flags) // Освободить спин-блокировку
// Вызов функции buf_finish, платформа imx6ull не реализована
call_void_vb_qop(vb, buf_finish, vb)
->__fill_v4l2_buffer(vb, b) // Заполнить информацию о буфере удаления из очереди в пользовательское пространство
list_del(&vb->queued_entry); // Удалите буфер из связанного спискаqueued_entry.
q->queued_count--; // Уменьшить буфер очереди на 1.
->__vb2_dqbuf
vb->state = VB2_BUF_STATE_DEQUEUED; // Установите буфер в состояние VB2_BUF_STATE_DEQUEUED.
[include/media/videobuf2-core.h]
// Структура данных очереди q-буфера указатель vb2_queue
// type-тип буфера
// Возвращаемое значение равно -0 в случае успеха; если оно меньше 0, произойдет ошибка.
int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type);
Основная работа vb2_streamoff заключается в следующем:
(1) Вызовите функцию stop_streaming, предоставленную драйвером, чтобы остановить поток, а платформа imx6ull вызывает функцию mx6s_stop_streaming, чтобы выключить устройство захвата видео.
(2) Очистите связанный список постановки в очередь и очистите связанный список завершения.
(3) Вывести из очереди все буферы в пользовательское пространство и установить состояние VB2_BUF_STATE_DEQUEUED.
vb2_streamoff
->vb2_internal_streamoff
// отменить буфер
->__vb2_queue_cancel
// Вызовите функцию, предоставленную драйвером, чтобы остановить поток.
call_void_qop(q, stop_streaming, q)
->mx6s_stop_streaming
->mx6s_csi_disable // Выключите устройство видеозахвата
q->streaming = 0; // Очистить флаг открытия потока
q->start_streaming_called = 0; // Очистить флаг вызова VIDIOC_STREAMON
q->queued_count = 0; // Очистить счетчик буфера очереди
q->error = 0;
INIT_LIST_HEAD(&q->queued_list) // Очистить список очереди
INIT_LIST_HEAD(&q->done_list) // Очистить список завершения
->atomic_set(&q->owned_by_drv_count, 0) // Установите счетчик ссылок драйвера на 0.
->wake_up_all(&q->done_wq) // Разбудить поток, ожидающий в очереди буфера
->__vb2_dqbuf(vb) // Удалить все буферы из очереди в пользовательское пространство
vb->state = VB2_BUF_STATE_DEQUEUED
Анализируя процесс выполнения команды ioctl в отношении буфера, можно резюмировать процесс изменения состояния буфера, как показано на рисунке ниже. Желтый цвет означает, что буфер принадлежит пространству пользователя, зеленый — что буфер принадлежит videobuf2, а голубой — что буфер принадлежит драйверу.
(1) После подачи заявки на буфер с помощью команды VIDIOC_REQBUFS статус буфера — VB2_BUF_STATE_DEQUEUED, и буфер принадлежит пользовательскому пространству.
(2) Добавьте буфер в пространство ядра с помощью команды VIDIOC_QBUF. Состояние буфера сначала изменится на VB2_BUF_STATE_PREPARING, а затем изменится на VB2_BUF_STATE_QUEUED. Буфер принадлежит videobuf2. Если вызывается VIDIOC_STREAMON, start_streaming_ Called=1, VIDIOC_QBUF также добавит буфер в очередь буферов драйвера, и в это время буфер принадлежит драйверу.
(3) После открытия потока с помощью команды VIDIOC_STREAMON буфер добавляется в очередь буферов драйвера. Статус буфера — VB2_BUF_STATE_ACTIVE. В этот момент буфер принадлежит драйверу.
(4) Если передача данных DMA завершена и буфер заполнен данными, драйвер вызывает vb2_buffer_done, чтобы изменить состояние буфера на VB2_BUF_STATE_DONE. В этот момент буфер принадлежит videobuf2.
Используйте системный вызов mmap для сопоставления видеоустройства, которое в конечном итоге вызовет vb2_mmap. Ядро использует функцию vb2_mmap для сопоставления буфера видеоустройства с пользовательским пространством. Чтобы использовать mmap, тип буферной памяти должен быть V4L2_MEMORY_MMAP, область виртуальной памяти должна быть общей VM_SHARED, область виртуальной памяти должна быть доступна для записи VM_WRITE в режиме вывода изображения, а область виртуальной памяти должна быть доступна для чтения VM_READ в режиме получения изображения.
[include/media/videobuf2-core.h]
// Структура данных очереди q-буфера указатель vb2_queue
// vma-virtual Указатель структуры управления областью Память
// Возвращаемое значение равно -0 в случае успеха; если оно меньше 0, произойдет ошибка.
int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma);
Основной рабочий процесс vb2_mmap выглядит следующим образом:
(1) Получите смещение буферной плоскости.
(2) Проверьте, выровнена ли отображаемая память по страницам. mmap требует, чтобы память была выровнена по страницам.
(3) Вызовите функцию сопоставления памяти mmap, предоставленную драйвером для сопоставления. Платформа imx6ull вызывает vb2_dc_mmap для отображения памяти.
vb2_mmap
// Получить смещение буферной плоскости
->__find_plane_by_offset
PAGE_ALIGN // mmap требует, чтобы Память было выровнено по страницам
->mutex_lock(&q->mmap_lock) // Получить блокировку mmap_lock
// Вызов mmapфункции, предоставляемой драйвером
call_memop(vb, mmap, vb->planes[plane].mem_priv, vma)
->vb2_dc_mmap // Платформа imx6ull вызывает vb2_dc_mmap для сопоставления Память.
->dma_mmap_coherent // DMAПамять сопоставление согласованности
// Установить виртуальную Память не может расширяться и ядро флаг дампа
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_private_data = &buf->handler; // Установить личные данные
vma->vm_ops = &vb2_common_vm_ops; // Как настроить виртуальную Памятьфункция
vma->vm_ops->open(vma); // Открыть виртуальную Память
->mutex_unlock(&q->mmap_lock); // Снимите блокировку mmap_lock
Используйте системный вызов read для чтения данных видеоустройства, которое в конечном итоге вызовет vb2_read. Ядро использует vb2_read для копирования данных из буфера видеоустройства в пространство пользователя. Использование системного вызова read скопирует данные из пространства ядра в пространство пользователя. Видеоустройства генерируют большой объем данных, и копирование серьезно повлияет на производительность, поэтому обычно используются mmap или userptr.
[include/media/videobuf2-core.h]
// Структура данных очереди q-буфера указатель vb2_queue
// data - указатель буфера пользовательского пространства
// count - количество прочитанных байтов данных
// ppos — указатель смещения файла
// неблокирующий-неблокирующий флаг
// Возвращаемое значение — количество успешно прочитанных байт, если оно больше или равно 0, если меньше 0, то произойдет ошибка.
size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
loff_t *ppos, int nonblocking);
Используйте систему закрытия, чтобы закрыть видеоустройство, которое в конечном итоге вызовет vb2_queue_release. Ядро использует vb2_queue_release, чтобы закрыть видеоустройство, очистить очередь буфера и освободить память, занятую буфером.
[include/media/videobuf2-core.h]
// Структура данных очереди q-буфера указатель vb2_queue
// Возвращаемое значение — нет
void vb2_queue_release(struct vb2_queue *q);
Основная работа vb2_queue_release заключается в следующем:
(1) Освободите ресурсы, занятые симулятором файлового ввода-вывода.
(2) Остановите поток, закройте видеоустройство, очистите список очереди и список завершения, удалите все буферы из очереди в пространство пользователя и установите состояние VB2_BUF_STATE_DEQUEUED.
(3) Освободите буферную память.
void vb2_queue_release(struct vb2_queue *q)
{
__vb2_cleanup_fileio(q); // Освободите ресурсы, занятые симулятором файлового ввода-вывода.
__vb2_queue_cancel(q); // Выйти и остановить потоки
mutex_lock(&q->mmap_lock);
// Освободить буфер Память
__vb2_queue_free(q, q->num_buffers);
mutex_unlock(&q->mmap_lock);
}
Ссылки:
Ссылка на статью: http://t.csdn.cn/O6QBV.