Linux第103步_了解I2C总线框架
了解Linux中的I2C总线框架为后面做I2C实验做准备,学驱动,就是学习框架,了解是必须的。
1、了解Linux下的I2C子系统中的相关数据结构
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo;
/*i2c_algorithm是I2C适配器与IIC设备进行通信的方法the algorithm to access the bus*/
void *algo_data;
/*对所有设备都有效的数据字段,data fields that are valid for all devices*/
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /*适配器设备,the adapter device */
unsigned long locked_flags; /* owned by the I2C core */
#define I2C_ALF_IS_SUSPENDED 0
#define I2C_ALF_SUSPEND_REPORTED 1
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
struct irq_domain *host_notify_domain;
};
struct i2c_algorithm {
/*如果适配器算法不能进行i2c级访问,则将master_xfer设置为NULL。如果适配器算法可以进行SMBus访问,则设置smbus_xfer。如果设置为NULL,则使用普通I2C消息模拟SMBus协议。
Master_xfer应该返回成功处理的消息数,如果出错则返回负值。
If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If set to NULL, the SMBus protocol is simulated using common I2C messages.
master_xfer should return the number of messages successfully processed, or a negative value on error.
*/
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
/*master_xfer是I2C适配器的传输函数,可以通过此函数来完成与IIC设备之间的通信*/
int (*master_xfer_atomic)(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);
/*smbus_xfer是SMBUS总线的传输函数*/
int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/*确定适配器支持什么,To determine what the adapter supports */
u32 (*functionality)(struct i2c_adapter *adap);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
一个I2C设备对应一个i2c_client结构体变量,系统每检测到一个“I2C从设备”就会给这个设备分配一个i2c_client。
struct i2c_client {
unsigned short flags; /* div., see below*/
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
#define I2C_CLIENT_TEN 0x10
/*我们有一个10位的芯片地址;we have a ten bit chip address */
/* Must equal I2C_M_TEN below */
#define I2C_CLIENT_SLAVE 0x20 /*我们是从机;we are the slave */
#define I2C_CLIENT_HOST_NOTIFY 0x40
/*我们想使用I2C主机通知;We want to use I2C host notify */
#define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */
#define I2C_CLIENT_SCCB 0x9000
/* Use Omnivision SCCB protocol */
/* Must match I2C_M_STOP|IGNORE_NAK */
unsigned short addr;
/*芯片的7位地址, 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 device dev; /* the device structure*/
int init_irq; /* irq set at initialization */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /*从模式回调;callback for slave mode */
#endif
};
struct i2c_driver {
unsigned int class;
/*标准驱动模型接口; Standard driver model interfaces */
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *client);
/* New driver model interface to aid the seamless removal of the current probe()'s, more commonly unused than used second parameter.*/
int (*probe_new)(struct i2c_client *client);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *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").
* For the SMBus Host Notify protocol, the data corresponds to the
* 16-bit payload data reported by the slave device acting as master.
*/
void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol,
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;
/*如果使用设备树的话,需要设置device_driver的of_match_table成员变量,
也就是驱动的兼容(compatible)属性*/
const struct i2c_device_id *id_table;
/*id_table是传统的、未使用设备树的设备匹配ID表*/
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *client, struct i2c_board_info *info);
const unsigned short *address_list;
struct list_head clients;
bool disable_i2c_core_irq_mapping;
};
struct i2c_board_info {
char type[I2C_NAME_SIZE];/*I2C设备的名字*/
unsigned short flags;
unsigned short addr;/*I2C器件地址*/
const char *dev_name;
void *platform_data;
struct device_node *of_node;
struct fwnode_handle *fwnode;
const struct property_entry *properties;
const struct resource *resources;
unsigned int num_resources;
int irq;
};
#define I2C_BOARD_INFO(dev_type, dev_addr) .type = dev_type, .addr = (dev_addr)
static struct i2c_board_info armadillo5x0_i2c_rtc = {
I2C_BOARD_INFO("s35390a", 0x30),
/*type[]="s35390a"表示I2C设备的名字,addr=0x30表示I2C器件地址*/
};
struct i2c_msg {
__u16 addr; /*从机地址,slave address*/
__u16 flags; /*标志*/
#define I2C_M_RD 0x0001 /* read data, from slave to master */
/* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN 0x0010
/*0x0010表示10位的芯片地址, this is a ten bit chip address */
#define I2C_M_DMA_SAFE 0x0200
/* the buffer of this message is DMA safe */
/* makes only sense in kernelspace */
/* userspace buffers are copied anyway */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
__u16 len; /*消息长度;msg length*/
__u8 *buf; /*指向消息数据;pointer to msg data*/
};
2、了解Linux下的I2C子系统中的相关函数
int i2c_add_adapter(struct i2c_adapter *adapter);
//动态分配一个总线编号;adapter是I2C适配器;
//返回值:0表示成功;负值表示失败;
int i2c_add_numbered_adapter(struct i2c_adapter *adap);
//指定一个静态的总线编号;adap是I2C适配器;
//返回值:0表示成功;负值表示失败;
void i2c_del_adapter(struct i2c_adapter *adap);
//删除I2C适配器,adap是要删除的I2C适配器;
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
//owner: 一般为THIS_MODULE.
//driver:要注册的i2c_driver;
//返回值:0表示成功;负值表示失败;
#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver)
void i2c_del_driver(struct i2c_driver *driver);
//注销I2C设备驱动;driver:要注销的i2c_driver;
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
//adap是I2C适配器;msgs表示I2C要发送的消息;num表示消息的数量;
//返回值:非幅值表示发送的msgs的数量;负值表示失败;
static inline int i2c_master_send(const struct i2c_client *client,const char *buf, int count);
//client表示I2C设备对应的i2c_client;
//buf指向要发送的数据;count表示要发送的数据字节数量
//注意:count要小于64Kb,因为i2c_msg结构中的len成员是u16型的;
//返回值:非幅值表示发送的字节的数量;负值表示失败;
static inline int i2c_master_recv(const struct i2c_client *client,char *buf, int count);
//client表示I2C设备对应的i2c_client;
//buf指向要接收的数据;count表示要接收的数据字节数量
//注意:count要小于64Kb,因为i2c_msg结构中的len成员是u16型的;
//返回值:非幅值表示接收到的字节的数量;负值表示失败;
3、i2c_driver注册流程:
/* i2c驱动的probe函数 */
static int xxx_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
/* 函数具体程序 */
return 0;
}
/* i2c驱动的remove函数 */
static int ap3216c_remove(struct i2c_client *client)
{
/* 函数具体程序 */
return 0;
}
/*传统匹配方式ID列表,无设备树的时候匹配ID表*/
static const struct i2c_device_id xxx_id[] = {
{"xxx", 0},
{}
};
/*设备树匹配列表,设备树所使用的匹配表*/
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx" },
/*在stm32mp157d-atk.dts设备树文件中,定义“compatible = "xxx";”*/
{ /*这是一个空元素,在编写of_device_id时最后一个元素一定要为空*/
/* Sentinel */
}
};
/* i2c驱动结构体*/
static struct i2c_driver xxx_driver = {
.probe = xxx_probe,/*i2c驱动的probe函数为xxx_probe()*/
.remove = xxx_remove,/*i2c驱动的remove函数为xxx_remove()*/
.driver = {
.owner = THIS_MODULE,
.name = "xxx",
.of_match_table = xxx_of_match,
},
.id_table = xxx_id,
};
/*驱动入口函数*/
static int __init xxx_init(void)
{
int ret = 0;
ret = i2c_add_driver(&xxx_driver);
return ret;
}
/*驱动出口函数*/
static void __exit xxx_exit(void)
{
i2c_del_driver(&xxx_driver);
}
module_init(xxx_init);//声明xxx_init()为驱动入口函数
module_exit(xxx_exit);//声明xxx_exit()为驱动出口函数
4、I2C总线的数据结构:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,/*I2C总线的设备和驱动匹配函数*/
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
/* Attempt an OF style match */
if (i2c_of_match_device(drv->of_match_table, client)) return 1;
/*比较I2C设备节点的compatible属性和of_device_id中的compatible属性是否相等,如果相当的话就表示I2C设备和驱动匹配。*/
/*用于ACPI形式的匹配;Then ACPI style match */
if (acpi_driver_match_device(dev, drv)) return 1;
driver = to_i2c_driver(drv);
/* Finally an I2C match */
if (i2c_match_id(driver->id_table, client)) return 1;
/*用于传统的、无设备树的I2C设备和驱动匹配过程。比较I2C设备名字和i2c_device_id的 name字段是否相等,相等的话就说明I2C设备和驱动匹配成功。*/
return 0;
}
5、I2C设备多寄存器数据读写:
/* 设备结构体 */
struct xxx_dev {
......
void *private_data; /* 私有数据,一般会设置为i2c_client */
};
/*
* @description : 读取I2C设备多个寄存器数据
* @param – dev : I2C设备
* @param – reg : 要读取的寄存器首地址
* @param – val : 读取到的数据
* @param – len : 要读取的数据长度
* @return : 操作结果
*/
static int xxx_read_regs(struct xxx_dev *dev, u8 reg, void *val, int len)
{
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *) dev->private_data;
/* msg[0],第一条写消息,发送要读取的寄存器首地址 */
msg[0].addr = client->addr; /* I2C器件地址 */
msg[0].flags = 0; /* 标记为发送数据 */
msg[0].buf = ® /* 读取的首地址 */
msg[0].len = 1; /* reg长度 */
/* msg[1],第二条读消息,读取寄存器数据 */
msg[1].addr = client->addr; /* I2C器件地址 */
msg[1].flags = I2C_M_RD; /* 标记为读取数据 */
msg[1].buf = val; /* 读取数据缓冲区 */
msg[1].len = len; /* 要读取的数据长度 */
ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {ret = 0;}
else { ret = -EREMOTEIO;}
return ret;
}
/*
* @description : 向I2C设备多个寄存器写入数据
* @param – dev : 要写入的设备结构体
* @param – reg : 要写入的寄存器首地址
* @param – val : 要写入的数据缓冲区
* @param – len : 要写入的数据长度
* @return : 操作结果
*/
static s32 xxx_write_regs(struct xxx_dev *dev, u8 reg, u8 *buf, u8 len)
{
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *) dev->private_data;
b[0] = reg; /* 寄存器首地址*/
memcpy(&b[1],buf,len); /* 将要发送的数据拷贝到数组b里面 */
msg.addr = client->addr; /* I2C器件地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = b; /* 要发送的数据缓冲区,发送的数据是数组b[] */
msg.len = len + 1; /* 要发送的数据长度,加1是因为要加上一个字节的寄存器地址 */
return i2c_transfer(client->adapter, &msg, 1);
}
原文地址:https://blog.csdn.net/weixin_42550185/article/details/145291116
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!