I2C子系統框架二

lethe1203發表於2024-03-31
學習資料:韋東山第三期
本節為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
0
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傳輸
... ...

相關文章