obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
假如打开了CONFIG_I2C_CHARDEV的话,就可以将i2c 作为一个标准的字符设备来访问
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
if (res)
goto out;
//注册i2c对应的class,之后就可以看到/sys/class/i2c-dev,这样如果有i2c总线设备的话,就会在/sys/class/i2c-dev/i2c-0等设备
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
//i2c-dev对应的group ,会显示i2c-dev下每个字符设备的name
i2c_dev_class->dev_groups = i2c_groups;
//bus_register_notifier和i2c_for_each_dev 都是为新的i2c 总线设备insmod 后调用i2cdev_attach_adapter。这不过i2c_for_each_dev 针对当前已经存在的i2c 总线设备,而bus_register_notifier是为未来可能insmod i2c设备调用i2cdev_attach_adapter
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
module_init(i2c_dev_init);
在i2c_dev_init 中i2c_dev_class->dev_groups = i2c_groups;中用到的i2c_groups 定义如下
static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt));
if (!i2c_dev)
return -ENODEV;
return sprintf(buf, "%s\n", i2c_dev->adap->name);
}
static DEVICE_ATTR_RO(name);
static struct attribute *i2c_attrs[] = {
&dev_attr_name.attr,
NULL,
};
ATTRIBUTE_GROUPS(i2c);
可见这个通过cat name就key打印这个adapter对应的name.
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
int res;
mutex_lock(&core_lock);
res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
mutex_unlock(&core_lock);
return res;
}
为bus_type 为i2c_bus_type的设备调用i2cdev_attach_adapter
而bus_register_notifier(&i2c_bus_type, &i2cdev_notifier); 则是为未来肯能增加或减少的adapter调用i2cdev_notifier
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 struct notifier_block i2cdev_notifier = {
.notifier_call = i2cdev_notifier_call,
};
可见如果是新增的adapter 同样调用的是i2cdev_attach_adapter,这是就和i2c_for_each_dev做的事情一样了
总结一下i2c_dev_init主要就是注册了一个i2c-dev,并为每个已经存在或者未来可能存在的adapter调用i2cdev_attach_adapter
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
cdev_init(&i2c_dev->cdev, &i2cdev_fops);
i2c_dev->cdev.owner = THIS_MODULE;
res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);
if (res)
goto error_cdev;
/* register this i2c device with the driver core */
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
error:
cdev_del(&i2c_dev->cdev);
error_cdev:
put_i2c_dev(i2c_dev);
return res;
}
而在i2cdev_attach_adapter 中会为每个i2c adapter生产一个字符设备,这样就可以在user space方位i2c 设备了
linux-zpo1:/sys/class/i2c-dev/i2c-0 # ls /dev |grep i2c
i2c-0
i2c-1
linux-zpo1:/sys/class/i2c-dev # ls
i2c-0 i2c-1
下面的name就是通过i2c_dev_class->dev_groups = i2c_groups;产生的
linux-zpo1:/sys/class/i2c-dev/i2c-0 # cat name
Synopsys DesignWare I2C adapter