Linux設(shè)備模型(9)_device resource management作者:蝸蝸 發(fā)布于:2014-9-24 23:28 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),,可能的資源包括:
而在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位于“drivers/base/devres.c”中,它的實(shí)現(xiàn)非常簡(jiǎn)單,,為什么呢,?因?yàn)橘Y源的種類有很多,表現(xiàn)形式也多種多樣,,而devres不可能一一知情,,也就不能進(jìn)行具體的分配和回收。因此,,devres能做的(也是它的唯一功能),,就是:
而更為具體的事情,如怎么抽象某一種設(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: }
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è)園。 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é)寫的,呵呵~~~ linuxer 2014-09-29 12:49 @forion:的確,我和蝸窩性格還是有些不一樣的,,我比較溫和,,他比較犀利,如果是一個(gè)人的話的確是有人格分裂的嫌疑,,呵呵~~~ 工作不忙的時(shí)候別忘了也寫寫文章哦~~~蝸窩是大家的蝸窩,,呵呵~~~ forion 2014-09-30 14:01 @linuxer:還在積累階段,怕寫出來(lái)誤導(dǎo)大家,,另外最近項(xiàng)目太忙,,天天加班到很晚,,都沒(méi)精力看文檔了。 linuxer 2014-09-29 12:46 @perr:我們?cè)?jīng)在一個(gè)公司,,有相同的理念,,對(duì)linux kernel有相同的熱情,自然也就很容易成為朋友,,一起做事情,。 我也是從蝸蝸那里學(xué)到很多,我很少見到有一個(gè)年輕人能象蝸蝸那么有大局觀,。 我們經(jīng)常通話,確定短期內(nèi)的方向,,當(dāng)然不會(huì)寫重復(fù)的,,呵呵~~~ 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)也在,。 蝸蝸 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了,。哈哈,。 |
|
來(lái)自: 老匹夫 > 《Device Driver》