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

       }

……

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章