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 裝置.
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.