快樂蝦
http://blog.csdn.net/lights_joy/
lights@hb165.com
本文適用於
ADSP-BF561
uclinux-2008r1.5-rc3 (smp patch)
Visual DSP++ 5.0(update 5)
BF561-EZKIT
歡迎轉載,但請保留作者資訊
本文僅對BF561-EZKIT視頻採集做一簡單學習。
1.1 整體結構
要進行視頻採集首先在硬體上要採用一種視頻採集晶片完成類比訊號到數字訊號的轉換,在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。
1.2 adv7183驅動
這個驅動由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中調用。
1.3 Blackfin CMOS Camera驅動
核心為串連到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放棄了這一機會。
1.4 vedio4linux驅動
這個驅動由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,
};
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/lights_joy/archive/2009/07/02/4316334.aspx