欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

嵌入式软件开发之------浅析linux驱动模型(五)I2C驱动

程序员文章站 2022-04-10 23:46:27
...

Linux代码版本:linux3.0
开发板环境: tiny4412
导读:i2c控制器作为platform_device挂接在platform总线上,在《嵌入式软件开发之------浅谈linux驱动模型(四)device和driver》以i2c控制器为例,分析了

s3c_device_i2c1和s3c24xx_i2c_driver的注册过程,总结下来就是下图:

嵌入式软件开发之------浅析linux驱动模型(五)I2C驱动

每个i2c控制器都是一个i2c 总线,i2c控制器即挂接在platform总线上,也为i2c提供总线。platform总线有platform_device和platform_driver,相应的i2c总线也有对应的device和driver,只不过是i2c_client和i2c_driver.

一、i2c_adapter的注册及 i2c_client 的实例化

下面看i2c_client,有没有觉得和platform_device很像?都是封装了devcie后添加了部分各自特点的成员:

struct i2c_client {
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct i2c_driver *driver;	/* and our access routines	*/
	struct device dev;		/* the device structure		*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;
};

unsigned short flags;

标志,用 I2C_CLIENT_TEN 表示 10 bit 地址,用 I2C_CLIENT_PEC 表示SMBus的错误数据检测

unsigned short addr;

器件的地址

char name[I2C_NAME_SIZE];

设备的名字,在 new_device 属性文件中,低7位代表地址

struct i2c_adapter *adapter; /* the adapter we sit on */
虽然是挂接在I2C总线上的,client->dev.bus = &i2c_bus_type,可是 总要 指向所在的i2c控制器吗

 

struct i2c_driver *driver; /* and our access routines */

 

对应的驱动

struct device dev; /* the device structure */

封装的device结构体

int irq; /* irq issued by device */

用来表示此设备产生的IRQ

struct list_head detected;

用于插入 i2c_driver.clients 的节点,在遍历 i2c_drive r探测到实际挂接在 i2c_adapter 但又未注册

的 i2c 设备时,实例化成 i2c_client 后以此成员插入 i2c_driver.clients。

看到i2c_client就不得不看i2c设备的注册,这个时候可能有人会说,大概和platform设备注册差不多吧,确实 差不多,可还是有点区别下面就以mma7660为例,

static struct i2c_board_info i2c_devs3[] __initdata = {
	{
		I2C_BOARD_INFO("mma7660", 0x4c),
		.platform_data = &mma7660_pdata,
	},
};

展开

static struct i2c_board_info i2c_devs3[] __initdata = {
	{
        .type = "mma7660", 
        .addr = 0x4c, 
		.platform_data = &mma7660_pdata,
	},
};

有没有发现什么不对?不是说i2c设备的结构体说 i2c_client ?咋变成了 i2c_board_info ?platform_device就是对应的啊。这里面肯定有蹊跷,i2c_board_info 最后肯定还得转成成 i2c_client,是的,这既是i2c设备的实例化,从定义的 i2c_board_info 组装成 i2c_client,这个过程将在代码中分析。接下来看一下 platform_device通过 platform_device_register来注册 ,那么 i2c_board_info 设备是通过i2c_register_board_info。

先看下 i2c_board_info 结构体

struct i2c_board_info {
	char		type[I2C_NAME_SIZE];
	unsigned short	flags;
	unsigned short	addr;
	void		*platform_data;
	struct dev_archdata	*archdata;
	struct device_node *of_node;
	int		irq;
};

再看一个结构体,下面会用到

struct i2c_devinfo {
	struct list_head	list;
	int			busnum;
	struct i2c_board_info	board_info;
};

下面再看 i2c_register_board_info 实例

i2c_register_board_info(3, i2c_devs3, ARRAY_SIZE(i2c_devs3));
{
	int status;

	down_write(&__i2c_board_lock);

	/* dynamic bus numbers will be assigned after the last static one */
    /*__i2c_first_dynamic_bus_num总是要比最大的bus号大1,后面会用到*/
	if (busnum >= __i2c_first_dynamic_bus_num)
		__i2c_first_dynamic_bus_num = busnum + 1;

	for (status = 0; len; len--, info++) {
		struct i2c_devinfo	*devinfo;

		devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
		if (!devinfo) {
			pr_debug("i2c-core: can't register boardinfo!\n");
			status = -ENOMEM;
			break;
		}
        /*将i2c_board_info的信息赋值给 devinfo ,然后将devinfo插入 __i2c_board_list ,所有的i2c设备都会插入
        __i2c_board_list,所以一定要记住__i2c_board_list,
        devinfo->busnum  = 3
        devinfo->board_info = &i2c_devs3 */
        
		devinfo->busnum = busnum;
		devinfo->board_info = *info;
		list_add_tail(&devinfo->list, &__i2c_board_list);
	}

	up_write(&__i2c_board_lock);

	return status;
}

上面的知识将在下面的代码中用到,下面接着 分析 s3c24xx_i2c_probe,由于是在分析驱动框架,不是分析BSP,

硬件设备千变万化,看芯片手册写或者调试驱动是一个驱动工程师的基本功,所以对于硬件相关的设置将省略。

下面接着分析经过platform总线匹配后调用的 s3c24xx_i2c_probe

 
static int s3c24xx_i2c_probe(&s3c_device_i2c3)
{
	struct s3c24xx_i2c *i2c;
	struct s3c2410_platform_i2c *pdata;
	struct resource *res;
	int ret;

	......
    
    /*赋值 i2c->adap.name = "s3c2410-i2c",后面会用到*/
	strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
    /*i2c->adap进行复制,又看到了 THIS_MODULE 只有当此驱动编译成module的时候指向此模块
    ,如果没有编译成module则为NULL,绝大多数owner都是 THIS_MODULE */
	i2c->adap.owner   = THIS_MODULE;
    /*i2c->adap.algo,对应实际硬件的通信方法,控制硬件产生读写时序的函数,adapter是对硬件控制器的抽象,
    实际产生硬件通信时序的s3c24xx_i2c_algorithm*/
	i2c->adap.algo    = &s3c24xx_i2c_algorithm;
    /*通信失败重新尝试的次数*/
	i2c->adap.retries = 2;
    /*此adapter支持的硬件类型,公共也就是三种 I2C_CLASS_HWMON、I2C_CLASS_SPD 和 I2C_CLASS_DDC 
    相应的,i2c_client或者i2c_driver肯定也会有相应的class来和 i2c->adap.class 匹配 */
	i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
	......
    
	/* setup info block for the i2c core */

	i2c->adap.algo_data = i2c;
    /*i2c->adap.dev.parent 指向 s3c_device_i2c1 ,
    意味着i2c->adap对应的目录会在/sys/devices/platform/s3c2440-i2c.3下*/
	i2c->adap.dev.parent = &pdev->dev;

	......
    
	/* Note, previous versions of the driver used i2c_add_adapter()
	 * to add the bus at any number. We now pass the bus number via
	 * the platform data, so if unset it will now default to always
	 * being bus 0.
	 */
    /*此adapter所对应的i2c总线号,这里是通过 paltform_device的私有结构体传递,实际还有另一个代表
    /sys/devices/platform/s3c2440-i2c.3
    i2c总线号的,还记得 s3c_device_i2c3.id = 3吗*/
	i2c->adap.nr = pdata->bus_num;
    /*注册adapter*/
	ret = i2c_add_numbered_adapter(&i2c->adap);
        {
        	int	id;
        	int	status;

        	if (adap->nr & ~MAX_ID_MASK)
        		return -EINVAL;
        /*下面涉及idr机制,简单的说idr机制就是将一个整数id和特定指针映射起来,然后可以通过id号
            找到对应的指针*/
        retry:
            /*为idr分配内存,通过idr获取id之前需要先分配内存*/
        	if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
        		return -ENOMEM;

        	mutex_lock(&core_lock);
        	/* "above" here means "above or equal to", sigh;
        	 * we need the "equal to" result to force the result
        	 */
        	 /*分配id号并和adap指针关联,对于i2c adapter分配的id就是bus号,所以下面会有
            id和adap->nr的判断*/
        	status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
        	if (status == 0 && id != adap->nr) {
        		status = -EBUSY;
        		idr_remove(&i2c_adapter_idr, id);
        	}
        	mutex_unlock(&core_lock);
        	if (status == -EAGAIN)
        		goto retry;

        	if (status == 0)
                /*开始注册adapter*/
        		status = i2c_register_adapter(adap);
                        {
                        	int res = 0;

                        	/* Can't register until after driver model init */
                            /*i2c被初始化过才能注册adapter,显然注册 i2c_bus_type 就已经初始化过*/
                        	if (unlikely(WARN_ON(!i2c_bus_type.p))) {
                        		res = -EAGAIN;
                        		goto out_list;
                        	}

                        	/* Sanity checks */
                            /*前面已经初始化 adap->name[0] = "s3c2410-i2c" */
                        	if (unlikely(adap->name[0] == '\0')) {
                        		pr_err("i2c-core: Attempt to register an adapter with "
                        		       "no name!\n");
                        		return -EINVAL;
                        	}
                            /*上面已经初始化过 i2c->adap.algo    = &s3c24xx_i2c_algorithm */
                        	if (unlikely(!adap->algo)) {
                        		pr_err("i2c-core: Attempt to register adapter '%s' with "
                        		       "no algo!\n", adap->name);
                        		return -EINVAL;
                        	}

                        	rt_mutex_init(&adap->bus_lock);
                        	mutex_init(&adap->userspace_clients_lock);
                        	INIT_LIST_HEAD(&adap->userspace_clients);

                        	/* Set default timeout to 1 second if not already set */
                            /*上面没有初始化超时时间,所以默认设置为1s*/
                        	if (adap->timeout == 0)
                        		adap->timeout = HZ;
                            /*赋值 i2c->adap->dev->kobj->name = "i2c-3",意味着将来创建目录的名字为 i2c-3 */
                        	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
                            /*adapter也是一个device,类型为 i2c_adapter_type ,所以指向i2c_bus_type,其本身提供具体的i2c总线*/
                        	adap->dev.bus = &i2c_bus_type;
                        	adap->dev.type = &i2c_adapter_type;
                            /*下面即使device的注册过程*/
                        	res = device_register(&adap->dev);
                                {
                                	device_initialize(dev);
                                    {
                                        /*adapter也是一个device,所以其 kobject 属于 devices_kset,类型为 device_ktype*/
                                    	dev->kobj.kset = devices_kset;
                                    	kobject_init(&dev->kobj, &device_ktype);
                                    	INIT_LIST_HEAD(&dev->dma_pools);
                                    	mutex_init(&dev->mutex);
                                    	lockdep_set_novalidate_class(&dev->mutex);
                                    	spin_lock_init(&dev->devres_lock);
                                    	INIT_LIST_HEAD(&dev->devres_head);
                                    	device_pm_init(dev);
                                    	set_dev_node(dev, -1);
                                    }
                                	return device_add(dev);
                                            {
                                            	struct device *parent = NULL;
                                            	struct class_interface *class_intf;
                                            	int error = -EINVAL;

                                            	dev = get_device(dev);
                                            	if (!dev)
                                            		goto done;

                                            	if (!dev->p) {
                                            		error = device_private_init(dev);
                                                            {
                                                            	dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);
                                                            	if (!dev->p)
                                                            		return -ENOMEM;
                                                            	dev->p->device = dev;
                                                            	klist_init(&dev->p->klist_children, klist_children_get,
                                                            		   klist_children_put);
                                                            	return 0;
                                                            }
                                            		if (error)
                                            			goto done;
                                            	}

                                            	/*
                                            	 * for statically allocated devices, which should all be converted
                                            	 * some day, we need to initialize the name. We prevent reading back
                                            	 * the name, and force the use of dev_name()
                                            	 */
                                            	 /* i2c->adap->dev->init_name未被赋值,也就是为NULL*/
                                            	if (dev->init_name) {
                                            		dev_set_name(dev, "%s", dev->init_name);
                                            		dev->init_name = NULL;
                                            	}
                                                /* i2c->adap->dev->kobj->name = "i2c-3" ,所以此条件不会被满足 */
                                            	if (!dev_name(dev)) {
                                            		error = -EINVAL;
                                            		goto name_error;
                                            	}

                                            	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
                                                /*前面初始化 i2c->adap.dev.parent = &pdev->dev */
                                            	parent = get_device(dev->parent);
                                                 /*device层面的parent已经建立,setup_parent要建立kobject 层面 的parent关系*/
                                            	setup_parent(dev, parent);

                                            	/* use parent numa_node */
                                                /*NUMA架构一般应用于大型多CPU的设备,嵌入式单个多核CPU使用SMP架构*/
                                            	if (parent)
                                            		set_dev_node(dev, dev_to_node(parent));

                                            	/* first, register with generic layer. */
                                            	/* we require the name to be set before, and pass NULL */
                                                /*又看见kobject_add,在sys/devices/platform/s3c2440-i2c.3创建i2c-3目录*/
                                            	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
                                            	if (error)
                                            		goto Error;

                                            	/* notify platform of device entry */
                                                /*暂时不知道具体用途,代码中极少给platform_notify赋值*/
                                            	if (platform_notify)
                                            		platform_notify(dev);
                                                /*在sys/devices/platform/s3c2440-i2c.1/i2c-3创建uevent文件*/
                                            	error = device_create_file(dev, &uevent_attr);
                                            	if (error)
                                            		goto attrError;
                                                /*此时 i2c->adap->dev 还没有设备号,所以执行不到,等到 device_creat 的时候再详细分析*/
                                            	if (MAJOR(dev->devt)) {
                                            		error = device_create_file(dev, &devt_attr);
                                            		if (error)
                                            			goto ueventattrError;

                                            		error = device_create_sys_dev_entry(dev);
                                            		if (error)
                                            			goto devtattrError;

                                            		devtmpfs_create_node(dev);
                                            	}
                                                
                                            	error = device_add_class_symlinks(dev);
                                                        {
                                                        	int error;
                                                            /*i2c->adap->dev->class未赋值,也就是为NULL,在此返回*/
                                                        	if (!dev->class)
                                                        		return 0;
                                                          
                                                        	error = sysfs_create_link(&dev->kobj,
                                                        				  &dev->class->p->subsys.kobj,
                                                        				  "subsystem");
                                                        	if (error)
                                                        		goto out;

                                                        	if (dev->parent && device_is_not_partition(dev)) {
                                                        		error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
                                                        					  "device");
                                                        		if (error)
                                                        			goto out_subsys;
                                                        	}

                                                            #ifdef CONFIG_BLOCK
                                                        	/* /sys/block has directories and does not need symlinks */
                                                        	if (sysfs_deprecated && dev->class == &block_class)
                                                        		return 0;
                                                            #endif

                                                        	/* link in the class directory pointing to the device */
                                                        	error = sysfs_create_link(&dev->class->p->subsys.kobj,
                                                        				  &dev->kobj, dev_name(dev));
                                                        	if (error)
                                                        		goto out_device;

                                                        	return 0;

                                                        out_device:
                                                        	sysfs_remove_link(&dev->kobj, "device");

                                                        out_subsys:
                                                        	sysfs_remove_link(&dev->kobj, "subsystem");
                                                        out:
                                                        	return error;
                                                        }
                                            	if (error)
                                            		goto SymlinkError;
                                                /*为 i2c->adap 创建属性文件*/
                                            	error = device_add_attrs(dev);
                                                        {
                                                        	struct class *class = dev->class;
                                                        	const struct device_type *type = dev->type;
                                                        	int error;
                                                            /*i2c->adap->dev->class未赋值,也就是为NULL,在此返回*/
                                                        	if (class) {
                                                        		error = device_add_attributes(dev, class->dev_attrs);
                                                                        {
                                                                        	int error = 0;
                                                                        	int i;

                                                                        	if (attrs) {
                                                                        		for (i = 0; attr_name(attrs[i]); i++) {
                                                                        			error = device_create_file(dev, &attrs[i]);
                                                                        			if (error)
                                                                        				break;
                                                                        		}
                                                                        		if (error)
                                                                        			while (--i >= 0)
                                                                        				device_remove_file(dev, &attrs[i]);
                                                                        	}
                                                                        	return error;
                                                                        }
                                                        		if (error)
                                                        			return error;
                                                        		error = device_add_bin_attributes(dev, class->dev_bin_attrs);
                                                        		if (error)
                                                        			goto err_remove_class_attrs;
                                                        	}
                                                            /*前面赋值 i2c->adap->dev->type = &i2c_adapter_type,其中
                                                            i2c_adapter_type->groups = i2c_adapter_attr_groups,
                                                            i2c_adapter_attr_groups = &i2c_adapter_attr_group,
                                                            i2c_adapter_attr_group->attr = i2c_adapter_attrs,
                                                            i2c_adapter_attrs[] = {
                                                            	&dev_attr_name.attr,
                                                            	&dev_attr_new_device.attr,
                                                            	&dev_attr_delete_device.attr,
                                                            	NULL
                                                            };

                                                            所以最终将在/sys/devices/platform/s3c2440-i2c.3/i2c-3下创建 name、new_device和delete_device的属性文件*/
                                                        	if (type) {
                                                        		error = device_add_groups(dev, type->groups);
                                                                        {
                                                                        	int error = 0;
                                                                        	int i;

                                                                        	if (groups) {
                                                                        		for (i = 0; groups[i]; i++) {
                                                                        			error = sysfs_create_group(&dev->kobj, groups[i]);
                                                                        			if (error) {
                                                                        				while (--i >= 0)
                                                                        					sysfs_remove_group(&dev->kobj,
                                                                        							   groups[i]);
                                                                        				break;
                                                                        			}
                                                                        		}
                                                                        	}
                                                                        	return error;
                                                                        }
                                                        		if (error)
                                                        			goto err_remove_class_bin_attrs;
                                                        	}
                                                            /*i2c->adap->dev->groups也是未初始化的*/
                                                        	error = device_add_groups(dev, dev->groups);
                                                        	if (error)
                                                        		goto err_remove_type_groups;

                                                        	return 0;

                                                         err_remove_type_groups:
                                                        	if (type)
                                                        		device_remove_groups(dev, type->groups);
                                                         err_remove_class_bin_attrs:
                                                        	if (class)
                                                        		device_remove_bin_attributes(dev, class->dev_bin_attrs);
                                                         err_remove_class_attrs:
                                                        	if (class)
                                                        		device_remove_attributes(dev, class->dev_attrs);

                                                        	return error;
                                                        }
                                            	if (error)
                                            		goto AttrsError; 
                                                /*i2c->adap 添加到 i2c_bus_type*/
                                            	error = bus_add_device(dev);
                                                        {
                                                        	struct bus_type *bus = bus_get(dev->bus);
                                                        	int error = 0;

                                                        	if (bus) {
                                                        		pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
                                                        		error = device_add_attrs(bus, dev);
                                                                        {
                                                                        	int error = 0;
                                                                        	int i;
                                                                            /*由于i2c_bus_type->dev_attrs = NULL,
                                                                            所以不会创建任何属性文件*/
                                                                        	if (!bus->dev_attrs)
                                                                        		return 0;

                                                                        	for (i = 0; attr_name(bus->dev_attrs[i]); i++) {
                                                                        		error = device_create_file(dev, &bus->dev_attrs[i]);
                                                                        		if (error) {
                                                                        			while (--i >= 0)
                                                                        				device_remove_file(dev, &bus->dev_attrs[i]);
                                                                        			break;
                                                                        		}
                                                                        	}
                                                                        	return error;
                                                                        }
                                                        		if (error)
                                                        			goto out_put;
                                                                /*在注册i2c_bus_type的时候
                                                                 priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj);
                                                                 下面就是要在/sys/bus/i2c/devices创建指向 /sys/devices/platform/s3c2440-i2c.3/i2c-3
                                                                 的链接,名字也为i2c-3*/
                                                        		error = sysfs_create_link(&bus->p->devices_kset->kobj,
                                                        						&dev->kobj, dev_name(dev));
                                                        		if (error)
                                                        			goto out_id;
                                                                /*在/sys/devices/platform/s3c2440-i2c.3/i2c-3创建指向/sys/bus/i2c/的链接,
                                                                名字为subsystem*/
                                                        		error = sysfs_create_link(&dev->kobj,
                                                        				&dev->bus->p->subsys.kobj, "subsystem");
                                                        		if (error)
                                                        			goto out_subsys;
                                                                /*将 i2c->adap 加入到i2c_bus_type->p->klist_devices链表中*/
                                                        		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
                                                        	}
                                                        	return 0;

                                                        out_subsys:
                                                        	sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
                                                        out_id:
                                                        	device_remove_attrs(bus, dev);
                                                        out_put:
                                                        	bus_put(dev->bus);
                                                        	return error;
                                                        }
                                            	if (error)
                                            		goto BusError;
                                                /*电源管理相关,后面分析的电源管理机制的时候再详细分析*/
                                            	error = dpm_sysfs_add(dev);
                                            	if (error)
                                            		goto DPMError;
                                            	device_pm_add(dev);

                                            	/* Notify clients of device addition.  This call must come
                                            	 * after dpm_sysf_add() and before kobject_uevent().
                                            	 */
                                            	if (dev->bus)
                                            		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                            					     BUS_NOTIFY_ADD_DEVICE, dev);

                                            	kobject_uevent(&dev->kobj, KOBJ_ADD);
                                            	bus_probe_device(dev);
                                                {
                                                	struct bus_type *bus = dev->bus;
                                                	int ret;
                                                    /* i2c_bus_type 注册的时候初始化 i2c_bus_type->p->drivers_autoprobe = 1
                                                    /sys/bus/i2c 下的drivers_autoprobe,drivers_autoprobe被置1
                                                    则自动进行驱动匹配*/
                                                	if (bus && bus->p->drivers_autoprobe) {
                                                		ret = device_attach(dev);
                                                            {
                                                            	int ret = 0;

                                                            	device_lock(dev);
                                                                /*现在只注册 i2c->adap  , i2c->adap  ->dev->driver = NULL*/
                                                            	if (dev->driver) {
                                                            		if (klist_node_attached(&dev->p->knode_driver)) {
                                                            			ret = 1;
                                                            			goto out_unlock;
                                                            		}
                                                            		ret = device_bind_driver(dev);
                                                                        {
                                                                        	int ret;

                                                                        	ret = driver_sysfs_add(dev);
                                                                        	if (!ret)
                                                                        		driver_bound(dev);
                                                                        	return ret;
                                                                        }
                                                            		if (ret == 0)
                                                            			ret = 1;
                                                            		else {
                                                            			dev->driver = NULL;
                                                            			ret = 0;
                                                            		}
                                                            	} 
                                                                else {
                                                            		pm_runtime_get_noresume(dev);
                                                                    /*通过 __device_attach 函数逐个匹配 i2c_bus_type->p->klist_drivers成员
                                                                     i2c->adap 只是作为一个device注册,没有所谓的驱动,类似 platform_bus 一样,*/
                                                            		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);

                                                            		pm_runtime_put_sync(dev);
                                                            	}
                                                            out_unlock:
                                                            	device_unlock(dev);
                                                            	return ret;
                                                            }
                                                		WARN_ON(ret < 0);
                                                	}
                                                }
                                                /*将 i2c->adap 加入 s3c_device_i2c3 的子设备链表*/
                                            	if (parent)
                                            		klist_add_tail(&dev->p->knode_parent,
                                            			       &parent->p->klist_children);
                                                
                                                /*将 i2c->adap 加入到class的设备链表中,目前 i2c->adap->dev->class = NULL */
                                            	if (dev->class) {
                                            		mutex_lock(&dev->class->p->class_mutex);
                                            		/* tie the class to the device */
                                            		klist_add_tail(&dev->knode_class,
                                            			       &dev->class->p->klist_devices);

                                            		/* notify any interfaces that the device is here */
                                                    /*暂时还没弄清class_interface的具体用途*/
                                            		list_for_each_entry(class_intf,
                                            				    &dev->class->p->class_interfaces, node)
                                            			if (class_intf->add_dev)
                                            				class_intf->add_dev(dev, class_intf);
                                            		mutex_unlock(&dev->class->p->class_mutex);
                                            	}
                                            done:
                                            	put_device(dev);
                                            	return error;
                                             DPMError:
                                            	bus_remove_device(dev);
                                             BusError:
                                            	device_remove_attrs(dev);
                                             AttrsError:
                                            	device_remove_class_symlinks(dev);
                                             SymlinkError:
                                            	if (MAJOR(dev->devt))
                                            		devtmpfs_delete_node(dev);
                                            	if (MAJOR(dev->devt))
                                            		device_remove_sys_dev_entry(dev);
                                             devtattrError:
                                            	if (MAJOR(dev->devt))
                                            		device_remove_file(dev, &devt_attr);
                                             ueventattrError:
                                            	device_remove_file(dev, &uevent_attr);
                                             attrError:
                                            	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
                                            	kobject_del(&dev->kobj);
                                             Error:
                                            	cleanup_device_parent(dev);
                                            	if (parent)
                                            		put_device(parent);
                                            name_error:
                                            	kfree(dev->p);
                                            	dev->p = NULL;
                                            	goto done;
                                            }
                                }
                        	if (res)
                        		goto out_list;

                        	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

                #ifdef CONFIG_I2C_COMPAT
                            /* i2c_adapter_compat_class = class_compat_register("i2c-adapter"); 
                                在 sys/calss/i2c-adapter 目录下创建链接*/
                        	res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,adap->dev.parent);
                                {
                                	int error;
                                    /* 在 sys/calss/i2c-adapter 目录下创建链接 指向 sys/devices/platform/s3c2440-i2c.3/i2c-3 的链接,名字仍为 i2c-3 */
                                	error = sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev));
                                            {
                                            	return sysfs_do_create_link(kobj, target, name, 1);
                                            }
                                	if (error)
                                		return error;

                                	/*
                                	 * Optionally add a "device" link (typically to the parent), as a
                                	 * class device would have one and we want to provide as much
                                	 * backwards compatibility as possible.
                                	 */
                                	if (device_link) {
                                        /*在 sys/devices/platform/s3c2440-i2c.3/i2c-3 目录下创建指向 sys/devices/platform/s3c2440-i2c.3 ,名字为device */
                                		error = sysfs_create_link(&dev->kobj, &device_link->kobj,
                                					  "device");
                                		if (error)
                                			sysfs_remove_link(cls->kobj, dev_name(dev));
                                	}

                                	return error;
                                }
                        	if (res)
                        		dev_warn(&adap->dev,
                        			 "Failed to create compatibility class link\n");
                #endif

                        	/* create pre-declared device nodes */
                            /* 重点来了, i2c_client 的实例化,由 i2c_board_info 生成 i2c_client */
                        	if (adap->nr < __i2c_first_dynamic_bus_num)
                        		i2c_scan_static_board_info(adap);
                                {
                                	struct i2c_devinfo	*devinfo;

                                	down_read(&__i2c_board_lock);
                                    /*还记得前面 i2c_register_board_info 把所有的 i2c_board_info 都注册到 __i2c_board_list ,
                                    下面就是遍历 __i2c_board_list ,查找 属于 i2c->adap 的设备 */
                                	list_for_each_entry(devinfo, &__i2c_board_list, list) {
                                		if (devinfo->busnum == adapter->nr
                                				&& !i2c_new_device(adapter,&devinfo->board_info))
                                				    {
                                				        /*每查到一个 属于 i2c->adap 的设备 ,声明一个 i2c_client ,这里就以前面注册的 i2c_devs3 为例*/
                                                    	struct i2c_client	*client;
                                                    	int			status;

                                                    	client = kzalloc(sizeof *client, GFP_KERNEL);
                                                    	if (!client)
                                                    		return NULL;
                                                        /*初始化该 i2c_client 所属的 adap */
                                                    	client->adapter = adap;
                                                        /* client->dev.platform_data = i2c_devs3->platform_data = &mma7660_pdata*/
                                                    	client->dev.platform_data = info->platform_data;

                                                    	if (info->archdata)
                                                    		client->dev.archdata = *info->archdata;
                                                       /*i2c_devs3->flags 未初始化,也就是为      0   */ 
                                                    	client->flags = info->flags;
                                                        /*client->addr = 0x4c*/
                                                    	client->addr = info->addr;
                                                        
                                                    	client->irq = info->irq;
                                                        /* client->name = "mma7660" */
                                                    	strlcpy(client->name, info->type, sizeof(client->name));

                                                    	/* Check for address validity */
                                                        /*检查 client->addr 的合法性 */
                                                    	status = i2c_check_client_addr_validity(client);
                                                    	if (status) {
                                                    		dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
                                                    			client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
                                                    		goto out_err_silent;
                                                    	}

                                                    	/* Check for address business */
                                                        /*检查 i2c->adap 是否注册过 相同 address 的 i2c 期间,一个 i2c 总线上的器件address 必须唯一*/
                                                    	status = i2c_check_addr_busy(adap, client->addr);
                                                                {
                                                                	struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
                                                                                                {
                                                                                                    /*前面初始化 i2c->adap->dev->parent = s3c_device_i2c3->dev,所以 parent = NULL */
                                                                                                	struct device *parent = adapter->dev.parent;

                                                                                                	if (parent != NULL && parent->type == &i2c_adapter_type)
                                                                                                		return to_i2c_adapter(parent);
                                                                                                	else
                                                                                                		return NULL;
                                                                                                }
                                                                	int result = 0;
                                                                    /* parent = NULL */
                                                                	if (parent)
                                                                		result = i2c_check_mux_parents(parent, addr);
                                                                                {
                                                                                	struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
                                                                                	int result;
                                                                                    
                                                                                	result = device_for_each_child(&adapter->dev, &addr,
                                                                                					__i2c_check_addr_busy);

                                                                                	if (!result && parent)
                                                                                		result = i2c_check_mux_parents(parent, addr);

                                                                                	return result;
                                                                                }

                                                                	if (!result)
                                                                		result = device_for_each_child(&adapter->dev, &addr,i2c_check_mux_children);
                                                                                {
                                                                                	struct klist_iter i;
                                                                                	struct device *child;
                                                                                	int error = 0;

                                                                                	if (!parent->p)
                                                                                		return 0;

                                                                                	klist_iter_init(&parent->p->klist_children, &i);
                                                                                	while ((child = next_device(&i)) && !error)
                                                                                		error = fn(child, data);   //fn = i2c_check_mux_children
                                                                                		        {
                                                                                                	int result;

                                                                                                	if (dev->type == &i2c_adapter_type)
                                                                                                		result = device_for_each_child(dev, addrp,
                                                                                                						i2c_check_mux_children);
                                                                                                	else
                                                                                                        /*此地址已经被注册过,则返回 -EBUSY */
                                                                                                		result = __i2c_check_addr_busy(dev, addrp);
                                                                                                                {
                                                                                                                	struct i2c_client	*client = i2c_verify_client(dev);
                                                                                                                	int			addr = *(int *)addrp;

                                                                                                                	if (client && client->addr == addr)
                                                                                                                		return -EBUSY;
                                                                                                                	return 0;
                                                                                                                }

                                                                                                	return result;
                                                                                                }
                                                                                	klist_iter_exit(&i);
                                                                                	return error;
                                                                                }

                                                                	return result;
                                                                }
                                                    	if (status)
                                                    		goto out_err;

                                                    	client->dev.parent = &client->adapter->dev;
                                                    	client->dev.bus = &i2c_bus_type;
                                                    	client->dev.type = &i2c_client_type;
                                                    	client->dev.of_node = info->of_node;
                                                        /* client->dev->kobj = "3-004c" */
                                                    	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
                                                    		     client->addr);
                                                        /*将 client 注册到 /sys/devices/platform/s3c2440-i2c.3/i2c-3 目录下,并创建 3-004c 目录,同事创建
                                                                 uevent、name和 new_device 属性文件和一些链接文件等等 */
                                                    	status = device_register(&client->dev);
                                                    	if (status)
                                                    		goto out_err;

                                                    	dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
                                                    		client->name, dev_name(&client->dev));

                                                    	return client;

                                                    out_err:
                                                    	dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
                                                    		"(%d)\n", client->name, client->addr, status);
                                                    out_err_silent:
                                                    	kfree(client);
                                                    	return NULL;
                                                    }  
                                			dev_err(&adapter->dev,
                                				"Can't create device at 0x%02x\n",
                                				devinfo->board_info.addr);
                                	}
                                	up_read(&__i2c_board_lock);
                                }

                        	/* Notify drivers */
                        	mutex_lock(&core_lock);
                            /*遍历已经注册 i2c_driver 和   i2c->adap    ,然后探测 i2c->adap 上i2c_driver支持的地址,
                            如果已经注册则跳过,未注册但有该 i2c 设备,则进行实例化*/  
                        	bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
                            {
                            	struct klist_iter i;
                            	struct device_driver *drv;
                            	int error = 0;

                            	if (!bus)
                            		return -EINVAL;
                                   /*遍历 i2c_bus_type->p->klist_drivers ,然后调用 __process_new_adapter*/
                            	klist_iter_init_node(&bus->p->klist_drivers, &i,
                            			     start ? &start->p->knode_bus : NULL);
                            	while ((drv = next_driver(&i)) && !error)
                            		error = fn(drv, data);    //fn = __process_new_adapter
                            		{
                                    	return i2c_do_add_adapter(to_i2c_driver(d), data);
                                                {
                                                	/* Detect supported devices on that bus, and instantiate them */
                                                	i2c_detect(adap, driver);
                                                    {   /* driver 所支持i2c设备的 address 列表*/
                                                    	const unsigned short *address_list;
                                                        /* 声明一个临时的 i2c_client */
                                                    	struct i2c_client *temp_client;
                                                    	int i, err = 0;
                                                    	int adap_id = i2c_adapter_id(adapter);
                                                        /*如果 */
                                                    	address_list = driver->address_list;
                                                    	if (!driver->detect || !address_list)
                                                    		return 0;

                                                    	/* Stop here if the classes do not match */
                                                    	if (!(adapter->class & driver->class))
                                                    		return 0;

                                                    	/* Set up a temporary client to help detect callback */
                                                    	temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
                                                    	if (!temp_client)
                                                    		return -ENOMEM;
                                                    	temp_client->adapter = adapter;

                                                    	for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
                                                    		dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
                                                    			"addr 0x%02x\n", adap_id, address_list[i]);
                                                    		temp_client->addr = address_list[i];
                                                    		err = i2c_detect_address(temp_client, driver);
                                                                {
                                                                	struct i2c_board_info info;
                                                                	struct i2c_adapter *adapter = temp_client->adapter;
                                                                	int addr = temp_client->addr;
                                                                	int err;

                                                                	/* Make sure the address is valid */
                                                                	err = i2c_check_addr_validity(addr);
                                                                        {
                                                                        	/*
                                                                        	 * Reserved addresses per I2C specification:
                                                                        	 *  0x00       General call address / START byte
                                                                        	 *  0x01       CBUS address
                                                                        	 *  0x02       Reserved for different bus format
                                                                        	 *  0x03       Reserved for future purposes
                                                                        	 *  0x04-0x07  Hs-mode master code
                                                                        	 *  0x78-0x7b  10-bit slave addressing
                                                                        	 *  0x7c-0x7f  Reserved for future purposes
                                                                        	 */
                                                                        	if (addr < 0x08 || addr > 0x77)
                                                                        		return -EINVAL;
                                                                        	return 0;
                                                                        }
                                                                	if (err) {
                                                                		dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
                                                                			 addr);
                                                                		return err;
                                                                	}

                                                                	/* Skip if already in use */
                                                                	if (i2c_check_addr_busy(adapter, addr))
                                                                        {
                                                                            /*前面初始化 i2c->adap->dev->parent = s3c_device_i2c3->dev,所以 parent = NULL */
                                                                        	struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
                                                                        	int result = 0;

                                                                        	if (parent)
                                                                        		result = i2c_check_mux_parents(parent, addr);

                                                                        	if (!result)
                                                                        		result = device_for_each_child(&adapter->dev, &addr,i2c_check_mux_children);
                                                                                        {
                                                                                        	struct klist_iter i;
                                                                                        	struct device *child;
                                                                                        	int error = 0;

                                                                                        	if (!parent->p)
                                                                                        		return 0;
                                                                                            /*遍历 查询 i2c->adap 下的设备,也就是 i2c  _client */
                                                                                        	klist_iter_init(&parent, &i);
                                                                                        	while ((child = next_device(&i)) && !error)
                                                                                         		error = fn(child, data);    //fn = i2c_check_mux_children
                                                                                         		        {
                                                                                                        	int result;
                                                                                                        	if (dev->type == &i2c_adapter_type)
                                                                                                        		result = device_for_each_child(dev, addrp,i2c_check_mux_children);
                                                                                                                        
                                                                                                        	else
                                                                                                        		result = __i2c_check_addr_busy(dev, addrp);
                                                                                                                        {
                                                                                                                        	struct i2c_client	*client = i2c_verify_client(dev);
                                                                                                                        	int			addr = *(int *)addrp;

                                                                                                                        	if (client && client->addr == addr)
                                                                                                                        		return -EBUSY;
                                                                                                                        	return 0;
                                                                                                                        }

                                                                                                        	return result;
                                                                                                        }
                                                                                        	klist_iter_exit(&i);
                                                                                        	return error;
                                                                                        }
                                                                        	return result;
                                                                        }
                                                                		return 0;

                                                                	/* Make sure there is something at this address */
                                                                	if (!i2c_default_probe(adapter, addr))
                                                                        {
                                                                        	int err;
                                                                        	union i2c_smbus_data dummy;

                                                                    #ifdef CONFIG_X86
                                                                        	if (addr == 0x73 && (adap->class & I2C_CLASS_HWMON)
                                                                        	 && i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA))
                                                                        		err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
                                                                        				     I2C_SMBUS_BYTE_DATA, &dummy);
                                                                        	else
                                                                    #endif
                                                                        	if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50)
                                                                        	 && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK))
                                                                        		err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0,
                                                                        				     I2C_SMBUS_QUICK, NULL);
                                                                        	else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE))
                                                                        		err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
                                                                        				     I2C_SMBUS_BYTE, &dummy);
                                                                        	else {
                                                                        		dev_warn(&adap->dev, "No suitable probing method supported\n");
                                                                        		err = -EOPNOTSUPP;
                                                                        	}

                                                                        	return err >= 0;
                                                                        }
                                                                		return 0;

                                                                	/* Finally call the custom detection function */
                                                                	memset(&info, 0, sizeof(struct i2c_board_info));
                                                                	info.addr = addr;
                                                                	err = driver->detect(temp_client, &info);
                                                                	if (err) {
                                                                		/* -ENODEV is returned if the detection fails. We catch it
                                                                		   here as this isn't an error. */
                                                                		return err == -ENODEV ? 0 : err;
                                                                	}

                                                                	/* Consistency check */
                                                                	if (info.type[0] == '\0') {
                                                                		dev_err(&adapter->dev, "%s detection function provided "
                                                                			"no name for 0x%x\n", driver->driver.name,
                                                                			addr);
                                                                	} else {
                                                                		struct i2c_client *client;

                                                                		/* Detection succeeded, instantiate the device */
                                                                		dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
                                                                			info.type, info.addr);
                                                                        /*驱动探测到 adapter 上有该驱动支持的设备但又未 注册 ,则给其实例化*/
                                                                		client = i2c_new_device(adapter, &info);
                                                                		if (client)
                                                                			list_add_tail(&client->detected, &driver->clients);
                                                                		else
                                                                			dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
                                                                				info.type, info.addr);
                                                                	}
                                                                	return 0;
                                                                }
                                                    		if (unlikely(err))
                                                    			break;
                                                    	}

                                                    	kfree(temp_client);
                                                    	return err;
                                                    }

                                                	/* Let legacy drivers scan this bus for matching devices */
                                                	if (driver->attach_adapter) {
                                                		dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
                                                			 driver->driver.name);
                                                		dev_warn(&adap->dev, "Please use another way to instantiate "
                                                			 "your i2c_client\n");
                                                		/* We ignore the return code; if it fails, too bad */
                                                		driver->attach_adapter(adap);
                                                	}
                                                	return 0;
                                                }
                                    }
                            	klist_iter_exit(&i);
                            	return error;
                            }
                        	mutex_unlock(&core_lock);

                        	return 0;

                        out_list:
                        	mutex_lock(&core_lock);
                        	idr_remove(&i2c_adapter_idr, adap->nr);
                        	mutex_unlock(&core_lock);
                        	return res;
                        }
        	return status;
        }
    
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to add bus to i2c core\n");
		goto err_cpufreq;
	}

	platform_set_drvdata(pdev, i2c);

	dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
	clk_disable(i2c->clk);
	return 0;

 err_cpufreq:
	s3c24xx_i2c_deregister_cpufreq(i2c);

 err_irq:
	free_irq(i2c->irq, i2c);

 err_iomap:
	iounmap(i2c->regs);

 err_ioarea:
	release_resource(i2c->ioarea);
	kfree(i2c->ioarea);

 err_clk:
	clk_disable(i2c->clk);
	clk_put(i2c->clk);

 err_noclk:
	kfree(i2c);
	return ret;
}

上面的主要工作,就是初始化 i2c_adapter 的硬件,并且注册 i2c_adapter ,注册后将会 遍历 __i2c_board_list 查找挂接在此  i2c_adapter 的 i2c_board_info ,然后实例化 成  i2c_client ,然后 遍历 已经注册的 i2c_driver ,查找此 i2c_adapter  是否有 已经注册的 i2c_driver 支持的 但又未 注册的 i2c 器件,查到后实例化成 i2c_client 并注册。

整个过程下来,仍然会生成一些 属性文件 和一些链接文件,下面关注 new_device 和 delete_device 两个属性文件。

static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);

展开

struct device_attribute dev_attr_new_device = { 
	.attr = {
        .name = "new_device", 
        .mode = S_IWUSR 
    },	
	.show	= NULL,					
	.store	= i2c_sysfs_new_device,					
}

看store函数

static ssize_t i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
{
	struct i2c_adapter *adap = to_i2c_adapter(dev);
	struct i2c_board_info info;
	struct i2c_client *client;
	char *blank, end;
	int res;

	memset(&info, 0, sizeof(struct i2c_board_info));

	blank = strchr(buf, ' ');
	if (!blank) {
		dev_err(dev, "%s: Missing parameters\n", "new_device");
		return -EINVAL;
	}
	if (blank - buf > I2C_NAME_SIZE - 1) {
		dev_err(dev, "%s: Invalid device name\n", "new_device");
		return -EINVAL;
	}
	memcpy(info.type, buf, blank - buf);

	/* Parse remaining parameters, reject extra parameters */
	res = sscanf(++blank, "%hi%c", &info.addr, &end);
	if (res < 1) {
		dev_err(dev, "%s: Can't parse I2C address\n", "new_device");
		return -EINVAL;
	}
	if (res > 1  && end != '\n') {
		dev_err(dev, "%s: Extra parameters\n", "new_device");
		return -EINVAL;
	}

	client = i2c_new_device(adap, &info);
	if (!client)
		return -EINVAL;

	/* Keep track of the added device */
	mutex_lock(&adap->userspace_clients_lock);
	list_add_tail(&client->detected, &adap->userspace_clients);
	mutex_unlock(&adap->userspace_clients_lock);
	dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
		 info.type, info.addr);

	return count;
}

new_device 文件就是为用户空间提供 一种添加 i2c 设备的接口,在不管 系统没有注册 此设备的时候 ,可以尝试通过 new_device 写入 i2c 期间的名字 添加该设备。

static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device);

展开

struct device_attribute dev_attr_delete_device = { 
	.attr = {
        .name = "delete_device", 
        .mode = S_IWUSR 
    },	
	.show	= NULL,					
	.store	= i2c_sysfs_delete_device,					
}

查看 store 函数

static ssize_t i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
{
	struct i2c_adapter *adap = to_i2c_adapter(dev);
	struct i2c_client *client, *next;
	unsigned short addr;
	char end;
	int res;

	/* Parse parameters, reject extra parameters */
	res = sscanf(buf, "%hi%c", &addr, &end);
	if (res < 1) {
		dev_err(dev, "%s: Can't parse I2C address\n", "delete_device");
		return -EINVAL;
	}
	if (res > 1  && end != '\n') {
		dev_err(dev, "%s: Extra parameters\n", "delete_device");
		return -EINVAL;
	}

	/* Make sure the device was added through sysfs */
	res = -ENOENT;
	mutex_lock(&adap->userspace_clients_lock);
	list_for_each_entry_safe(client, next, &adap->userspace_clients,
				 detected) {
		if (client->addr == addr) {
			dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
				 "delete_device", client->name, client->addr);

			list_del(&client->detected);
			i2c_unregister_device(client);
			res = count;
			break;
		}
	}
	mutex_unlock(&adap->userspace_clients_lock);

	if (res < 0)
		dev_err(dev, "%s: Can't find device in list\n",
			"delete_device");
	return res;
}

二、i2c_driver的注册

    上面的代码已经 实例化 i2c_client 并且注册,下面就该注册相应的 i2c_driver ,然后通过 i2c_bus_type 的 match 调用 到

i2c_driver->probe 即 mma7660_probe 。

struct i2c_driver {
	unsigned int class;

	/* Notifies the driver that a new bus has appeared or is about to be
	 * removed. You should avoid using this, it will be removed in a
	 * near future.
	 */
	int (*attach_adapter)(struct i2c_adapter *) __deprecated;
	int (*detach_adapter)(struct i2c_adapter *) __deprecated;

	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
	int (*remove)(struct i2c_client *);

	/* driver model interfaces that don't relate to enumeration  */
	void (*shutdown)(struct i2c_client *);
	int (*suspend)(struct i2c_client *, pm_message_t mesg);
	int (*resume)(struct i2c_client *);

	/* Alert callback, for example for the SMBus alert protocol.
	 * The format and meaning of the data value depends on the protocol.
	 * For the SMBus alert protocol, there is a single bit of data passed
	 * as the alert response's low bit ("event flag").
	 */
	void (*alert)(struct i2c_client *, unsigned int data);

	/* a ioctl like command that can be used to perform specific functions
	 * with the device.
	 */
	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

	struct device_driver driver;
	const struct i2c_device_id *id_table;

	/* Device detection callback for automatic device creation */
	int (*detect)(struct i2c_client *, struct i2c_board_info *);
	const unsigned short *address_list;
	struct list_head clients;
};

 

unsigned int class;

此driver支持的i2c设备种类,用来和i2c_adapter中的class做对比,共三种类型I2C_CLASS_HWMON、I2C_CLASS_SPD 和 I2C_CLASS_DDC 

int (*attach_adapter)(struct i2c_adapter *) __deprecated;

i2c 总线增加设备时的回调函数,已经弃用

int (*detach_adapter)(struct i2c_adapter *) __deprecated;

i2c 总线移除设备时的回调函数,已经弃用

 

int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
void (*alert)(struct i2c_client *, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;

 

const struct i2c_device_id *id_table;

此driver兼容的设备列表,i2c_device_match 时会调用

int (*detect)(struct i2c_client *, struct i2c_board_info *);

发现设备时的回调函数,在代码中发现 未注册 设备时会调用

const unsigned short *address_list;

此驱动支持的地址列表,注册 i2c_adapter 或 i2c_driver 时会调用此 列表判断

 i2c_adapter 是否挂有此驱动兼容的设备

struct list_head clients;

遍历驱动发现的i2c设备将挂入此链表中,和i2c_client的detected对应

上面以 mma7660 为例实例化的 i2c_client,下面还以 mma7660 为例分析 i2c_driver 的注册

i2c_mma7660_driver的定义

static struct i2c_driver i2c_mma7660_driver = {
	.driver		= {
		.name	= MMA7660_NAME,
	},

	.probe		= mma7660_probe,
	.remove		= __devexit_p(mma7660_remove),
	.suspend	= mma7660_suspend,
	.resume		= mma7660_resume,
	.id_table	= mma7660_ids,
};

其中的 id_table 为

static const struct i2c_device_id mma7660_ids[] = {
	{ "mma7660", 0 },
	{ },
};

下面就看  i2c_mma7660_driver 的注册过程。

static int __init init_mma7660(void)
{
	int ret;

	ret = i2c_add_driver(&i2c_mma7660_driver);
        {
        	return i2c_register_driver(THIS_MODULE, driver);
                    {
                    	int res;

                    	/* Can't register until after driver model init */
                    	if (unlikely(WARN_ON(!i2c_bus_type.p)))
                    		return -EAGAIN;

                    	/* add the driver to the list of i2c drivers in the driver core */
                    	driver->driver.owner = owner;
                    	driver->driver.bus = &i2c_bus_type;

                    	/* When registration returns, the driver core
                    	 * will have called probe() for all matching-but-unbound devices.
                    	 */
                    	res = driver_register(&driver->driver);
                            {
                            	int ret;
                            	struct device_driver *other;

                            	BUG_ON(!drv->bus->p);
                                /* i2c_bus_type->probe = i2c_device_probe
                                i2c_bus_type->remove = i2c_device_remove
                                i2c_bus_type->shutdown = i2c_device_shutdown
                                i2c_mma7660_driver->driver->probe = NULL
                                i2c_mma7660_driver->driver->remove = NULL
                                i2c_mma7660_driver->driver->shutdown = NULL*/
                            	if ((drv->bus->probe && drv->probe) ||
                            	    (drv->bus->remove && drv->remove) ||
                            	    (drv->bus->shutdown && drv->shutdown))
                            		printk(KERN_WARNING "Driver '%s' needs updating - please use "
                            			"bus_type methods\n", drv->name);
                                /*通过name查找是否已经注册过此driver*/
                            	other = driver_find(drv->name, drv->bus);
                                        {
                                        	struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
                                        	struct driver_private *priv;

                                        	if (k) {
                                        		priv = to_driver(k);
                                        		return priv->driver;
                                        	}
                                        	return NULL;
                                        }
                            	if (other) {
                            		put_driver(other);
                            		printk(KERN_ERR "Error: Driver '%s' is already registered, "
                            			"aborting...\n", drv->name);
                            		return -EBUSY;
                            	}

                            	ret = bus_add_driver(drv);
                                    {
                                    	struct bus_type *bus;
                                    	struct driver_private *priv;
                                    	int error = 0;
                                /*前面初始化 i2c_mma7660_driver->driver.bus = &i2c_bus_type*/
                                    	bus = bus_get(drv->bus);
                                    	if (!bus)
                                    		return -EINVAL;

                                    	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

                                        /*为 i2c_mma7660_driver->driver申请私有结构体*/

                                    	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
                                    	if (!priv) {
                                    		error = -ENOMEM;
                                    		goto out_put_bus;
                                    	}
                                    	klist_init(&priv->klist_devices, NULL, NULL);
                                    	priv->driver = drv;
                                    	drv->p = priv;
                                        /*i2c_mma7660_driver->driver->priv->kobj.kset = i2c_bus_type->p->drivers_kset
                                        还记得sys/bus/i2c 下的drivers目录吗
                                        priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);*/
                                    	priv->kobj.kset = bus->p->drivers_kset;
                                        /*在sys/bus/i2c/drivers目录下创建 mma7660 的目录*/
                                    	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
                                    				     "%s", drv->name);
                                    	if (error)
                                    		goto out_unregister;
                                        /*又见到了drivers_autoprobe,还记得/sys/bus/i2c 下的drivers_autoprobe文件吗
                                        i2c_type->p->drivers_autoprobe = 1,自动匹配device和driver*/
                                    	if (drv->bus->p->drivers_autoprobe) {
                                    		error = driver_attach(drv);
                                                    {
                                                    	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
                                                               {
                                                                	struct klist_iter i;
                                                                	struct device *dev;
                                                                	int error = 0;

                                                                	if (!bus || !bus->p)
                                                                		return -EINVAL;
                                                                    /*device 注册的时候就是挂接到 i2c_bus_type->p->klist_devices
                                                                    下面就是遍历 i2c_bus_type 的device 链表,然后通过__driver_attach和driver
                                                                    做匹配*/

                                                                	klist_iter_init_node(&bus->p->klist_devices, &i,
                                                                			     (start ? &start->p->knode_bus : NULL));
                                                                	while ((dev = next_device(&i)) && !error)
                                                                		error = fn(dev, data);  //fn = __driver_attach
                                                                                {
                                                                                	struct device_driver *drv = data;

                                                                                	/*
                                                                                	 * Lock device and try to bind to it. We drop the error
                                                                                	 * here and always return 0, because we need to keep trying
                                                                                	 * to bind to devices and some drivers will return an error
                                                                                	 * simply if it didn't support the device.
                                                                                	 *
                                                                                	 * driver_probe_device() will spit a warning if there
                                                                                	 * is an error.
                                                                                	 */

                                                                                	if (!driver_match_device(drv, dev))
                                                                                        {   /* drv->bus->match = i2c_device_match ,最终还是调用到 i2c 总线 的match函数
                                                                                            最终会匹配到 mma7660 的 i2c_client*/
                                                                                        	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
                                                                                        }
                                                                                		return 0;
                                                                                    /*dev = client->dev
                                                                                      client->dev.parent = &client->adapter->dev */

                                                                                	if (dev->parent)	/* Needed for USB */
                                                                                		device_lock(dev->parent);
                                                                                	device_lock(dev);
                                                                                    /*虽然device和driver都找到了,可还未将driver赋值给device呢*/
                                                                                	if (!dev->driver)
                                                                                		driver_probe_device(drv, dev);
                                                                                        {
                                                                                        	int ret = 0;

                                                                                        	if (!device_is_registered(dev))
                                                                                        		return -ENODEV;

                                                                                        	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
                                                                                        		 drv->bus->name, __func__, dev_name(dev), drv->name);

                                                                                        	pm_runtime_get_noresume(dev);
                                                                                        	pm_runtime_barrier(dev);
                                                                                        	ret = really_probe(dev, drv);
                                                                                                {
                                                                                                	int ret = 0;

                                                                                                	atomic_inc(&probe_count);
                                                                                                	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
                                                                                                		 drv->bus->name, __func__, drv->name, dev_name(dev));
                                                                                                	WARN_ON(!list_empty(&dev->devres_head));
                                                                                                    /*这里才将driver和device关联起来*/
                                                                                                	dev->driver = drv;
                                                                                                	if (driver_sysfs_add(dev)) {
                                                                                                        {
                                                                                                        	int ret;

                                                                                                        	if (dev->bus)
                                                                                                        		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                                                                                        					     BUS_NOTIFY_BIND_DRIVER, dev);
                                                                                                            /*sys/bus/i2c/drivers/mma7660创建指向 3-004c 的链接,
                                                                                                            名字仍然为 3-004c */
                                                                                                        	ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
                                                                                                        			  kobject_name(&dev->kobj));
                                                                                                        	if (ret == 0) {
                                                                                                                /*在 /sys/devices/platform/s3c2440-i2c.3/i2c-3/3-004c/ 下
                                                                                                                创建指向 /sys/bus/i2c/drivers/mma7660 的链接,名字为driver*/
                                                                                                        		ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
                                                                                                        					"driver");
                                                                                                        		if (ret)
                                                                                                        			sysfs_remove_link(&dev->driver->p->kobj,
                                                                                                        					kobject_name(&dev->kobj));
                                                                                                        	}
                                                                                                        	return ret;
                                                                                                        }
                                                                                                		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
                                                                                                			__func__, dev_name(dev));
                                                                                                		goto probe_failed;
                                                                                                	}
                                                                                                    /*i2c_bus_type->probe = i2c_device_probe ,所以调用
                                                                                                    i2c_device_probe ,最终调用 mma7660_probe*/
                                                                                                	if (dev->bus->probe) {
                                                                                                		ret = dev->bus->probe(dev);
                                                                                                		if (ret)
                                                                                                			goto probe_failed;
                                                                                                	} else if (drv->probe) {
                                                                                                		ret = drv->probe(dev);
                                                                                                		if (ret)
                                                                                                			goto probe_failed;
                                                                                                	}

                                                                                                	driver_bound(dev);
                                                                                                    {   /*前面 client->dev->p->knode_driver未初始化,也就是为NULL*/
                                                                                                    	if (klist_node_attached(&dev->p->knode_driver)) {
                                                                                                    		printk(KERN_WARNING "%s: device %s already bound\n",
                                                                                                    			__func__, kobject_name(&dev->kobj));
                                                                                                    		return;
                                                                                                    	}

                                                                                                    	pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
                                                                                                    		 __func__, dev->driver->name);
                                                                                                        /*将device挂接在driver的设备链表中*/
                                                                                                    	klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

                                                                                                    	if (dev->bus)
                                                                                                    		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                                                                                    					     BUS_NOTIFY_BOUND_DRIVER, dev);
                                                                                                    }
                                                                                                	ret = 1;
                                                                                                	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
                                                                                                		 drv->bus->name, __func__, dev_name(dev), drv->name);
                                                                                                	goto done;

                                                                                                probe_failed:
                                                                                                	devres_release_all(dev);
                                                                                                	driver_sysfs_remove(dev);
                                                                                                	dev->driver = NULL;

                                                                                                	if (ret != -ENODEV && ret != -ENXIO) {
                                                                                                		/* driver matched but the probe failed */
                                                                                                		printk(KERN_WARNING
                                                                                                		       "%s: probe of %s failed with error %d\n",
                                                                                                		       drv->name, dev_name(dev), ret);
                                                                                                	}
                                                                                                	/*
                                                                                                	 * Ignore errors returned by ->probe so that the next driver can try
                                                                                                	 * its luck.
                                                                                                	 */
                                                                                                	ret = 0;
                                                                                                done:
                                                                                                	atomic_dec(&probe_count);
                                                                                                	wake_up(&probe_waitqueue);
                                                                                                	return ret;
                                                                                                }
                                                                                        	pm_runtime_put_sync(dev);

                                                                                        	return ret;
                                                                                        }
                                                                                	device_unlock(dev);
                                                                                	if (dev->parent)
                                                                                		device_unlock(dev->parent);

                                                                                	return 0;
                                                                                }
                                                                	klist_iter_exit(&i);
                                                                	return error;
                                                               }
                                                    }
                                    		if (error)
                                    			goto out_unregister;
                                    	}
                                        /*将 i2c_mma7660_driver 挂接在 i2c_bus_type 的driver链表中*/
                                    	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
                                    	module_add_driver(drv->owner, drv);
                                        /*在/sys/bus/i2c/drivers/mma7660 创建 uevent 文件*/
                                    	error = driver_create_file(drv, &driver_attr_uevent);
                                    	if (error) {
                                    		printk(KERN_ERR "%s: uevent attr (%s) failed\n",
                                    			__func__, drv->name);
                                    	}
                                        /*为 i2c_mma7660_driver 创建默认的属性文件,可是i2c_bus_type->drv_attrs = NULL,所以此例中没有
                                        没有创建任何文件*/
                                    	error = driver_add_attrs(bus, drv);
                                                {
                                                	int error = 0;
                                                	int i;

                                                	if (bus->drv_attrs) {
                                                		for (i = 0; attr_name(bus->drv_attrs[i]); i++) {
                                                			error = driver_create_file(drv, &bus->drv_attrs[i]);
                                                			if (error)
                                                				goto err;
                                                		}
                                                	}
                                                done:
                                                	return error;
                                                err:
                                                	while (--i >= 0)
                                                		driver_remove_file(drv, &bus->drv_attrs[i]);
                                                	goto done;
                                                }
                                    	if (error) {
                                    		/* How the hell do we get out of this pickle? Give up */
                                    		printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
                                    			__func__, drv->name);
                                    	}
                                        /*i2c_mma7660_driver->drv->suppress_bind_attrs 未赋值,也就是为0*/
                                    	if (!drv->suppress_bind_attrs) {
                                    		error = add_bind_files(drv);
                                                    {
                                                    	int ret;
                                                        /*在/sys/bus/i2c/drivers/mma7660 目录下创建 ubind 文件*/
                                                    	ret = driver_create_file(drv, &driver_attr_unbind);
                                                    	if (ret == 0) {
                                                            /*在/sys/bus/i2c/drivers/mma7660目录下创建 bind 文件*/
                                                    		ret = driver_create_file(drv, &driver_attr_bind);
                                                    		if (ret)
                                                    			driver_remove_file(drv, &driver_attr_unbind);
                                                    	}
                                                    	return ret;
                                                    }
                                    		if (error) {
                                    			/* Ditto */
                                    			printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
                                    				__func__, drv->name);
                                    		}
                                    	}
                                        /*发送 KOBJ_ADD */
                                    	kobject_uevent(&priv->kobj, KOBJ_ADD);
                                    	return 0;

                                    out_unregister:
                                    	kobject_put(&priv->kobj);
                                    	kfree(drv->p);
                                    	drv->p = NULL;
                                    out_put_bus:
                                    	bus_put(bus);
                                    	return error;
                                    }
                            	if (ret)
                            		return ret;
                            	ret = driver_add_groups(drv, drv->groups);
                            	if (ret)
                            		bus_remove_driver(drv);
                            	return ret;
                            }
                    	if (res)
                    		return res;

                    	/* Drivers should switch to dev_pm_ops instead. */
                    	if (driver->suspend)
                    		pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
                    			driver->driver.name);
                    	if (driver->resume)
                    		pr_warn("i2c-core: driver [%s] using legacy resume method\n",
                    			driver->driver.name);

                    	pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

                    	INIT_LIST_HEAD(&driver->clients);
                    	/* Walk the adapters that are already present */
                    	i2c_for_each_dev(driver, __process_new_driver);
                        {
                        	int res;

                        	mutex_lock(&core_lock);
                        	res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
                                {
                                	struct klist_iter i;
                                	struct device *dev;
                                	int error = 0;

                                	if (!bus || !bus->p)
                                		return -EINVAL;

                                	klist_iter_init_node(&bus->p->klist_devices, &i,
                                			     (start ? &start->p->knode_bus : NULL));
                                	while ((dev = next_device(&i)) && !error)
                                		error = fn(dev, data);    //fn = __process_new_driver
                                		        {
                                                	if (dev->type != &i2c_adapter_type)
                                                		return 0;
                                                	return i2c_do_add_adapter(data, to_i2c_adapter(dev));
                                                            {
                                                            	/* Detect supported devices on that bus, and instantiate them */
                                                            	i2c_detect(adap, driver);
                                                                {   /* driver 所支持i2c设备的 address 列表*/
                                                                	const unsigned short *address_list;
                                                                    /* 声明一个临时的 i2c_client */
                                                                	struct i2c_client *temp_client;
                                                                	int i, err = 0;
                                                                	int adap_id = i2c_adapter_id(adapter);
                                                                    /* i2c_mma7660_driver->address_list 未赋值,也就是为 NULL 函数在此返回*/
                                                                	address_list = driver->address_list;
                                                                	if (!driver->detect || !address_list)
                                                                		return 0;

                                                                    ......
                                                                    
                                                                	kfree(temp_client);
                                                                	return err;
                                                                }

                                                            	/* Let legacy drivers scan this bus for matching devices */
                                                            	if (driver->attach_adapter) {
                                                            		dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
                                                            			 driver->driver.name);
                                                            		dev_warn(&adap->dev, "Please use another way to instantiate "
                                                            			 "your i2c_client\n");
                                                            		/* We ignore the return code; if it fails, too bad */
                                                            		driver->attach_adapter(adap);
                                                            	}
                                                            	return 0;
                                                            }
                                                }
                                	klist_iter_exit(&i);
                                	return error;
                                }
                        	mutex_unlock(&core_lock);

                        	return res;
                        }

                    	return 0;
                    }
        }
	printk(KERN_INFO "MMA7660 sensor driver registered.\n");

	return ret;
}

从上面的代码中可以看出,其实和platform的那一套机制是一回事,驱动框架完成了大量的工作,然后实际驱动只需要注册device和driver,驱动框架就会调用到probe函数,开始真正的硬件初始化相关的工作。

三、i2c-dev

不是说 linux 秉承一切皆文件吗(网络设备除外),驱动不是应该完成open、close、read和write等函数吗?到目前为止不管platform还是i2c都没有这些信息。不要忘了,bus、device和driver驱动模型,只是驱动的一种框架,并没有完成任何实质性工作,真正的注册char、block还是网络设备都是开发者自己的事,驱动模型不是最终保证会调用到probe函数吗,对的,你可以在probe函数中完成实际的设备驱动注册,如 register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops),驱动模型只是套了个架子而已,实际驱动工作还得自己做。

对于实际的 i2c 器件,只是将 i2c 作为通信通道而已,再简单的说实际设备功能并不关心是通过spi还是 i2c 进行,比如驱动中的 read 函数,按顺序读写寄存器,并不关心 是 i2c还是spi甚至usb,只要进行数据传输达到目的就行。对于 i2c 器件一般会是input、rtc或者misc,文件系统的接口自然由 input、rtc或misc模型提供,当然你也可以专门为此期间写一个char类型的驱动,完成open、close、read和write等函数,但对于i2c部分,只是利用 i2c_client->adapter->i2c_algorithm->master_xfer和器件进行数据交互。

下面看一下i2c-dev,linux提供的 i2c-dev 并不是实际存在具体的一个i2c器件,而是代表一个i2c总线的设备,可以通过 i2c-dev 提供的 read、write和ioctl来访问总线上的 i2c 器件(根据指定的器件地址)。由于不同的 i2c 器件的 read 和 write 实现各不相同,所以linux提供的 i2c-dev 并不是很通用。

下面简单看一下i2c-dev的注册过程

static const struct file_operations i2cdev_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.read		= i2cdev_read,
	.write		= i2cdev_write,
	.unlocked_ioctl	= i2cdev_ioctl,
	.open		= i2cdev_open,
	.release	= i2cdev_release,
};
static int __init i2c_dev_init(void)
{
	int res;

	printk(KERN_INFO "i2c /dev entries driver\n");
        /*注册为char型设备,主设备号为 I2C_MAJOR(123)(终于见到设备号了) */
	res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
	if (res)
		goto out;

	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;
	}

	/* 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(I2C_MAJOR, "i2c");
out:
	printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
	return res;
}

其中 i2cdev_attach_adapter中会创建设备文件,也就是在/dev/目录下创建 i2c- %d的文件,如 i2c-3 就代表第三个i2c 上的设备或者说i2c_adapter,可通过/dev/i2c-3 来访问该总线上设备(如open、close、read和write等)。

static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
	......
    
	/* register this i2c device with the driver core */
    /*创建设备文件,这回 是带 设备号的,所以也会在 /sys/dev/目录下创建文件,如i2c-3*/
	i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
				     MKDEV(I2C_MAJOR, adap->nr), NULL,
				     "i2c-%d", adap->nr);
	......
	return res;
}

上面就是i2c驱动框架的大致内容了,整体来说还是比较复杂的,不过从代码一点一点分析,还是可以理解的,但是鉴于个人的水平有限,可能会出现理解错误的地方,欢迎交流。

 

相关标签: linux驱动 i2c