概述
系列文章:
Linux V4L2驱动框架分析之(一):架构介绍
Linux V4L2驱动框架分析之(二):平台v4l2设备驱动
Linux V4L2驱动框架分析之(三):v4l2设备的缓存管理
Linux V4L2驱动框架分析之(四):sensor驱动
为了实现代码的重用,sensor驱动只需实现各种设备控制方法供上层调用并注册v4l2_subdev,而无需关心video_device和v4l2_dev。
struct v4l2_subdev结构体定义如下:
struct v4l2_subdev {
......
struct list_head list; //用于挂入v4l2_dev的subdevs链表
struct module *owner;
bool owner_v4l2_dev;
u32 flags;
struct v4l2_device *v4l2_dev;
const struct v4l2_subdev_ops *ops; //操作集
......
char name[V4L2_SUBDEV_NAME_SIZE];
......
struct video_device *devnode;
struct device *dev;
struct device_node *of_node;
struct list_head async_list;
struct v4l2_async_subdev *asd;
struct v4l2_async_notifier *notifier;
struct v4l2_subdev_platform_data *pdata;
};
如果说v4l2_subdev是个i2c设备,可调用v4l2_i2c_subdev_init函数初始化v4l2_subdev:
void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
const struct v4l2_subdev_ops *ops)
{
//初始化v4l2_subdev里的ops等
v4l2_subdev_init(sd, ops);
sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
sd->owner = client->dev.driver->owner;
sd->dev = &client->dev;
v4l2_set_subdevdata(sd, client);
i2c_set_clientdata(client, sd);
/* initialize name */
snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
client->dev.driver->name, i2c_adapter_id(client->adapter),
client->addr);
}
struct v4l2_subdev_ops结构体定义如下:
struct v4l2_subdev_ops {
const struct v4l2_subdev_core_ops *core;
const struct v4l2_subdev_video_ops *video;
const struct v4l2_subdev_pad_ops *pad;
......
};
对于sensor,一般要实现struct v4l2_subdev_ops的core、video和pad成员。
struct v4l2_subdev_video_ops结构体定义如下:
struct v4l2_subdev_video_ops {
......
//获取当前正在使用的标准
int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm);
//设置视频标准
int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);
int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
int (*g_frame_interval)(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval);
int (*s_frame_interval)(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *interval);
......
};
如应用层要获取v4l2设备当前正在使用的视频标准,会"ioctl /dev/video0",ioctl传入到平台驱动层,那么平台驱动可以调用v4l2_subdev->ops->video->g_std回调函数获取sensor的视频标准。
设置好v4l2_subdev后,调用v4l2_async_register_subdev进行注册:
int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{
struct v4l2_async_notifier *notifier;
if (!sd->of_node && sd->dev)
sd->of_node = sd->dev->of_node;
mutex_lock(&list_lock);
INIT_LIST_HEAD(&sd->async_list);
//匹配v4l2_device,具体怎么匹配后面在分析
list_for_each_entry(notifier, ¬ifier_list, list) {
struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
if (asd) {
int ret = v4l2_async_test_notify(notifier, sd, asd);
mutex_unlock(&list_lock);
return ret;
}
}
/* 未匹配则挂入全局链表subdev_list */
list_add(&sd->async_list, &subdev_list);
mutex_unlock(&list_lock);
return 0;
}
对于未匹配的v4l2_subdev会挂入全局链表subdev_list:
为了能让平台v4l2设备驱动能匹配到sensor驱动注册的v4l2_subdev,平台驱动需要注册一个struct v4l2_async_notifier:
struct v4l2_async_notifier {
unsigned int num_subdevs; //需要匹配v4l2_subdev的数目
struct v4l2_async_subdev **subdevs; //v4l2_async_subdev数组
struct v4l2_device *v4l2_dev;
struct list_head waiting;
struct list_head done;
struct list_head list;
//与v4l2_subdev匹配后,调用bound回调
int (*bound)(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd);
int (*complete)(struct v4l2_async_notifier *notifier);
void (*unbind)(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd);
};
struct v4l2_async_subdev {
//匹配类型
enum v4l2_async_match_type match_type;
union {
struct {
const struct device_node *node; //设备节点匹配
} of;
struct {
const char *name; //名称匹配
} device_name;
struct {
int adapter_id;
unsigned short address; //i2c设备地址匹配
} i2c;
struct {
bool (*match)(struct device *,
struct v4l2_async_subdev *);
void *priv;
} custom;
} match;
/* v4l2-async core private: not to be used by drivers */
struct list_head list;
};
//匹配类型
enum v4l2_async_match_type {
V4L2_ASYNC_MATCH_CUSTOM,
V4L2_ASYNC_MATCH_DEVNAME,
V4L2_ASYNC_MATCH_I2C,
V4L2_ASYNC_MATCH_OF,
};
平台v4l2设备驱动设置好struct v4l2_async_notifier后,调用v4l2_async_notifier_register进行注册:
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
struct v4l2_async_notifier *notifier)
{
struct v4l2_subdev *sd, *tmp;
struct v4l2_async_subdev *asd;
int i;
......
notifier->v4l2_dev = v4l2_dev;
INIT_LIST_HEAD(¬ifier->waiting);
INIT_LIST_HEAD(¬ifier->done);
for (i = 0; i < notifier->num_subdevs; i++) {
asd = notifier->subdevs[i];
switch (asd->match_type) {
case V4L2_ASYNC_MATCH_CUSTOM:
case V4L2_ASYNC_MATCH_DEVNAME:
case V4L2_ASYNC_MATCH_I2C:
case V4L2_ASYNC_MATCH_OF:
break;
default:
......
return -EINVAL;
}
//把v4l2_async_subdev都挂入v4l2_async_notifier的waiting链表
list_add_tail(&asd->list, ¬ifier->waiting);
}
mutex_lock(&list_lock);
/* 把v4l2_async_notifier挂入全局链表notifier_list */
list_add(¬ifier->list, ¬ifier_list);
//遍历subdev_list链表
list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
int ret;
//进行匹配
asd = v4l2_async_belongs(notifier, sd);
if (!asd)
continue;
ret = v4l2_async_test_notify(notifier, sd, asd);
if (ret < 0) {
mutex_unlock(&list_lock);
return ret;
}
}
mutex_unlock(&list_lock);
return 0;
}
调用v4l2_async_belongs函数进行匹配:
static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *sd)
{
bool (*match)(struct v4l2_subdev *, struct v4l2_async_subdev *);
struct v4l2_async_subdev *asd;
//从v4l2_async_notifier的waiting链表取出每项v4l2_async_subdev
list_for_each_entry(asd, ¬ifier->waiting, list) {
/* bus_type has been verified valid before */
switch (asd->match_type) {
case V4L2_ASYNC_MATCH_CUSTOM:
match = match_custom;
break;
case V4L2_ASYNC_MATCH_DEVNAME:
match = match_devname;
break;
case V4L2_ASYNC_MATCH_I2C:
match = match_i2c;
break;
case V4L2_ASYNC_MATCH_OF:
match = match_of;
break;
default:
/* Cannot happen, unless someone breaks us */
WARN_ON(true);
return NULL;
}
/* 调用match进行v4l2_async_subdev与v4l2_subdev的匹配,不同的匹配类型
match回调函数不一样
*/
if (match(sd, asd))
return asd;
}
return NULL;
}
//对于V4L2_ASYNC_MATCH_OF类型,match为match_of
static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
return sd->of_node == asd->match.of.node; //判断设备节点是否一致
}
匹配成功后调用v4l2_async_test_notify:
static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *sd,
struct v4l2_async_subdev *asd)
{
int ret;
/* Remove from the waiting list */
list_del(&asd->list);
//设置v4l2_subdev的asd、notifier
sd->asd = asd;
sd->notifier = notifier;
if (notifier->bound) {
ret = notifier->bound(notifier, sd, asd); //回调v4l2_async_notifier的bound
if (ret < 0)
return ret;
}
/* Move from the global subdevice list to notifier's done */
list_move(&sd->async_list, ¬ifier->done);
//把v4l2_subdev挂入v4l2_device的subdevs链表
ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
if (ret < 0) {
if (notifier->unbind)
notifier->unbind(notifier, sd, asd);
return ret;
}
if (list_empty(¬ifier->waiting) && notifier->complete)
return notifier->complete(notifier);
return 0;
}
匹配前:
匹配后:
最后
以上就是寂寞篮球为你收集整理的Linux V4L2驱动框架分析之(四):sensor驱动的全部内容,希望文章能够帮你解决Linux V4L2驱动框架分析之(四):sensor驱动所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复