下面以内核源码中的radio-tea5764.c来简单说明一下V4L2 radio驱动的注册流程
1、首先按照通常的设备定义文件系统接口
/* File system interface */
/*文件系统通用接口即供应用层使用的接口*/
static const struct v4l2_file_operations tea5764_fops = {
.owner = THIS_MODULE,
.open = tea5764_open,
.release = tea5764_close,
.ioctl = video_ioctl2,/*通过ioctl接口来调用v4l2的接口*/
};
//v4l2接口
static const struct v4l2_ioctl_ops tea5764_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_audio = vidioc_g_audio,
.vidioc_s_audio = vidioc_s_audio,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
};
tea5764_fops结构体其实跟普通的字符设备类似,填充基本的操作即可。其中,.ioctl指向的接口video_ioctl2是v4l2接口的入口,这个就是v4l2驱动的关键。
接口video_ioctl2具体如下:
long video_ioctl2(struct file *file,unsigned int cmd, unsigned long arg);
它是v4l2核心提供的标准控制接口,与我们平时自己去实现设备的ioctl有所区别,video_ioctl2的代码不需要我们去实现,但是它通过file指针来区分每一个设备,再使用cmd来区分每一个专用接口,即每一个命令对应于v4l2_ioctl_ops里面的接口,arg就是应用传入的参数。所以,我们在实现V4L2驱动时,就是去实现v4l2_ioctl_ops里面的专用接口,使用这些接口去读写设备。
2、具体V4L2 radio设备驱动注册流程
//自定义一个tea5764设备结构体
struct tea5764_device {
struct i2c_client *i2c_client;
struct video_device *videodev;
struct tea5764_regs regs;
struct mutex mutex;
int users;
};
//定义一个video_device设备
/* V4L2 interface */
static struct video_device tea5764_radio_template = {
.name = "TEA5764 FM-Radio",
.fops = &tea5764_fops,/*一套通用文件系统接口供应用层使用*/
.ioctl_ops = &tea5764_ioctl_ops,/*一套v4l2接口供tea5764_fops的.ioctl通过命令调用*/
.release = video_device_release,
};
//radio-tea5764是一个ic2设备,所以注册一般在probe里面进行
/* I2C probe: check if the device exists and register with v4l if it is */
static int __devinit tea5764_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tea5764_device *radio;
struct tea5764_regs *r;
int ret;
PDEBUG("probe");
radio = kmalloc(sizeof(struct tea5764_device), GFP_KERNEL);
if (!radio)
return -ENOMEM;
mutex_init(&radio->mutex);
radio->i2c_client = client;
ret = tea5764_i2c_read(radio);
if (ret)
goto errfr;
r = &radio->regs;
PDEBUG("chipid = %04X, manid = %04X", r->chipid, r->manid);
if (r->chipid != TEA5764_CHIPID ||
(r->manid & 0x0fff) != TEA5764_MANID) {
PWARN("This chip is not a TEA5764!");
ret = -EINVAL;
goto errfr;
}
//分配一个video_device空间
radio->videodev = video_device_alloc();
if (!(radio->videodev)) {
ret = -ENOMEM;
goto errfr;
}
//把tea5764_radio_template填充到radio->videodev
memcpy(radio->videodev, &tea5764_radio_template,
sizeof(tea5764_radio_template));
i2c_set_clientdata(client, radio);
video_set_drvdata(radio->videodev, radio);
//注册V4L2设备
//注册完成后 应用层就可以使用ioctl的接口传入命令
//来调用v4l2的接口了即tea5764_ioctl_ops的接口
//生成/dev/radiox设备名
ret = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
if (ret < 0) {
PWARN("Could not register video device!");
goto errrel;
}
/* initialize and power off the chip */
tea5764_i2c_read(radio);
tea5764_set_audout_mode(radio, V4L2_TUNER_MODE_STEREO);
tea5764_mute(radio, 1);
tea5764_power_down(radio);
PINFO("registered.");
return 0;
errrel:
video_device_release(radio->videodev);
errfr:
kfree(radio);
return ret;
}