Linux下I2c框架分析之虚拟设备(六)

参考:https://www.cnblogs.com/zzb-Dream-90Time/p/7605473.html
宋宝华《Linux设备驱动开发详解:基于最新的Linux4.0内核》
看过宋宝华《Linux设备驱动开发详解:基于最新的Linux4.0内核》的朋友们会发现,在i2c章有一节是i2c-dev.c文件分析,这一节只做了一个大概的分析,今天咱们看看它到底能做什么。
i2c-dev.c文件完全可以被看作是一个I2C设备驱动,它实现的i2c_client是虚拟、临时的, 主要是为了便于从用户空间操作I2C外设。 i2c-dev.c针对每个I2C适配器生成一个主设备号为89的设备文件, 实现了i2c_driver的成员函数以及文件操作接口, 因此i2c-dev.c的主体是“i2c_driver成员函数+字符设备驱动”。

 /* \drivers\i2c\i2c-dev.c */
static int __init i2c_dev_init(void)
{
	/* 注册i2c字符设备,连接i2cdev_fops,向应用层提供操作接口
	res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
	/* 创建/sys/class/ i2c-dev  */
	i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
	/*  注册i2cdev_notifier到内核通知链,响应事件就调用其成员函数 */
	res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
	
	/* Bind to already existing adapters right away */
	i2c_for_each_dev(NULL, i2cdev_attach_adapter)return 0}
/* 提供通知链的响应函数 */
static struct notifier_block i2cdev_notifier = {
	.notifier_call = i2cdev_notifier_call,
};
static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action, void *data)
{
	struct device *dev = data;

	switch (action) {
	case BUS_NOTIFY_ADD_DEVICE:  /* 添加设备事件 */
		return i2cdev_attach_adapter(dev, NULL);
	case BUS_NOTIFY_DEL_DEVICE: /* 删除设备事件 */
		return i2cdev_detach_adapter(dev, NULL);
	}
	return 0;
}
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
	struct i2c_adapter *adap;
	struct i2c_dev *i2c_dev;
	int res;
	adap = to_i2c_adapter(dev);
	/* 配并初始化了一个struct i2c_dev结构 */
	i2c_dev = get_free_i2c_dev(adap);
	
	/* 注册i2c_dev会被链入链表i2c_dev_list中。再分别以I2C_MAJOR,、adap->nr为主次设备号创建了一个device,创建/dev/i2c-0 */
	i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
				     MKDEV(I2C_MAJOR, adap->nr), NULL,
				     "i2c-%d", adap->nr);
	res = device_create_file(i2c_dev->dev, &dev_attr_name);
	return 0;
}

此时我们就可以在/dev下看到i2c-*的字符设备了,可以通过read、write、ioctl等系统调用对它操作了,read、write实现的功能比较单一,只进行一次时序读写,所以很少使用,一般都用ioctl访问终端寄存器

static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct i2c_client *client = file->private_data;
	unsigned long funcs;
	switch (cmd) {
	case I2C_RDWR:
		return i2cdev_ioctl_rdrw(client, arg);
	}
	return 0;
}

又调用到上章讲的终极函数了。

static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
		unsigned long arg)
{
	res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
}

刚才说到通知链,只有调用到通知链的响应函数才在/dev下添加了设备,那它就很重要了。对通知链原理不做分析,这里只分析i2c设备实现代码,有兴趣的参考以下链接。
参考:https://blog.csdn.net/qq_22340085/article/details/78457005
返回查看i2c-core.c中i2c adapter的注册函数

/*  \drivers\i2c\i2c-core.c  */
static int i2c_register_adapter(struct i2c_adapter *adap)
{	
	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
	adap->dev.bus = &i2c_bus_type;
	adap->dev.type = &i2c_adapter_type;
	res = device_register(&adap->dev);
}
int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}
int device_add(struct device *dev)
{    /*   \drivers\base\core.c  */
	struct device *parent = NULL;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;

if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier, 
BUS_NOTIFY_ADD_DEVICE, dev);
}

blocking_notifier_call_chain的调用,说明了通知链的链表头、遍历的类型为BUS_NOTIFY_ADD_DEVICE、传递的数据为 struct device dev。
在i2c总线注册时也做了通知链的初始化

 /*  \drivers\i2c\i2c-core.c  */
static int __init i2c_init(void)
{  
	int retval;
	retval = bus_register(&i2c_bus_type);
}
int bus_register(struct bus_type *bus)
{      /*   \drivers\base\bus.c  */
	/* 初始化总线中阻塞通知链 */
	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
}

所以当i2c总线发布通知,当i2c-dev.c中的i2c虚拟字符设备注册到系统后,注册的响应函数就开始响应添加设备的事件,然后我们就在文件系统的/dev下看到了i2c-*的字符设备。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值