<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深入理解Camera 七(V4L2)

          共 14220字,需瀏覽 29分鐘

           ·

          2021-01-18 15:11

          和你一起終身學(xué)習(xí),這里是程序員Android

          經(jīng)典好文推薦,通過(guò)閱讀本文,您將收獲以下知識(shí)點(diǎn):

          一、概覽
          二、流程簡(jiǎn)介
          三、關(guān)鍵結(jié)構(gòu)體
          四、模塊初始化
          五、處理用戶空間請(qǐng)求

          相機(jī)驅(qū)動(dòng)層–V4L2框架解析

          一、概覽

          相機(jī)驅(qū)動(dòng)層位于HAL Moudle與硬件層之間,借助linux內(nèi)核驅(qū)動(dòng)框架,以文件節(jié)點(diǎn)的方式暴露接口給用戶空間,讓HAL Module通過(guò)標(biāo)準(zhǔn)的文件訪問(wèn)接口,從而能夠?qū)⒄?qǐng)求順利地下發(fā)到內(nèi)核中,而在內(nèi)核中,為了更好的支持視頻流的操作,早先提出了v4l視頻處理框架,但是由于操作復(fù)雜,并且代碼無(wú)法進(jìn)行較好的重構(gòu),難以維護(hù)等原因,之后便衍生出了v4l2框架。

          按照v4l2標(biāo)準(zhǔn),它將一個(gè)數(shù)據(jù)流設(shè)備抽象成一個(gè)videoX節(jié)點(diǎn),從屬的子設(shè)備都對(duì)應(yīng)著各自的v4l2_subdev實(shí)現(xiàn),并且通過(guò)media controller進(jìn)行統(tǒng)一管理,整個(gè)流程復(fù)雜但高效,同時(shí)代碼的擴(kuò)展性也較高。

          而對(duì)高通平臺(tái)而言,高通整個(gè)內(nèi)核相機(jī)驅(qū)動(dòng)是建立在v4l2框架上的,并且對(duì)其進(jìn)行了相應(yīng)的擴(kuò)展,創(chuàng)建了一個(gè)整體相機(jī)控制者的CRM,它以節(jié)點(diǎn)video0暴露給用戶空間,主要用于管理內(nèi)核中的Session、Request以及與子設(shè)備,同時(shí)各個(gè)子模塊都實(shí)現(xiàn)了各自的v4l2_subdev設(shè)備,并且以v4l2_subdev節(jié)點(diǎn)暴露給用戶空間,與此同時(shí),高通還創(chuàng)建了另一個(gè)video1設(shè)備Camera SYNC,該設(shè)備主要用于同步數(shù)據(jù)流,保證用戶空間和內(nèi)核空間的buffer能夠高效得進(jìn)行傳遞。

          再往下與相機(jī)驅(qū)動(dòng)交互的便是整個(gè)相機(jī)框架的最底層Camera Hardware了,驅(qū)動(dòng)部分控制著其上下電邏輯以及寄存器讀取時(shí)序并按照I2C協(xié)議進(jìn)行與硬件的通信,和根據(jù)MIPI CSI協(xié)議傳遞數(shù)據(jù),從而達(dá)到控制各個(gè)硬件設(shè)備,并且獲取圖像數(shù)據(jù)的目的。

          V4L2英文是Video for Linux 2,該框架是誕生于Linux系統(tǒng),用于提供一個(gè)標(biāo)準(zhǔn)的視頻控制框架,其中一般默認(rèn)會(huì)嵌入media controller框架中進(jìn)行統(tǒng)一管理,v4l2提供給用戶空間操作節(jié)點(diǎn),media controller控制對(duì)于每一個(gè)設(shè)備的枚舉控制能力,于此同時(shí),由于v4l2包含了一定數(shù)量的子設(shè)備,而這一系列的子設(shè)備都是處于平級(jí)關(guān)系,但是在實(shí)際的圖像采集過(guò)程中,子設(shè)備之間往往還存在著包含于被包含的關(guān)系,所以為了維護(hù)并管理這種關(guān)系,media controller針對(duì)多個(gè)子設(shè)備建立了的一個(gè)拓?fù)鋱D,數(shù)據(jù)流也就按照這個(gè)拓?fù)鋱D進(jìn)行流轉(zhuǎn)。

          二、流程簡(jiǎn)介

          整個(gè)對(duì)于v4l2的操作主要包含了如下幾個(gè)主要流程:


          程序員Android 轉(zhuǎn)于網(wǎng)絡(luò)

          a) 打開(kāi)video設(shè)備
          在需要進(jìn)行視頻數(shù)據(jù)流的操作之前,首先要通過(guò)標(biāo)準(zhǔn)的字符設(shè)備操作接口open方法來(lái)打開(kāi)一個(gè)video設(shè)備,并且將返回的字符句柄存在本地,之后的一系列操作都是基于該句柄,而在打開(kāi)的過(guò)程中,會(huì)去給每一個(gè)子設(shè)備的上電,并完成各自的一系列初始化操作。

          b) 查看并設(shè)置設(shè)備
          在打開(kāi)設(shè)備獲取其文件句柄之后,就需要查詢(xún)?cè)O(shè)備的屬性,該動(dòng)作主要通過(guò)ioctl傳入VIDIOC_QUERYCAP參數(shù)來(lái)完成,其中該系列屬性通過(guò)v4l2_capability結(jié)構(gòu)體來(lái)表達(dá),除此之外,還可以通過(guò)傳入VIDIOC_ENUM_FMT來(lái)枚舉支持的數(shù)據(jù)格式,通過(guò)傳入VIDIOC_G_FMT/VIDIOC_S_FMT來(lái)分別獲取和獲取當(dāng)前的數(shù)據(jù)格式,通過(guò)傳入VIDIOC_G_PARM/VIDIOC_S_PARM來(lái)分別獲取和設(shè)置參數(shù)。

          c) 申請(qǐng)幀緩沖區(qū)
          完成設(shè)備的配置之后,便可以開(kāi)始向設(shè)備申請(qǐng)多個(gè)用于盛裝圖像數(shù)據(jù)的幀緩沖區(qū),該動(dòng)作通過(guò)調(diào)用ioctl并且傳入VIDIOC_REQBUFS命令來(lái)完成,最后將緩沖區(qū)通過(guò)mmap方式映射到用戶空間。

          d) 將幀緩沖區(qū)入隊(duì)
          申請(qǐng)好幀緩沖區(qū)之后,通過(guò)調(diào)用ioctl方法傳入VIDIOC_QBUF命令來(lái)將幀緩沖區(qū)加入到v4l2 框架中的緩沖區(qū)隊(duì)列中,靜等硬件模塊將圖像數(shù)據(jù)填充到緩沖區(qū)中。

          e) 開(kāi)啟數(shù)據(jù)流
          將所有的緩沖區(qū)都加入隊(duì)列中之后便可以調(diào)用ioctl并且傳入VIDIOC_STREAMON命令,來(lái)通知整個(gè)框架開(kāi)始進(jìn)行數(shù)據(jù)傳輸,其中大致包括了通知各個(gè)子設(shè)備開(kāi)始進(jìn)行工作,最終將數(shù)據(jù)填充到V4L2框架中的緩沖區(qū)隊(duì)列中。

          f) 將幀緩沖區(qū)出隊(duì)
          一旦數(shù)據(jù)流開(kāi)始進(jìn)行流轉(zhuǎn)了,我們就可以通過(guò)調(diào)用ioctl下發(fā)VIDIOC_DQBUF命令來(lái)獲取幀緩沖區(qū),并且將緩沖區(qū)的圖像數(shù)據(jù)取出,進(jìn)行預(yù)覽、拍照或者錄像的處理,處理完成之后,需要將此次緩沖區(qū)再次放入V4L2框架中的隊(duì)列中等待下次的圖像數(shù)據(jù)的填充。

          整個(gè)采集圖像數(shù)據(jù)的流程現(xiàn)在看來(lái)還是比較簡(jiǎn)單的,接口的控制邏輯很清晰,主要原因是為了提供給用戶的接口簡(jiǎn)單而且抽象,這樣方便用戶進(jìn)行集成開(kāi)發(fā),其中的大部分復(fù)雜的業(yè)務(wù)處理都被V4L2很好的封裝了,接下來(lái)我們來(lái)詳細(xì)了解下V4L2框架內(nèi)部是如何表達(dá)以及如何運(yùn)轉(zhuǎn)的。

          三、關(guān)鍵結(jié)構(gòu)體

          程序員Android 轉(zhuǎn)于網(wǎng)絡(luò)

          從上圖不難看出,v4l2_device作為頂層管理者,一方面通過(guò)嵌入到一個(gè)video_device中,暴露video設(shè)備節(jié)點(diǎn)給用戶空間進(jìn)行控制,另一方面,video_device內(nèi)部會(huì)創(chuàng)建一個(gè)media_entity作為在media controller中的抽象體,被加入到media_device中的entitie鏈表中,此外,為了保持對(duì)所從屬子設(shè)備的控制,內(nèi)部還維護(hù)了一個(gè)掛載了所有子設(shè)備的subdevs鏈表。

          而對(duì)于其中每一個(gè)子設(shè)備而言,統(tǒng)一采用了v4l2_subdev結(jié)構(gòu)體來(lái)進(jìn)行描述,一方面通過(guò)嵌入到video_device,暴露v4l2_subdev子設(shè)備節(jié)點(diǎn)給用戶空間進(jìn)行控制,另一方面其內(nèi)部也維護(hù)著在media controller中的對(duì)應(yīng)的一個(gè)media_entity抽象體,而該抽象體也會(huì)鏈入到media_device中的entities鏈表中。

          通過(guò)加入entities鏈表的方式,media_device保持了對(duì)所有的設(shè)備信息的查詢(xún)和控制的能力,而該能力會(huì)通過(guò)media controller框架在用戶空間創(chuàng)建meida設(shè)備節(jié)點(diǎn),將這種能力暴露給用戶進(jìn)行控制。

          由此可見(jiàn),V4L2框架都是圍繞著以上幾個(gè)主要結(jié)構(gòu)體來(lái)進(jìn)行的,接下來(lái)我們依次簡(jiǎn)單介紹下:
          v4l2_device 源碼如下:

          struct v4l2_device {
          struct device *dev;
          #if defined(CONFIG_MEDIA_CONTROLLER)
          struct media_device *mdev;
          #endif
          struct list_head subdevs;
          spinlock_t lock;
          char name[V4L2_DEVICE_NAME_SIZE];
          void (*notify)(struct v4l2_subdev *sd,
          unsigned int notification, void *arg);
          struct v4l2_ctrl_handler *ctrl_handler;
          struct v4l2_prio_state prio;
          struct kref ref;
          void (*release)(struct v4l2_device *v4l2_dev);
          };

          該結(jié)構(gòu)體代表了一個(gè)整個(gè)V4L2設(shè)備,作為整個(gè)V4L2的頂層管理者,內(nèi)部通過(guò)一個(gè)鏈表管理著整個(gè)從屬的所有的子設(shè)備,并且如果將整個(gè)框架放入media conntroller進(jìn)行管理,便在初始化的時(shí)候需要將創(chuàng)建成功的media_device賦值給內(nèi)部變量 mdev,這樣便建立了于與media_device的聯(lián)系,驅(qū)動(dòng)通過(guò)調(diào)用v4l2_device_register方法和v4l2_device_unregister方法分別向系統(tǒng)注冊(cè)和釋放一個(gè)v4l2_device。

          v4l2_subdev源碼如下:

          struct v4l2_subdev {
          #if defined(CONFIG_MEDIA_CONTROLLER)
          struct media_entity entity;
          #endif
          struct list_head list;
          struct module *owner;
          bool owner_v4l2_dev;
          u32 flags;
          struct v4l2_device *v4l2_dev;
          const struct v4l2_subdev_ops *ops;
          const struct v4l2_subdev_internal_ops *internal_ops;
          struct v4l2_ctrl_handler *ctrl_handler;
          char name[V4L2_SUBDEV_NAME_SIZE];
          u32 grp_id;
          void *dev_priv;
          void *host_priv;
          struct video_device *devnode;
          struct device *dev;
          struct fwnode_handle *fwnode;
          struct list_head async_list;
          struct v4l2_async_subdev *asd;
          struct v4l2_async_notifier *notifier;
          struct v4l2_subdev_platform_data *pdata;
          };

          該結(jié)構(gòu)體代表了一個(gè)子設(shè)備,每一個(gè)子設(shè)備都需要在初始化的時(shí)候掛載到一個(gè)總的v4l2_device上,并且將該v4l2設(shè)備賦值給內(nèi)部的v4l2_dev變量,之后將自身加入到v4l2_device中的子設(shè)備鏈表中進(jìn)行統(tǒng)一管理,這種方式提高了遍歷訪問(wèn)所有子設(shè)備的效率,同時(shí)為了表達(dá)不同硬件模塊的特殊操作行為,v4l2_subdev定義了一個(gè)v4l2_subdev_ops 結(jié)構(gòu)體來(lái)進(jìn)行定義,其實(shí)現(xiàn)交由不同的硬件模塊來(lái)具體完成。其中如果使能了CONFIG_MEDIA_CONTROLLER宏,便會(huì)在media_controller中生成一個(gè)對(duì)應(yīng)的media_entity,來(lái)代表該子設(shè)備,而該entity便會(huì)存入子設(shè)備結(jié)構(gòu)體中的entity變量中,最后,如果需要?jiǎng)?chuàng)建一個(gè)設(shè)備節(jié)點(diǎn)的話,通過(guò)video_device調(diào)用標(biāo)準(zhǔn)API接口進(jìn)行實(shí)現(xiàn),而相應(yīng)的video_device便會(huì)存入其內(nèi)部devnode變量中。

          video_device源碼如下:

          struct video_device
          {
          #if defined(CONFIG_MEDIA_CONTROLLER)
          struct media_entity entity;
          struct media_intf_devnode *intf_devnode;
          struct media_pipeline pipe;
          #endif
          const struct v4l2_file_operations *fops;

          u32 device_caps;

          /* sysfs */
          struct device dev;
          struct cdev *cdev;

          struct v4l2_device *v4l2_dev;
          struct device *dev_parent;

          struct v4l2_ctrl_handler *ctrl_handler;

          struct vb2_queue *queue;

          struct v4l2_prio_state *prio;

          /* device info */
          char name[32];
          int vfl_type;
          int vfl_dir;
          int minor;
          u16 num;
          unsigned long flags;
          int index;

          /* V4L2 file handles */
          spinlock_t fh_lock;
          struct list_head fh_list;

          int dev_debug;

          v4l2_std_id tvnorms;

          /* callbacks */
          void (*release)(struct video_device *vdev);
          const struct v4l2_ioctl_ops *ioctl_ops;
          DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);

          DECLARE_BITMAP(disable_locking, BASE_VIDIOC_PRIVATE);
          struct mutex *lock;
          };

          如果需要給v4l2_device或者v4l2_subdev在系統(tǒng)中創(chuàng)建節(jié)點(diǎn)的話,便需要實(shí)現(xiàn)該結(jié)構(gòu)體,并且通過(guò)video_register_device方法進(jìn)行創(chuàng)建,而其中的fops便是video_device所對(duì)應(yīng)的操作方法集,在v4l2框架內(nèi)部,會(huì)將video_device嵌入到一個(gè)具有特定主設(shè)備號(hào)的字符設(shè)備中,而其方法集會(huì)在操作節(jié)點(diǎn)時(shí)被調(diào)用到。除了這些標(biāo)準(zhǔn)的操作集外,還定義了一系列的ioctl操作集,通過(guò)內(nèi)部ioctl_ops來(lái)描述。

          media_device源碼如下:

          struct media_device {
          /* dev->driver_data points to this struct. */
          struct device *dev;
          struct media_devnode *devnode;

          char model[32];
          char driver_name[32];
          char serial[40];
          char bus_info[32];
          u32 hw_revision;

          u64 topology_version;

          u32 id;
          struct ida entity_internal_idx;
          int entity_internal_idx_max;

          struct list_head entities;
          struct list_head interfaces;
          struct list_head pads;
          struct list_head links;

          /* notify callback list invoked when a new entity is registered */
          struct list_head entity_notify;

          /* Serializes graph operations. */
          struct mutex graph_mutex;
          struct media_graph pm_count_walk;

          void *source_priv;
          int (*enable_source)(struct media_entity *entity,
          struct media_pipeline *pipe);
          void (*disable_source)(struct media_entity *entity);

          const struct media_device_ops *ops;
          };

          如果使能了CONFIG_MEDIA_CONTROLLER宏,則當(dāng)v4l2_device初始化的過(guò)程中便會(huì)去創(chuàng)建一個(gè)media_device,而這個(gè)media_device便是整個(gè)media controller的抽象管理者,每一個(gè)v4l2設(shè)備以及從屬的子設(shè)備都會(huì)對(duì)應(yīng)的各自的entity,并且將其存入media_device中進(jìn)行統(tǒng)一管理,與其它抽象設(shè)備一樣,media_device也具有自身的行為,比如用戶可以通過(guò)訪問(wèn)media節(jié)點(diǎn),枚舉出所有的從屬于同一個(gè)v4l2_device的子設(shè)備,另外,在開(kāi)啟數(shù)據(jù)流的時(shí)候,media_device通過(guò)將各個(gè)media_entity按照一定的順序連接起來(lái),實(shí)現(xiàn)了數(shù)據(jù)流向的整體控制。

          vb2_queue源碼如下:

          struct vb2_queue {
          unsigned int type;
          unsigned int io_modes;
          struct device *dev;
          unsigned long dma_attrs;
          unsigned bidirectional:1;
          unsigned fileio_read_once:1;
          unsigned fileio_write_immediately:1;
          unsigned allow_zero_bytesused:1;
          unsigned quirk_poll_must_check_waiting_for_buffers:1;

          struct mutex *lock;
          void *owner;

          const struct vb2_ops *ops;
          const struct vb2_mem_ops *mem_ops;
          const struct vb2_buf_ops *buf_ops;

          void *drv_priv;
          unsigned int buf_struct_size;
          u32 timestamp_flags;
          gfp_t gfp_flags;
          u32 min_buffers_needed;

          /* private: internal use only */
          struct mutex mmap_lock;
          unsigned int memory;
          enum dma_data_direction dma_dir;
          struct vb2_buffer *bufs[VB2_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;
          wait_queue_head_t done_wq;

          struct device *alloc_devs[VB2_MAX_PLANES];

          unsigned int streaming:1;
          unsigned int start_streaming_called:1;
          unsigned int error:1;
          unsigned int waiting_for_buffers:1;
          unsigned int is_multiplanar:1;
          unsigned int is_output:1;
          unsigned int copy_timestamp:1;
          unsigned int last_buffer_dequeued:1;

          struct vb2_fileio_data *fileio;
          struct vb2_threadio_data *threadio;

          #ifdef CONFIG_VIDEO_ADV_DEBUG
          /*
          * Counters for how often these queue-related ops are
          * called. Used to check for unbalanced ops.
          */

          u32 cnt_queue_setup;
          u32 cnt_wait_prepare;
          u32 cnt_wait_finish;
          u32 cnt_start_streaming;
          u32 cnt_stop_streaming;
          #endif
          };

          在整個(gè)V4L2框架運(yùn)轉(zhuǎn)過(guò)程中,最為核心的是圖像數(shù)據(jù)緩沖區(qū)的管理,而這個(gè)管理工作便是由vb2_queue來(lái)完成的,vb2_queue通常在打開(kāi)設(shè)備的時(shí)候被創(chuàng)建,其結(jié)構(gòu)體中的vb2_ops可以由驅(qū)動(dòng)自己進(jìn)行實(shí)現(xiàn),而vb2_mem_ops代表了內(nèi)存分配的方法集,另外,還有一個(gè)用于將管理用戶空間和內(nèi)核空間的相互傳遞的方法集buf_ops,而該方法集一般都定義為v4l2_buf_ops這一標(biāo)準(zhǔn)方法集。除了這些方法集外,vb2_queue還通過(guò)一個(gè)vb2_buffer的數(shù)組來(lái)管理申請(qǐng)的所有數(shù)據(jù)緩沖區(qū),并且通過(guò)queued_list來(lái)管理入隊(duì)狀態(tài)的所有buffer,通過(guò)done_list來(lái)管理被填充了數(shù)據(jù)等待消費(fèi)的所有buffer。

          vb2_buffer源碼如下:

          struct vb2_buffer {
          struct vb2_queue *vb2_queue;
          unsigned int index;
          unsigned int type;
          unsigned int memory;
          unsigned int num_planes;
          struct vb2_plane planes[VB2_MAX_PLANES];
          u64 timestamp;

          /* private: internal use only
          *
          * state: current buffer state; do not change
          * queued_entry: entry on the queued buffers list, which holds
          * all buffers queued from userspace
          * done_entry: entry on the list that stores all buffers ready
          * to be dequeued to userspace
          */

          enum vb2_buffer_state state;

          struct list_head queued_entry;
          struct list_head done_entry;
          };

          該結(jié)構(gòu)體代表了V4L2框架中的圖像緩沖區(qū),當(dāng)處于入隊(duì)狀態(tài)時(shí)內(nèi)部queued_entry會(huì)被鏈接到vb2_queue中的queued_list中,當(dāng)處于等待消費(fèi)的狀態(tài)時(shí)其內(nèi)部done_entry會(huì)被鏈接到vb2_queue 中的done_list中,而其中的vb2_queue便是該緩沖區(qū)的管理者。

          以上便是V4L2框架的幾個(gè)核心結(jié)構(gòu)體,從上面的簡(jiǎn)單分析不難看出,v4l2_device作為一個(gè)相機(jī)內(nèi)核體系的頂層管理者,內(nèi)部使用一個(gè)鏈表控制著所有從屬子設(shè)備v4l2_subdev,使用vb2_queue來(lái)申請(qǐng)并管理所有數(shù)據(jù)緩沖區(qū),并且通過(guò)video_device向用戶空間暴露設(shè)備節(jié)點(diǎn)以及控制接口,接收來(lái)自用戶空間的控制指令,通過(guò)將自身嵌入media controller中來(lái)實(shí)現(xiàn)枚舉、連接子設(shè)備同時(shí)控制數(shù)據(jù)流走向的目的。

          四、模塊初始化

          整個(gè)v4l2框架是在linux內(nèi)核中實(shí)現(xiàn)的,所以按照內(nèi)核驅(qū)動(dòng)的運(yùn)行機(jī)制,會(huì)在系統(tǒng)啟動(dòng)的過(guò)程中,通過(guò)標(biāo)準(zhǔn)的module_init方式進(jìn)行初始化操作,而其初始化主要包含兩個(gè)方面,一個(gè)是v4l2_device的初始化,一個(gè)是子設(shè)備的初始化,首先我們來(lái)看下v4l2_device的初始化動(dòng)作的基本流程。

          由于驅(qū)動(dòng)的實(shí)現(xiàn)都交由各個(gè)平臺(tái)廠商進(jìn)行實(shí)現(xiàn),所有內(nèi)部邏輯都各不相同,這里我們抽離出主要方法來(lái)進(jìn)行梳理:

          首先對(duì)于v4l2_device的初始化而言,在系統(tǒng)啟動(dòng)的過(guò)程中,linux內(nèi)核會(huì)找到module_init聲明的驅(qū)動(dòng),調(diào)用其probe方法進(jìn)行探測(cè)相應(yīng)設(shè)備,一旦探測(cè)成功,便表示初始化工作完成。

          而在probe方法內(nèi)部,主要做了以下操作:

          • 獲取dts硬件信息,初始化部分硬件設(shè)備。

          • 創(chuàng)建v4l2_device結(jié)構(gòu)體,填充信息,通過(guò)v4l2_device_register方法向系統(tǒng)注冊(cè)并且創(chuàng)建video設(shè)備節(jié)點(diǎn)。

          • 創(chuàng)建media_device結(jié)構(gòu)體,填充信息,通過(guò)media_device_register向系統(tǒng)注冊(cè),并創(chuàng)建media設(shè)備節(jié)點(diǎn),并將其賦值給v4l2_device中的mdev。

          • 創(chuàng)建v4l2_device的media_entity,并將其添加到media controller進(jìn)行管理。

          類(lèi)似于v4l2_device的初始化工作,子設(shè)備的流程如下:

          • 獲取dts硬件信息,初始化子設(shè)備硬件模塊

          • 創(chuàng)建v4l2_subdev結(jié)構(gòu)體,填充信息,通過(guò)v4l2_device_register_subdev向系統(tǒng)注冊(cè),并將其掛載到v4l2_device設(shè)備中

          • 創(chuàng)建對(duì)應(yīng)的media_entity,并通過(guò)media_device_register_entity方法其添加到media controller中進(jìn)行統(tǒng)一管理。

          • 最后調(diào)用v4l2_device_register_subdev_nodes方法,為所有的設(shè)置了V4L2_SUBDEV_FL_HAS_DEVNODE屬性的子設(shè)備創(chuàng)建設(shè)備節(jié)點(diǎn)。

          五、處理用戶空間請(qǐng)求

          系統(tǒng)啟動(dòng)之后,初始化工作便已經(jīng)完成,現(xiàn)在一旦用戶想要使用圖像采集功能,便會(huì)觸發(fā)整個(gè)視頻采集流程,會(huì)通過(guò)操作相應(yīng)的video節(jié)點(diǎn)來(lái)獲取圖像數(shù)據(jù),一般來(lái)講,標(biāo)準(zhǔn)的V4L2框架只需要通過(guò)操作video節(jié)點(diǎn)即可,但是由于現(xiàn)在的硬件功能越來(lái)越復(fù)雜,常規(guī)的v4l2_controller已經(jīng)滿足不了采集需求,所以現(xiàn)在的平臺(tái)廠商通常會(huì)暴露子設(shè)備的設(shè)備節(jié)點(diǎn),在用戶空間直接通過(guò)標(biāo)準(zhǔn)的字符設(shè)備控制接口來(lái)控制各個(gè)設(shè)備,而現(xiàn)在我們的目的是梳理V4L2框架,所以暫時(shí)默認(rèn)不創(chuàng)建子設(shè)備節(jié)點(diǎn),簡(jiǎn)單介紹下整個(gè)流程。

          在操作之前,還有一個(gè)準(zhǔn)備工作需要做,那就是需要找到哪些是我們所需要的設(shè)備,而它的設(shè)備節(jié)點(diǎn)是什么,此時(shí)便可以通過(guò)打開(kāi)media設(shè)備節(jié)點(diǎn),并且通過(guò)ioctl注入MEDIA_IOC_ENUM_ENTITIES參數(shù)來(lái)獲取v4l2_device下的video設(shè)備節(jié)點(diǎn),該操作會(huì)調(diào)用到內(nèi)核中的media_device_ioctl方法,而之后根據(jù)傳入的命令,進(jìn)而調(diào)用到media_device_enum_entities方法來(lái)枚舉所有的設(shè)備。

          整個(gè)采集流程,主要使用三個(gè)標(biāo)準(zhǔn)字符設(shè)備接口來(lái)完成,分別是用于打開(kāi)設(shè)備的open方法、用于控制設(shè)備的ioctl方法以及關(guān)閉設(shè)備的close方法。

          1. 打開(kāi)設(shè)備(open)

          一旦確認(rèn)了我們需要操作的video節(jié)點(diǎn)是哪一個(gè),便可以通過(guò)調(diào)用字符設(shè)備標(biāo)準(zhǔn)接口open方法來(lái)打開(kāi)設(shè)備,而這個(gè)方法會(huì)首先陷入內(nèi)核空間,然后調(diào)用file_operations中的open方法,再到v4l2_file_operations中的open方法,而該方法由驅(qū)動(dòng)自己進(jìn)行實(shí)現(xiàn),其中主要包括了給各個(gè)硬件模塊上電,并且調(diào)用vb2_queue_init方法創(chuàng)建并初始化一個(gè)vb2_queue用于數(shù)據(jù)緩沖區(qū)的管理。

          2. 控制設(shè)備(ioctl)

          在打開(kāi)設(shè)備之后,接下來(lái)的大部分操作都是通過(guò)ioctl方法來(lái)完成的,而在該方法中,會(huì)首先陷入到內(nèi)核空間,之后調(diào)用字符設(shè)備的v4l2_fops中的v4l2_ioctl方法,而在該方法中又會(huì)去調(diào)用video_device的video_ioctl2方法,video_ioctl2方法定義了一系列video標(biāo)準(zhǔn)的方法,通過(guò)不同的命令在v4l2_ioctls中找到相應(yīng)的標(biāo)準(zhǔn)方法實(shí)現(xiàn),同時(shí)為了滿足用戶自定義命令的實(shí)現(xiàn),在video_ioctl2方法中會(huì)去調(diào)用到之前注冊(cè)video_device時(shí)賦予的ioctl_ops中的vidioc_default方法,在該方法中加入用戶自己的控制邏輯。

          在整個(gè)控制流程中,首先通過(guò)命令VIDIOC_QUERYCAP來(lái)獲取設(shè)備所具有的屬性,通過(guò)VIDIOC_G_PARM/VIDIOC_S_PARM來(lái)分別獲取和設(shè)置設(shè)備參數(shù),在這一系列操作配置完成之后,便需要向內(nèi)核申請(qǐng)用于數(shù)據(jù)流轉(zhuǎn)的緩沖區(qū)(Buffer),該操作通過(guò)命令VIDIOC_REQBUFS來(lái)完成,在內(nèi)核部分主要調(diào)用了標(biāo)準(zhǔn)方法vb2_reqbufs,進(jìn)而調(diào)用__vb2_queue_alloc來(lái)向內(nèi)核申請(qǐng)已知個(gè)數(shù)的Buffer,并且將其存入之前創(chuàng)建的vb2_queue中進(jìn)行管理。

          申請(qǐng)好了Buffer之后,便可以通過(guò)傳入VIDIOC_QBUF命令將申請(qǐng)的Buffer入隊(duì),具體操作最終會(huì)調(diào)用vb2_qbuf方法,而在該方法中會(huì)從vb2_queue的bufs數(shù)組中取出Buffer,將其加入queued_list鏈表中,并且更新Buffer狀態(tài),等待數(shù)據(jù)的填充或者來(lái)自用戶空間的出隊(duì)操作。

          在完成上面的操作后,整個(gè)數(shù)據(jù)流并沒(méi)有開(kāi)始流轉(zhuǎn)起來(lái),所以需要下發(fā)VIDIOC_STREAMON命令來(lái)通知整個(gè)框架開(kāi)始出數(shù)據(jù),在驅(qū)動(dòng)中主要會(huì)去調(diào)用vb2_streamon方法,進(jìn)而調(diào)用vb2_start_streaming方法,其中該方法會(huì)去將隊(duì)列中的的Buffer放入到相應(yīng)的驅(qū)動(dòng)中,等待被填充,緊接著會(huì)去調(diào)用vb2_queue.ops.start_streaming方法來(lái)通知設(shè)備開(kāi)始出圖,而該方法一般由驅(qū)動(dòng)自己實(shí)現(xiàn),最后會(huì)調(diào)用v4l2_subdev_call(subdev, video, s_stream, mode)方法通知各個(gè)子設(shè)備開(kāi)始出圖。

          當(dāng)有圖像產(chǎn)生時(shí),會(huì)填充到之前傳入的buffe中,并且調(diào)用vb2_buffer_done方法通知vb2_queue將buffer加入到done_list鏈表中,并更新?tīng)顟B(tài)為VB2_BUF_STATE_DONE。

          在整個(gè)數(shù)據(jù)流開(kāi)啟之后,并不會(huì)自動(dòng)的將圖像傳入用戶空間,必須通過(guò)VIDIOC_DQBUF命令來(lái)從設(shè)備中讀取一個(gè)幀圖像數(shù)據(jù),具體操作是通過(guò)層層調(diào)用會(huì)調(diào)用到vb2_dqbuf方法,而在該方法中會(huì)調(diào)用__vb2_get_done_vb方法去從done_list中獲取Buffer,如果當(dāng)前鏈表為空則會(huì)等待最終數(shù)據(jù)準(zhǔn)備好,如果有準(zhǔn)備好的buffer便直接從done_list取出,并且將其從queued_list中去掉,最后通過(guò)__vb2_dqbuf方法將Buffer返回用戶空間。

          獲取到圖像數(shù)據(jù)之后,便可以進(jìn)行后期的圖像處理流程了,在處理完成之后,需要下發(fā)VIDIOC_QBUF將此次buffer重新加入queued_list中,等待下一次的數(shù)據(jù)的填充和出隊(duì)操作。

          但不需要進(jìn)行圖像的采集時(shí),可以通過(guò)下發(fā)VIDIOC_STREAMOFF命令來(lái)停止整個(gè)流程,具體流程首先會(huì)調(diào)用v4l2_subdev_call(subdev, video, s_stream, 0)通知所有子設(shè)備停止出圖操作,其次調(diào)用vb2_buffer_done喚醒可能的等待Buffer的線程,同時(shí)更新Buffer狀態(tài)為VB2_BUF_STATE_ERROR,然后調(diào)用vb2_streamoff取消所有的數(shù)據(jù)流并更新vb2_queue.streaming的為disable狀態(tài)。

          3. 關(guān)閉設(shè)備(close)

          但確認(rèn)不使用當(dāng)前設(shè)備進(jìn)行圖像采集操作之后,便可以調(diào)用標(biāo)準(zhǔn)方法close來(lái)關(guān)閉設(shè)備。其中主要包括了調(diào)用vb2_queue_release方法釋放了vb2_queue以及設(shè)備下電操作和相關(guān)資源的釋放。

          通過(guò)上面的介紹,我相信我們已經(jīng)對(duì)整個(gè)V4L2框架有了一個(gè)比較深入的認(rèn)識(shí), 然而對(duì)于一個(gè)優(yōu)秀的軟件架構(gòu)而言,僅僅是支持現(xiàn)有的功能是遠(yuǎn)遠(yuǎn)不夠的,隨著功能的不斷完善,勢(shì)必會(huì)出現(xiàn)需要進(jìn)行擴(kuò)展的地方,而v4l2在設(shè)計(jì)之初便很好的考慮到了這一點(diǎn),所以提供了用于擴(kuò)展的方法集,開(kāi)發(fā)者可以通過(guò)加入自定的命令來(lái)擴(kuò)充整個(gè)框架,高通在這一點(diǎn)上做的非常好,在v4l2框架基礎(chǔ)上,設(shè)計(jì)出了一個(gè)獨(dú)特的KMD框架,提供給UMD CSL進(jìn)行訪問(wèn)的接口。

          原文鏈接:https://blog.csdn.net/u012596975/article/details/107137555

          友情推薦:

          Android 開(kāi)發(fā)干貨集錦

          至此,本篇已結(jié)束。轉(zhuǎn)載網(wǎng)絡(luò)的文章,小編覺(jué)得很優(yōu)秀,歡迎點(diǎn)擊閱讀原文,支持原創(chuàng)作者,如有侵權(quán),懇請(qǐng)聯(lián)系小編刪除,歡迎您的建議與指正。同時(shí)期待您的關(guān)注,感謝您的閱讀,謝謝!

          點(diǎn)個(gè)在看,方便您使用時(shí)快速查找!

          瀏覽 146
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产综合久久久 | 日韩中文字幕在线免费观看视频 | 诱惑无码一区二区三区 | 我要操逼网 | 成人毛片一区二区三区 |