【技术】linux i2c_driver 结构体解析

转自:https://blog.csdn.net/liujiaoyage/article/details/36424573
驱动程序的主要工作就是定义并初始化一个i2c_driver结构体(定义于i2c.h中),i2c_driver的成员参考下面。

struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared. You should avoid
* using this, it will be removed in a near future. */
int (*attach_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_driverdriver;
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_headclients;
}; 

i2c_driver中的driver成员至少应该初始化它的name成员。

Legacy model的驱动i2c_driver的函数指针至少应该初始化attach_adapter和detach_ client,另外attach_adapter会使用example_attach函数,这个函数主要是将我们的client注册到系统中。这两个函数指针对应的函数实现比较固定。

Standard driver model的驱动需要注册板级信息。板级信息必须要有driver的id name还有设备的7位从机地址。驱动中不再需要创建i2c_client结构体,它是由i2c内核创建的。驱动中不需要定义设备的地址,取而代之的是i2c_device_id,用来保存支持的设备类型。这里面保存的设备名将会和板级信息中注册的i2c_board_info的名字进行比较,在i2c_device_id中存在的名字才能依附于本驱动。i2c_driver函数指针成员只需要初始化probe和remove就就够了。其它的函数都是可选的。特别需要注意的是,如果同时初始化两种模式需要用到的i2c_driver的成员,那么会报错,因为i2c内核无法判断是哪种模式的驱动。i2c_driver中的probe、remove、detect任何一个被初始化意味着这是一个Standard driver model模式的驱动,attach_adapter和detach_adapter绝对不可以初始化。

PS: Linux下的i2c驱动的编写有两种类型:Legacy model和Standard driver model(new style)。

第一种风格的驱动需要自己创建i2c_client,并且需要驱动作者知道i2c设备的地址。第二种风格的驱动不需要自己创建i2c_client,但是需要填写支持的设备列表或者支持设备的地址列表。

另外,i2c_driver的shutdown、suspend、resume这三个函数指针是否初始化是可选的。这三个函数指针分别对应关机、挂起、唤醒。

如果已经将i2c驱动正确的编译并插入内核,那么内核中提供了一些接口和设备通信:

extern int i2c_master_send(struct i2c_client *client, const char* buf, int len);

extern int i2c_master_recv(struct i2c_client * client,char* buf, int len);

这两个函数都是让client对应的适配器以主机的身份和client->addr地址的设备进行通信,返回值是实际读写的字节数。Linux下的i2c适配器不支持从机模式。

上面的两个函数有个弊端,那就是只能完成单方向的通信,如果通信的过程既有发送又有接收而且接收和发送不能分开,那就需要调用另一个函数:

extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num);

实际上,上面两个函数也是直接调用了i2c_transfer。

i2c_transfer中的参数有三个,第一个是适配器结构体的指针,第二个是消息数组的头指针,第三个是消息的数量。这个函数发送一系列的消息。每个消息可以是读,也可以是写,也可以混合。发送过程是连贯的,在发送中没有停止条件。它的返回值是成功执行的消息数目。

消息的格式定义如下。

struct i2c_msg {
         __u16 addr; // slave address
         __u16 flags;
#define I2C_M_TEN                                0x0010       //10bit地址
#define I2C_M_RD                                  0x0001       //读取数据标志,清零表示写
#define I2C_M_NOSTART                     x4000         //不发送起始位
#define I2C_M_REV_DIR_ADDR        0x2000       // 反转读写标志
#define I2C_M_IGNORE_NAK            x1000         //忽略I2C器件的ack和nack信号
#define I2C_M_NO_RD_ACK               0x0800       //读操作时不去ACK
#define I2C_M_RECV_LEN                   0x0400       //length will be first received byte
         __u16 len; // msg length
         __u8 *buf; // pointer to msg data
};

flags各位的含义已经用宏定义好了。如果连续多条消息的话,除了第一条之外,余下的都不需要发送起始条件。

猜你喜欢

转载自blog.csdn.net/antchen88/article/details/80411050
今日推荐