久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Linux設(shè)備模型(9)

 老匹夫 2015-03-30

Linux設(shè)備模型(9)_device resource management


作者:蝸蝸 發(fā)布于:2014-9-24 23:28
分類:統(tǒng)一設(shè)備模型


1. 前言

 

蝸蝸建議,每一個(gè)Linux驅(qū)動(dòng)工程師,,都能瞄一眼本文,。

 

之所以用“瞄”,因此它很簡(jiǎn)單,,幾乎不需要花費(fèi)心思就能理解,。之所有這建議,是因?yàn)樗浅?shí)用,,可以解答一些困惑,,可以使我們的代碼變得簡(jiǎn)單、簡(jiǎn)潔,。先看一個(gè)例子:

 
  
    
   1: /* drivers/media/platform/soc_camera/mx1_camera.c, line 695 */



   
   2: static int __init mx1_camera_probe(struct platform_device *pdev)



   
   3: {



   
   4:     ...



   
   5:  



   
   6:     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);



   
   7:     irq = platform_get_irq(pdev, 0);



   
   8:     if (!res || (int)irq <= 0) {



   
   9:         err = -ENODEV;



   
  10:         goto exit;



   
  11:     }



   
  12:  



   
  13:     clk = clk_get(&pdev->dev, "csi_clk");



   
  14:     if (IS_ERR(clk)) {



   
  15:         err = PTR_ERR(clk);



   
  16:         goto exit;



   
  17:     }



   
  18:  



   
  19:     pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);



   
  20:     if (!pcdev) {



   
  21:         dev_err(&pdev->dev, "Could not allocate pcdev\n");



   
  22:         err = -ENOMEM;



   
  23:         goto exit_put_clk;



   
  24:     }



   
  25:  



   
  26:     ...



   
  27:  



   
  28:     /*



   
  29:      * Request the regions.



   
  30:      */



   
  31:     if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {



   
  32:         err = -EBUSY;



   
  33:         goto exit_kfree;



   
  34:     }



   
  35:  



   
  36:     base = ioremap(res->start, resource_size(res));



   
  37:     if (!base) {



   
  38:         err = -ENOMEM;



   
  39:         goto exit_release;



   
  40:     }



   
  41:     ...



   
  42:  



   
  43:     /* request dma */



   
  44:     pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH);



   
  45:     if (pcdev->dma_chan < 0) {



   
  46:         dev_err(&pdev->dev, "Can't request DMA for MX1 CSI\n");



   
  47:         err = -EBUSY;



   
  48:         goto exit_iounmap;



   
  49:     }



   
  50:     ...



   
  51:  



   
  52:     /* request irq */



   
  53:     err = claim_fiq(&fh);



   
  54:     if (err) {



   
  55:         dev_err(&pdev->dev, "Camera interrupt register failed\n");



   
  56:         goto exit_free_dma;



   
  57:     }



   
  58:  



   
  59:     ...



   
  60:     err = soc_camera_host_register(&pcdev->soc_host);



   
  61:     if (err)



   
  62:         goto exit_free_irq;



   
  63:  



   
  64:     dev_info(&pdev->dev, "MX1 Camera driver loaded\n");



   
  65:  



   
  66:     return 0;



   
  67:  



   
  68: exit_free_irq:



   
  69:     disable_fiq(irq);



   
  70:     mxc_set_irq_fiq(irq, 0);



   
  71:     release_fiq(&fh);



   
  72: exit_free_dma:



   
  73:     imx_dma_free(pcdev->dma_chan);



   
  74: exit_iounmap:



   
  75:     iounmap(base);



   
  76: exit_release:



   
  77:     release_mem_region(res->start, resource_size(res));



   
  78: exit_kfree:



   
  79:     kfree(pcdev);



   
  80: exit_put_clk:



   
  81:     clk_put(clk);



   
  82: exit:



   
  83:     return err;



   
  84: }




相信每一個(gè)寫過(guò)Linux driver的工程師,,都在probe函數(shù)中遇到過(guò)上面的困惑:要順序申請(qǐng)多種資源(IRQ、Clock,、memory,、regions、ioremap,、dma,、等等),只要任意一種資源申請(qǐng)失敗,,就要回滾釋放之前申請(qǐng)的所有資源,。于是函數(shù)的最后,一定會(huì)出現(xiàn)很多的goto標(biāo)簽(如上面的exit_free_irq,、exit_free_dma,、等等),并在申請(qǐng)資源出錯(cuò)時(shí),,小心翼翼的goto到正確的標(biāo)簽上,,以便釋放已申請(qǐng)資源。



正像上面代碼一樣,,整個(gè)函數(shù)被大段的,、重復(fù)的“if (condition) { err = xxx; goto xxx; }”充斥,浪費(fèi)精力,,容易出錯(cuò),,不美觀,。有困惑,就有改善的余地,,最終,,Linux設(shè)備模型借助device resource management(設(shè)備資源管理),幫我們解決了這個(gè)問(wèn)題,。就是:driver你只管申請(qǐng)就行了,,不用考慮釋放,我設(shè)備模型幫你釋放,。最終,,我們的driver可以這樣寫:




 

   
   1: static int __init mx1_camera_probe(struct platform_device *pdev)



   
   2: {



   
   3:     ...



   
   4:  



   
   5:     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);



   
   6:     irq = platform_get_irq(pdev, 0);



   
   7:     if (!res || (int)irq <= 0) {



   
   8:         return -ENODEV;



   
   9:     }



   
  10:  



   
  11:     clk = devm_clk_get(&pdev->dev, "csi_clk");



   
  12:     if (IS_ERR(clk)) {



   
  13:         return PTR_ERR(clk);



   
  14:     }



   
  15:  



   
  16:     pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);



   
  17:     if (!pcdev) {



   
  18:         dev_err(&pdev->dev, "Could not allocate pcdev\n");



   
  19:         return -ENOMEM;



   
  20:     }



   
  21:  



   
  22:     ...



   
  23:  



   
  24:     /*



   
  25:      * Request the regions.



   
  26:      */



   
  27:     if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), DRIVER_NAME)) {



   
  28:         return -EBUSY;



   
  29:     }



   
  30:  



   
  31:     base = devm_ioremap(&pdev->dev, res->start, resource_size(res));



   
  32:     if (!base) {



   
  33:         return -ENOMEM;



   
  34:     }



   
  35:     ...



   
  36:  



   
  37:     /* request dma */



   
  38:     pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH);



   
  39:     if (pcdev->dma_chan < 0) {



   
  40:         dev_err(&pdev->dev, "Can't request DMA for MX1 CSI\n");



   
  41:         return -EBUSY;



   
  42:     }



   
  43:     ...



   
  44:  



   
  45:     /* request irq */



   
  46:     err = claim_fiq(&fh);



   
  47:     if (err) {



   
  48:         dev_err(&pdev->dev, "Camera interrupt register failed\n");



   
  49:         return err;



   
  50:     }



   
  51:  



   
  52:     ...



   
  53:     err = soc_camera_host_register(&pcdev->soc_host);



   
  54:     if (err)



   
  55:         return err;



   
  56:  



   
  57:     dev_info(&pdev->dev, "MX1 Camera driver loaded\n");



   
  58:  



   
  59:     return 0;



   
  60: }




怎么做到呢?注意上面“devm_”開頭的接口,,答案就在那里,。不要再使用那些常規(guī)的資源申請(qǐng)接口,用devm_xxx的接口代替,。為了保持兼容,,這些新接口和舊接口的參數(shù)保持一致,只是名字前加了“devm_”,,并多加一個(gè)struct device指針,。



2. devm_xxx



下面列舉一些常用的資源申請(qǐng)接口,它們由各個(gè)framework(如clock,、regulator、gpio,、等等)基于device resource management實(shí)現(xiàn),。使用時(shí),直接忽略“devm_”的前綴,,后面剩下的部分,,driver工程師都很熟悉。只需記住一點(diǎn),,driver可以只申請(qǐng),,不釋放,設(shè)備模型會(huì)幫忙釋放,。不過(guò)如果為了嚴(yán)謹(jǐn),,在driver remove時(shí),可以主動(dòng)釋放(也有相應(yīng)的接口,,這里沒(méi)有列出),。




 

   
   1: extern void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp);



   
   2:  



   
   3: void __iomem *devm_ioremap_resource(struct device *dev, 



   
   4:   struct resource *res);



   
   5: void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,



   
   6:   unsigned long size);



   
   7:  



   
   8: struct clk *devm_clk_get(struct device *dev, const char *id);



   
   9:  



   
  10: int devm_gpio_request(struct device *dev, unsigned gpio,



   
  11:   const char *label);



   
  12:  



   
  13: static inline struct pinctrl * devm_pinctrl_get_select(



   
  14:   struct device *dev, const char *name)



   
  15:  



   
  16: static inline struct pwm_device *devm_pwm_get(struct device *dev,



   
  17:   const char *consumer);



   
  18:  



   
  19: struct regulator *devm_regulator_get(struct device *dev, const char *id);



   
  20:  



   
  21: static inline int devm_request_irq(struct device *dev, unsigned int irq, 



   
  22:   irq_handler_t handler, unsigned long irqflags, 



   
  23:   const char *devname, void *dev_id);



   
  24:  



   
  25: struct reset_control *devm_reset_control_get(struct device *dev, 



   
  26:   const char *id);




3. 什么是“設(shè)備資源”



一個(gè)設(shè)備能工作,需要依賴很多的外部條件,,如供電,、時(shí)鐘等等,,這些外部條件稱作設(shè)備資源(device resouce)。對(duì)于現(xiàn)代計(jì)算機(jī)的體系結(jié)構(gòu),,可能的資源包括:




 

a)power,,供電。
   
b)clock,,時(shí)鐘,。

   
c)memory,內(nèi)存,,在kernel中一般使用kzalloc分配,。

   
d)GPIO,用戶和CPU交換簡(jiǎn)單控制,、狀態(tài)等信息,。

   
e)IRQ,觸發(fā)中斷,。

   
f)DMA,,無(wú)CPU參與情況下進(jìn)行數(shù)據(jù)傳輸。

   
g)虛擬地址空間,,一般使用ioremap,、request_region等分配。

   
h)等等




而在Linux kernel的眼中,,“資源”的定義更為廣義,,比如PWM、RTC,、Reset,,都可以抽象為資源,供driver使用,。



在較早的kernel中,,系統(tǒng)還不是特別復(fù)雜,且各個(gè)framework還沒(méi)有成型,,因此大多的資源都由driver自行維護(hù),。但隨著系統(tǒng)復(fù)雜度的增加,driver之間共用資源的情況越來(lái)越多,,同時(shí)電源管理的需求也越來(lái)越迫切,。于是kernel就將各個(gè)resource的管理權(quán)收回,基于“device resource management”的框架,,由各個(gè)framework統(tǒng)一管理,,包括分配和回收。
 



4. device resource management的軟件框架



device resource management device resource management位于“drivers/base/devres.c”中,它的實(shí)現(xiàn)非常簡(jiǎn)單,,為什么呢,?因?yàn)橘Y源的種類有很多,表現(xiàn)形式也多種多樣,,而devres不可能一一知情,,也就不能進(jìn)行具體的分配和回收。因此,,devres能做的(也是它的唯一功能),,就是:




 

提供一種機(jī)制,將系統(tǒng)中某個(gè)設(shè)備的所有資源,,以鏈表的形式,,組織起來(lái),以便在driver detach的時(shí)候,,自動(dòng)釋放,。




而更為具體的事情,如怎么抽象某一種設(shè)備,,則由上層的framework負(fù)責(zé),。這些framework包括:regulator framework(管理power資源),clock framework(管理clock資源),,interrupt framework(管理中斷資源),、gpio framework(管理gpio資源),pwm framework(管理PWM),,等等,。



其它的driver,位于這些framework之上,,使用它們提供的機(jī)制和接口,,開發(fā)起來(lái)就非常方便了。



5. 代碼分析



5.1 數(shù)據(jù)結(jié)構(gòu)


先從struct device開始吧,!該結(jié)構(gòu)中有一個(gè)名稱為“devres_head”的鏈表頭,用于保存該設(shè)備申請(qǐng)的所有資源,,如下:




 

   
   1: struct device {



   
   2:         ...



   
   3:         spinlock_t              devres_lock;



   
   4:         struct list_head        devres_head;



   
   5:         ...



   
   6: }



   
   7:  




那資源的數(shù)據(jù)結(jié)構(gòu)呢,?在“drivers/base/devres.c”中,名稱為struct devres,,如下:




 

   
   1: struct devres {



   
   2:         struct devres_node              node;



   
   3:         /* -- 3 pointers */



   
   4:         unsigned long long              data[]; /* guarantee ull alignment */



   
   5: };




咋一看非常簡(jiǎn)單,,一個(gè)struct devres_node的變量node,一個(gè)零長(zhǎng)度數(shù)組data,,但其中有無(wú)窮奧妙,,讓我們繼續(xù)分析。



node用于將devres組織起來(lái),方便插入到device結(jié)構(gòu)的devres_head鏈表中,,因此一定也有一個(gè)list_head(見下面的entry),。另外,資源的存在形式到底是什么,,device resource management并不知情,,因此需要上層模塊提供一個(gè)release的回調(diào)函數(shù),用于release資源,,如下:




 

   
   1: struct devres_node {



   
   2:         struct list_head                entry;



   
   3:         dr_release_t                    release;



   
   4: #ifdef CONFIG_DEBUG_DEVRES



   
   5:         const char                      *name;



   
   6:         size_t                          size;



   
   7: #endif



   
   8: };




拋開用于debug的變量不說(shuō),,也很簡(jiǎn)單,一個(gè)entry list_head,,一個(gè)release回調(diào)函數(shù),。看不出怎么抽象資源??!別急,奧妙都在data這個(gè)零長(zhǎng)度數(shù)組上面呢,。



注1:不知道您是否注意到,,devres有關(guān)的數(shù)據(jù)結(jié)構(gòu),是在devres.c中定義的(是C文件哦?。?。換句話說(shuō),是對(duì)其它模塊透明的,。這真是優(yōu)雅的設(shè)計(jì)(盡量屏蔽細(xì)節(jié)),!



5.2 一個(gè)無(wú)關(guān)話題:零長(zhǎng)度數(shù)組


零長(zhǎng)度數(shù)組的英文原名為Arrays of Length Zero,是GNU C的規(guī)范,,主要用途是用來(lái)作為結(jié)構(gòu)體的最后一個(gè)成員,,然后用它來(lái)訪問(wèn)此結(jié)構(gòu)體對(duì)象之后的一段內(nèi)存(通常是動(dòng)態(tài)分配的內(nèi)存)。什么意思呢,?



以struct devres為例,,node變量的長(zhǎng)度為3個(gè)指針的長(zhǎng)度,而struct devres的長(zhǎng)度也是3個(gè)指針的長(zhǎng)度,。而data只是一個(gè)標(biāo)記,,當(dāng)有人分配了大于3個(gè)指針長(zhǎng)度的空間并把它轉(zhuǎn)換為struct devres類型的變量后,我們就可以通過(guò)data來(lái)訪問(wèn)多出來(lái)的memory,。也就是說(shuō),,有了零長(zhǎng)度數(shù)組data,struct devres結(jié)構(gòu)的長(zhǎng)度可以不定,,完全依賴于你分配的空間的大小,。有什么用呢?



以本文的應(yīng)用場(chǎng)景為例,多出來(lái)的,、可通過(guò)data訪問(wèn)的空間,,正是具體的device resource所占的空間。資源的類型不同,,占用的空間的多少也不同,,但devres模塊的主要功能又是釋放資源所占的資源。這是就是零長(zhǎng)度數(shù)組的功能之一,,因?yàn)檎麄€(gè)memory空間是連續(xù)的,,因此可以通過(guò)釋devres指針,釋放所有的空間,,包括data所指的那片不定長(zhǎng)度的,、具體資源所用的空間。



零長(zhǎng)度數(shù)組(data[0]),,在不同的C版本中,,有不同的實(shí)現(xiàn)方案,包括1長(zhǎng)度數(shù)組(data[1])和不定長(zhǎng)度數(shù)組(data[],,本文所描述就是這一種),,具體可參考GCC的規(guī)范:



https://gcc./onlinedocs/gcc/Zero-Length.html



5.3 向上層framework提供的接口:devres_alloc/devres_free、devres_add/devres_remove


先看一個(gè)使用device resource management的例子(IRQ模塊):




 

   
   1: /* include/linux/interrupt.h */



   
   2: static inline int __must_check



   
   3: devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,



   
   4:                  unsigned long irqflags, const char *devname, void *dev_id)



   
   5: {



   
   6:         return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags,



   
   7:                                          devname, dev_id);



   
   8: }



   
   9:  



   
  10:  



   
  11: /* kernel/irq/devres.c */



   
  12: int devm_request_threaded_irq(struct device *dev, unsigned int irq,



   
  13:                               irq_handler_t handler, irq_handler_t thread_fn,



   
  14:                               unsigned long irqflags, const char *devname,



   
  15:                               void *dev_id)



   
  16: {



   
  17:         struct irq_devres *dr;



   
  18:         int rc;



   
  19:  



   
  20:         dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres),



   
  21:                           GFP_KERNEL);



   
  22:         if (!dr)



   
  23:                 return -ENOMEM;



   
  24:  



   
  25:         rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname,



   
  26:                                   dev_id);



   
  27:         if (rc) {



   
  28:                 devres_free(dr);



   
  29:                 return rc;



   
  30:         }



   
  31:  



   
  32:         dr->irq = irq;



   
  33:         dr->dev_id = dev_id;



   
  34:         devres_add(dev, dr);



   
  35:  



   
  36:         return 0;



   
  37: }



   
  38: EXPORT_SYMBOL(devm_request_threaded_irq);



   
  39:  



   
  40: void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id)



   
  41: {



   
  42:         struct irq_devres match_data = { irq, dev_id };



   
  43:  



   
  44:         WARN_ON(devres_destroy(dev, devm_irq_release, devm_irq_match,



   
  45:                                &match_data));



   
  46:         free_irq(irq, dev_id);



   
  47: }



   
  48: EXPORT_SYMBOL(devm_free_irq);




前面我們提過(guò),,上層的IRQ framework,,會(huì)提供兩個(gè)和request_irq/free_irq基本兼容的接口,這兩個(gè)接口的實(shí)現(xiàn)非常簡(jiǎn)單,,就是在原有的實(shí)現(xiàn)之上,,封裝一層devres的操作,如要包括:



1)一個(gè)自定義的數(shù)據(jù)結(jié)構(gòu)(struct irq_devres),,用于保存和resource有關(guān)的信息(對(duì)中斷來(lái)說(shuō),,就是IRQ num),如下:




 

   
   1: /*



   
   2:  * Device resource management aware IRQ request/free implementation.



   
   3:  */



   
   4: struct irq_devres {



   
   5:         unsigned int irq;



   
   6:         void *dev_id;



   
   7: };




2)一個(gè)用于release resource的回調(diào)函數(shù)(這里的release,,和memory無(wú)關(guān),,例如free IRQ),如下:




 

   
   1: static void devm_irq_release(struct device *dev, void *res)



   
   2: {



   
   3:         struct irq_devres *this = res;



   
   4:  



   
   5:         free_irq(this->irq, this->dev_id);



   
   6: }





 

因?yàn)榛卣{(diào)函數(shù)是由devres模塊調(diào)用的,,由它的參數(shù)可知,,struct irq_devres變量就是實(shí)際的“資源”,但對(duì)devres而言,,它并不知道該資源的實(shí)際形態(tài),因而是void類型指針,。也只有這樣,,devres模塊才可以統(tǒng)一的處理所有類型的資源。




3)以回調(diào)函數(shù)、resource的size為參數(shù),,調(diào)用devres_alloc接口,,為resource分配空間。該接口的定義如下:




 

   
   1: void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp)



   
   2: {



   
   3:         struct devres *dr;



   
   4:  



   
   5:         dr = alloc_dr(release, size, gfp);



   
   6:         if (unlikely(!dr))



   
   7:                 return NULL;



   
   8:         return dr->data;



   
   9: }




調(diào)用alloc_dr,,分配一個(gè)struct devres類型的變量,并返回其中的data指針(5.2小節(jié)講過(guò)了,data變量實(shí)際上是資源的代表),。alloc_dr的定義如下:




 

   
   1: static __always_inline struct devres * alloc_dr(dr_release_t release,



   
   2:                                                 size_t size, gfp_t gfp)



   
   3: {



   
   4:         size_t tot_size = sizeof(struct devres) + size;



   
   5:         struct devres *dr;



   
   6:  



   
   7:         dr = kmalloc_track_caller(tot_size, gfp);



   
   8:         if (unlikely(!dr))



   
   9:                 return NULL;



   
  10:  



   
  11:         memset(dr, 0, tot_size);



   
  12:         INIT_LIST_HEAD(&dr->node.entry);



   
  13:         dr->node.release = release;



   
  14:         return dr;



   
  15: }




看第一句就可以了,,在資源size之前,加一個(gè)struct devres的size,,就是total分配的空間,。除去struct devres的,就是資源的(由data指針訪問(wèn)),。之后是初始化struct devres變量的node,。



4)調(diào)用原來(lái)的中斷注冊(cè)接口(這里是request_threaded_irq),注冊(cè)中斷,。該步驟和device resource management無(wú)關(guān),。



5)注冊(cè)成功后,以設(shè)備指針(dev)和資源指針(dr)為參數(shù),,調(diào)用devres_add,,將資源添加到設(shè)備的資源鏈表頭(devres_head)中,該接口定義如下:




 

   
   1: void devres_add(struct device *dev, void *res)



   
   2: {



   
   3:         struct devres *dr = container_of(res, struct devres, data);



   
   4:         unsigned long flags;



   
   5:  



   
   6:         spin_lock_irqsave(&dev->devres_lock, flags);



   
   7:         add_dr(dev, &dr->node);



   
   8:         spin_unlock_irqrestore(&dev->devres_lock, flags);



   
   9: }




從資源指針中,,取出完整的struct devres指針,,調(diào)用add_dr接口。add_dr也很簡(jiǎn)單,,把struct devres指針掛到設(shè)備的devres_head中即可:




 

   
   1: static void add_dr(struct device *dev, struct devres_node *node)



   
   2: {



   
   3:         devres_log(dev, node, "ADD");



   
   4:         BUG_ON(!list_empty(&node->entry));



   
   5:         list_add_tail(&node->entry, &dev->devres_head);



   
   6: }




6)如果失敗,,可以通過(guò)devres_free接口釋放資源占用的空間,devm_free_irq接口中,,會(huì)調(diào)用devres_destroy接口,,將devres從devres_head中移除,并釋放資源,。這里就不詳細(xì)描述了,。



5.4 向設(shè)備模型提供的接口:devres_release_all


這里是重點(diǎn),用于自動(dòng)釋放資源,。



先回憶一下設(shè)備模型中probe的流程(可參考“Linux設(shè)備模型(5)_device和device driver”),,devres_release_all接口被調(diào)用的時(shí)機(jī)有兩個(gè):



1)probe失敗時(shí),調(diào)用過(guò)程為(就不詳細(xì)的貼代碼了):



__driver_attach/__device_attach-->driver_probe_device—>really_probe,,really_probe調(diào)用driver或者bus的probe接口,,如果失?。ǚ祷刂捣橇悖蓞⒖急疚拈_頭的例子),,則會(huì)調(diào)用devres_release_all,。



2)deriver dettach時(shí)(就是driver remove時(shí))



driver_detach/bus_remove_device-->__device_release_driver-->devres_release_all



devres_release_all的實(shí)現(xiàn)如下:




 

   
   1: int devres_release_all(struct device *dev)



   
   2: {



   
   3:         unsigned long flags;



   
   4:  



   
   5:         /* Looks like an uninitialized device structure */



   
   6:         if (WARN_ON(dev->devres_head.next == NULL))



   
   7:                 return -ENODEV;



   
   8:         spin_lock_irqsave(&dev->devres_lock, flags);



   
   9:         return release_nodes(dev, dev->devres_head.next, &dev->devres_head,



   
  10:                              flags);



   
  11: }




以設(shè)備指針為參數(shù),直接調(diào)用release_nodes:




 

   
   1: static int release_nodes(struct device *dev, struct list_head *first,



   
   2:                          struct list_head *end, unsigned long flags)



   
   3:         __releases(&dev->devres_lock)



   
   4: {



   
   5:         LIST_HEAD(todo);



   
   6:         int cnt;



   
   7:         struct devres *dr, *tmp;



   
   8:  



   
   9:         cnt = remove_nodes(dev, first, end, &todo);



   
  10:  



   
  11:         spin_unlock_irqrestore(&dev->devres_lock, flags);



   
  12:  



   
  13:         /* Release.  Note that both devres and devres_group are



   
  14:          * handled as devres in the following loop.  This is safe.



   
  15:          */



   
  16:         list_for_each_entry_safe_reverse(dr, tmp, &todo, node.entry) {



   
  17:                 devres_log(dev, &dr->node, "REL");



   
  18:                 dr->node.release(dev, dr->data);



   
  19:                 kfree(dr);



   
  20:         }



   
  21:  



   
  22:         return cnt;



   
  23: }




release_nodes會(huì)先調(diào)用remove_nodes,,將設(shè)備所有的struct devres指針從設(shè)備的devres_head中移除,。然后,調(diào)用所有資源的release回調(diào)函數(shù)(如5.3小節(jié)描述的devm_irq_release),,回調(diào)函數(shù)會(huì)回收具體的資源(如free_irq),。最后,調(diào)用free,,釋放devres以及資源所占的空間,。



 



原創(chuàng)文章,轉(zhuǎn)發(fā)請(qǐng)注明出處,。蝸窩科技,,www.

標(biāo)簽: Linux 設(shè)備模型 設(shè)備資源管理 devres




評(píng)論:





Issues
2015-02-05 08:54
謝謝樓主的解答,,可這些頭文件,,在安裝內(nèi)核樹之前應(yīng)該已經(jīng)存在了啊(/usr/src/ 下本來(lái)就有內(nèi)核的頭文件)。內(nèi)核樹構(gòu)建的的是source文件和編譯后的vmlinux.o鏡像文件,。如果編譯新內(nèi)核時(shí)這些文件沒(méi)用,,哪這些文件是干什么的。而且只有頭文件,,編譯新model的時(shí)候引用的內(nèi)核符號(hào)在哪,??






wowo
2015-02-05 09:53
@Issues:如果你目標(biāo)kernel和主機(jī)kernel是相同版本的話,,確實(shí)可以使用主機(jī)的header……





Issues
2015-02-05 16:28
@wowo:按樓主的意思,,如果我編譯和的module是和主機(jī)kernel相同版本的話,那不用構(gòu)建內(nèi)核源碼樹應(yīng)該也能編譯成功,??蛇@樣是會(huì)失敗的啊,編譯時(shí)會(huì)提示要求安裝源碼樹





wowo
2015-02-05 21:14
@Issues:先確認(rèn)是否有header文件,。

再確認(rèn)是否有模塊編譯目錄:/lib/modules/$(uanem -r)/build

以及header的搜索路徑是否正確,。

等等。





Issues
2015-02-07 08:12
@wowo:最后還是在ldd 3rd上找到答案了,,2.6內(nèi)核的模塊要和內(nèi)核源碼樹中的目標(biāo)文件連接,,這樣可以得到更加健壯的模塊加載器,因此就需要這些目標(biāo)文件存在于內(nèi)核目錄樹中,。以前版本的內(nèi)核是只需要一套頭文件的,。第一遍看書的時(shí)候根本沒(méi)理解是啥意思?,F(xiàn)在發(fā)現(xiàn),這句話是解釋的最明白的,。



謝謝樓主的耐心解答。網(wǎng)站的內(nèi)容非常不錯(cuò),,有很多我需要學(xué)習(xí)的文章,。希望以后窩窩可以變成linux愛(ài)好者的樂(lè)園。






wowo
2015-02-07 21:46
@Issues:非常感謝您的分享,,以后多交流,。










Issues
2015-01-28 15:41
剛轉(zhuǎn)入linux驅(qū)動(dòng)開發(fā),從樓主的系列文章學(xué)到了很多,。不過(guò)鑒于個(gè)人還是內(nèi)核菜鳥,,有幾個(gè)困擾多時(shí),網(wǎng)上也沒(méi)有找到確切答案的問(wèn)題,,想請(qǐng)樓主百忙之中撥冗解答一下,。



1.在已安裝linux系統(tǒng)的pc上進(jìn)行驅(qū)動(dòng)開發(fā)(面向本機(jī),如給嵌入式開發(fā),,還比較好理解),,為何還要安裝內(nèi)核源碼樹。已安裝的linux系統(tǒng)沒(méi)有內(nèi)核么,,兩者有何區(qū)別,。個(gè)人理解,是不是因?yàn)椴僮飨到y(tǒng)的內(nèi)核是一個(gè)完整的可執(zhí)行程序,,因此,,新建module是無(wú)法鏈接其中的目標(biāo)文件,因此需要安裝源碼樹,。源碼樹安裝并make后,,形成目標(biāo)文件,是不是只生成了一個(gè)vmlinux.o(ubuntu),,之后新建的module文件就可以通過(guò)/linux/*.h鏈接到此目標(biāo)文件完成編譯,。



2.新建的moudle編譯完成后,insmod加載模塊,,新module是加載到當(dāng)前的linux操作系統(tǒng)中,,還是新安裝的內(nèi)核源碼樹上。



希望樓主能詳細(xì)解說(shuō)一下,,非常感謝,。






wowo
2015-01-29 10:37
@Issues:我想你問(wèn)的是有關(guān)”編譯linux kernel module“的問(wèn)題。

首先,,編譯kernel module(無(wú)論target machine是Host還是嵌入式本機(jī)),,kernel source tree(從kernel.org下載的)不是必須的,。而 linux kernel header才是必須的。

那么,,什么是 linux kernel header呢,?打個(gè)比方,我們使用一個(gè)第三方庫(kù)(靜態(tài)庫(kù)或者動(dòng)態(tài)庫(kù))的時(shí)候,,除了庫(kù)文件,,往往還需要一些頭文件,以便知道這些庫(kù)文件中符號(hào)(其實(shí)這些也不是必須的,,如果我們知道所有的符號(hào)的話,,但這對(duì)linux kernel是不可能的)。

module編譯也類似,,我們編寫驅(qū)動(dòng),,需要用到很多很多kernel已有的機(jī)制(如各種framework,其它driver等等,,這些表現(xiàn)出來(lái)的就是一個(gè)一個(gè)符號(hào),,位于各個(gè)模塊的頭文件中)。這也是為什么需要linux kernel header,。

最后,,沒(méi)有安裝在”內(nèi)核源碼樹“上這一說(shuō)法。module文件就像一個(gè)可執(zhí)行文件(windows下的EXE,,Linux的EFL,,等等),需要由操作系統(tǒng),,加載到內(nèi)存,,解析并執(zhí)行之。







perr
2014-09-29 10:59
linuxer辛苦了,講的真好.device tree的設(shè)備節(jié)點(diǎn)所占用的資源如何反映到struct device中?






linuxer
2014-09-29 12:34
@perr:你沒(méi)有發(fā)現(xiàn)嗎,?這個(gè)網(wǎng)站主要是由2個(gè)人維護(hù)的,,這份文檔是蝸蝸同學(xué)寫的,呵呵~~~





forion
2014-09-29 12:35
@linuxer:嘿嘿,,最開始我以為是有人格分裂的一個(gè)人,。





linuxer
2014-09-29 12:49
@forion:的確,我和蝸窩性格還是有些不一樣的,,我比較溫和,,他比較犀利,如果是一個(gè)人的話的確是有人格分裂的嫌疑,,呵呵~~~



工作不忙的時(shí)候別忘了也寫寫文章哦~~~蝸窩是大家的蝸窩,,呵呵~~~





forion
2014-09-30 14:01
@linuxer:還在積累階段,怕寫出來(lái)誤導(dǎo)大家,,另外最近項(xiàng)目太忙,,天天加班到很晚,,都沒(méi)精力看文檔了。








perr
2014-09-29 12:38
@linuxer:挺有默契的嘛.不會(huì)寫同一個(gè)地方





linuxer
2014-09-29 12:46
@perr:我們?cè)?jīng)在一個(gè)公司,,有相同的理念,,對(duì)linux kernel有相同的熱情,自然也就很容易成為朋友,,一起做事情,。

我也是從蝸蝸那里學(xué)到很多,我很少見到有一個(gè)年輕人能象蝸蝸那么有大局觀,。

我們經(jīng)常通話,確定短期內(nèi)的方向,,當(dāng)然不會(huì)寫重復(fù)的,,呵呵~~~





perr
2014-09-29 12:52
@linuxer:希望有時(shí)間能把clocksource和clk給講講






linuxer
2014-09-29 12:59
@perr:clocksource是linux時(shí)間子系統(tǒng)的一部分,我搞完中斷子系統(tǒng)這部分,,下一個(gè)專題就是時(shí)間子系統(tǒng)了,。

clk是和clock distribution相關(guān)的內(nèi)容,屬于內(nèi)核common clock framework,,可以歸入電源管理子系統(tǒng),,蝸蝸同學(xué)應(yīng)該會(huì)寫這部分內(nèi)容的






蝸蝸
2014-09-29 22:09
@linuxer:linuxer過(guò)獎(jiǎng)了,實(shí)在不敢當(dāng)?shù)?,需要向你學(xué)習(xí)的還很多,,以前在學(xué),現(xiàn)在和將來(lái)也在,。







perr
2014-09-29 12:51
@perr:希望有時(shí)間能把clocksource和clk給講講








蝸蝸
2014-09-29 22:35
@perr:device tree的設(shè)備節(jié)點(diǎn),,準(zhǔn)確的說(shuō),是用來(lái)描述資源,,例如device的I/O memory,、IRQ number等。系統(tǒng)啟動(dòng)后,,device tree會(huì)把這些節(jié)點(diǎn)統(tǒng)統(tǒng)掛到struct device的of_node指針上,。

另外,對(duì)于一些標(biāo)準(zhǔn)資源(IO,、IRQ,、DMA、GPIO,、等等),,device tree會(huì)幫助解析(需要和對(duì)應(yīng)的framework交互),并以platform resource(struct resource)的形式保存下來(lái),,因此driver可以通過(guò)platform_get_xxx系列接口獲取,。

而對(duì)于一些非標(biāo)準(zhǔn)節(jié)點(diǎn)(如自定義的配置參數(shù)),,則需要調(diào)用of_xxx系列接口,自行解析,。

要完全分析清楚,,估計(jì)也得一篇不短的文章啊,Linuxer寫了三篇DTS的文章,,把DTS的原理分析的很透徹了,。如果能從這個(gè)角度,再寫一篇,,闡述從Linux驅(qū)動(dòng)開發(fā)者的角度,,怎么使用,就perfect了,。哈哈,。





wowo
2014-09-30 08:39
@蝸蝸:補(bǔ)充一點(diǎn),這里的解析,,只是解析描述,,具體的資源,還是需要使用devm_xxx接口獲取的,。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多