天天看點

vivi虛拟攝像頭驅動代碼分析記錄---學習記錄

一、安裝攝像頭應用程式(初體驗vivi)

我們可以線上擷取安裝包,好像是高版本的ubuntu已經去掉了vivi這個功能。

//在保證自己的ubuntu能夠聯網的情況下執行這條指令:
1、[email protected]:~$ sudo apt-get install xawtv
//安裝成功之後
2、[email protected]:~$ sudo modprobe -i vivi
//執行上面這條指令之後,可以通過ls指令檢視dev目錄下自動生成了video0這個字元驅動裝置
[email protected]:~$ ls /dev/video0 -al
crw-rw----+ 1 root video 81, 0 Dec 10 04:14 /dev/video0
3、[email protected]:~$ xawtv
//執行這個攝像頭應用程式xawtv
           

執行完xawtv攝像頭應用程式之後就會出現這樣的現象:

vivi虛拟攝像頭驅動代碼分析記錄---學習記錄

二、vivi虛拟攝像頭驅動代碼分析

如果下載下傳了linux核心源碼在核心頂層目錄下的驅動檔案drivers/media/video/vivi.c,如果你手中沒有現成的核心源碼,可以通過網頁線上浏覽,一般浏覽器搜尋lxr就會出現linux或者android中的,具體網址:https://lxr.missinglinkelectronics.com/linux/

1、配置設定一個對象

/**通過模式入口函數vivi_create_instance,根據驅動的架構,主要有四步:配置設定一個對象、
對象初始化、注冊對象、登出對象。要想掌握攝像頭的驅動架構,就必須确認這四個步驟具體
怎麼實作的,然後根據别人的架構來寫自己的驅動**/
static int __init vivi_create_instance(int inst)
 {
   struct vivi_dev *dev; /****這個其實不是配置設定的對象,而是已經被封
   裝過的video_device,是以檢視必須vivi_dev這個結構體如何實作的****/
   struct video_device *vfd;
   struct v4l2_ctrl_handler *hdl;
   struct vb2_queue *q;                                                             
   int ret;

 dev = kzalloc(sizeof(*dev), GFP_KERNEL);    //開辟空間配置設定記憶體
    if (!dev)
        return -ENOMEM;

    snprintf(dev->v4l2_dev.name,sizeof(dev->v4l2_dev.name),              "%s-%03d", VIVI_MODULE_NAME, inst);
    ret = v4l2_device_register(NULL, &dev->v4l2_dev);
    /*v4l2_device_registe對video_device進行初始化繼承了device對象,
    這個就是再/sys下建立檔案或者目錄供使用者檢視*/
    if (ret)
        goto free_dev;  
//上邊注冊函數vivi_create_instance第一個結構體在這兒實作
struct vivi_dev {       
    struct list_head           vivi_devlist;
    struct v4l2_device     v4l2_dev;
    struct v4l2_ctrl_handler   ctrl_handler;

    /* controls */
    struct v4l2_ctrl       *brightness;   /*這些往下都是亮度等等的一些圖像參數*/
    struct v4l2_ctrl       *contrast;
    struct v4l2_ctrl       *saturation;
    struct v4l2_ctrl       *hue;
    struct {
        /* autogain/gain cluster */
        struct v4l2_ctrl       *autogain;
        struct v4l2_ctrl       *gain;
    };
    struct v4l2_ctrl       *volume;
    struct v4l2_ctrl       *button;
    struct v4l2_ctrl       *boolean;                                                      
    struct v4l2_ctrl       *int32;
    struct v4l2_ctrl       *int64;
    struct v4l2_ctrl       *menu;
    struct v4l2_ctrl       *string;
struct v4l2_ctrl       *bitmask;
struct video_device        *vfd;   //注冊一個對象的函數
vfd =video_device_alloc()    //給注冊的對象配置設定記憶體
*vfd = vivi_template;   //初始化對象,直接指派的vivi_template
}
           

2、對象初始化

static struct video_device vivi_template = {
    .name         = "vivi",       //攝像頭名字比如:vivi或者ov3640
    .fops         = &vivi_fops,  //操作方法
    .ioctl_ops    = &vivi_ioctl_ops,   //ioctl函數的操作方法
    .release      = video_device_release,  //釋放資源的函數
                                                                                          
    .tvnorms              = V4L2_STD_525_60,
    .current_norm         = V4L2_STD_NTSC_M,
};

           

3、注冊對象

video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); /***ideo_register_device這個是注冊對象的函數,發現在這個函數裡隻調用了下面的函數
__video_register_device****/
__video_register_device(vdev, type, nr, 1, vdev->fops->owner); //V4L2-dev.c
{
   switch (type) {    //type代表本次配置設定的裝置類型
	case VFL_TYPE_GRABBER:   // VFL_TYPE_GRABBER攝像頭裝置
		name_base = "video";   
		break;
	case VFL_TYPE_VBI:   // VFL_TYPE_VBI有線電視裝置
		name_base = "vbi";
		break;
	case VFL_TYPE_RADIO:  // VFL_TYPE_RADIO聲音機的裝置
		name_base = "radio";
		break;
	case VFL_TYPE_SUBDEV:
		name_base = "v4l-subdev";
		break;
	default:
		printk(KERN_ERR "%s called with unknown type: %d\n",
		       __func__, type);
		return -EINVAL;
	}
    switch (type) {   //用來指定次裝置号的
	case VFL_TYPE_GRABBER:   //0-63
		minor_offset = 0;
		minor_cnt = 64;
		break;
	case VFL_TYPE_RADIO:  //64-127
		minor_offset = 64;
		minor_cnt = 64;
		break;
	case VFL_TYPE_VBI: //  224 - 257
		minor_offset = 224;
		minor_cnt = 32;
		break;
	default:
		minor_offset = 128;  // 128-191
		minor_cnt = 64;
		break;
	}
vdev->cdev = cdev_alloc();  //注冊字元裝置驅動
vdev->cdev->ops = &v4l2_fops;  /*回調vivi_fops,當vivi_fops裡的ioctl函數調用的時候回調vivi_ioctl_ops*/
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);

           

4、登出對象

//登出的函數為video_unregister_device
void video_unregister_device(struct video_device *vdev)
{
	/* Check if vdev was ever registered at all */
	if (!vdev || !video_is_registered(vdev))
		return;

	mutex_lock(&videodev_lock);
	/* This must be in a critical section to prevent a race with v4l2_open.
	 * Once this bit has been cleared video_get may never be called again.
	 */
	clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
	mutex_unlock(&videodev_lock);
	device_unregister(&vdev->dev);
}
           

三、實作攝像頭驅動的主要ioctl函數有這些

V4l2_ioctl_ops這個結構體裡對這些主要的ioctl函數進行初始化

static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
/* vidioc_querycap就是表明身份,你是誰?比如:我是一個攝像頭裝置*/
	.vidioc_querycap 		= vidioc_querycap,
	
	/* 用于列舉、獲得、測試、設定攝像頭的資料的格式 */
 	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
	.vidioc_g_fmt_vid_cap   = vidioc_g_fmt_vid_cap,
	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
	.vidioc_s_fmt_vid_cap   = vidioc_s_fmt_vid_cap, 
	
	/* 緩沖區操作: 申請/查詢/放入隊列/取出隊列 */
	.vidioc_reqbufs 	    = vidioc _reqbufs,
	.vidioc_querybuf 		= vidioc_querybuf,
	.vidioc_qbuf 			= vidioc_qbuf,
	.vidioc_dqbuf 	 		= vidioc_dqbuf,

	/*啟動/停止*/
	.vidioc_streamon 		= vidioc_streamon,
	.vidioc_streamoff 		= vidioc_streamoff,
}
           

待續……

繼續閱讀