3 、 Video 核心層的實現
參見內核 /drivers/media/videodev.c
( 1 )註冊 256 個視頻設備
static int __init videodev_init(void)
{
int ret;
if (register_chrdev (VIDEO_MAJOR, VIDEO_NAME, &video_fops)) {
return -EIO;
}
ret = class_register(&video_class);
……
}
上面的代碼註冊了 256 個視頻設備,並註冊了 video_class 類。 video_fops 爲這 256 個設備共同的操作方法。
( 2 ) V4L2 驅動註冊函數的實現
int video_register_device(struct video_device *vfd, int type, int nr)
{
int i=0;
int base;
int end;
int ret;
char *name_base;
switch(type) // 根據不同的 type 確定設備名稱、次設備號
{
case VFL_TYPE_GRABBER:
base=MINOR_VFL_TYPE_GRABBER_MIN;
end=MINOR_VFL_TYPE_GRABBER_MAX+1;
name_base = "video";
break;
case VFL_TYPE_VTX:
base=MINOR_VFL_TYPE_VTX_MIN;
end=MINOR_VFL_TYPE_VTX_MAX+1;
name_base = "vtx";
break;
case VFL_TYPE_VBI:
base=MINOR_VFL_TYPE_VBI_MIN;
end=MINOR_VFL_TYPE_VBI_MAX+1;
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
base=MINOR_VFL_TYPE_RADIO_MIN;
end=MINOR_VFL_TYPE_RADIO_MAX+1;
name_base = "radio";
break;
default:
printk(KERN_ERR "%s called with unknown type: %d/n",
__func__, type);
return -1;
}
/* 計算出次設備號 */
mutex_lock(&videodev_lock);
if (nr >= 0 && nr < end-base) {
/* use the one the driver asked for */
i = base+nr;
if (NULL != video_device[i]) {
mutex_unlock(&videodev_lock);
return -ENFILE;
}
} else {
/* use first free */
for(i=base;i<end;i++)
if (NULL == video_device[i])
break;
if (i == end) {
mutex_unlock(&videodev_lock);
return -ENFILE;
}
}
video_device[i]=vfd; // 保存 video_device 結構指針到系統的結構數組中,最終的次設備號和 i 相關。
vfd->minor=i;
mutex_unlock(&videodev_lock);
mutex_init(&vfd->lock);
/* sysfs class */
memset(&vfd->class_dev, 0x00, sizeof(vfd->class_dev));
if (vfd->dev)
vfd->class_dev.parent = vfd->dev;
vfd->class_dev.class = &video_class;
vfd->class_dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor);
sprintf(vfd->class_dev.bus_id, "%s%d", name_base, i - base);// 最後在 /dev 目錄下的名稱
ret = device_register(&vfd->class_dev);// 結合 udev 或 mdev 可以實現自動在 /dev 下創建設備節點
……
}
從上面的註冊函數中可以看出 V4L2 驅動的註冊事實上只是完成了設備節點的創建,如: /dev/video0 。和video_device 結構指針的保存。
( 3 )視頻驅動的打開過程
當用戶空間調用 open 打開對應的視頻文件時,如:
int fd = open(/dev/video0, O_RDWR );
對應 /dev/video0 的文件操作結構是 /drivers/media/videodev.c 中定義的 video_fops 。
static const struct file_operations video_fops=
{
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = video_open,
};
奇怪吧,這裏只實現了 open 操作。那麼後面的其它操作呢?還是先看看 video_open 吧。
static int video_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
int err = 0;
struct video_device *vfl;
const struct file_operations *old_fops;
if(minor>=VIDEO_NUM_DEVICES)
return -ENODEV;
mutex_lock(&videodev_lock);
vfl=video_device[minor];
if(vfl==NULL) {
mutex_unlock(&videodev_lock);
request_module("char-major-%d-%d", VIDEO_MAJOR, minor);
mutex_lock(&videodev_lock);
vfl=video_device[minor]; // 根據次設備號取出 video_device 結構
if (vfl==NULL) {
mutex_unlock(&videodev_lock);
return -ENODEV;
}
}
old_fops = file->f_op;
file->f_op = fops_get(vfl->fops);// 替換此打開文件的 file_operation 結構。後面的其它針對此文件的操作都由新的結構來負責了。也就是由每個具體的 video_device 的 fops 負責。
if(file->f_op->open)
err = file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
……
}