V4L2驅動的移植與應用(一)
V4L2(video for linux) 可以支持多種設備,它可以有以下5種接口:
1、視頻采集接口(video capture interface):這種應用的設備可以是高頻頭或者攝像頭.V4L2的最初設計就是應用於這種功能的.下麵也是著重講解這種應用;
2、視頻輸出接口(video output interface):可以驅動計算機的外圍視頻圖像設備——像可以輸出電視信號格式的設備;
3、直接傳輸視頻接口(video overlay interface):它的主要工作是把從視頻采集設備采集過來的信號直接輸出到輸出設備之上,而不用經過係統的CPU;
4、視頻間隔消隱信號接口(VBI interface):它可以使應用可以訪問傳輸消隱期的視頻信號;
5、收音機接口(radio interface):可用來處理從AM或FM高頻頭設備接收來的音頻流;
V4L2驅動的主要功能是使程序有發現設備的能力和操作設備.它主要是用過一係列的回調函數來實現這些功能.像設置高頻頭的頻率,幀頻,視頻壓縮格式和圖像像參數等等.
一、V4L2的移植
V4L2提供了三種不同的API來傳輸外圍設備和用戶空間的數據。下麵就vivi(drivers/media/video/vivi.c)來講解一個V4L2驅動的編寫。注意它是一個虛擬的設備驅動,沒有與實際的硬件打交道。
1、分析幾個重要數據結構:
vivi.c包含頭文件v4l2-device.h和v4l2-ioctl.h,其中v4l2-device.h中包含了v4l2-subdev.h,v4l2-subdev.h中又包含了v4l2-common.h,v4l2-common.h中包含了v4l2-dev.h。
在v4l2-dev.h中定義了結構體video_device和v4l2_file_operations;
在v4l2-ioctl.h中定義了結構體v4l2_ioctl_ops;
在v4l2-device.h中定義了結構體v4l2_device;
1) vivi_fops
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = vivi_open,
.release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
.ioctl = video_ioctl2, /* V4L2 ioctl handler */
.mmap = vivi_mmap,
};
2) vivi_ioctl_ops
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.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_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
};
3) vivi_template
static struct video_device vivi_template = {
.name = "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.minor = -1,
.release = video_device_release,
.tvnorms = V4L2_STD_525_60,
.current_norm = V4L2_STD_NTSC_M,
};
其中函數vivi_xxx和vidioc_xxx都是在vivi.c中實現的。如果要基於某個硬件來實現V4L2的接口,那這些函數就需要調用硬件的驅動去實現。
4) vivi_dev
struct vivi_dev {
struct list_head vivi_devlist; //內核雙向鏈表,在內核數據結構裏有描述
struct semaphore lock; //信號量,防止競態訪問
int users; //用戶數量計數
/* various device info */
unsigned int resources;
struct video_device video_dev; //這個成員是這個結構的核心,用麵向對象的話來說就是基類
struct vivi_dmaqueue vidq; //DMA隊列
/* Several counters */
int h,m,s,us,jiffies; //定時器定義
char timestr[13]; //其它一些資源變量.
};
像這樣變義的結構在Linux C 中很普遍,這也是利用C來實現麵向對象編程的強大方法。建立這個結構對象之後,所有的操作都是基於這個結構,或者這個結構派生出的來的其它結構。
5) vivi_fh
struct vivi_fh {
struct vivi_dev *dev;
/* video capture */
struct vivi_fmt *fmt;
unsigned int width,height;
struct videobuf_queue vb_vidq;
enum v4l2_buf_type type;
};
這個結構即是vivi_dev結構的更深層次封裝,基於那個結構加入了更多的描述信息,如視頻製式、視頻畫麵大小、視頻緩衝隊列等等。在open的時候,會把這個結構賦給file結構中的private_data域。在釋放設備時注銷.其它的像ioctl,mmap,read,write等等都會用到這個結構,其實整個模塊的編寫的cdev差不多。隻是視頻設備的基類是video_device,而字符設備的基類是cdev而已。
2、數據傳輸方式:
在設備與應用程序之間有三種數據傳輸方式:
1)read與write這種方式,它像其它設備驅動一樣,但是這種方式很慢,對於數據視頻流不能滿足其要求;
2)直接的內存訪問,可以通過其映射方式來傳輸(IO數據流,交換指向緩衝區指針的方法);這是視頻設備通常用的方法,采用mmap()的方法,即有內核空間裏開辟內存,再在程序裏把這部分的內存映射到程序空間。如果有設備內存,即直接映射到設備的內核,這種性能更高。
3)異步IO口訪問,但是這種方法在V4L2模塊中還沒有實現。(重要:需要確認)
vivi中的mmap是利用第二種方法來實現的,這也是視頻設備常用的方法:
static int
vivi_mmap(struct file *file, struct vm_area_struct * vma)
{
struct vivi_fh *fh = file->private_data;
int ret;
dprintk (1,"mmap called, vma=0x%08lx/n",(unsigned long)vma);
ret=videobuf_mmap_mapper(&fh->vb_vidq, vma);
dprintk (1,"vma start=0x%08lx, size=%ld, ret=%d/n",
(unsigned long)vma->vm_start,
(unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
ret);
return ret;
}
videobuf_mmap_mapper(&fh->vb_vidq, vma); 這個核心函數把設備的I/O內存或者設備內存映射到係統為它開辟的虛擬內存。
3、操控設備的實現: ioctl
static int vivi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
return video_usercopy(inode, file, cmd, arg, vivi_do_ioctl);
}
vivi_do_ioctl 這個函數裏調用一些命令來設備V4L2模塊中的一些結構參數來改變或者獲取設備的參數
最後更新:2017-04-03 16:48:59