生活随笔
收集整理的這篇文章主要介紹了
Linux I2C子系统分析-I2C设备驱动
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
接下來以一個實際的例子來看I2C設備驅動,就以drivers/i2c/i2c-dev.c為例。
先看它的初始化和注銷函數
[cpp]?view plaincopy
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))?{?? ????????res?=?PTR_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;?? }?? ?? static?void?__exit?i2c_dev_exit(void)?? {?? ????i2c_del_driver(&i2cdev_driver);?? ????class_destroy(i2c_dev_class);?? ????unregister_chrdev(I2C_MAJOR,"i2c");?? }??
首先調用register_chrdev注冊了一個字符設備,這是老的字符驅動注冊方式。然后到了接下來的主角,i2c_add_driver,在I2C子系統中,I2C設備驅動就是采用這個函數注冊,注銷一個I2C設備驅動使用下面的i2c_del_driver函數,那就具體看看這個I2C設備驅動注冊函數。
[cpp]?view plaincopy
static?inline?int?i2c_add_driver(struct?i2c_driver?*driver)?? {?? ????return?i2c_register_driver(THIS_MODULE,?driver);?? }?? int?i2c_register_driver(struct?module?*owner,?struct?i2c_driver?*driver)?? {?? ????int?res;?? ?? ?????? ????if?(unlikely(WARN_ON(!i2c_bus_type.p)))?? ????????return?-EAGAIN;?? ?? ?????? ????driver->driver.owner?=?owner;?? ????driver->driver.bus?=?&i2c_bus_type;??? ?? ????? ? ?? ????res?=?driver_register(&driver->driver);??? ????if?(res)?? ????????return?res;?? ?? ????pr_debug("i2c-core:?driver?[%s]?registered\n",?driver->driver.name);?? ?? ????INIT_LIST_HEAD(&driver->clients);?? ?????? ????mutex_lock(&core_lock);?? ????bus_for_each_dev(&i2c_bus_type,?NULL,?driver,?__attach_adapter);?? ????mutex_unlock(&core_lock);?? ?? ????return?0;?? }??
再來看看i2c設備驅動注銷函數
[cpp]?view plaincopy
void?i2c_del_driver(struct?i2c_driver?*driver)?? {?? ????mutex_lock(&core_lock);?? ????bus_for_each_dev(&i2c_bus_type,?NULL,?driver,?__detach_adapter);?? ????mutex_unlock(&core_lock);?? ?? ????driver_unregister(&driver->driver);?? ????pr_debug("i2c-core:?driver?[%s]?unregistered\n",?driver->driver.name);?? }??
也沒什么,最后調用的就是驅動的注銷函數driver_unregister函數。
來看傳遞給注冊和注銷i2c驅動函數的參數什么,i2cdev_driver它是structi2c_driver結構類型,i2c設備驅動就是使用這個結構類型描述,這個結構類型定義在include/linux/i2c.h
[cpp]?view plaincopy
struct?i2c_driver?{?? ????unsigned?int?class;?? ?? ????? ? ? ?? ????int?(*attach_adapter)(struct?i2c_adapter?*);?? ????int?(*detach_adapter)(struct?i2c_adapter?*);?? ?? ?????? ????int?(*probe)(struct?i2c_client?*,?const?struct?i2c_device_id?*);?? ????int?(*remove)(struct?i2c_client?*);?? ?? ?????? ????void?(*shutdown)(struct?i2c_client?*);?? ????int?(*suspend)(struct?i2c_client?*,?pm_message_t?mesg);?? ????int?(*resume)(struct?i2c_client?*);?? ?? ????? ? ?? ????int?(*command)(struct?i2c_client?*client,?unsigned?int?cmd,?void?*arg);?? ?? ????struct?device_driver?driver;?? ????const?struct?i2c_device_id?*id_table;?? ?? ?????? ????int?(*detect)(struct?i2c_client?*,?int?kind,?struct?i2c_board_info?*);?? ????const?struct?i2c_client_address_data?*address_data;?? ????struct?list_head?clients;?? };??
來看i2c-dev.c中是怎么定義的
[cpp]?view plaincopy
static?struct?i2c_driver?i2cdev_driver?=?{?? ????.driver?=?{?? ????????.name???=?"dev_driver",?? ????},?? ????.attach_adapter?=?i2cdev_attach_adapter,?? ????.detach_adapter?=?i2cdev_detach_adapter,?? };??
這是老的方式,所以它只是給attach_adapter和detach_adapter賦了值,由于這里是老的方式,所以我們也就不去具體看這個函數了,我們直接去看它的數據傳輸部分吧。
[cpp]?view plaincopy
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?%zu?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;?? }??
這是i2c設備讀函數,我們看它是調用的i2c_master_recv函數去操作的,去看這個函數
[cpp]?view plaincopy
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);?? ?? ????? ?? ????return?(ret?==?1)???count?:?ret;?? }??
i2c設備寫函數
[cpp]?view plaincopy
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?%zu?bytes.\n",?? ????????iminor(file->f_path.dentry->d_inode),?count);?? ?? ????ret?=?i2c_master_send(client,tmp,count);?? ????kfree(tmp);?? ????return?ret;?? }?? int?i2c_master_send(struct?i2c_client?*client,const?char?*buf?,int?count)?? {?? ????int?ret;?? ????struct?i2c_adapter?*adap=client->adapter;?? ????struct?i2c_msg?msg;?? ?? ????msg.addr?=?client->addr;?? ????msg.flags?=?client->flags?&?I2C_M_TEN;?? ????msg.len?=?count;?? ????msg.buf?=?(char?*)buf;?? ?? ????ret?=?i2c_transfer(adap,?&msg,?1);?? ?? ????? ?? ????return?(ret?==?1)???count?:?ret;?? }??
這兩個函數最終都是調用的i2c_transfer函數去完成數據的傳輸,只是他們的msg的flags不一樣,讀操作的flags要加上I2C_M_RD這個標志。
再看它們兩個共同的i2c_transfer函數
[cpp]?view plaincopy
int?i2c_transfer(struct?i2c_adapter?*adap,?struct?i2c_msg?*msgs,?int?num)?? {?? ????unsigned?long?orig_jiffies;?? ????int?ret,?try;?? ?? ????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?? ????if?(adap->algo->master_xfer)?{?? #ifdef?DEBUG?? ????????for?(ret?=?0;?ret?<?num;?ret++)?{?? ????????????dev_dbg(&adap->dev,?"master_xfer[%d]?%c,?addr=0x%02x,?"?? ????????????????"len=%d%s\n",?ret,?(msgs[ret].flags?&?I2C_M_RD)?? ??????????????????'R'?:?'W',?msgs[ret].addr,?msgs[ret].len,?? ????????????????(msgs[ret].flags?&?I2C_M_RECV_LEN)???"+"?:?"");?? ????????}?? #endif?? ?? ????????if?(in_atomic()?||?irqs_disabled())?{?? ????????????ret?=?mutex_trylock(&adap->bus_lock);?? ????????????if?(!ret)?? ?????????????????? ????????????????return?-EAGAIN;?? ????????}?else?{?? ????????????mutex_lock_nested(&adap->bus_lock,?adap->level);?? ????????}?? ?? ?????????? ????????orig_jiffies?=?jiffies;?? ????????for?(ret?=?0,?try?=?0;?try?<=?adap->retries;?try++)?{?? ????????????ret?=?adap->algo->master_xfer(adap,?msgs,?num);?? ????????????if?(ret?!=?-EAGAIN)?? ????????????????break;?? ????????????if?(time_after(jiffies,?orig_jiffies?+?adap->timeout))?? ????????????????break;?? ????????}?? ????????mutex_unlock(&adap->bus_lock);?? ?? ????????return?ret;?? ????}?else?{?? ????????dev_dbg(&adap->dev,?"I2C?level?transfers?not?supported\n");?? ????????return?-EOPNOTSUPP;?? ????}?? }??
我們看就是調用總線的master_xfer方法,我們在前面分析使用gpio模擬i2c總線時,看過這樣一句?.master_xfer =bit_xfer,?,所以最終調用的是這個函數來完成數據傳輸。使用i2c_master_recv和i2c_master_send函數一次只能傳輸一個msg,由于它一次只能傳輸一個msg,所以它的傳輸方向不能改變,也就是一次只能完成讀或寫操作,并且讀操作時還不能傳遞設備的基地址,所以通常是不會用這兩個函數的,直接的做法時,構造兩個msg,一個msg的數據為操作設備基地址,另外一個msg才是我們真正要讀寫的數據,最后調用i2c_transfer函數去完成數據的傳送。
總結
以上是生活随笔為你收集整理的Linux I2C子系统分析-I2C设备驱动的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。