LINUX裝置驅動i2c架構分析(四)
來源:互聯網
上載者:User
七:i2c dev節點操作現在來分析上面架構圖中的i2c-dev.c中的部份.這個部份為使用者空間提供了操作adapter的介面.這部份代碼其實對應就晃一個模組.它的初始化函數為:module_init(i2c_dev_init);i2c_dev_init()代碼如下:static int __init i2c_dev_init(void){ int res; printk(KERN_INFO "i2c /dev entries driver/n"); res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); if (res) goto out; i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); if (IS_ERR(i2c_dev_class)) goto out_unreg_chrdev; res = i2c_add_driver(&i2cdev_driver); if (res) goto out_unreg_class; return 0; out_unreg_class: class_destroy(i2c_dev_class);out_unreg_chrdev: unregister_chrdev(I2C_MAJOR, "i2c");out: printk(KERN_ERR "%s: Driver Initialisation failed/n", __FILE__); return res;}首先為主冊了一個主裝置號為I2C_MAJOR(89),操作集為i2cdev_fops的字元裝置.然後註冊了一個名為”i2c-dev”的class.之後再註冊了一個i2c的driver.如下所示: res = i2c_add_driver(&i2cdev_driver); if (res) goto out_unreg_class;i2cdev_driver定義如下:static struct i2c_driver i2cdev_driver = { .driver = { .name = "dev_driver", }, .id = I2C_DRIVERID_I2CDEV, .attach_adapter = i2cdev_attach_adapter, .detach_adapter = i2cdev_detach_adapter, .detach_client = i2cdev_detach_client,};也就是說,當它註冊或者有新的adapter註冊後,就會它的attach_adapter()函數.該函數代碼如下:static int i2cdev_attach_adapter(struct i2c_adapter *adap){ struct i2c_dev *i2c_dev; int res; i2c_dev = get_free_i2c_dev(adap); if (IS_ERR(i2c_dev)) return PTR_ERR(i2c_dev); /* register this i2c device with the driver core */ i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, MKDEV(I2C_MAJOR, adap->nr), "i2c-%d", adap->nr); if (IS_ERR(i2c_dev->dev)) { res = PTR_ERR(i2c_dev->dev); goto error; } res = device_create_file(i2c_dev->dev, &dev_attr_name); if (res) goto error_destroy; pr_debug("i2c-dev: adapter [%s] registered as minor %d/n", adap->name, adap->nr); return 0;error_destroy: device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));error: return_i2c_dev(i2c_dev); return res;}這個函數也很簡單,首先調用get_free_i2c_dev()分配並初始化了一個struct i2c_dev結構,使i2c_dev->adap指向操作的adapter.之後,該i2c_dev會被鏈入鏈表i2c_dev_list中.再分別以I2C_MAJOR, adap->nr為主次裝置號建立了一個device.如果此時系統配置了udev或者是hotplug,那麼就麼在/dev下自動建立相關的裝置節點了.剛才我們說過,所有主裝置號為I2C_MAJOR的裝置節點的操作函數是i2cdev_fops.它的定義如下所示:static const struct file_operations i2cdev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = i2cdev_read, .write = i2cdev_write, .ioctl = i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release,}; 7.1:i2c dev的open操作Open操作對應的函數為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; struct i2c_dev *i2c_dev; //以次裝置號從i2c_dev_list鏈表中取得i2c_dev i2c_dev = i2c_dev_get_by_minor(minor); if (!i2c_dev) return -ENODEV; //以apapter的匯流排號從i2c_adapter_idr中找到adapter adap = i2c_get_adapter(i2c_dev->adap->nr); if (!adap) return -ENODEV; /* This creates an anonymous i2c_client, which may later be * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. * * This client is ** NEVER REGISTERED ** with the driver model * or I2C core code!! It just holds private copies of addressing * information and maybe a PEC flag. */ //分配並初始化一個i2c_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); client->driver = &i2cdev_driver; //clinet->adapter指向操作的adapter client->adapter = adap; //關聯到file file->private_data = client; return 0;}注意這裡分配並初始化了一個struct i2c_client結構.但是沒有註冊這個clinet.此外,這個函數中還有一個比較奇怪的操作.不是在前面已經將i2c_dev->adap指向要操作的adapter麼?為什麼還要以adapter->nr為關鍵字從i2c_adapter_idr去找這個操作的adapter呢?注意了,調用i2c_get_adapter()從匯流排號nr找到操作的adapter的時候,還會增加module的引用計數.這樣可以防止模組意外被釋放掉.也許有人會有這樣的疑問,那 i2c_dev->adap->nr操作,如果i2c_dev->adap被釋放掉的話,不是一樣會引起系統崩潰麼?這裡因為,在i2cdev_attach_adapter()間接的增加了一次adapter的一次引用計數.如下:tatic int i2cdev_attach_adapter(struct i2c_adapter *adap){......i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, MKDEV(I2C_MAJOR, adap->nr), "i2c-%d", adap->nr);......}看到了麼,i2c_dev內嵌的device是以adap->dev為父結點,在device_create()中會增次adap->dev的一次引用計數.好了,open()操作到此就完成了. 7.2:read操作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 = (struct i2c_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 %zd bytes./n", iminor(file->f_path.dentry->d_inode), count); ret = i2c_master_recv(client,tmp,count); if (ret >= 0) ret = copy_to_user(buf,tmp,count)?-EFAULT:ret; kfree(tmp); return ret;}首先從file結構中取得struct i2c_clinet.然後在kernel同分配相同長度的緩衝區,隨之調用i2c_master_recv()從裝置中讀取資料.再將讀取出來的資料copy到使用者空間中.I2c_master_recv()代碼如下:int i2c_master_recv(struct i2c_client *client, char *buf ,int count){ struct i2c_adapter *adap=client->adapter; struct i2c_msg msg; int ret; msg.addr = client->addr; msg.flags = client->flags & I2C_M_TEN; msg.flags |= I2C_M_RD; msg.len = count; msg.buf = buf; ret = i2c_transfer(adap, &msg, 1); /* If everything went ok (i.e. 1 msg transmitted), return #bytes transmitted, else error code. */ return (ret == 1) ? count : ret;}看完前面的代碼之後,這個函數應該很簡單了,就是為讀操作初始化了一個i2c_msg.然後調用i2c_tanster().代碼中的client->flags & I2C_M_TEN表示adapter是否採用10位定址的方式.在這裡就不再詳細分析了.另外,有人可能看出了一個問題.這裡clinet->addr是從哪來的呢?對,在read之前應該還要有一步操作來設定clinet->addr的值.這個過程是ioctl的操作.ioctl可以設定PEC標誌,重試次數,逾時時間,和發送接收資料等,我們在這裡只看一下clinet->addr的設定.程式碼片段如下示:static int i2cdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ ...... ...... switch ( cmd ) { case I2C_SLAVE: case I2C_SLAVE_FORCE: /* NOTE: devices set up to work with "new style" drivers * can't use I2C_SLAVE, even when the device node is not * bound to a driver. Only I2C_SLAVE_FORCE will work. * * Setting the PEC flag here won't affect kernel drivers, * which will be using the i2c_client node registered with * the driver model core. Likewise, when that client has * the PEC flag already set, the i2c-dev driver won't see * (or use) this setting. */ if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) return -EINVAL; if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg)) return -EBUSY; /* REVISIT: address could become busy later */ client->addr = arg; return 0; ...... ......}由此可見,調用I2C_SLAVE或者I2C_SLAVE_FORCE的Ioctl就會設定clinet->addr.另外,注釋中也說得很清楚了.如果是I2C_SLAVE的話,還會調用其所長i2cdev_check_addr().進行地址檢查,如果adapter已經關聯到這個地址的裝置,就會檢查失敗. 7.2:write操作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 = (struct i2c_client *)file->private_data; if (count > 8192) count = 8192; tmp = kmalloc(count,GFP_KERNEL); if (tmp==NULL) return -ENOMEM; if (copy_from_user(tmp,buf,count)) { kfree(tmp); return -EFAULT; } pr_debug("i2c-dev: i2c-%d writing %zd bytes./n", iminor(file->f_path.dentry->d_inode), count); ret = i2c_master_send(client,tmp,count); kfree(tmp); return ret;}該操作比較簡單,就是將使用者空間的資料發送到i2c 裝置.