###1. i2c总线:

i2c是“Inter-IC” bus 的缩写,它是一种广泛应用于低速率通信的简单的总线协议。由于它是一个许可商标, 有些厂商也使用其它的名称(比如”Two-Wire Interface”, TWI) ,i2c只需要两根信号线(时钟线SCL,数据线SDA),大部分i2c设备使用7bit地址,总线速率可达400KHz(高速扩展可达3.4MHz,但很少被使用)。i2c是一条多主机总线,开漏信号被用来在主机之间来进行仲裁,以及在慢速设备之间进行握手及同步时钟。

###2. linux的i2c接口:

linux的i2c接口只支持主机端的通信,i2c接口围绕两种驱动和两种设备来组织,适配器驱动(“Adapter Driver”)用来抽象硬件上的i2c控制器,它绑定到一个物理设备(可能是PCI设备或者平台设备),并为它管理的每一条i2c总线使用一个结构体“i2c_adapter”来表示。而在i2c总线上, 使用结构体”i2c_client”来代表挂接在上面的i2c设备。这些i2c设备都应该绑定到一个结构体“i2c_driver”上以符合linux的驱动模型。有一系列的函数用来进行i2c协议的操作,到目前为止, 只能从任务上下文中使用它们.

linux中i2c驱动框架中,分为总线(BUS)驱动和设备(DEVICE)驱动,总线驱动的职责是为系统中每一个i2c总线实现相应的读写方法,提供给设备驱动来使用,但是总线驱动并不进行任何的通信。设备驱动则是与挂接在i2c总线上的设备进行通讯的驱动,通过i2c总线驱动提供的方法, 设备驱动可以忽略不同的i2c总线控制器的差异。

###3. i2c总线驱动:

在系统开机时, 首先装载总线驱动,一个驱动用于支持一条特定的i2c总线的读写,一个总线驱动通常使用两个数据结构来描述:

  • i2c_adapter
  • i2c_algorithm

####3.1 i2c_adapter

i2c_adapter 用于描述一个特定的i2c总线控制器

struct i2c_adapter {
	struct module *owner;
	unsigned int class;               		/* classes to allow probing for */
	const struct i2c_algorithm *algo; 		/* the algorithm to access the bus */
	void *algo_data;

	/* data fields that are valid for all devices   */
	struct rt_mutex bus_lock;

	int timeout;                    		/* 单位为 jiffies */
	int retries;
	struct device dev;

	int nr;					/*i2c bus 编号, 若置为-1, 则代表动态分配*/
	char name[48];
	struct completion dev_released;

	struct mutex userspace_clients_lock;
	struct list_head userspace_clients;
};

向系统中注册 i2c adpater 可以使用如下api

int i2c_add_numbered_adapter(struct i2c_adapter *adap)
int i2c_add_adapter(struct i2c_adapter *adapter)

这两个api使用任意一个即可, 它们都会自动检查是否需要动态分配总线号还是使用指定的总线号

注销 i2c adapter 可以使用

void i2c_del_adapter(struct i2c_adapter *adap)

####3.2 i2c_algorithm

i2c_algorithm 用于描述i2c总线的传输方法的实现

struct i2c_algorithm {

	/* 如果adapter不能支持i2c访问, 则置 master_xfer 为NULL */
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

	/* 如果adapter支持SMBus访问, 则设置smbus_xfer, 若 smbus_xfer 为NULL, 则使用I2C访问来模拟SMBus访问 */
	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 *);		/* 用于查询i2c adapter支持那些function */
};

i2c_adaptrer对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个i2c适配器需要i2c_algorithm提供的通信函数来控制适配器器上产生特定的访问信号,因此在i2c_adapter中包含其使用的i2c_algorithm的指针。

不同的i2c总线控制器,都有各自的 i2c_adapter, 但是若它们的操作方式相同, 则可以共享同一 i2c_algorithm, 例如, 移动设备的SoC上通常集成有多条i2c总线, 但是他们的操作方式是相同的, 因此可以共享同一 i2c_algorithm

####3.3 i2c_msg

i2c_algorithm中的通信函数以 i2c_msg 为通信的基本单位:

struct i2c_msg {
	__u16 addr;     /* slave address                        */
	__u16 flags;
		#define I2C_M_TEN               0x0010  /* this is a ten bit chip address */
		#define I2C_M_RD                0x0001  /* read data, from slave to master */
		#define I2C_M_NOSTART           0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */
		#define I2C_M_REV_DIR_ADDR      0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
		#define I2C_M_IGNORE_NAK        0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
		#define I2C_M_NO_RD_ACK         0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
		#define I2C_M_RECV_LEN          0x0400  /* length will be first received byte */
		#define I2C_M_NEED_DELAY        0x0020  // add by kfx
		#define I2C_M_REG8_DIRECT       0x0040  // add by kfx
		__u16 len;              /* msg length                           */
		__u8 *buf;              /* pointer to msg data                  */
};

###4. i2c设备驱动:

####4.1 i2c_client

总线驱动只是实现了对一条总线的读写,与具体的设备进行通信是由i2c设备驱动来进行的,一个i2c的设备驱动由两个模块(i2c_driver 和 i2c_client)来描述:

struct i2c_client {
	unsigned short flags;
	unsigned short addr;		//设备地址
	char name[I2C_NAME_SIZE];		//设备名称
	struct i2c_adapter * adapter;	//设备所在的i2c总线的adapter
	struct i2c_driver * driver;		//绑定的driver
	struct device dev;			
	int irq;				//设备的irq号
	struct list_head detected;		//用于将同一个i2c_driver所驱动的i2c_client形成链表
};

i2c_client对应于真实的物理设备,每一个i2c设备都需要一个i2c_client来表示

####4.2 i2c_driver

struct i2c_driver {
	unsigned int class;
	int (* attach_adapter) (struct i2c_adapter *);	/* 旧式i2c driver的方法, 不要再使用 */
	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;
	int (* detect) (struct i2c_client *, struct i2c_board_info *);
	const unsigned short * address_list;
	struct list_head clients;
};

i2c_driver对应一个驱动的方法,不对应任何的物理实体

####4.3 i2c_device_id

struct 用于描述i2c_driver和i2c_client匹配的条件

struct i2c_device_id {
	char name[I2C_NAME_SIZE];		//该name和i2c_client.name相同,则i2c_client和i2c_driver匹配成功, 进行后续的probe过程
	kernel_ulong_t driver_data;		//传递给driver的私有数据, 不使用则置0
};

例如

struct i2c_device_id kxtj2_id[] = {  
	{ "kxtj2", 0, }
	{ "kxtj9", 0, }
	{}	/*空成员, 用于标识结尾*/  
};  

struct i2c_driver kxtj2_driver = {  
	.driver = {  
		.name = "gsensor-kxtj2",  
		.owner = THIS_MODULE,  
	},  
	.id_table = kxtj2_id,  
	......
}

i2c_driver.id_table 中可以保存多个struct i2c_device_id, 在匹配时, 依次比较其中的每一个struct i2c_device_id, 直到结束或者匹配成功

一个i2c_client只能绑定到一个i2c_driver, 一个i2c_driver可以绑定到多个i2c_client, 当注册一个i2c_client或者i2c_driver时, linux中的i2c bus core会遍历已经注册的i2c_driver或者已注册但是未绑定driver的i2c_client, 当i2c_client和i2c_driver匹配成功后,进行linux driver model的probe过程

利用i2c_driver.id_table 进行匹配是linux i2 core的标准做法, 为了支持ACPI和Open Firmware, 还扩展了 i2c_driver.acpi_match_table 和 i2c_driver.of_match_table, 后续章节将会讲到

####4.4 如何生成i2c_client

i2c总线不具备枚举能力, 因此, 需要使用合适的方式来注册i2c_client, 有多种方式可以实现这一目的

#####4.4.1 使用 i2c_register_board_info() 来注册i2c_clien

如果明确的知道存在哪些i2c设备, 他们的地址以及所连接的总线, 那么,可以使用struct i2c_board_info来描述i2c设备

struct i2c_board_info {
	char		type[I2C_NAME_SIZE];	//设备名称, 和i2c_driver匹配时需要使用这一名称
	unsigned short	flags;			//用于初始化i2c_client.flag成员
	unsigned short	addr;			//i2c设备的地址
	void		*platform_data;		//i2c设备的私有数据, 自定义
	struct dev_archdata	*archdata;	//用于初始化 i2c_client.dev.archdata
	struct device_node *of_node;		//指向openfirmware的device node
	struct acpi_dev_node acpi_node;		//指向ACPI的device node
	int		irq;			//i2c设备的irq号
};

然后使用i2c_register_board_info() 来根据i2c_board_info注册i2c_client (i2c_board_info中最少需要填充 type和addr成员)

int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned n);

例如:

struct i2c_board_info kxtj2_info[] = {  
	{	
		.type = "kxtj2",  
		.addr = 0x0f,  
		},
};  

struct i2c_device_id kxtj2_id = {  
	"kxtj2",  
	0,  
};  

struct i2c_driver kxtj2_driver = {  
	.driver = {  
		.name = "gsensor-kxtj2",  
		.owner = THIS_MODULE,  
	},  
	.id_table = kxtj2_id,  
	......
}

static int __init kxtj2_init(void)	{  
	i2c_register_board_info(5,kxtj2_info, ARRAY_SIZE(kxtj2_info) );  
	return i2c_add_driver(kxtj2_driver);  
}      

module_init(kxtj2_init);  

上述的实例代码只是展示i2c_register_board_info()的使用方式, 事实上,作为一种标准的做法, i2c_board_info 应该放在 linux kernel 源码中 /arch 下架构相关的子目录里面的board.c(名称并不固定)文件中定义, 并在MACHINE_START中会间接调用(比module_init的调用过程早)i2c_register_board_info来注册i2c设备, 在linux源码中的(arch/arm/plat-xxx和arch/arm/mach-xxx)中可以找到大量的i2c_client的注册过程

i2c_register_board_info()可以算作是静态初始化i2c_client,通过其注册的i2c_board_info会被存储在链表 __i2c_board_list中, 当对应的i2c总线的driver初始化时(即通过i2c_add_adapter()来注册i2c_adapter时), 或遍历__i2c_board_list, 根据其中的内容来注册i2c_client, 这意味着, i2c_register_board_info()必须先于i2c_add_adapter()被调用, 否则,i2c core将不会去注册i2c_client, 因为i2c_register_board_info()都是在MACHINE_START()中被调用, 早于i2c controller driver的初始化, 因此不会有这一问题, 但若是在i2c device driver中才调用i2c_register_board_info()来注册i2c_client则有可能为时太晚(特别是driver被编译为模块时)

#####4.4.2 使用 i2c_new_device 和 i2c_new_probed_device 创建i2c_client

使用 i2c_register_board_info() 来注册i2c_clien的方式存在诸多限制:

  1. 必须在MACHINE_STAR()阶段就注册i2c_board_info
  2. 必须在编译内核时确定i2c总线编号和设备地址

但是如果开发者不确定有哪些i2c设备,有多少i2c总线,就需要自己动态注册i2c_client了

动态注册i2c_client时, 同样需要使用 struct i2c_board_info 来描述i2c设备的相关信息, 然后使用i2c_new_device()来动态注册i2c_client了

i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

例如

struct i2c_board_info kxtj2_info = {  
	.type = "kxtj2",  
	.addr = 0x0f,
};  

struct i2c_device_id kxtj2_id = {  
	"kxtj2",  
	0,  
};  

struct i2c_driver kxtj2_driver = {  
	.driver = {  
		.name = "gsensor-kxtj2",  
		.owner = THIS_MODULE,  
	},  
	.id_table = kxtj2_id,
	......
}

static int __init kxtj2_init(void)	{  
	struct i2c_adapter *adapter;  
	struct i2c_client *client;  
    
	/* 请确定此时对应的adapter的的driver已经加载好, 否无法获取adapter*/  
	if( NULL == ( adapter = i2c_get_adapter(4) ) )  
		return -1;  
        
	if( NULL == ( client = i2c_new_device(adapter, &kxtj2_info) ) )  
		return -2;  
        
	i2c_put_adapter(adapter);  
	return i2c_add_driver(kxtj2_driver);  
}  

	module_init(kxtj2_init);  

如果某个i2c设备作为一种特性, 在同一种产品的不同配置中可能存在,也有可能不存在,又或者它可能使用不同的slave address,那么,可以列出所有可能的地址值,然后使用i2c_new_probed_device()在这些地址上进行probe(), 根据probe的结构, 会动态生成对应的i2c_client

struct i2c_client * i2c_new_probed_device(struct i2c_adapter *adap,
			struct i2c_board_info *info,
	      		unsigned short const *addr_list,
	      		int (*probe)(struct i2c_adapter *, unsigned short addr));
			
			//probe回调函数可以不指定, 使用i2c core默认的函数 i2c_default_probe()

例如, kxtj2可能使用 0x0e和0x0f这两个地址中的一种

struct i2c_board_info kxtj2_info = {  
	.type = "kxtj2",  
	.addr = 0x0f,	/* 此地址值无用,将使用探测到的地址来替代它*/  
};  

/* kxtj2可能使用的slave address */
static const unsigned short kxtj2_valid_addr = {
	0x0e,
	0x0f,
	I2C_CLIENT_END,
};

struct i2c_device_id kxtj2_id = {  
	"kxtj2",  
	0,  
};  

struct i2c_driver kxtj2_driver = {  
	.driver = {  
		.name = "gsensor-kxtj2",  
		.owner = THIS_MODULE,  
	},  
	.id_table = kxtj2_id,
	......
	}

static int __init kxtj2_init(void)	{  
	struct i2c_adapter *adapter;  
	struct i2c_client *client;  
    
	/* 请确定此时对应的adapter的的driver已经加载好, 否无法获取adapter*/  
	if( NULL == ( adapter = i2c_get_adapter(4) ) )  
		return -1;  
        
	if( NULL == ( client = i2c_new_probed_device(adapter, &kxtj2_info,  kxtj2_valid_addr, NULL ) ) )  
		return -2;  
        
	i2c_put_adapter(adapter);
    
	return i2c_add_driver(kxtj2_driver);  
}  

	module_init(kxtj2_init);  

由于i2c adapter可能在一个即插即用设备上, 它有可能在任何时候被插接在系统上, 这意味着你的i2c设备驱动有可能在i2c总线驱动之前被加载, 这个时候, 在使用i2c_get_adapter的时候将会失败, 因为此时对应的adapter还未被建立起来, 那么在 module_init 里面进行i2c_new_device之类的操作并不安全。为此, 可以将这些操作放在i2c_driver中的.attach_adapter中。

使用 i2c_new_device() 和 i2c_new_probed_device() 这两个函数来创建i2c_client的driver有义务在退出时销毁创建的i2c_client, 可以使用i2c_unregister_device()来注销创建的i2c_client

#####4.4.3 在所有的i2c总线上探测i2c设备

如果你连有多少i2c设备, 挂接在哪一条总线上, 设备地址等信息都一无所知的话, 那你就没办法使用 i2c_register_board_info() 方法和 i 2c_new_device() 的方法来创建i2c_client了(这些方法要求指定i2c总线号)但是,如果你知道通过设备ID来判别设备的话, 那么你还有 i2c_driver.detect() 方法可以使用

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

给出设备可能的addr的列表, 并且提供一个detect回掉函数,在i2c_add_driver()时, i2c core会在所有已经注册的i2c总线上, 遍历所有指定的地址, 若检测到有设备存在, 则生成一个临时的i2c_client, 然后回调 i2c_driver.detect() 函数, 在该函数中, device driver需要自行判断是否支持该设备, 若是, 则可以填充i2c_board_info中的相关信息然后返回0, i2c core会根据填充的 i2c_board_info 来调用i2c_new_device() 来正式注册一个i2c_client

例如

struct i2c_device_id kxtj2_id = {  
	"kxtj2",  
	0,  		/* 此地址值无用,将使用探测到的地址来替代它*/
};  

static const unsigned short kxtj2_valid_addr[] = {  
	0x0e,  
	0x0f,  
	};  

int kxtj2_detect(struct i2c_client *client, struct i2c_board_info *info)  
{  
	unsigned short chip_id = i2c_smbus_read_byte_data(client, 0x00);  
  
	/* 如果检测到设备, 至少需要填充info的type域 */  
	if( 0x09 == chip_id )  
		info->type = "kxtj2";  
	else  
		return -ENODEV;  
        
	/* 此函数中不得修改info的type域 */
	return 0;
}

struct i2c_driver kxtj2_driver = {  
	.driver = {  
		.name = "gsensor-kxtj2",  
		.owner = THIS_MODULE,  
	},  
	.id_table = kxtj2_id
	......  
	.detect = kxtj2_detect,  
	.address_list = kxtj2_valid_addr,  
	......
}

module_i2c_driver(kxtj2_driver);  

要使用detect的方法, 前提是总线adapter支持该类driver的探测(i2c_adapter->class & i2c_driver->class != 0).

要使用i2c_driver.detect() 回调接口, 需要填充i2c_driver中的address_list域,声明所有需要探测的设备地址,并实现它的detect回调函数。 在i2c_add_driver()时, i2c_core会在所有已经注册的adapter上探测address_list中的所有地址, 探测到存在i2c设备且未绑定driver后, 会调用detect回调函数,在该回调函数中, device driver需要自行判断是否支持该设备, 若是, 则可以填充i2c_board_info中的相关信息然后返回0, i2c core会根据填充的 i2c_board_info 来调用i2c_new_device() 来正式注册一个i2c_client(如果info->type不为空, 则建立一个i2c_client),这一步会为每一个adapter上的每一个detect成功的地址创建一个i2c_client.

使用这一种方法建立的client在driver退出或者所在的i2c bus脱离时会被自动销毁, 该方法比i2c_register_board_info() 方法和 i 2c_new_device() 的方法更为灵活, 但是不如前两种方法快速和安全,应该尽可能使用前两种方法。

#####4.4.4 在用户空间枚举建立

每一个i2c_adapter会在sys文件系统下面建立两个只写的属性文件“new_device”和“delete_device”用来建立和删除i2c设备:

# echo kxtj2 0x0f > /sys/class/i2c-adapter/i2c-5/new_device  
# echo 0x0f > /sys/calss/i2c-adapter/i2c-5/delete_device

该方法使用方便, 在某些情况下(缺少相关信息, 方法1和方法2不能使用,而adapter不支持driver的detect)只能使用该方式。

#####4.4.5 使用ACPI的信息来建立i2c_client

在使用ACPI(主要是x86)的平台上, 可以通过ACPI来上报i2c设备信息,那么,linux中提供了 acpi_i2c_register_devices()接口来遍历ACPI信息, 注册i2c_client

void acpi_i2c_register_devices(struct i2c_adapter *adapter)

通常是在i2c adapter的driver的probe过程中调用此接口来注册i2c_client

这一接口事实上还是根据ACPI上报的设备信息,调用i2c_new_device()来注册i2c_client

通过ACPI上报i2c设备信息的系统上, 还可以使用 i2c_driver.acpi_match_table 来进行 i2c_client 和 i2c_driver 的匹配

struct acpi_device_id {
	__u8 id[ACPI_ID_LEN];
	kernel_ulong_t driver_data;
};

例如

static struct acpi_device_id kxtj2_acpi_match[] = {
	{ "kxtj2", 0 }, 
	{ },
};

static struct i2c_driver kxtj2_driver = {
	.driver = {
		.name   = "kxtj2",
		.owner  = THIS_MODULE,
		.acpi_match_table = ACPI_PTR(kxtj2_acpi_match),
	},   
	.probe      = kxtj2_probe,
	.remove     = kxtj2_remove,
	.id_table   = kxtj2_id,
}

i2c_core 先使用 i2c_driver.acpi_match_table进行匹配, 若匹配不成功, 再尝试使用i2c_driver.id_table进行匹配

即使是利用 i2c_driver.acpi_match_table 也需要保证 i2c_driver.id_table 不为空, 当 i2c_driver.probe 或者 i2c_driver.id_table 为空时, i2c_driver.probe 将不会被调用(见 i2c_device_probe() )

#####4.4.6 使用dts信息来建立i2c_client

早期的arm平台上, 都是在MACHINE_START()过程中完成板级设备信息的初始化, 因此, “arch/arm/plat-xxx“和”arch/arm/mach-xxx”中存在大量的垃圾代码, 后续arm平台引入了open firmware的dts(Device Tree Source)的方式来描述板级设备/资源信息, 同样的, i2c设备也能使用dts来描述, 例如,G-sensor kxtj2在dtsi中的描述如下

i2c@f9925000 { /* BLSP-1 QUP-3 */
	kionix@f {
		compatible = "kionix,kxtj9";
		reg = <0x0f>;
		interrupt-parent = <&msmgpio>;
		interrupts = <81 0x2>;
		vdd-supply = <&pm8110_l19>;
		vio-supply = <&pm8110_l14>;
		kionix,min-interval = <5>;
		kionix,init-interval = <200>;
		kionix,axis-map-x = <1>;
		kionix,axis-map-y = <0>;
		kionix,axis-map-z = <2>;
		kionix,g-range = <2>;
		kionix,negate-x;
		kionix,negate-y;
		kionix,negate-z;
		kionix,res-12bit;
	};
};  

其中“kionix, xxx”是设备特定的信息,只关注i2c设备的相关信息

i2c@f9925000 { /* BLSP-1 QUP-3 */
	kionix@f {
		compatible = "kionix,kxtj2";
		reg = <0x0f>;
	};
};

dts信息描述了kxtj2的i2c地址为0x0f

Open Firmware 提供了一个接口用于根据dts信息来注册 i2c_client

void of_i2c_register_devices(struct i2c_adapter *adap);

该接口一般由i2c controller driver来调用, 来根据dts信息为i2c bus上的i2c设备注册 i2c_client

通过Open firmware 的dts上报的i2c设备, 其driver还可以使用 i2c_driver.of_match_table 来进行i2c_client 和 i2c_driver 的匹配

struct of_device_id
{
	char	name[32];
	char	type[32];
	char	compatible[128];
	const void *data;
};

例如

static struct of_device_id kxtj2_match_table[] = {
		{ .compatible = "kionix,kxtj2", },
		{ }, 
};

static struct i2c_driver kxtj2_driver = {
		.driver = {
    			.name   = DEVICE_NAME,
    			.owner  = THIS_MODULE,
    			.of_match_table = kxtj2_match_table,
		},   
		.probe      = kxtj2_probe,
		.remove     = kxtj2_remove,
		.id_table   = kxtj2_id,
};

i2c_core 先使用 i2c_driver.of_match_table进行匹配,若匹配不成功, 则尝试使用i2c_driver.acpi_match_table进行匹配, 若匹配不成功, 再尝试使用i2c_driver.id_table进行匹配

即使是利用 i2c_driver.of_match_table 也需要保证 i2c_driver.id_table 不为空, 当 i2c_driver.probe 或者 i2c_driver.id_table 为空时, i2c_driver.probe 将不会被调用(见 i2c_device_probe() )

####4.5 i2c client data

i2c device driver可以支持多个driver, 有时在driver中需要为某个device维护一份private data, 则可以使用i2c client data, i2c client data 本质是在 i2c_client->dev->p 中保存一个指针, 指向一块数据

如下两个接口可用于 set/get i2c client data

void i2c_set_clientdata(struct i2c_client *dev, void *data);
void *i2c_get_clientdata(const struct i2c_client *dev);

####4.6 i2c 传输的接口

在 i2c device driver 中,一定会对i2c device 发起读写操作, 可以例如 i2c core 提供的接口

i2c core提供了最基本的传输接口, 可以完成i2c的单向传输和复合传输

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

####4.7 SMBus 传输接口

i2c core同样还提供了接口, 用于完成SMBus命令操作

最基本的SMBus传输接口

s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
	   char read_write, u8 command, int protocol,
	   union i2c_smbus_data *data)

在此基础上, 还封装了如下的接口, 用于完成不同类型的SMBus传输操作

s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, u8 *values)

s32 i2c_smbus_read_byte(const struct i2c_client *client);

s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)

s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, u8 *values)

s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command)

s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values)

s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)

s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value)

s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values)

s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command,u16 value)

####4.8 获取对应i2c总线的adapter

某些操作需要指定 i2c_adapter, 根据i2c_client可以获取对应的adapter( i2c_client->adapter)

还使用如下api根据总线号获取adapter

struct i2c_adapter *i2c_get_adapter(int nr)

释放对i2c adapter的引用

void i2c_put_adapter(struct i2c_adapter *adap)

i2c_get_adapter() / i2c_put_adapter() 应成对出现

有时候, 需要根据 i2c_adapter.dev 的地址, 取得i2c_adapter指针, 可以使用如下的两种接口

struct i2c_adapter *i2c_verify_adapter(struct device *dev)

#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)

####4.9 检查i2c adapter支持的function

如下api可以获取某个i2c adapter所支持的function

u32 i2c_get_functionality(struct i2c_adapter *adap)

如下api可以检查某个i2c adapter是否支持所给的function

int i2c_check_functionality(struct i2c_adapter *adap, u32 func)