devres简介
在驱动中经常要在初始化函数或probe函数中对设备分配一些资源,比如:irq、regulator、gpio等。在驱动进行初始化的时候如果失败,那么通常会goto到某个地方释放资源。有时候编写驱动时会忘记释放资源,linux为了解决这个问题而引入了devres机制。devres 是一种设备资源管理机制(device resource management), 类似于一种垃圾收集处理器。而资源的处理时机是在驱动的 install / remove 时候。这样我们在为设备分配相关资源之后, 就不必要关心如何释放它们了。
设备资源
linux中设备资源包含:
power
clock
memory
gpio
irq
dma
虚拟地址空间
api函数
clock
//drivers/clk/clk-devres.cdevm_clk_get()devm_clk_put()devm_clk_hw_register()devm_of_clk_add_hw_provider()
dma
//drivers/base/dma-mapping.cdmaenginem_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.cdevm_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.cdevm_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.cdevm_input_allocate_device()
io region
//kernel/resource.cdevm_release_mem_region()devm_release_region()devm_release_resource()devm_request_mem_region()devm_request_region()devm_request_resource()
iomap
//lib/devres.cdevm_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.cdevm_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.cdevm_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.cdevm_pci_alloc_host_bridge()devm_pci_remap_cfgspace()devm_pci_remap_cfg_resource()pcim_enable_device()pcim_pin_device()
pinctrl
//drivres/pinctrl/core.cdevm_pinctrl_get()devm_pinctrl_put()devm_pinctrl_register()devm_pinctrl_unregister()
regulator
//drivers/regulator/core.cdevm_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 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 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 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 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;}
通过上面的比对大概能知道这些函数的差别了。
总结
目前除了一些旧代码之外,大部分驱动都使用devres相关的接口。也推荐大家在代码中更多的使用相关接口,这样代码会更简洁,不容易出现内存泄露。
电子平板电脑的出货量将在2021年及以后下降
电池隔膜的透气性测试[论文]
5G商用步伐推进 LED产业将迎来新一轮发展机遇
天玑9000成安卓手机最强芯片,联发科成功改写旗舰机市场格局
高光谱成像光谱仪镜头的分类与选择
Linux为何引入devres机制
群雄竞逐,无人小车如何拔得头筹?
利用TM4C129x构建嵌入式网络服务器(3)
时机是C5ISR的核心
浅谈KUKA机器人的X11现场布线
Apple Car或实现完全无人驾驶
怎样用LED霓虹灯制作徽标
国产手机厂商正在布局物联网领域,加速抢占智能终端的先机
关于全波整流电路的相关知识
华为与XL Axiata开展5G City项目合作并签署合作备忘录
直播预告 | 今晚19:00-20:00,OpenHarmony 领学课堂系列技术直播课 (第2期 ),不见不散!
OLED屏上的3D Touch零部件成本翻倍 将进一步提升iPhone8的价格
水解UV胶和UVLED固化机整体解决方案
华为P10 Plus、小米Mix、Vivo X6play哪款手机性价比最高?配置、价格、外形、人气大比拼!
如何制作无线数字音频广播系统