当前位置: 星创客 > 学习资源 > 讲师博文 > IIC相关结构体、API、文件及源码剖析
IIC相关结构体、API、文件及源码剖析 时间:2017-11-08     来源:星创客
一、结构体分析;
(1)struct i2c_adapter {
		 struct module *owner;
		 unsigned int id __deprecated;
		 unsigned int class;                该I2C bus支持哪些类型的slave device,只有匹配的slave device才能和bus绑定。具体的类型包括(可参考include/linux/i2c.h中的定义和注释): 
											I2C_CLASS_HWMON,硬件监控类,如lm_sensors等; 
											I2C_CLASS_DDC,DDC是数字显示通道(Digital Display Channel)的意思, 通常用于显示设备信息的获取; 
											I2C_CLASS_SPD,存储类的模组; 
											I2C_CLASS_DEPRECATED,不再使用的class。
		 const struct i2c_algorithm *algo; 该总线上的通信方法 :时钟控制,s/p,中断等
		 void *algo_data;                  通信方法的附加数据

		 struct rt_mutex bus_lock;         对所有设备的锁结构

		 int timeout;         超时时间,在iic总线上发送信号多久没回的超时时间 
		 int retries;         重复的次数 
		 struct device dev;   适配器设备

		 int nr;              总线的编号 
		 char name[48];       名字 
		 struct completion dev_released;

		 struct mutex userspace_clients_lock;
		 struct list_head userspace_clients;
	 };
(2)struct i2c_algorithm {
		 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num); 消息发送函数指针 ,不同适配器其实现不一样
		 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);//系统管理总线
		 u32 (*functionality) (struct i2c_adapter *);  适配器所支持的功能,functionality,通过一个bitmap,告诉调用者该I2C adapter支持的功能,包括(具体可参考include/uapi/linux/i2c.h中的定义和注释):
														I2C_FUNC_I2C,支持传统的I2C功能; 
														I2C_FUNC_10BIT_ADDR,支持10bit地址; 
														I2C_FUNC_PROTOCOL_MANGLING,支持非标准的协议行为
														I2C_FUNC_NOSTART,支持不需要发送START信号的I2C传输
														I2C_FUNC_SMBUS_xxx,SMBUS相关的功能,不再详细介绍。
	};
(3)struct i2c_driver {
		 unsigned int class;

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

		 标准的驱动模型 
		 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);类似ioctl 
		 struct device_driver driver;用于设备驱动模型 
	
		 const struct i2c_device_id *id_table;   列出该设备驱动所支持的设备 

	     int (*detect)(struct i2c_client *, struct i2c_board_info *);设备探测的回调函数 
	     const unsigned short *address_list;用于探测的i2c设备地址 
	     struct list_head clients;   /* 链接所有探测出的i2c设备 */
	};
(4)struct i2c_client {   i2c_client 代表i2c从设备
		 unsigned short flags;  I2C_CLIENT_TEN表示设备使用的是10位的地址,I2C_CLIENT_PEC表示使用SMBus包用在错误检查
		 unsigned short addr;   I2C设备在总线上的的地址  
		 char name[I2C_NAME_SIZE]; 设备名
		 struct i2c_adapter *adapter;   指向该I2C设备挂载的I2C适配器
		 struct i2c_driver *driver;     指向支持该I2C设备的驱动
		 struct device dev;             用于总线设备驱动模型
		 int irq;                       该设备能产生的中断号
		 struct list_head detected;
	};
(5)struct i2c_msg {   			I2C总线上传输信息的小单位
		  __u16 addr  i2c			设备地址,可以是10位的地址,如果是10位的地址flags标志中需设置I2C_M_TEN
		  __u16 flags I2C_M_RD 	    读标志,所有的适配器都必须支持,其他的标志见I2C_FUNC_*
	#define I2C_M_TEN  0x0010   	10位的从设备的地址
	#define I2C_M_RD   0x0001    	从I2C设备中读数据
	#define I2C_M_NOSTART  0x4000 	读写混合操作的情况下,假如要传输多个msg(以2个为例),如果第二个msg携带了该标志,则不再发送'S Addr Wr/Rd [A]'信号,即从
									S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P
									S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P
									变为
									S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P
									S Addr Rd [A] [Data] NA  Data [A] P
	#define I2C_M_REV_DIR_ADDR 0x2000 	将读写flag翻转,即读的时候发Wr信号,写的时候发Rd信号。至于为什么这么用,只有天知道。
	#define I2C_M_IGNORE_NAK 0x1000 	读操作的时候,忽略slave返回的NA,把它当做ACK信号,继续读取。还别说,那真有那比较贱的slave,比如电视(通过I2C读取EDID的时候)。
	#define I2C_M_NO_RD_ACK  0x0800 	读操作的时候,忽略所有的NACK/ACK信号。
	#define I2C_M_RECV_LEN  0x0400  	SMBUS的一个flag,意义不明。
	__u16 len;	    /* 消息长度 */
	__u8 *buf;	    /* 消息指针*/
	};
(6)struct i2c_board_info {   		 创建I2C设备的模版
		 char  type[I2C_NAME_SIZE];  设备类型,用于填充i2c_client.name
		 unsigned short flags;       用于填充i2c_client.flags
		 unsigned short addr;        用于填充i2c_client.addr
		 void  *platform_data;       存储i2c_client.dev.platform_data
		 struct dev_archdata *archdata; 拷贝到i2c_client.dev.archdata
	#ifdef CONFIG_OF             指向打开固件的设备节点
		 struct device_node *of_node;
	#endif
		 int  irq;                  存储到i2c_client.irq
	};
	
二、框架分析:目录
			  核心层i2c-core.c,
			  总线驱动i2c-s3c2410.c ,
			  通用设备驱动
	-------------------------------------------------------------------------------------------------------------------	  
(1)目录
	I2C core使用I2C adapter和I2C algorithm两个子模块抽象I2C controller的功能,
	使用I2C client和I2C driver抽象I2C slave device的功能(对应设备模型中的
	device和device driver)。另外,基于I2C协议,通过smbus模块实现SMBus(System
	Management Bus,系统管理总线)的功能。
	/driver/i2c/busses:i2c控制器驱动
	/driver/i2c/algos :i2c总线通信方法
	/driver/i2c/muxes :用于实现I2C bus的多路复用功能,属于奇葩的冷门功能。
	-------------------------------------------------------------------------------------------------------------------
(2)i2c-core.c分析
		 [1]大框架i2c_bus
			postcore_initcall(i2c_init);
				retval = bus_register(&i2c_bus_type);							//注册i2c_bus:/sys/bus/i2c
				i2c_adapter_compat_class = class_compat_register("i2c-adapter");//注册适配器class :/sys/class/i2c-adapter
				retval = i2c_add_driver(&dummy_driver);							//注册i2c驱动dummy_driver:/sys/bus/i2c/driver/dummy
			
			提供的功能函数:
				注册注销i2c_adapter:
					int i2c_add_adapter(struct i2c_adapter *adapter);
					void i2c_del_adapter(struct i2c_adapter *adap);
				注册注销i2c_driver:
					int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
					void i2c_del_driver(struct i2c_driver *driver);
				数据传输函数:
					int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
					int i2c_master_send(const struct i2c_client *client, const char *buf, int count);
					int i2c_master_recv(const struct i2c_client *client, char *buf, int count);
			
				备注1:
					struct bus_type i2c_bus_type = {
						.name		= "i2c",
						.match		= i2c_device_match,
						.probe		= i2c_device_probe,
						.remove		= i2c_device_remove,
						.shutdown	= i2c_device_shutdown,
						.pm		= &i2c_device_pm_ops,
					};
				备注2:
					static struct i2c_driver dummy_driver = {
						.driver.name	= "dummy",
						.probe		= dummy_probe,
						.remove		= dummy_remove,
						.id_table	= dummy_id,
					};
	 -------------------------------------------------------------------------------------------------------------------
(3)总线适配器驱动i2c-s3c2410.c分析:主要功能:分配设置并初始化i2c相关设备结构体成员,初始化iic适配器,并注册i2c_adapter,
	 从设备树、acpi(高级配置与电源管理接口),i2c_board_list中获取该i2c_adapter所支持的iic从设备并生成i2c_client
		
	 [1]大框架i2c_adapter:
		subsys_initcall(i2c_adap_s3c_init);
			i2c_adap_s3c_init(void)
				platform_driver_register(&s3c24xx_i2c_driver);
				
		
		备注1:
			static struct platform_driver s3c24xx_i2c_driver = {
				.probe		= s3c24xx_i2c_probe,
				.remove		= s3c24xx_i2c_remove,
				.id_table	= s3c24xx_driver_ids,
				.driver		= {
					.owner	= THIS_MODULE,
					.name	= "s3c-i2c",
					.pm	= S3C24XX_DEV_PM_OPS,
					.of_match_table = of_match_ptr(s3c24xx_i2c_match),
				},
			};
		adapter匹配方式:
		 [1.1].id_table	= s3c24xx_driver_ids,
			static struct platform_device_id s3c24xx_driver_ids[] = {
				{
					.name		= "s3c2410-i2c",
					.driver_data	= 0,
				}, {
					.name		= "s3c2440-i2c",
					.driver_data	= QUIRK_S3C2440,
				}, {
					.name		= "s3c2440-hdmiphy-i2c",
					.driver_data	= QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
				}, { },
			};
		 [1.2].name	= "s3c-i2c",
		 
		 [1.3].of_match_table = of_match_ptr(s3c24xx_i2c_match),
		 #ifdef CONFIG_OF
			static const struct of_device_id s3c24xx_i2c_match[] = {
				{ .compatible = "samsung,s3c2410-i2c", .data = (void *)0 },
				{ .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 },
				{ .compatible = "samsung,s3c2440-hdmiphy-i2c",
				  .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) },
				{ .compatible = "samsung,exynos5440-i2c",
				  .data = (void *)(QUIRK_S3C2440 | QUIRK_NO_GPIO) },
				{ .compatible = "samsung,exynos5-sata-phy-i2c",
				  .data = (void *)(QUIRK_S3C2440 | QUIRK_POLL | QUIRK_NO_GPIO) },
				{},
			};
		MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
		#endif
		
		
	[2]匹配成功.probe		= s3c24xx_i2c_probe,:主要功能:分配设置并初始化i2c相关设备结构体成员,初始化iic适配器,并注册i2c_adapter
		static int s3c24xx_i2c_probe(struct platform_device *pdev)
			/*准备好指针变量,准备从传入的对象提取数据*/
			struct s3c24xx_i2c *i2c;
			struct s3c2410_platform_i2c *pdata = NULL;
			struct resource *res;
			if (!pdev->dev.of_node) {		/*如果pdev->dev.of_node为空*/
				/*则从platform->dev->platform_data进行相应的提取,
				该platform_data在platform_device设置是已经进行了相应的填充*/----------------------------》先主线,再找
				pdata = dev_get_platdata(&pdev->dev);
			/*分配struct s3c24xx_i2c、s3c2410_platform_i2c相应的结构体空间*/
			/*剖析devm_kzalloc与kzalloc的区别:一个专用于设备空间分配,当设备移除或驱动移除时自动释放空间,另一个需要手动释放*/
			i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);	
			i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
			/*如果s3c2410_platform_i2c空间被分配成功,则将pdata拷贝至i2c->pdata,反之从设备树获取并解析数据存储到i2c->pdata*/
			if (pdata)
				memcpy(i2c->pdata, pdata, sizeof(*pdata));
			else
				s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
			/*填充结构体成员*/
			strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
			i2c->adap.owner   = THIS_MODULE;
			i2c->adap.algo    = &s3c24xx_i2c_algorithm;//i2c数据传输算法----------------------------》先主线,再找
			i2c->adap.retries = 2;
			i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
			i2c->tx_setup     = 50;
			/*初始化等待队列头*/
			init_waitqueue_head(&i2c->wait);
			i2c->dev = &pdev->dev;
			/*获取i2c时钟*/
			i2c->clk = devm_clk_get(&pdev->dev, "i2c");
			/*获取i2c_adapter寄存器基地址并映射*/
			res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
			i2c->regs = devm_ioremap_resource(&pdev->dev, res);
			/*将i2c赋值到i2c->adap.algo_data为了xfer接口函数调用*/
			i2c->adap.algo_data = i2c;
			i2c->adap.dev.parent = &pdev->dev;
			/*获取管脚信息*/
			i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev);
			/*如果i2c->pdata->cfg_gpio存在则调用cfg_gpio配置相应管脚*/
			if (i2c->pdata->cfg_gpio) {
				i2c->pdata->cfg_gpio(to_platform_device(i2c->dev));----------------------------》先主线,再找
			} else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) {
				return -EINVAL;
			}
			ret = s3c24xx_i2c_init(i2c);//初始化iic控制器,时钟频率等
			i2c->adap.nr = i2c->pdata->bus_num;
			i2c->adap.dev.of_node = pdev->dev.of_node;
			/*注册适配器*/
			ret = i2c_add_numbered_adapter(&i2c->adap);
			/*将i2c设置为platform私有数据:platform_device->dev->p->driver_data*/
			platform_set_drvdata(pdev, i2c);
			/*设置dev/adapter电源管理*/
			pm_runtime_enable(&pdev->dev);
			pm_runtime_enable(&i2c->adap.dev);
			
			
	[3]	注册适配器:
		ret = i2c_add_numbered_adapter(&i2c->adap);	
			if (adap->nr == -1) /*如果 adap->nr = -1 ,则动态分配总线id:i2c-0bus*/
				return i2c_add_adapter(adap);
			走这条线-------->
			return __i2c_add_numbered_adapter(adap);//静态 
				/*调用idr_alloc,动态分配一个id号,并将该id号做为i2c_adapter的I2C总线号。关于idr机制,我们不详细分析,
				只需要知道它是一种快速索引机制,它将一个整数ID与一个需要被索引的指针建立联系,方便进行查找。*/
				id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1,GFP_KERNEL);
				return i2c_register_adapter(adap);
					INIT_LIST_HEAD(&adap->userspace_clients);//初始化i2c_adapter上连接i2c_client的链表
					adap->dev.bus = &i2c_bus_type;			//i2c_adapter也算是一个设备,其挂载在i2c_bus上,此处指定挂载总线
					adap->dev.type = &i2c_adapter_type;		//指定设备类型为i2c_adapter_type,即当对i2c_adapter进行相应操作时执行
															//i2c_adapter_type中相应的操作方法----------->关联应用层创建i2c_client
					res = device_register(&adap->dev);		//将i2c_adapter注册进i2c_bus上
					
					创建i2c_client的几种方式:
					(1)of_i2c_register_devices(adap);
							for_each_available_child_of_node(adap->dev.of_node, node);//查找i2c_adapter下的指代i2c_cient的子节点
								if (of_modalias_node(node, info.type, sizeof(info.type)) < 0)//获取i2c_adapter子节点下compatible的“,”后的字符串,存放至i2c_board_info.type中
								
								addr = of_get_property(node, "reg", &len);//获取i2c_adapter子节点下获取reg
								info.addr = be32_to_cpup(addr);//大小端转换,存放至i2c_board_info.addr中
							
								info.irq = irq_of_parse_and_map(node, 0);//获取i2c_adapter子节点下获取irq,存放至i2c_board_info.irq中
								info.of_node = of_node_get(node);//获取i2c_adapter子节点下的子节点,存放至i2c_board_info.of_node中
								info.archdata = &dev_ad;//填充i2c_board_info.archdata
								if (of_get_property(node, "wakeup-source", NULL))//如果i2c_adapter属性wakeup-source存在键值,则将i2c_board_info.flags设置为I2C_CLIENT_WAKE
								info.flags |= I2C_CLIENT_WAKE;
								//在i2c_adapter上根据i2c_baord_info信息创建i2c_client
								result = i2c_new_device(adap, &info);		
								
					(2)acpi_i2c_register_devices(adap);

					(3)if (adap->nr < __i2c_first_dynamic_bus_num)
							i2c_scan_static_board_info(adap);//扫描板级信息
								//如果devinfo->busnum == adapter->nr的总线号相等,则尝试根据i2c_board_info的信息创建i2c_client
								if (devinfo->busnum == adapter->nr&& !i2c_new_device(adapter,&devinfo->board_info))
									
							#define container_of(ptr, type, member) ({			\
							const typeof(((type *)0)->member) * __mptr = (ptr);	\
							(type *)((char *)__mptr - offsetof(type, member)); })
									---------------------------------------------------------------
									#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
									((TYPE *)0):将0强制转换为type类型的一个指针;
									&((TYPE *)0)->MEMBER:获取以0地址为开头的type类型结构体成员member的地址;
									((size_t) &((TYPE *)0)->MEMBER):强制转换为long unsigned int类型:即一个数字也就是MEMBER相对于TYPE的地址偏移
									---------------------------------------------------------------
							((type *)0):将0强制转换为type类型的一个指针;
							(((type *)0)->member):获取以0地址为开头的type类型结构体成员member的地址;
							(((type *)0)->member) * __mptr = (ptr):将ptr指针赋值给__mptr
							typeof(((type *)0)->member) * __mptr = (ptr);//提取该指针指向的变量的类型	
							--------------------------------------------------------------------------
							(type *)((char *)__mptr - offsetof(type, member));	//强制转换__mptr为char*,加减一为一
								 
					(4)/* Notify drivers */
					mutex_lock(&core_lock);
					bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
					mutex_unlock(&core_lock);
					
					bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);	//对于挂接在i2c_bus上的每一个设备,均可调用__process_new_adapter					
						while ((drv = next_driver(&i)) && !error)
							error = fn(drv, data);	
							
					static int __process_new_adapter(struct device_driver *d, void *data)分析
						 i2c_do_add_adapter(to_i2c_driver(d), data);
							i2c_detect(adap, driver);
							
								int adap_id = i2c_adapter_id(adapter);//获取i2c_adapter的id号
								address_list = driver->address_list;  //获取i2c_driver支持的设备地址数组
								temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);//分配i2c_client空间
								temp_client->adapter = adapter;//绑定设备与适配器
								temp_client->addr = address_list[i];//将i2c_driver支持的设备地址赋值
								err = i2c_detect_address(temp_client, driver);//探测该设备地址的设备是否真实存在?
									err = i2c_check_addr_validity(addr);//设备地址是否有效
									if (i2c_check_addr_busy(adapter, addr));//设备地址是否被占用?
									memset(&info, 0, sizeof(struct i2c_board_info));//清空i2c_board_info
									info.addr = addr;//赋值地址
									err = driver->detect(temp_client, &info);//探测
									if (info.type[0] == '\0') //如果设备名称无则报错,相反则:
									client = i2c_new_device(adapter, &info);//创建相应的i2c_client
									if (client)
										list_add_tail(&client->detected, &driver->clients);//将i2c_client挂接到i2c_driver支持的链表上
			==========================================
			函数宏DEVICE_ATTR内封装的是__ATTR(_name,_mode,_show,_stroe)方法,_show表示的是读方法,_stroe表示的是写方法。
			当然_ATTR不是独生子女,他还有一系列的姊妹__ATTR_RO宏只有读方法,__ATTR_NULL等等
			如 对设备的使用  DEVICE_ATTR  ,对总线使用  BUS_ATTR  ,对驱动使用 DRIVER_ATTR  ,对类 别 (class) 使用  CLASS_ATTR, 
			这四个高级的宏来自于<include/linux/device.h> 
			DEVICE_ATTR  宏声明有四个参数,分别是名称、权限位、读函数、写函数。其中读函数和写函数是读写功能函数的函数名。
			如果你完成了DEVICE_ATTR函数宏的填充,下面就需要创建接口了
			例如:
				static DEVICE_ATTR(polling, S_IRUGO | S_IWUSR, show_polling, set_polling);
				static struct attribute *dev_attrs[] = {
						&dev_attr_polling.attr,
						NULL,
				};
			当你想要实现的接口名字是polling的时候,需要实现结构体struct attribute *dev_attrs[]
			其中成员变量的名字必须是&dev_attr_polling.attr
			然后再封装
				static struct attribute_group dev_attr_grp = {
						.attrs = dev_attrs,
				};
			在利用sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp);创建接口
			==========================================
	备注1:
	struct s3c24xx_i2c {
		wait_queue_head_t	wait;//等待队列
		struct i2c_msg		*msg;//i2c_msg
		unsigned int		irq; //中断号
		struct clk		   *clk; //时钟
		struct device		*dev;//设备模型
		struct i2c_adapter	adap;//适配器结构体
	};	
	
	 -------------------------------------------------------------------------------------------------------------------	
(4)通用设备驱动分析i2c-dev.c:
	 [1]代码分析
	 module_init(i2c_dev_init);
		res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);//注册主设备号为89的字符设备驱动,相应
		i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");//创建i2c_class
		res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);//事件通知链相关
		i2c_for_each_dev(NULL, i2cdev_attach_adapter);//查找i2c_bus上的相关设备
			res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
				while ((dev = next_device(&i)) && !error)//找到设备时执行回调函数fn
					error = fn(dev, data);
		
	i2cdev_attach_adapter
		if (dev->type != &i2c_adapter_type)//如果i2c_bus上不是adapter,则返回0
		adap = to_i2c_adapter(dev);//获取i2c_adapter
		i2c_dev = get_free_i2c_dev(adap);//分配i2c_dev
			i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
			i2c_dev->adap = adap;//绑定adapter与i2c_adapter
			list_add_tail(&i2c_dev->list, &i2c_dev_list);//将i2c_dev添加至i2c_dev_list链表
		i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,MKDEV(I2C_MAJOR, adap->nr), NULL,"i2c-%d", adap->nr);//创建对应的i2c_adapter节点
		
		备注一:
		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,
		};
		
		
	[2] 通用iic代码使用配置:
		i2c-dev驱动的使用
			3.1配置内核
				Device Drivers  --->  
					-*- I2C support  --->
						<*>   I2C device interface  
						
	
	[3]程序编写
		(1)编写配置,读取寄存器数据的子函数
		(2)打开
		(3)设置从机地址 + 超时时间 + 重试次数 
		(4)设置寄存器
		(5)读取数据

前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2018 北京华清远见科技发展有限公司 版权所有 ,京ICP备16055225号,京公海网安备11010802025203号
返回

讲师博文

星创客 - 华清远见旗下高端IT培训品牌

当前位置: 星创客 > 学习资源 > 讲师博文 >

IIC相关结构体、API、文件及源码剖析
来源: 星创客 作者: 星创客 时间:2017-11-08

一、结构体分析; (1)struct i2c_adapter { struct module *owner; unsigned int id __deprecated; unsigned int class; 该I2C bus支持哪些类型的slave device,只有匹配的slave device才能和bus绑定。具体的类型...

一、结构体分析;
(1)struct i2c_adapter {
		 struct module *owner;
		 unsigned int id __deprecated;
		 unsigned int class;                该I2C bus支持哪些类型的slave device,只有匹配的slave device才能和bus绑定。具体的类型包括(可参考include/linux/i2c.h中的定义和注释): 
											I2C_CLASS_HWMON,硬件监控类,如lm_sensors等; 
											I2C_CLASS_DDC,DDC是数字显示通道(Digital Display Channel)的意思, 通常用于显示设备信息的获取; 
											I2C_CLASS_SPD,存储类的模组; 
											I2C_CLASS_DEPRECATED,不再使用的class。
		 const struct i2c_algorithm *algo; 该总线上的通信方法 :时钟控制,s/p,中断等
		 void *algo_data;                  通信方法的附加数据

		 struct rt_mutex bus_lock;         对所有设备的锁结构

		 int timeout;         超时时间,在iic总线上发送信号多久没回的超时时间 
		 int retries;         重复的次数 
		 struct device dev;   适配器设备

		 int nr;              总线的编号 
		 char name[48];       名字 
		 struct completion dev_released;

		 struct mutex userspace_clients_lock;
		 struct list_head userspace_clients;
	 };
(2)struct i2c_algorithm {
		 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num); 消息发送函数指针 ,不同适配器其实现不一样
		 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);//系统管理总线
		 u32 (*functionality) (struct i2c_adapter *);  适配器所支持的功能,functionality,通过一个bitmap,告诉调用者该I2C adapter支持的功能,包括(具体可参考include/uapi/linux/i2c.h中的定义和注释):
														I2C_FUNC_I2C,支持传统的I2C功能; 
														I2C_FUNC_10BIT_ADDR,支持10bit地址; 
														I2C_FUNC_PROTOCOL_MANGLING,支持非标准的协议行为
														I2C_FUNC_NOSTART,支持不需要发送START信号的I2C传输
														I2C_FUNC_SMBUS_xxx,SMBUS相关的功能,不再详细介绍。
	};
(3)struct i2c_driver {
		 unsigned int class;

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

		 标准的驱动模型 
		 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);类似ioctl 
		 struct device_driver driver;用于设备驱动模型 
	
		 const struct i2c_device_id *id_table;   列出该设备驱动所支持的设备 

	     int (*detect)(struct i2c_client *, struct i2c_board_info *);设备探测的回调函数 
	     const unsigned short *address_list;用于探测的i2c设备地址 
	     struct list_head clients;   /* 链接所有探测出的i2c设备 */
	};
(4)struct i2c_client {   i2c_client 代表i2c从设备
		 unsigned short flags;  I2C_CLIENT_TEN表示设备使用的是10位的地址,I2C_CLIENT_PEC表示使用SMBus包用在错误检查
		 unsigned short addr;   I2C设备在总线上的的地址  
		 char name[I2C_NAME_SIZE]; 设备名
		 struct i2c_adapter *adapter;   指向该I2C设备挂载的I2C适配器
		 struct i2c_driver *driver;     指向支持该I2C设备的驱动
		 struct device dev;             用于总线设备驱动模型
		 int irq;                       该设备能产生的中断号
		 struct list_head detected;
	};
(5)struct i2c_msg {   			I2C总线上传输信息的小单位
		  __u16 addr  i2c			设备地址,可以是10位的地址,如果是10位的地址flags标志中需设置I2C_M_TEN
		  __u16 flags I2C_M_RD 	    读标志,所有的适配器都必须支持,其他的标志见I2C_FUNC_*
	#define I2C_M_TEN  0x0010   	10位的从设备的地址
	#define I2C_M_RD   0x0001    	从I2C设备中读数据
	#define I2C_M_NOSTART  0x4000 	读写混合操作的情况下,假如要传输多个msg(以2个为例),如果第二个msg携带了该标志,则不再发送'S Addr Wr/Rd [A]'信号,即从
									S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P
									S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P
									变为
									S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P
									S Addr Rd [A] [Data] NA  Data [A] P
	#define I2C_M_REV_DIR_ADDR 0x2000 	将读写flag翻转,即读的时候发Wr信号,写的时候发Rd信号。至于为什么这么用,只有天知道。
	#define I2C_M_IGNORE_NAK 0x1000 	读操作的时候,忽略slave返回的NA,把它当做ACK信号,继续读取。还别说,那真有那比较贱的slave,比如电视(通过I2C读取EDID的时候)。
	#define I2C_M_NO_RD_ACK  0x0800 	读操作的时候,忽略所有的NACK/ACK信号。
	#define I2C_M_RECV_LEN  0x0400  	SMBUS的一个flag,意义不明。
	__u16 len;	    /* 消息长度 */
	__u8 *buf;	    /* 消息指针*/
	};
(6)struct i2c_board_info {   		 创建I2C设备的模版
		 char  type[I2C_NAME_SIZE];  设备类型,用于填充i2c_client.name
		 unsigned short flags;       用于填充i2c_client.flags
		 unsigned short addr;        用于填充i2c_client.addr
		 void  *platform_data;       存储i2c_client.dev.platform_data
		 struct dev_archdata *archdata; 拷贝到i2c_client.dev.archdata
	#ifdef CONFIG_OF             指向打开固件的设备节点
		 struct device_node *of_node;
	#endif
		 int  irq;                  存储到i2c_client.irq
	};
	
二、框架分析:目录
			  核心层i2c-core.c,
			  总线驱动i2c-s3c2410.c ,
			  通用设备驱动
	-------------------------------------------------------------------------------------------------------------------	  
(1)目录
	I2C core使用I2C adapter和I2C algorithm两个子模块抽象I2C controller的功能,
	使用I2C client和I2C driver抽象I2C slave device的功能(对应设备模型中的
	device和device driver)。另外,基于I2C协议,通过smbus模块实现SMBus(System
	Management Bus,系统管理总线)的功能。
	/driver/i2c/busses:i2c控制器驱动
	/driver/i2c/algos :i2c总线通信方法
	/driver/i2c/muxes :用于实现I2C bus的多路复用功能,属于奇葩的冷门功能。
	-------------------------------------------------------------------------------------------------------------------
(2)i2c-core.c分析
		 [1]大框架i2c_bus
			postcore_initcall(i2c_init);
				retval = bus_register(&i2c_bus_type);							//注册i2c_bus:/sys/bus/i2c
				i2c_adapter_compat_class = class_compat_register("i2c-adapter");//注册适配器class :/sys/class/i2c-adapter
				retval = i2c_add_driver(&dummy_driver);							//注册i2c驱动dummy_driver:/sys/bus/i2c/driver/dummy
			
			提供的功能函数:
				注册注销i2c_adapter:
					int i2c_add_adapter(struct i2c_adapter *adapter);
					void i2c_del_adapter(struct i2c_adapter *adap);
				注册注销i2c_driver:
					int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
					void i2c_del_driver(struct i2c_driver *driver);
				数据传输函数:
					int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
					int i2c_master_send(const struct i2c_client *client, const char *buf, int count);
					int i2c_master_recv(const struct i2c_client *client, char *buf, int count);
			
				备注1:
					struct bus_type i2c_bus_type = {
						.name		= "i2c",
						.match		= i2c_device_match,
						.probe		= i2c_device_probe,
						.remove		= i2c_device_remove,
						.shutdown	= i2c_device_shutdown,
						.pm		= &i2c_device_pm_ops,
					};
				备注2:
					static struct i2c_driver dummy_driver = {
						.driver.name	= "dummy",
						.probe		= dummy_probe,
						.remove		= dummy_remove,
						.id_table	= dummy_id,
					};
	 -------------------------------------------------------------------------------------------------------------------
(3)总线适配器驱动i2c-s3c2410.c分析:主要功能:分配设置并初始化i2c相关设备结构体成员,初始化iic适配器,并注册i2c_adapter,
	 从设备树、acpi(高级配置与电源管理接口),i2c_board_list中获取该i2c_adapter所支持的iic从设备并生成i2c_client
		
	 [1]大框架i2c_adapter:
		subsys_initcall(i2c_adap_s3c_init);
			i2c_adap_s3c_init(void)
				platform_driver_register(&s3c24xx_i2c_driver);
				
		
		备注1:
			static struct platform_driver s3c24xx_i2c_driver = {
				.probe		= s3c24xx_i2c_probe,
				.remove		= s3c24xx_i2c_remove,
				.id_table	= s3c24xx_driver_ids,
				.driver		= {
					.owner	= THIS_MODULE,
					.name	= "s3c-i2c",
					.pm	= S3C24XX_DEV_PM_OPS,
					.of_match_table = of_match_ptr(s3c24xx_i2c_match),
				},
			};
		adapter匹配方式:
		 [1.1].id_table	= s3c24xx_driver_ids,
			static struct platform_device_id s3c24xx_driver_ids[] = {
				{
					.name		= "s3c2410-i2c",
					.driver_data	= 0,
				}, {
					.name		= "s3c2440-i2c",
					.driver_data	= QUIRK_S3C2440,
				}, {
					.name		= "s3c2440-hdmiphy-i2c",
					.driver_data	= QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
				}, { },
			};
		 [1.2].name	= "s3c-i2c",
		 
		 [1.3].of_match_table = of_match_ptr(s3c24xx_i2c_match),
		 #ifdef CONFIG_OF
			static const struct of_device_id s3c24xx_i2c_match[] = {
				{ .compatible = "samsung,s3c2410-i2c", .data = (void *)0 },
				{ .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 },
				{ .compatible = "samsung,s3c2440-hdmiphy-i2c",
				  .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) },
				{ .compatible = "samsung,exynos5440-i2c",
				  .data = (void *)(QUIRK_S3C2440 | QUIRK_NO_GPIO) },
				{ .compatible = "samsung,exynos5-sata-phy-i2c",
				  .data = (void *)(QUIRK_S3C2440 | QUIRK_POLL | QUIRK_NO_GPIO) },
				{},
			};
		MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
		#endif
		
		
	[2]匹配成功.probe		= s3c24xx_i2c_probe,:主要功能:分配设置并初始化i2c相关设备结构体成员,初始化iic适配器,并注册i2c_adapter
		static int s3c24xx_i2c_probe(struct platform_device *pdev)
			/*准备好指针变量,准备从传入的对象提取数据*/
			struct s3c24xx_i2c *i2c;
			struct s3c2410_platform_i2c *pdata = NULL;
			struct resource *res;
			if (!pdev->dev.of_node) {		/*如果pdev->dev.of_node为空*/
				/*则从platform->dev->platform_data进行相应的提取,
				该platform_data在platform_device设置是已经进行了相应的填充*/----------------------------》先主线,再找
				pdata = dev_get_platdata(&pdev->dev);
			/*分配struct s3c24xx_i2c、s3c2410_platform_i2c相应的结构体空间*/
			/*剖析devm_kzalloc与kzalloc的区别:一个专用于设备空间分配,当设备移除或驱动移除时自动释放空间,另一个需要手动释放*/
			i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);	
			i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
			/*如果s3c2410_platform_i2c空间被分配成功,则将pdata拷贝至i2c->pdata,反之从设备树获取并解析数据存储到i2c->pdata*/
			if (pdata)
				memcpy(i2c->pdata, pdata, sizeof(*pdata));
			else
				s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c);
			/*填充结构体成员*/
			strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
			i2c->adap.owner   = THIS_MODULE;
			i2c->adap.algo    = &s3c24xx_i2c_algorithm;//i2c数据传输算法----------------------------》先主线,再找
			i2c->adap.retries = 2;
			i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
			i2c->tx_setup     = 50;
			/*初始化等待队列头*/
			init_waitqueue_head(&i2c->wait);
			i2c->dev = &pdev->dev;
			/*获取i2c时钟*/
			i2c->clk = devm_clk_get(&pdev->dev, "i2c");
			/*获取i2c_adapter寄存器基地址并映射*/
			res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
			i2c->regs = devm_ioremap_resource(&pdev->dev, res);
			/*将i2c赋值到i2c->adap.algo_data为了xfer接口函数调用*/
			i2c->adap.algo_data = i2c;
			i2c->adap.dev.parent = &pdev->dev;
			/*获取管脚信息*/
			i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev);
			/*如果i2c->pdata->cfg_gpio存在则调用cfg_gpio配置相应管脚*/
			if (i2c->pdata->cfg_gpio) {
				i2c->pdata->cfg_gpio(to_platform_device(i2c->dev));----------------------------》先主线,再找
			} else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) {
				return -EINVAL;
			}
			ret = s3c24xx_i2c_init(i2c);//初始化iic控制器,时钟频率等
			i2c->adap.nr = i2c->pdata->bus_num;
			i2c->adap.dev.of_node = pdev->dev.of_node;
			/*注册适配器*/
			ret = i2c_add_numbered_adapter(&i2c->adap);
			/*将i2c设置为platform私有数据:platform_device->dev->p->driver_data*/
			platform_set_drvdata(pdev, i2c);
			/*设置dev/adapter电源管理*/
			pm_runtime_enable(&pdev->dev);
			pm_runtime_enable(&i2c->adap.dev);
			
			
	[3]	注册适配器:
		ret = i2c_add_numbered_adapter(&i2c->adap);	
			if (adap->nr == -1) /*如果 adap->nr = -1 ,则动态分配总线id:i2c-0bus*/
				return i2c_add_adapter(adap);
			走这条线-------->
			return __i2c_add_numbered_adapter(adap);//静态 
				/*调用idr_alloc,动态分配一个id号,并将该id号做为i2c_adapter的I2C总线号。关于idr机制,我们不详细分析,
				只需要知道它是一种快速索引机制,它将一个整数ID与一个需要被索引的指针建立联系,方便进行查找。*/
				id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1,GFP_KERNEL);
				return i2c_register_adapter(adap);
					INIT_LIST_HEAD(&adap->userspace_clients);//初始化i2c_adapter上连接i2c_client的链表
					adap->dev.bus = &i2c_bus_type;			//i2c_adapter也算是一个设备,其挂载在i2c_bus上,此处指定挂载总线
					adap->dev.type = &i2c_adapter_type;		//指定设备类型为i2c_adapter_type,即当对i2c_adapter进行相应操作时执行
															//i2c_adapter_type中相应的操作方法----------->关联应用层创建i2c_client
					res = device_register(&adap->dev);		//将i2c_adapter注册进i2c_bus上
					
					创建i2c_client的几种方式:
					(1)of_i2c_register_devices(adap);
							for_each_available_child_of_node(adap->dev.of_node, node);//查找i2c_adapter下的指代i2c_cient的子节点
								if (of_modalias_node(node, info.type, sizeof(info.type)) < 0)//获取i2c_adapter子节点下compatible的“,”后的字符串,存放至i2c_board_info.type中
								
								addr = of_get_property(node, "reg", &len);//获取i2c_adapter子节点下获取reg
								info.addr = be32_to_cpup(addr);//大小端转换,存放至i2c_board_info.addr中
							
								info.irq = irq_of_parse_and_map(node, 0);//获取i2c_adapter子节点下获取irq,存放至i2c_board_info.irq中
								info.of_node = of_node_get(node);//获取i2c_adapter子节点下的子节点,存放至i2c_board_info.of_node中
								info.archdata = &dev_ad;//填充i2c_board_info.archdata
								if (of_get_property(node, "wakeup-source", NULL))//如果i2c_adapter属性wakeup-source存在键值,则将i2c_board_info.flags设置为I2C_CLIENT_WAKE
								info.flags |= I2C_CLIENT_WAKE;
								//在i2c_adapter上根据i2c_baord_info信息创建i2c_client
								result = i2c_new_device(adap, &info);		
								
					(2)acpi_i2c_register_devices(adap);

					(3)if (adap->nr < __i2c_first_dynamic_bus_num)
							i2c_scan_static_board_info(adap);//扫描板级信息
								//如果devinfo->busnum == adapter->nr的总线号相等,则尝试根据i2c_board_info的信息创建i2c_client
								if (devinfo->busnum == adapter->nr&& !i2c_new_device(adapter,&devinfo->board_info))
									
							#define container_of(ptr, type, member) ({			\
							const typeof(((type *)0)->member) * __mptr = (ptr);	\
							(type *)((char *)__mptr - offsetof(type, member)); })
									---------------------------------------------------------------
									#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
									((TYPE *)0):将0强制转换为type类型的一个指针;
									&((TYPE *)0)->MEMBER:获取以0地址为开头的type类型结构体成员member的地址;
									((size_t) &((TYPE *)0)->MEMBER):强制转换为long unsigned int类型:即一个数字也就是MEMBER相对于TYPE的地址偏移
									---------------------------------------------------------------
							((type *)0):将0强制转换为type类型的一个指针;
							(((type *)0)->member):获取以0地址为开头的type类型结构体成员member的地址;
							(((type *)0)->member) * __mptr = (ptr):将ptr指针赋值给__mptr
							typeof(((type *)0)->member) * __mptr = (ptr);//提取该指针指向的变量的类型	
							--------------------------------------------------------------------------
							(type *)((char *)__mptr - offsetof(type, member));	//强制转换__mptr为char*,加减一为一
								 
					(4)/* Notify drivers */
					mutex_lock(&core_lock);
					bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
					mutex_unlock(&core_lock);
					
					bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);	//对于挂接在i2c_bus上的每一个设备,均可调用__process_new_adapter					
						while ((drv = next_driver(&i)) && !error)
							error = fn(drv, data);	
							
					static int __process_new_adapter(struct device_driver *d, void *data)分析
						 i2c_do_add_adapter(to_i2c_driver(d), data);
							i2c_detect(adap, driver);
							
								int adap_id = i2c_adapter_id(adapter);//获取i2c_adapter的id号
								address_list = driver->address_list;  //获取i2c_driver支持的设备地址数组
								temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);//分配i2c_client空间
								temp_client->adapter = adapter;//绑定设备与适配器
								temp_client->addr = address_list[i];//将i2c_driver支持的设备地址赋值
								err = i2c_detect_address(temp_client, driver);//探测该设备地址的设备是否真实存在?
									err = i2c_check_addr_validity(addr);//设备地址是否有效
									if (i2c_check_addr_busy(adapter, addr));//设备地址是否被占用?
									memset(&info, 0, sizeof(struct i2c_board_info));//清空i2c_board_info
									info.addr = addr;//赋值地址
									err = driver->detect(temp_client, &info);//探测
									if (info.type[0] == '\0') //如果设备名称无则报错,相反则:
									client = i2c_new_device(adapter, &info);//创建相应的i2c_client
									if (client)
										list_add_tail(&client->detected, &driver->clients);//将i2c_client挂接到i2c_driver支持的链表上
			==========================================
			函数宏DEVICE_ATTR内封装的是__ATTR(_name,_mode,_show,_stroe)方法,_show表示的是读方法,_stroe表示的是写方法。
			当然_ATTR不是独生子女,他还有一系列的姊妹__ATTR_RO宏只有读方法,__ATTR_NULL等等
			如 对设备的使用  DEVICE_ATTR  ,对总线使用  BUS_ATTR  ,对驱动使用 DRIVER_ATTR  ,对类 别 (class) 使用  CLASS_ATTR, 
			这四个高级的宏来自于<include/linux/device.h> 
			DEVICE_ATTR  宏声明有四个参数,分别是名称、权限位、读函数、写函数。其中读函数和写函数是读写功能函数的函数名。
			如果你完成了DEVICE_ATTR函数宏的填充,下面就需要创建接口了
			例如:
				static DEVICE_ATTR(polling, S_IRUGO | S_IWUSR, show_polling, set_polling);
				static struct attribute *dev_attrs[] = {
						&dev_attr_polling.attr,
						NULL,
				};
			当你想要实现的接口名字是polling的时候,需要实现结构体struct attribute *dev_attrs[]
			其中成员变量的名字必须是&dev_attr_polling.attr
			然后再封装
				static struct attribute_group dev_attr_grp = {
						.attrs = dev_attrs,
				};
			在利用sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp);创建接口
			==========================================
	备注1:
	struct s3c24xx_i2c {
		wait_queue_head_t	wait;//等待队列
		struct i2c_msg		*msg;//i2c_msg
		unsigned int		irq; //中断号
		struct clk		   *clk; //时钟
		struct device		*dev;//设备模型
		struct i2c_adapter	adap;//适配器结构体
	};	
	
	 -------------------------------------------------------------------------------------------------------------------	
(4)通用设备驱动分析i2c-dev.c:
	 [1]代码分析
	 module_init(i2c_dev_init);
		res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);//注册主设备号为89的字符设备驱动,相应
		i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");//创建i2c_class
		res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);//事件通知链相关
		i2c_for_each_dev(NULL, i2cdev_attach_adapter);//查找i2c_bus上的相关设备
			res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
				while ((dev = next_device(&i)) && !error)//找到设备时执行回调函数fn
					error = fn(dev, data);
		
	i2cdev_attach_adapter
		if (dev->type != &i2c_adapter_type)//如果i2c_bus上不是adapter,则返回0
		adap = to_i2c_adapter(dev);//获取i2c_adapter
		i2c_dev = get_free_i2c_dev(adap);//分配i2c_dev
			i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
			i2c_dev->adap = adap;//绑定adapter与i2c_adapter
			list_add_tail(&i2c_dev->list, &i2c_dev_list);//将i2c_dev添加至i2c_dev_list链表
		i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,MKDEV(I2C_MAJOR, adap->nr), NULL,"i2c-%d", adap->nr);//创建对应的i2c_adapter节点
		
		备注一:
		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,
		};
		
		
	[2] 通用iic代码使用配置:
		i2c-dev驱动的使用
			3.1配置内核
				Device Drivers  --->  
					-*- I2C support  --->
						<*>   I2C device interface  
						
	
	[3]程序编写
		(1)编写配置,读取寄存器数据的子函数
		(2)打开
		(3)设置从机地址 + 超时时间 + 重试次数 
		(4)设置寄存器
		(5)读取数据

相关推荐

全国咨询热线:400-611-6270

?2004-2018华清远见教育科技集团 版权所有 京ICP备16055225号 京公海网安备11010802025203号