I2C子系统框架二
学习资料:韦东山第三期
本节为i2c-dev.c流程分析:
i2c-dev.c提供了上层直接访问I2C设备的接口,其中包括了I2C控制器的注册,字符设备接口的提供等
i2c-dev.c注册过程:
static int __init i2c_dev_init(void) { int res; pr_err("i2c /dev entries driver\n"); res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c"); // 注册字符设备的主设备号 if (res) goto out; i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); // 创建类i2c-dev,在/sys/class下可看到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; /* Bind to already existing adapters right away */ i2c_for_each_dev(NULL, i2cdev_attach_adapter); return 0; ... ... }
i2c控制器注册的时候就会产生总线通知:
static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action, void *data) { struct device *dev = data; switch (action) { case BUS_NOTIFY_ADD_DEVICE: return i2cdev_attach_adapter(dev, NULL); // i2c总线设备添加时调用 case BUS_NOTIFY_DEL_DEVICE: return i2cdev_detach_adapter(dev, NULL); // i2c总线设备删除时调用 } return 0; } static struct notifier_block i2cdev_notifier = { .notifier_call = i2cdev_notifier_call, };
i2cdev_attach_adapter调用:
static int i2cdev_detach_adapter(struct device *dev, void *dummy) { struct i2c_adapter *adap; struct i2c_dev *i2c_dev; if (dev->type != &i2c_adapter_type) return 0; adap = to_i2c_adapter(dev); i2c_dev = i2c_dev_get_by_minor(adap->nr); if (!i2c_dev) /* attach_adapter must have failed */ return 0; put_i2c_dev(i2c_dev, true); pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); return 0; }
应用层调用函数:
i2c-dev.c核心操作
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, };
主要调用接口:open、ioctl

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; adap = i2c_get_adapter(minor); // 根据设备节点找到i2c_adapter if (!adap) return -ENODEV; client = kzalloc(sizeof(*client), GFP_KERNEL); // 分配i2c_client,表示设备,但还没设备地址 if (!client) { i2c_put_adapter(adap); return -ENOMEM; } snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); client->adapter = adap; // client和adapter建立联系,设置私有数据暂存 file->private_data = client; return 0; }
i2cdev_ioctl: I2C_SLAVE/I2C_SLAVE_FORCE:
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct i2c_client *client = file->private_data; // 取出私有数据,也就是i2c_client unsigned long funcs; dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n", cmd, arg); switch (cmd) { case I2C_SLAVE: case I2C_SLAVE_FORCE: if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) return -EINVAL; if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg)) // I2C_SLAVE和I2C_SLAVE_FORCE区别在于I2C_SLAVE会检查addr是否有驱动 return -EBUSY; /* REVISIT: address could become busy later */ client->addr = arg; // 设置地址 return 0; ... ... 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); } ... ... } static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, unsigned int nmsgs, struct i2c_msg *msgs) { ... ... res = i2c_transfer(client->adapter, msgs, nmsgs); // 此处设置传输 while (i-- > 0) { if (res >= 0 && (msgs[i].flags & I2C_M_RD)) { if (copy_to_user(data_ptrs[i], msgs[i].buf, msgs[i].len)) res = -EFAULT; } kfree(msgs[i].buf); } ... ... }
i2cdev_ioctl:I2C_SMBus
... ... case I2C_SMBUS: { struct i2c_smbus_ioctl_data32 data32; if (copy_from_user(&data32, (void __user *) arg, sizeof(data32))) return -EFAULT; return i2cdev_ioctl_smbus(client, data32.read_write, data32.command, data32.size, compat_ptr(data32.data)); } ... ... if ((size == I2C_SMBUS_QUICK) || ((size == I2C_SMBUS_BYTE) && (read_write == I2C_SMBUS_WRITE))) /* These are special: we do not use data */ return i2c_smbus_xfer(client->adapter, client->addr, client->flags, read_write, command, size, NULL); // 发起SMBus传输 ... ...
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步