學習資料:韋東山第三期
本節為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傳輸 ... ...