Linux平台驱动程序和普通设备驱动程序有什么区别?
我以前有过关于平台驱动程序以及正常的设备驱动程序的想法:
- 平台驱动程序适用于片上设备。 而且,
-
正常的设备驱动程序适用于与proccesor芯片接口的驱动程序。 在遇到一个i2c驱动程序之前。
但在这里,我正在通过定义为平台驱动程序的多functioni2c驱动程序进行阅读。 我已经通过https://www.kernel.org/doc/Documentation/driver-model/platform.txt 。 但是仍然无法弄清楚如何定义驱动程序的结论,如同样的芯片以及接口设备。 我也通过这个链接.. http://meld.org/discussion/general-discussion/platform-driver-vs-ordinary-device-drivers
请有人解释。
你的引用是好的,但没有定义什么是平台设备 。 有一个在LWN上 。 我们可以从这个页面学到什么:
-
平台设备本质上是不可发现的 ,即硬件不能说“嗨!我现在!” 到软件。 典型的例子是i2c设备,
kernel/Documentation/i2c/instantiating-devices
状态:与PCI或USB设备不同,I2C设备不在硬件级别(运行时)枚举。 相反,软件必须知道(在编译时)每个I2C总线段上连接了哪些设备。 所以USB和PCI 不是平台设备。
-
平台设备通过匹配名称绑定到驱动程序,
- 平台设备应该在系统启动的早期进行注册 。 因为他们通常对系统(平台)的其他部分及其驱动程序至关重要。
所以基本上,这个问题“ 是平台设备还是标准设备? ” 更多的是它使用哪个总线的问题 。 要使用特定平台设备,您必须:
- 注册将pipe理此设备的平台驱动程序 。 它应该定义一个独特的名字,
- 注册您的平台设备 ,定义与驱动程序相同的名称。
平台驱动程序适用于片上设备。
不是真的(在理论上,但在实践中是真实的)。 i2c设备不是onChip,而是平台设备,因为它们是不可发现的。 我们也可以想到onChip设备是正常的设备。 示例:现代x86处理器上的集成PCI GPU芯片。 它是可发现的,因此不是平台设备。
正常的设备驱动程序适用于与处理器芯片接口的驱动程序。 在遇到一个i2c驱动程序之前。
不对。 许多普通的设备连接到处理器,但不通过i2c总线。 例如:一个USB鼠标。
[编辑]在你的情况下,看看drivers/usb/host/ohci-pnx4008.c
,这是一个USB主机控制器平台设备(这里的USB主控制器是不可发现的,而USB设备,将连接到它, 是)。 它是由电路板文件 ( arch/arm/mach-pnx4008/core.c:pnx4008_init
)注册的平台设备。 在其探测function中,它使用i2c_register_driver
将其i2c设备注册到总线。 我们可以推断USB主机控制器芯片组通过i2c总线与CPU通信。
为什么那个架构? 因为一方面,这个设备可以被认为是为系统提供一些function的裸设备。 另一方面,它是一个支持USB主机的设备。 它需要注册到USB堆栈( usb_create_hcd
)。 所以只有探究i2c将是不够的。 看看Documentation/i2c/instantiating-devices
。
最小的模块代码示例
用一些具体的例子来说,差异也会变得更加清晰。
平台设备示例
码:
- 驱动上游
- 最小的QEMU虚拟设备驱动 。
- 在Linux内核上的DTS条目修改
进一步的集成注意事项在: https : //stackoverflow.com/a/44612957/895245
怎么看:
- 寄存器和中断地址在设备树中被硬编码,并与QEMU-
-M versatilepb
机器描述相匹配,该机器描述代表SoC - 没有办法去除设备硬件(因为它是SoC的一部分)
- 通过与驱动程序中的
platform_driver.name
相匹配的compatible
设备树属性select正确的驱动程序 -
platform_driver_register
是主要的注册接口
#include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/platform_device.h> MODULE_LICENSE("GPL"); static struct resource res; static unsigned int irq; static void __iomem *map; static irqreturn_t lkmc_irq_handler(int irq, void *dev) { /* TODO this 34 and not 18 as in the DTS, likely the interrupt controller moves it around. * Understand precisely. 34 = 18 + 16. */ pr_info("lkmc_irq_handler irq = %d dev = %llx\n", irq, *(unsigned long long *)dev); /* ACK the IRQ. */ iowrite32(0x9ABCDEF0, map + 4); return IRQ_HANDLED; } static int lkmc_platform_device_probe(struct platform_device *pdev) { int asdf; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; dev_info(dev, "probe\n"); /* Play with our custom poperty. */ if (of_property_read_u32(np, "lkmc-asdf", &asdf) ) { dev_err(dev, "of_property_read_u32\n"); return -EINVAL; } if (asdf != 0x12345678) { dev_err(dev, "asdf = %llx\n", (unsigned long long)asdf); return -EINVAL; } /* IRQ. */ irq = irq_of_parse_and_map(dev->of_node, 0); if (request_irq(irq, lkmc_irq_handler, 0, "lkmc_platform_device", dev) < 0) { dev_err(dev, "request_irq"); return -EINVAL; } dev_info(dev, "irq = %u\n", irq); /* MMIO. */ if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { dev_err(dev, "of_address_to_resource"); return -EINVAL; } if (!request_mem_region(res.start, resource_size(&res), "lkmc_platform_device")) { dev_err(dev, "request_mem_region"); return -EINVAL; } map = of_iomap(pdev->dev.of_node, 0); if (!map) { dev_err(dev, "of_iomap"); return -EINVAL; } dev_info(dev, "res.start = %llx resource_size = %llx\n", (unsigned long long)res.start, (unsigned long long)resource_size(&res)); /* Test MMIO and IRQ. */ iowrite32(0x12345678, map); return 0; } static int lkmc_platform_device_remove(struct platform_device *pdev) { dev_info(&pdev->dev, "remove\n"); free_irq(irq, &pdev->dev); iounmap(map); release_mem_region(res.start, resource_size(&res)); return 0; } static const struct of_device_id of_lkmc_platform_device_match[] = { { .compatible = "lkmc_platform_device", }, {}, }; MODULE_DEVICE_TABLE(of, of_lkmc_platform_device_match); static struct platform_driver lkmc_plaform_driver = { .probe = lkmc_platform_device_probe, .remove = lkmc_platform_device_remove, .driver = { .name = "lkmc_platform_device", .of_match_table = of_lkmc_platform_device_match, .owner = THIS_MODULE, }, }; static int lkmc_platform_device_init(void) { pr_info("lkmc_platform_device_init\n"); return platform_driver_register(&lkmc_plaform_driver); } static void lkmc_platform_device_exit(void) { pr_info("lkmc_platform_device_exit\n"); platform_driver_unregister(&lkmc_plaform_driver); } module_init(lkmc_platform_device_init) module_exit(lkmc_platform_device_exit)
PCI非平台设备示例
- 驱动上游
- 最小的QEMU虚拟设备驱动
怎么看:
- 寄存器和中断地址由PCI系统dynamic分配,不使用设备树
- PCI
vendor:device
select了正确的驱动程序vendor:device
ID(QEMU_VENDOR_ID, EDU_DEVICE_ID
)。 这是烘烤到每个设备,供应商必须确保唯一性。 - 我们可以在现实生活中使用
device_add edu
和device_add edu
来插入和删除PCI设备。 探测不是自动的,但可以在启动后使用echo 1 > /sys/bus/pci/rescan
。 另请参见: 除了init之外,为什么在Linux设备驱动程序中需要探测方法?
#include <asm/uaccess.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> #define BAR 0 #define CDEV_NAME "lkmc_hw_pci_min" #define EDU_DEVICE_ID 0x11e9 #define QEMU_VENDOR_ID 0x1234 MODULE_LICENSE("GPL"); static struct pci_device_id id_table[] = { { PCI_DEVICE(QEMU_VENDOR_ID, EDU_DEVICE_ID), }, { 0, } }; MODULE_DEVICE_TABLE(pci, id_table); static int major; static struct pci_dev *pdev; static void __iomem *mmio; static struct file_operations fops = { .owner = THIS_MODULE, }; static irqreturn_t irq_handler(int irq, void *dev) { pr_info("irq_handler irq = %d dev = %d\n", irq, *(int *)dev); iowrite32(0, mmio + 4); return IRQ_HANDLED; } static int probe(struct pci_dev *dev, const struct pci_device_id *id) { pr_info("probe\n"); major = register_chrdev(0, CDEV_NAME, &fops); pdev = dev; if (pci_enable_device(dev) < 0) { dev_err(&(pdev->dev), "pci_enable_device\n"); goto error; } if (pci_request_region(dev, BAR, "myregion0")) { dev_err(&(pdev->dev), "pci_request_region\n"); goto error; } mmio = pci_iomap(pdev, BAR, pci_resource_len(pdev, BAR)); pr_info("dev->irq = %u\n", dev->irq); if (request_irq(dev->irq, irq_handler, IRQF_SHARED, "pci_irq_handler0", &major) < 0) { dev_err(&(dev->dev), "request_irq\n"); goto error; } iowrite32(0x12345678, mmio); return 0; error: return 1; } static void remove(struct pci_dev *dev) { pr_info("remove\n"); free_irq(dev->irq, &major); pci_release_region(dev, BAR); unregister_chrdev(major, CDEV_NAME); } static struct pci_driver pci_driver = { .name = CDEV_NAME, .id_table = id_table, .probe = probe, .remove = remove, }; static int myinit(void) { if (pci_register_driver(&pci_driver) < 0) { return 1; } return 0; } static void myexit(void) { pci_unregister_driver(&pci_driver); } module_init(myinit); module_exit(myexit);