Анализ инфраструктуры подсистемы Linux V4L2-videobuf2
Анализ инфраструктуры подсистемы Linux V4L2-videobuf2

1. Обзор:

Видеоустройства генерируют много данных, и традиционный механизм буферизации больше не может удовлетворить спрос. С этой целью ядро ​​Linux абстрагирует механизм videobuf2 для управления буфером кадров, в котором хранятся видеоизображения. Уровень абстракции videobuf2 подобен мосту, соединяющему пользовательское пространство и драйвер V4L2. Уровень абстракции videobuf2 обеспечивает стандартные системные вызовы ввода-вывода POSIX в пользовательское пространство, включая чтение, опрос, mmap и т. д., а также обеспечивает большое количество вызовов ioctl V4L2, связанных с потоковым вводом-выводом, включая выделение буфера и постановку буфера в очередь. , удаление буфера из очереди и управление потоком. Хотя использование videobuf2 потребует от драйвера некоторых проектных решений, преимущество его использования состоит в том, что videobuf2 позволяет сократить код драйвера и сохранить согласованность подсистемы V4L2 в API пользовательского пространства. Очевидно, использование videobuf2 более разумно.

2. Классификация:

Не все видеоустройства используют один и тот же тип videobuf2. В реальном времени в ядре Linux есть три разных типа videobuf2.

  • (1) Физический адрес и виртуальный адрес буфера не являются последовательными. Это относится к большинству буферов пользовательского пространства.,где это возможно,Для Ядро-пространства также имеет смысл распределять буферы таким образом. Однако в некоторых случаях,не применимо. Для прерывистых буферов,Требуется аппаратное обеспечение для поддержки операций разброса/сбора DMA. используйте этот буфер,Необходимо включить файлы заголовков<include/media/videobuf-dma-sg.h>(Применимо кV4L2)или<include/media/videobuf2-dma-sg.h>(Применимо кV4L2)。
  • (2) Физический адрес буфера не является непрерывным, но виртуальный адрес является непрерывным. Для распределения буфера используется vmalloc. Такие буферы нельзя передавать с помощью DMA. Но без использования DMA,Буферы с непрерывными виртуальными адресами удобны. используйте этот буфер,Необходимо включить файлы заголовков<include/media/videobuf-vmalloc.h>(Применимо кV4L)или<include/media/videobuf2-vmalloc.h>(Применимо кV4L2)。
  • (3) И физический адрес, и виртуальный адрес буфера являются непрерывными. В страничной системе управления Память,Распределение буферов с непрерывными физическими и виртуальными адресами ненадежно.,Потому что этот метод распределения может легко привести к увеличению количества фрагментов Память.,В некоторых случаях слишком большое количество фрагментов Память может привести к сбою выделения Памяти.,В результате система не может функционировать должным образом. Но это очень удобно для передачи DMA. используйте этот буфер,Необходимо включить файлы заголовков<include/media/videobuf-dma-contig.h>(Применимо кV4L)или<include/media/videobuf2-dma-contig.h>(Применимо кV4L2)。Этот типvideobuf2Чаще используется。

Кроме того, имеется буфер оверлея, который находится в видеопамяти системы. Буфер наложения в настоящее время устарел, но его все еще иногда можно увидеть в некоторых драйверах системы на кристалле.

Что касается того, какой videobuf2 использовать, разработчикам драйверов необходимо оценить его на основе фактической среды использования.

3. Структура данных:

Основная структура данных 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, который затем заполняет данные в памяти пользовательского пространства.

Язык кода:javascript
копировать
    [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.

Язык кода:javascript
копировать
    // Драйвер требует четко определенной функции для работы 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].

Язык кода:javascript
копировать
    [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. Подробности заключаются в следующем.

  • (1) функции get_userptr и put_userptr используются для обработки буферов типа USERPTR.
  • (2) функции alloc, put, num_users и mmap используются для обработки буферов типа MMAP.
  • (3) alloc, put, num_users и vaddrфункция используются для обработки буферов с типом доступа для чтения/записи.
  • (4)attach_dmabuf、detach_dmabuf、Функции map_dmabuf и unmap_dmabuf используются для обработки буферов типа DMABUF.

Структура struct vb2_mem_ops обычно инициализируется структурой vb2_dma_contig_memops, которая предоставляется ядром и может использоваться напрямую.

Язык кода:javascript
копировать
    [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);
    };

4. Анализ использования:

Использование videobuf2 сложное и его необходимо объяснять на конкретных примерах драйверов, чтобы его было легче понять. На следующем рисунке показано, как использовать videobuf2 контроллера CSI на платформе imx6ull. Приложения могут получить доступ к видеоустройству, вызывая системные вызовы open, close, ioctl, mmap и read. Ядро использует соответствующие методы для доступа к videobuf2 в соответствии с различными системными вызовами. Начнем с этих системных вызовов и разберем использование videobuf2 в ядре.

1、open

Приложение вызывает 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.

Язык кода:javascript
копировать
    [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;
    }

2、ioctl:

Подсистема V4L2 определяет множество команд ioctl для использования приложениями. Команда VIDIOC_REQBUFS используется для запроса буфера у Ядро.,Команда VIDIOC_QUERYBUF используется для получения информации о буфере.,Команда VIDIOC_QBUF возвращает пустой кэш после чтения данных в очередь кэша драйвера.,Команда VIDIOC_DQBUF возвращает заполненный буфер из драйвера в приложение.,Команда VIDIOC_STREAMOFF используется для закрытия потока.,Остановитесь немедленно Получение изображения,Команда VIDIOC_STREAMON используется для открытия потока.,Сразу открыть Получение изображения。Ядро Процесс вызова вv4l2_ioctl->video_ioctl2->__video_do_ioctl->Вызов разных драйверов на основе разных командфункция->Позвоните в соответствующееvideobuf2иметь дело сфункция,Пожалуйста, обратитесь к рисунку выше для конкретного процесса вызова. Ниже приводится подробный анализ функции обработки videobuf2, вызываемой ioctl.

  • VIDIOC_REQBUFS: используйте команду VIDIOC_REQBUFS для вызова ioctl.,В конечном итоге вызовет функцию vb2_reqbufs,Ядро использует функцию vb2_reqbufs для создания буферов.
Язык кода:javascript
копировать
    [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) Установите количество выделенных буферов и верните количество выделенных буферов приложению.

Язык кода:javascript
копировать
    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。вышенастраиватьиз Переменные называютсяфункцияуказательизформа передана в。

Язык кода:javascript
копировать
    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 структуры управления, а размер буфера сохраняется в. размер члена структуры управления.

Язык кода:javascript
копировать
    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.

Язык кода:javascript
копировать
    [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
    }


  • VIDIOC_QUERYBUF: используйте команду VIDIOC_QUERYBUF для вызова ioctl.,В конечном итоге вызовет функцию vb2_querybuf.,Ядро использует функцию vb2_querybufфункция для копирования информации буфера в пространство пользователя.,основной有час Временная меткаtimestamp、флаги、Длина буферадлина、Смещение буфера и другая информация.
Язык кода:javascript
копировать
    [include/media/videobuf2-core.h]
    // Структура данных очереди q-буфера указатель vb2_queue
    // b-v4l2_buffer указатель на структуру, Ядро хранит в нем информацию о буфере
    // Возвращаемое значение равно -0 в случае успеха; если оно меньше 0, произойдет ошибка.
    int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b);

  • VIDIOC_QBUF: используйте команду VIDIOC_QBUF для вызова ioctl.,В конечном итоге вызовет функцию vb2_qbufфункция,Ядро использует функцию vb2_qbuf для возврата пустого кеша после чтения данных в очередь кеша драйвера.
Язык кода:javascript
копировать
    [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, чтобы добавить буфер в очередь драйвера.

Язык кода:javascript
копировать
    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. Ее основная функция — установить полезную нагрузку, проверить, существует ли виртуальный адрес буфера и правильно ли установлена ​​полезная нагрузка.

Язык кода:javascript
копировать
    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  // Возвращает виртуальный адрес, по которому хранится изображение Память.

  • VIDIOC_STREAMON: используйте команду VIDIOC_STREAMON для вызова ioctl.,В конечном итоге он будет вызван в функцию vb2_streamon.,Ядро Используйте функцию vb2_streamon, чтобы открыть видеопоток,для Получение изображения По оснащению,Устройство начинает собирать изображения,и сохраните данные изображения в буфер.
Язык кода:javascript
копировать
    [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.

Язык кода:javascript
копировать
    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;  // Установить флаг открытия потока

  • VIDIOC_DQBUF: используйте команду VIDIOC_DQBUF для вызова ioctl.,В конечном итоге он будет вызван в функцию vb2_dqbuf.,Ядро использует функцию vb2_dqbuf для возврата кэша, заполненного данными из драйвера, в приложение.
Язык кода:javascript
копировать
    [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.

Язык кода:javascript
копировать
    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.

  • VIDIOC_STREAMOFF: используйте команду VIDIOC_STREAMOFF для вызова ioctl.,В конечном итоге он будет вызван в функцию vb2_streamoff.,Ядро использует функцию vb2_streamoff для закрытия потока.
Язык кода:javascript
копировать
    [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.

Язык кода:javascript
копировать
    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.

5、mmap:

Используйте системный вызов mmap для сопоставления видеоустройства, которое в конечном итоге вызовет vb2_mmap. Ядро использует функцию vb2_mmap для сопоставления буфера видеоустройства с пользовательским пространством. Чтобы использовать mmap, тип буферной памяти должен быть V4L2_MEMORY_MMAP, область виртуальной памяти должна быть общей VM_SHARED, область виртуальной памяти должна быть доступна для записи VM_WRITE в режиме вывода изображения, а область виртуальной памяти должна быть доступна для чтения VM_READ в режиме получения изображения.

Язык кода:javascript
копировать
    [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 для отображения памяти.

Язык кода:javascript
копировать
    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:

Используйте системный вызов read для чтения данных видеоустройства, которое в конечном итоге вызовет vb2_read. Ядро использует vb2_read для копирования данных из буфера видеоустройства в пространство пользователя. Использование системного вызова read скопирует данные из пространства ядра в пространство пользователя. Видеоустройства генерируют большой объем данных, и копирование серьезно повлияет на производительность, поэтому обычно используются mmap или userptr.

Язык кода:javascript
копировать
    [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);

  • close:

Используйте систему закрытия, чтобы закрыть видеоустройство, которое в конечном итоге вызовет vb2_queue_release. Ядро использует vb2_queue_release, чтобы закрыть видеоустройство, очистить очередь буфера и освободить память, занятую буфером.

Язык кода:javascript
копировать
    [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) Освободите буферную память.

Язык кода:javascript
копировать
    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);
    }

Ссылки:

  • Исходный код версии Linux Ядро4.1
  • Файл ядра-videobuf
  • https://blog.csdn.net/tommy_wxie/article/details/11486907
  • https://blog.csdn.net/u013904227/article/details/81054611
  • Полное руководство по разработке драйверов для Android
  • Подробное практическое объяснение разработки и трансплантации драйверов Android.
  • https://www.kernel.org/doc/html/v4.11/media/uapi/v4l/v4l2.html

Ссылка на статью: http://t.csdn.cn/O6QBV.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose