I2C驱动框架分析(1):I2C重要概念与数据结构
I2C驱动框架分析(2):I2C框架源码分析
I2C驱动框架分析(3):DW_I2C驱动分析
第二章:I2C框架源码分析
2.1 I2C框架初始化
linux系统启动后调用i2c_init
函数,向系统初始化i2c框架,函数主要内容为:
static int __init i2c_init(void)
{
int retval;
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
is_registered = true;
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
return 0;
...
}
上述函数主要有两个工作内容:
- 调用
bus_register
函数,向系统注册i2c bus; - 调用
i2c_add_driver
函数,向系统添加一个空驱动。
2.2 添加I2C驱动
在向系统添加某一个i2c设备驱动时,通常使用module_i2c_driver
宏来实现,其源码为:
#define module_i2c_driver(__i2c_driver) \
module_driver(__i2c_driver, i2c_add_driver, \
i2c_del_driver)
添加驱动调用i2c_add_driver
,删除驱动i2c_del_driver
。
i2c_add_driver
函数,向系统添加一个i2c驱动,实际调用的函数是i2c_register_driver
,函数内容为:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (WARN_ON(!is_registered))
return -EAGAIN;
// 1. 例化成员变量
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
INIT_LIST_HEAD(&driver->clients);
// 2.注册驱动
res = driver_register(&driver->driver);
if (res)
return res;
pr_debug("driver [%s] registered\n", driver->driver.name);
// 3.遍历已经存在的适配器
i2c_for_each_dev(driver, __process_new_driver);
return 0;
}
上述函数主要有以下工作内容:
-
例化成员变量
-
调用
driver_register
,向系统注册驱动 -
调用
i2c_for_each_dev
,遍历已经存在的适配器,其实现流程为:->i2c_for_each_dev(driver, __process_new_driver) ->__process_new_driver ->i2c_do_add_adapter ->i2c_detect //检测确认该设备是否在该适配器上 ->temp_client = kzalloc() //创建一个临时client ->temp_client->adapter = adapter //赋值遍历的适配器 ->i2c_detect_address ->i2c_check_addr_busy ->i2c_default_probe ->driver->detect() ->i2c_new_device() //创建新的client,并添加到链表中
2.3 添加I2C设备
向系统添加设备,调用i2c_dev_init
函数实现,其内容为:
static int __init i2c_dev_init(void)
{
int res;
//注册设备号
res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
if (res)
goto out;
//创建i2c的class
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
i2c_dev_class->dev_groups = i2c_groups;
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
//绑定到已经存在的适配器
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
...
}
在上述内容中,调用i2cdev_attach_adapter
函数绑定到已经存在的适配器,其内容为:
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);
//开辟i2c_dev
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
//初始化字符设备,提供操作符
cdev_init(&i2c_dev->cdev, &i2cdev_fops);
i2c_dev->cdev.owner = THIS_MODULE;
//i2c_dev成员变量初始化
device_initialize(&i2c_dev->dev);
i2c_dev->dev.devt = MKDEV(I2C_MAJOR, adap->nr);
i2c_dev->dev.class = i2c_dev_class;
i2c_dev->dev.parent = &adap->dev;
i2c_dev->dev.release = i2cdev_dev_release;
dev_set_name(&i2c_dev->dev, "i2c-%d", adap->nr);
//添加设备
res = cdev_device_add(&i2c_dev->cdev, &i2c_dev->dev);
if (res) {
put_i2c_dev(i2c_dev, false);
return res;
}
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
}
2.4 I2C设备操作符函数
上一小节中在初始化字符设备,提供操作符时,i2c_dev设备操作符定义为:
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.compat_ioctl = compat_i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
上层应用程序通过操作符参数来对设备进行读写,下面对常用操作符进行介绍。
2.4.1 i2cdev_read
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)
{
char *tmp;
int ret;
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
tmp = kmalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
iminor(file_inode(file)), count);
//从i2c设备读取数据
ret = i2c_master_recv(client, tmp, count);
if (ret >= 0)
//将读取的数据从内核拷贝到应用层
ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
kfree(tmp);
return ret;
}
读取数据函数i2c_master_recv
调用流程为:
-> i2c_master_recv
-> i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD)
-> i2c_transfer //传输函数
2.4.2 i2cdev_write
static ssize_t i2cdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
{
int ret;
char *tmp;
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
//将用户层数据拷贝到内核
tmp = memdup_user(buf, count);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
iminor(file_inode(file)), count);
//写数据
ret = i2c_master_send(client, tmp, count);
kfree(tmp);
return ret;
}
写数据根据函数i2c_master_send
调用流程为:
-> i2c_master_send
-> i2c_transfer_buffer_flags(client, (char *)buf, count, 0)
-> i2c_transfer //传输函数
2.4.3 i2cdev_open
static int i2cdev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
//获取adapter
adap = i2c_get_adapter(minor);
if (!adap)
return -ENODEV;
//开辟client
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
//将i2c_client对象作为file的私有数据供后续其他操作使用
client->adapter = adap;
file->private_data = client;
return 0;
}
2.4.4 i2cdev_ioctl
相较于read与write操作符接口,此函数在信息传输时调用更频繁,根据ioctl传入的参数进行读写操作,函数主要内容为:
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = file->private_data;
unsigned long funcs;
switch (cmd) {
...
case I2C_RDWR: {
struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_msg *rdwr_pa;
if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;
if (!rdwr_arg.msgs || rdwr_arg.nmsgs == 0)
return -EINVAL;
/*
* Put an arbitrary limit on the number of messages that can
* be sent at once
*/
if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
return -EINVAL;
rdwr_pa = memdup_user(rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg));
if (IS_ERR(rdwr_pa))
return PTR_ERR(rdwr_pa);
return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa);
}
case I2C_SMBUS: {
struct i2c_smbus_ioctl_data data_arg;
if (copy_from_user(&data_arg,
(struct i2c_smbus_ioctl_data __user *) arg,
sizeof(struct i2c_smbus_ioctl_data)))
return -EFAULT;
return i2cdev_ioctl_smbus(client, data_arg.read_write,
data_arg.command,
data_arg.size,
data_arg.data);
}
...
}
return 0;
}
-
I2C_RDWR:先将用户层数据拷贝到内核,最后调用
i2cdev_ioctl_rdwr
函数实现传输,其最终调用i2c_transfer
实现传输。 -
I2C_SMBUS:先将用户层数据拷贝到内核,最后调用
i2cdev_ioctl_smbus
函数实现传输,其调用流程为:-> i2cdev_ioctl_smbus -> i2c_smbus_xfer -> __i2c_smbus_xfer -> i2c_smbus_xfer_emulated -> __i2c_transfer
2.5 添加adapter
i2c_add_adapter
函数向总线添加adapter,函数内容为:
int i2c_add_adapter(struct i2c_adapter *adapter)
{
struct device *dev = &adapter->dev;
int id;
//从设备树中获取id
if (dev->of_node) {
id = of_alias_get_id(dev->of_node, "i2c");
if (id >= 0) {
adapter->nr = id;
return __i2c_add_numbered_adapter(adapter);
}
}
//动态分配id
mutex_lock(&core_lock);
id = idr_alloc(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
mutex_unlock(&core_lock);
if (WARN(id < 0, "couldn't get idr"))
return id;
adapter->nr = id;
return i2c_register_adapter(adapter);
}
在添加adapter时,首先获取其id。获取的方式有两种:第一种,从设备树获取;第二种,系统动态分配。一般会使用第二种方式。
分配完id后,调用i2c_register_adapter
函数注册daapter,其内容主要为:
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = -EINVAL;
...
dev_set_name(&adap->dev, "i2c-%d", adap->nr); //根据id设置name
adap->dev.bus = &i2c_bus_type; //设置bus类型
adap->dev.type = &i2c_adapter_type; //设置device类型
res = device_register(&adap->dev); //注册设备
if (res) {
pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
goto out_list;
}
res = of_i2c_setup_smbus_alert(adap);
if (res)
goto out_reg;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
pm_runtime_no_callbacks(&adap->dev);
pm_suspend_ignore_children(&adap->dev, true);
pm_runtime_enable(&adap->dev);
#ifdef CONFIG_I2C_COMPAT
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
adap->dev.parent);
if (res)
dev_warn(&adap->dev,
"Failed to create compatibility class link\n");
#endif
i2c_init_recovery(adap);
/* create pre-declared device nodes */
of_i2c_register_devices(adap);
i2c_acpi_install_space_handler(adap);
i2c_acpi_register_devices(adap);
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
mutex_lock(&core_lock);
//遍历drivers添加adapter
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);
return 0;
2.6 i2c_transfer传输函数
i2c数据传输时,由i2c_transfer
函数实现,函数原型为:
//struct i2c_adapter *adap: 此函数调用的i2c适配器
//struct i2c_msg *msgs: 要传输的数据
//int num: 数据长度
i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
此函数实现实际调用的函数是__i2c_transfer
,函数内容为:
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
...
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic)
ret = adap->algo->master_xfer_atomic(adap, msgs, num);
else
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
...
return ret;
}
在上述函数中,通过调用adap->algo->master_xfer(adap, msgs, num)
钩子函数实现数据的传输。
前两章简单介绍linux下i2c框架相关内容,下一章将正式分析i2c驱动。