devres簡介
在驅動中經常要在初始化函數(shù)或probe函數(shù)中對設備分配一些資源,比如:irq、regulator、gpio等。在驅動進行初始化的時候如果失敗,那么通常會goto到某個地方釋放資源。有時候編寫驅動時會忘記釋放資源,Linux為了解決這個問題而引入了devres機制。devres 是一種設備資源管理機制(device resource management), 類似于一種垃圾收集處理器。而資源的處理時機是在驅動的 install / remove 時候。這樣我們在為設備分配相關資源之后, 就不必要關心如何釋放它們了。
設備資源
Linux中設備資源包含:
Power
Clock
Memory
GPIO
IRQ
虛擬地址空間
API函數(shù)
Clock
//drivers/clk/clk-devres.c devm_clk_get() devm_clk_put() devm_clk_hw_register() devm_of_clk_add_hw_provider()
DMA
//drivers/base/dma-mapping.c dmaenginem_async_device_register() dmam_alloc_coherent() dmam_alloc_attrs() dmam_declare_coherent_memory() dmam_free_coherent() dmam_pool_create() dmam_pool_destroy()
GPIO
//drivers/gpio/gpiolib-devres.c devm_gpiod_get() devm_gpiod_get_index() devm_gpiod_get_index_optional() devm_gpiod_get_optional() devm_gpiod_put() devm_gpiochip_add_data() devm_gpiochip_remove() devm_gpio_request() devm_gpio_request_one() devm_gpio_free()
IIO
//drivers/iio/industrialio-core.c devm_iio_device_alloc() devm_iio_device_free() devm_iio_device_register() devm_iio_device_unregister() devm_iio_kfifo_allocate() devm_iio_kfifo_free() devm_iio_triggered_buffer_setup() devm_iio_triggered_buffer_cleanup() devm_iio_trigger_alloc() devm_iio_trigger_free() devm_iio_trigger_register() devm_iio_trigger_unregister() devm_iio_channel_get() devm_iio_channel_release() devm_iio_channel_get_all() devm_iio_channel_release_all()
Input
//drivers/input/input.c devm_input_allocate_device()
IO region
//kernel/resource.c devm_release_mem_region() devm_release_region() devm_release_resource() devm_request_mem_region() devm_request_region() devm_request_resource()
IOMAP
//lib/devres.c devm_ioport_map() devm_ioport_unmap() devm_ioremap() devm_ioremap_nocache() devm_ioremap_wc() devm_ioremap_resource() devm_iounmap() pcim_iomap() pcim_iomap_regions() pcim_iomap_table() pcim_iounmap()
IRQ
//kernel/irq/devres.c devm_free_irq() devm_request_any_context_irq() devm_request_irq() devm_request_threaded_irq() devm_irq_alloc_descs() devm_irq_alloc_desc() devm_irq_alloc_desc_at() devm_irq_alloc_desc_from() devm_irq_alloc_descs_from() devm_irq_alloc_generic_chip() devm_irq_setup_generic_chip() devm_irq_sim_init()
Memory
//drivers/base/devres.c devm_free_pages() devm_get_free_pages() devm_kasprintf() devm_kcalloc() devm_kfree() devm_kmalloc() devm_kmalloc_array() devm_kmemdup() devm_kstrdup() devm_kvasprintf() devm_kzalloc()
PCI
//drivers/pci/probe.c devm_pci_alloc_host_bridge() devm_pci_remap_cfgspace() devm_pci_remap_cfg_resource() pcim_enable_device() pcim_pin_device()
Pinctrl
//drivres/pinctrl/core.c devm_pinctrl_get() devm_pinctrl_put() devm_pinctrl_register() devm_pinctrl_unregister()
Regulator
//drivers/regulator/core.c devm_regulator_bulk_get() devm_regulator_get() devm_regulator_put() devm_regulator_register()
驅動對比
非devres驅動:
static int __init soc_camera_probe(struct platform_device *pdev) { ... res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (!res || (int)irq <= 0) { err = -ENODEV; goto exit; } clk = clk_get(&pdev->dev, "csi_clk"); if (IS_ERR(clk)) { err = PTR_ERR(clk); goto exit; } pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); if (!pcdev) { dev_err(&pdev->dev, "Could not allocate pcdev "); err = -ENOMEM; goto exit_put_clk; } ... /* * Request the regions. */ if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) { err = -EBUSY; goto exit_kfree; } base = ioremap(res->start, resource_size(res)); if (!base) { err = -ENOMEM; goto exit_release; } ... /* request dma */ pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH); if (pcdev->dma_chan < 0) { dev_err(&pdev->dev, "Can't request DMA for MX1 CSI "); err = -EBUSY; goto exit_iounmap; } ... /* request irq */ err = claim_fiq(&fh); if (err) { dev_err(&pdev->dev, "Camera interrupt register failed "); goto exit_free_dma; } ... err = soc_camera_host_register(&pcdev->soc_host); if (err) goto exit_free_irq; dev_info(&pdev->dev, "MX1 Camera driver loaded "); return 0; exit_free_irq: disable_fiq(irq); mxc_set_irq_fiq(irq, 0); release_fiq(&fh); exit_free_dma: imx_dma_free(pcdev->dma_chan); exit_iounmap: iounmap(base); exit_release: release_mem_region(res->start, resource_size(res)); exit_kfree: kfree(pcdev); exit_put_clk: clk_put(clk); exit: return err; }
devres驅動:
static int __init soc_camera_probe(struct platform_device *pdev) { ... res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (!res || (int)irq <= 0) { return -ENODEV; } clk = devm_clk_get(&pdev->dev, "csi_clk"); if (IS_ERR(clk)) { return PTR_ERR(clk); } pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); if (!pcdev) { dev_err(&pdev->dev, "Could not allocate pcdev "); return -ENOMEM; } ... /* * Request the regions. */ if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), DRIVER_NAME)) { return -EBUSY; } base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!base) { return -ENOMEM; } ... /* request dma */ pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH); if (pcdev->dma_chan < 0) { dev_err(&pdev->dev, "Can't request DMA for MX1 CSI "); return -EBUSY; } ... /* request irq */ err = claim_fiq(&fh); if (err) { dev_err(&pdev->dev, "Camera interrupt register failed "); return err; } ... err = soc_camera_host_register(&pcdev->soc_host); if (err) return err; dev_info(&pdev->dev, "MX1 Camera driver loaded "); return 0; }
通過上面的比對大概能知道這些函數(shù)的差別了。
總結
目前除了一些舊代碼之外,大部分驅動都使用devres相關的接口。也推薦大家在代碼中更多的使用相關接口,這樣代碼會更簡潔,不容易出現(xiàn)內存泄露。
-
處理器
+關注
關注
68文章
19286瀏覽量
229866 -
Linux
+關注
關注
87文章
11304瀏覽量
209535 -
函數(shù)
+關注
關注
3文章
4331瀏覽量
62629
原文標題:Linux devres機制
文章出處:【微信號:嵌入式軟件開發(fā)交流,微信公眾號:嵌入式軟件開發(fā)交流】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論