基于V4L2的视频驱动开发(4)

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

                     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);

       }

……

}

你可能感兴趣的:(Android,Linux,Kernel)