一、安裝攝像頭應用程式(初體驗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虛拟攝像頭驅動代碼分析
如果下載下傳了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,
}
待續……