快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
ADSP-BF561
uclinux-2008r1.5-rc3 (smp patch)
Visual DSP++ 5.0(update 5)
BF561-EZKIT
欢迎转载,但请保留作者信息
本文仅对BF561-EZKIT视频采集做一简单学习。
要进行视频采集首先在硬件上要采用一种视频采集芯片完成模拟信号到数字信号的转换,在BF561-EZKIT上采用的是ADV7183。这个芯片可以通过i2c总线进行配制,于是乎内核就必须为它提供一个驱动程序,这个驱动由drivers/media/video/blackfin/adv7183b.c这个文件实现。注意,这个驱动仅供bfin camera使用,而不会向内核注册任何东西,内核也感觉不到这个驱动的存在。
在有了采集芯片之后,必须将它连接到561上,通常是PPI接口。由于可以采用多种不同的采集芯片,内核将它们抽象出来,统统称之为camera,再包装成一个叫"Blackfin CMOS Camera"的驱动向v4l模块进行注册。这个驱动由drivers/media/video/blackfin/blackfin_cam.c这个文件实现。
在有了视频采集的硬件之后,内核向用户提供了一个虚拟的设备/dev/video4linux,它对各种不同的硬件进行了包装,提供了一个统一的视频操作接口,也就是所谓的v4l。
这个驱动由drivers/media/video/blackfin/adv7183b.c文件实现,但是纵观整个文件,却看不到常见的module_init这样的定义,也就是说在内核启动时并不会直接初始化这个模块。但是在这个文件的末尾,可以发现这样的代码:
static struct bcap_camera_ops adv7183b_ops = {
.cam_control = adv7183b_cam_control,
.create_sysfs = adv7183b_create_sysfs,
.power = adv7183b_power,
};
struct bcap_camera_ops *get_camops(void)
{
printk(KERN_INFO "driver for ADV7183B get_camops/n");
return (&adv7183b_ops);
}
查找get_camops函数很容易可以发现,在drivers/media/video/blackfin/目录下的多个文件中都提供了这样一个函数,且在blackfin_cam.c中进行了调用。因而可以确认,7183或者其它blackfin采集芯片的驱动正是通过struct bcap_camera_ops向blackfin_cam.c提供了统一的接口。
整个文件看下来,绝大部分的函数都直接返回,只有初始化的函数比较有意思:
static int adv7183b_init(struct i2c_client *client, u32 arg)
{
printk(KERN_INFO "driver for ADV7183B init/n");
if (adv7183b_probe(client)) {
return -ENODEV;
}
/* Configuration Example, taken from datasheet ADV7183B */
/* EXAMPLES USING 27 MHz CLOCK
* Mode 1 CVBS Input (Composite Video on AIN5)
* All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8.
*/
adv7183b_write_byte(client, 0x00, 0x04); /* CVBS input on AIN5. */
adv7183b_write_byte(client, 0x15, 0x00); /* Slow down digital clamps. */
adv7183b_write_byte(client, 0x17, 0x41); /* Set CSFM to SH1. */
adv7183b_write_byte(client, 0x3A, 0x16); /* Power down ADC 1 and ADC 2. */
adv7183b_write_byte(client, 0x50, 0x04); /* Set DNR threshold to 4 for flat response. */
adv7183b_write_byte(client, 0x0E, 0x80); /* ADI recommended programming sequence. */
/* This sequence must be followed exactly when setting up the decoder. */
adv7183b_write_byte(client, 0x50, 0x20); /* Recommended setting. */
adv7183b_write_byte(client, 0x52, 0x18); /* Recommended setting. */
adv7183b_write_byte(client, 0x58, 0xED); /* Recommended setting. */
adv7183b_write_byte(client, 0x77, 0xC5); /* Recommended setting. */
adv7183b_write_byte(client, 0x7C, 0x93); /* Recommended setting. */
adv7183b_write_byte(client, 0x7D, 0x00); /* Recommended setting. */
adv7183b_write_byte(client, 0xD0, 0x48); /* Recommended setting. */
adv7183b_write_byte(client, 0xD5, 0xA0); /* Recommended setting. */
adv7183b_write_byte(client, 0xD7, 0xEA); /* Recommended setting. */
adv7183b_write_byte(client, 0xE4, 0x3E); /* Recommended setting. */
adv7183b_write_byte(client, 0xE9, 0x3E); /* Recommended setting. */
adv7183b_write_byte(client, 0xEA, 0x0F); /* Recommended setting. */
adv7183b_write_byte(client, 0x0E, 0x00); /* Recommended setting. */
return 0;
}
这个函数将在blackfin_cam.c中调用。
内核为连接到PPI接口上的视频采集设备定义了一个统一的接口,然后用一个叫Blackfin CMOS Camera的驱动来代表这一类设备,这个驱动的实现由blackfin_cam.c完成。
先看看它的初始化部分:
static __init int bcap_init(void)
{
int err;
struct bcap_camera_ops *ops;
……………
err = i2c_add_driver(&sensor_driver);
…………….
err = setup_pin_mux(1);
……………
}
这个初始化函数主要就两个调用,i2c_add_driver用以注册一个i2c设备,setup_pin_mux则用于配置复用的GPIO / PPI引脚。
在注册i2c驱动的时候,它最终将调用sensor_attach_adapter函数:
static int sensor_attach_adapter(struct i2c_adapter *adapter)
{
int i;
BUG_ON(adapter == NULL);
pr_debug("%s: starting probe for adapter %s (0x%x)/n", sensor_name,
adapter->name, adapter->id);
i = i2c_probe(adapter, &addr_data, &sensor_detect_client);
return i;
}
在i2c_probe的作用下,转而执行sensor_detect_client函数:
static int sensor_detect_client(struct i2c_adapter *adapter, int address,
int kind)
{
int err;
struct i2c_client *new_client;
struct sensor_data *data;
u16 tmp = 0;
………………..
err = data->cam_ops->cam_control(new_client, CAM_CMD_INIT, 1);
if (err)
goto error_out;
data->cam_ops->cam_control(new_client, CAM_CMD_SET_PIXFMT,
default_palette(force_palette));
err = bcap_init_v4l(data);
…………….
}
这个函数的两个主要作用,一个是调用adv7183驱动的初始化函数,对其进行配置,另一个是为/dev/video这一虚拟设备的驱动准备一个统一的接口,这个工作由bcap_init_v4l完成:
static int bcap_init_v4l(struct sensor_data *data)
{
int err, i;
…………………
memcpy(bcap_dev->videodev, &bcap_template, sizeof(bcap_template));
bcap_dev->frame_count = 0;
err = video_register_device(bcap_dev->videodev, VFL_TYPE_GRABBER, 0);
if (err) {
printk(KERN_NOTICE
"Unable to register Video4Linux driver for %s/n",
bcap_dev->videodev->name);
goto error_out_video;
}
…………..
bcap_create_sysfs(bcap_dev);
……………
}
这个函数首先构造了一个标准接口供/dev/vedio4linux驱动使用,然后调用video_register_device将此接口注册到/dev/video的驱动中,最后调用bcap_create_sysfs给了adv7183一个在VFS中创建设备节点的机会,当然,7183放弃了这一机会。
这个驱动由drivers/media/video/videodev.c文件实现,看看其初始化过程:
static int __init videodev_init(void)
{
int ret;
printk(KERN_INFO "Linux video capture interface: v2.00/n");
if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) {
printk(KERN_WARNING "video_dev: unable to get major %d/n", VIDEO_MAJOR);
return -EIO;
}
ret = class_register(&video_class);
if (ret < 0) {
unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME);
printk(KERN_WARNING "video_dev: class_register failed/n");
return -EIO;
}
return 0;
}
很普通的一个初始化过程。这里比较有意思的是video_fops的初始值:
static const struct file_operations video_fops=
{
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = video_open,
};
它居然只实现了一个open函数!那么又该如何进行视频读取或者参数配置呢?姑且看看video_open再说:
/*
* Open a video device - FIXME: Obsoleted
*/
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;
…………………..
old_fops = file->f_op;
file->f_op = fops_get(vfl->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);
}
……………………
}
原来如此,在这里对file->f_op进行了替换!这里的vfl直接来自于blackfin_cam.c中传递过来的指针,对于adv7183,它将指向:
static struct file_operations bcap_fops = {
.owner = THIS_MODULE,
.open = bcap_open,
.release = bcap_close,
.ioctl = bcap_ioctl,
.compat_ioctl = (void *)v4l_ioctl,
.llseek = no_llseek,
.read = bcap_read,
.mmap = bcap_mmap,
};