linux驱动:i2c设备总线驱动
Posted 超凡东皇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux驱动:i2c设备总线驱动相关的知识,希望对你有一定的参考价值。
前言
linux下设备跟驱动是分开的,他们通过总线进行匹配,设备由设备树负责,在设备树中添加相应的结点,系统会自动向总线注册相应的设备,而驱动开发需要负责的主要就是驱动的编写,向总线注册驱动,如果通过of_device_id中的.compatible跟设备匹配成功进执行其中的probe函数。
i2c结构体
对于i2c设备我们都不陌生,i2c是一种实际存在的物理总线,不像有些字符设备,使用的是虚拟的platform总线,实际上是不存在的,是虚拟出来的总线。在linux下写i2c驱动,掌握4个结构体就够了,其定义都在/linux/include/i2c.h下
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
*/
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; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
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 i2c_algorithm {
/* 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);
int (*smbus_xfer) (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 *);
};
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_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;
};
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 device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
#ifdef CONFIG_MTK_I2C_EXTENSION
__u32 timing; /* parameters of timings */
__u32 ext_flag;
#endif
};
①i2c_adapter
对应物理上的一个适配器,这是跟i2c总线相关的结构体
②i2c_algorithm
对应相应的通信方法,与适配器进行绑定
③i2c_driver
描述驱动内容,对应一套驱动方法,i2c_device_id是该驱动所支持的I2C设备的ID表。
④i2c_client
对应真实的物理设备,并且一个设备对应一个i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client。
i2c核心代码
i2c总线的核心代码就在i2c-core.c中,包含了i2c总线的注册,没错,总线也是一种结构体(bus_type),它是通过bus_register接口向系统中注册的
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
每一种总线(bus)都包含两条链(device与driver),设备与驱动,当他们注册的时候,match函数中会挨个进行匹配,匹配上了就执行probe函数
i2c总线上也有2条链,i2c_client链和i2c_driver链,当任何一个driver或者client去注册时,i2c总线都会调用match函数去对client.name与driver.id_table.name进行循环匹配
struct bus_type i2c_bus_type = {
.name = "i2c",//在/sys/bus/下显示的名字
.match = i2c_device_match,//作用:负责driver与device进行匹配(一般用名字进行匹配)
.probe = i2c_device_probe,//一个驱动安装或者设备安装被匹配上的时候就执行probe函数
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
如果driver.id_table.name中所有的id都匹配不上则说明client并没有driver,就结束了;
如果匹配上了,则标明client和driver是适用的,那么i2c总线会调用自身的probe函数,自身的函数又会调用driver中提供的probe函数,driver中的probe函数会对设备进行硬件初始化和后续工作
static int __init i2c_init(void)
{
int retval;
retval = bus_register(&i2c_bus_type);//注册i2c总线,总线也是一个结构体
if (retval)
return retval;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);//添加一个空驱动
if (retval)
goto class_err;
return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
bus_unregister(&i2c_bus_type);
return retval;
}
static void __exit i2c_exit(void)
{
i2c_del_driver(&dummy_driver);
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
#endif
bus_unregister(&i2c_bus_type);
tracepoint_synchronize_unregister();
}
注:如果同样的设备有多个,可以用同一份驱动,在设备树中添加多个节点,使用不同的i2c地址,而i2c地址包含在设备结构体中(即i2c_client),在/sys/bus/i2c/devices/下根据不同地址进行访问,那么有人问了,能不能将多个i2c设备的地址弄成相同的,这种情况就像一个班里有好几个重名的同学,重名可以吗?是可以的,比如对他们说下课了,大家都一样收到信息知道下课了,这就是广播,只是某些情况有些麻烦,比如需要找其中某一个,就不好找了,如果是这种情况,i2c设备地址就应该区分,所以具体得看应用的场景。
以上是关于linux驱动:i2c设备总线驱动的主要内容,如果未能解决你的问题,请参考以下文章