做餐饮企业网站的费用,洛阳网上房地产,泉州优化怎么做搜索,怎么给产品做网络推广platform_device_系列函数#xff0c;实际上是注册了一个叫platform的虚拟总线。使用约定是如果一个不属于任何总线的设备#xff0c;例如蓝牙#xff0c;串口等设备#xff0c;都需要挂在这个虚拟总线上。 driver/base/platform.c //platform设备声明 struct device pla…platform_device_系列函数实际上是注册了一个叫platform的虚拟总线。使用约定是如果一个不属于任何总线的设备例如蓝牙串口等设备都需要挂在这个虚拟总线上。 driver/base/platform.c //platform设备声明 struct device platform_bus { .bus_id platform, }; EXPORT_SYMBOL_GPL(platform_bus); //platform总线设备声明 struct bus_type platform_bus_type { .name platform, .dev_attrs platform_dev_attrs, .match platform_match, .uevent platform_uevent, .suspend platform_suspend, .suspend_late platform_suspend_late, .resume_early platform_resume_early, .resume platform_resume, }; EXPORT_SYMBOL_GPL(platform_bus_type); int __init platform_bus_init(void) { int error; error device_register(platform_bus);//注册了platform的设备 if (error) return error; error bus_register(platform_bus_type);//注册了叫platform的总线 if (error) device_unregister(platform_bus); return error; } //这里在platform总线上挂设备 int platform_device_add(struct platform_device *pdev) { int i, ret 0; if (!pdev) return -EINVAL; if (!pdev-dev.parent) pdev-dev.parent platform_bus;//父设备设置为platform_bus pdev-dev.bus platform_bus_type;//设置挂在platform总线上 if (pdev-id ! -1) snprintf(pdev-dev.bus_id, BUS_ID_SIZE, %s.%d, pdev-name, pdev-id); else strlcpy(pdev-dev.bus_id, pdev-name, BUS_ID_SIZE); for (i 0; i pdev-num_resources; i) { struct resource *p, *r pdev-resource[i]; if (r-name NULL) r-name pdev-dev.bus_id; p r-parent; if (!p) { if (r-flags IORESOURCE_MEM) p iomem_resource; else if (r-flags IORESOURCE_IO) p ioport_resource; } if (p insert_resource(p, r)) { printk(KERN_ERR %s: failed to claim resource %d\n, pdev-dev.bus_id, i); ret -EBUSY; goto failed; } } pr_debug(Registering platform device %s. Parent at %s\n, pdev-dev.bus_id, pdev-dev.parent-bus_id); ret device_add(pdev-dev); if (ret 0) return ret; failed: while (--i 0) if (pdev-resource[i].flags (IORESOURCE_MEM|IORESOURCE_IO)) release_resource(pdev-resource[i]); return ret; } EXPORT_SYMBOL_GPL(platform_device_add); //常用的platform_device_register内部调用了platform_device_add将设备挂在了platform总线上 /** * platform_device_register - add a platform-level device * pdev: platform device were adding */ int platform_device_register(struct platform_device *pdev) { device_initialize(pdev-dev); return platform_device_add(pdev); } EXPORT_SYMBOL_GPL(platform_device_register); 要用注册一个platform驱动的步骤 1注册设备platform_device_register 2注册驱动platform_driver_register 注册时候的两个名字必须一样才能match上才能work,例如 struct platform_device pxa3xx_device_nand { .name pxa3xx-nand, .id -1, .dev { .dma_mask pxa3xx_nand_dma_mask, .coherent_dma_mask DMA_BIT_MASK(32), }, .resource pxa3xx_resource_nand, .num_resources ARRAY_SIZE(pxa3xx_resource_nand), }; static struct platform_driver pxa3xx_nand_driver { .driver { .name pxa3xx-nand, }, .probe pxa3xx_nand_probe, .remove pxa3xx_nand_remove, #ifdef CONFIG_PM .suspend pxa3xx_nand_suspend, .resume pxa3xx_nand_resume, #endif }; 而且device注册的时候可以给driver传参数 struct device { struct klist klist_children; struct klist_node knode_parent; /* node in sibling list */ struct klist_node knode_driver; struct klist_node knode_bus; struct device *parent; struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ struct device_type *type; unsigned is_registered:1; unsigned uevent_suppress:1; struct semaphore sem; /* semaphore to synchronize calls to * its driver. */ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *driver_data; /* data private to the driver */ void *platform_data; /* Platform specific data, device core doesnt touch it */ struct dev_pm_info power; #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif u64 *dma_mask; /* dma mask (if dmaable device) */ u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dmable) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ /* arch specific additions */ struct dev_archdata archdata; spinlock_t devres_lock; struct list_head devres_head; /* class_device migration path */ struct list_head node; struct class *class; dev_t devt; /* dev_t, creates the sysfs dev */ struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); }; 传参数都是通过platform_data传所以定义为void * void *platform_data; /* Platform specific data, device static struct pxa3xx_nand_platform_data XXX_nand_info { .parts android_256m_v75_partitions, .nr_parts ARRAY_SIZE(android_256m_v75_partitions), }; static void __init XXX_init_nand(void) { pxa3xx_device_nand.dev.platform_data XXX_nand_info; platform_device_register(pxa3xx_device_nand); } static int __init pxa3xx_nand_probe(struct platform_device *pdev) { struct pxa3xx_nand_platform_data *pdata; struct nand_chip *this; struct pxa3xx_nand_info *info; struct resource *res; struct clk *clk NULL, *smc_clk NULL; int status -1; struct mtd_partition *parts; unsigned int data_buf_len; #ifdef CONFIG_MTD_NAND_PXA3xx_DMA unsigned int buf_len; #endif int i, ret 0; #ifdef CONFIG_MTD_PARTITIONS int err; #endif pdata pdev-dev.platform_data; .... .... .... } 下面解释一下pxa_register_device函数 pxa_set_ohci_info(XXX_ohci_info); void __init pxa_set_ohci_info(struct pxaohci_platform_data *info) { pxa_register_device(pxa27x_device_ohci, info); } void __init pxa_register_device(struct platform_device *dev, void *data) { int ret; dev-dev.platform_data data; ret platform_device_register(dev); if (ret) dev_err(dev-dev, unable to register device: %d\n, ret); } 其实上也就是给driver传参数通过dev.platform_data。 到这里platform_device系列函数基本算通了系列函数还有一堆设置的函数和device_register同级别的那些功能函数用法基本差不多只不过都将设备挂在了platform总线上。 platform_device_register向系统注册设备
platform_driver_register向系统注册驱动过程中在系统寻找注册的设备根据.name)找到后运行.probe进行初始化。 *************************************************************** device_register()和platform_device_register()的区别(转载) 首先看device和platform_device区别 由struct platform_device { const char * name; //NOTE:此处设备的命名应和相应驱动程序命名一致 u32 id; //以实现driver binding struct device dev; u32 num_resources; struct resource * resource; }; 可知platform_device由device派生而来 内核中关于Platform devices的注释 Platform devices ~~~~~~~~~~~~~~~~ Platform devices are devices that typically appear as autonomous entities in the system. This includes legacy port-based devices and host bridges to peripheral buses, and most controllers integrated into system-on-chip platforms. What they usually have in common is direct addressing from a CPU bus. Rarely, a platform_device will be connected through a segment of some other kind of bus; but its registers will still be directly addressable. Platform devices are given a name, used in driver binding, and a list of resources such as addresses and IRQs. 个人理解Platform devices是相对独立的设备拥有各自独自的资源addresses and IRQs 一个Platform devices实例 static struct platform_device *smdk2410_devices[] __initdata { s3c_device_usb, //片上的各个设备 s3c_device_lcd, //下面以s3c_device_lcd为例 s3c_device_wdt, s3c_device_i2c, s3c_device_iis, }; /* LCD Controller */ static struct resource s3c_lcd_resource[] { //LCD的两个资源 [0] { .start S3C2410_PA_LCD, .end S3C2410_PA_LCD S3C2410_SZ_LCD, .flags IORESOURCE_MEM, }, [1] { .start IRQ_LCD, .end IRQ_LCD, .flags IORESOURCE_IRQ, } }; struct platform_device s3c_device_lcd {//s3c_device_lcd设备 .name s3c2410-lcd, .id -1, .num_resources ARRAY_SIZE(s3c_lcd_resource), .resource s3c_lcd_resource, .dev { //device实例 .dma_mask s3c_device_lcd_dmamask, .coherent_dma_mask 0xffffffffUL } }; s3c_device_lcd的resource中硬件地址: #define S3C2410_LCDREG(x) ((x) S3C2410_VA_LCD) /* LCD control registers */ #define S3C2410_LCDCON1 S3C2410_LCDREG(0x00) #define S3C2410_LCDCON2 S3C2410_LCDREG(0x04) #define S3C2410_LCDCON3 S3C2410_LCDREG(0x08) #define S3C2410_LCDCON4 S3C2410_LCDREG(0x0C) #define S3C2410_LCDCON5 S3C2410_LCDREG(0x10) #define S3C2410_LCDCON1_CLKVAL(x) ((x) 8) #define S3C2410_LCDCON1_MMODE (17) #define S3C2410_LCDCON1_DSCAN4 (05) #define S3C2410_LCDCON1_STN4 (15) #define S3C2410_LCDCON1_STN8 (25) #define S3C2410_LCDCON1_TFT (35) #define S3C2410_ADDR(x) (0xF0000000 (x)) /* LCD controller */ #define S3C2410_VA_LCD S3C2410_ADDR(0x00600000) #define S3C2410_PA_LCD (0x4D000000) #define S3C2410_SZ_LCD SZ_1M 再分析device_register()和platform_device_register()的实现代码 device_register() /** * device_register - register a device with the system. * dev: pointer to the device structure * * This happens in two clean steps - initialize the device * and add it to the system. The two steps can be called * separately, but this is the easiest and most common. * I.e. you should only call the two helpers separately if * have a clearly defined need to use and refcount the device * before it is added to the hierarchy. */ int device_register(struct device *dev) { device_initialize(dev); //初始化设备结构 return device_add(dev); //添加设备到设备层 } platform_device_register() /** * platform_device_register - add a platform-level device * pdev: platform device were adding * */ int platform_device_register(struct platform_device * pdev) { device_initialize(pdev-dev); //初始化设备结构 return platform_device_add(pdev); //添加一个片上的设备到设备层 } 由以上函数可知device_register()和platform_device_register()都会首先初始化设备 区别在于第二步其实platform_device_add()包括device_add(),只不过要先注册resources platform_device_add() /** * platform_device_add - add a platform device to device hierarchy * pdev: platform device were adding * * This is part 2 of platform_device_register(), though may be called * separately _iff_ pdev was allocated by platform_device_alloc(). */ int platform_device_add(struct platform_device *pdev) { int i, ret 0; if (!pdev) return -EINVAL; if (!pdev-dev.parent) pdev-dev.parent platform_bus; pdev-dev.bus platform_bus_type; /* The platform_device.dev.bus_id is the canonical name for the devices. Its built from two components: * platform_device.name ... which is also used to for driver matching. * platform_device.id ... the device instance number, or else -1 to indicate theres only one. These are concatenated, so name/id serial/0 indicates bus_id serial.0, and serial/3 indicates bus_id serial.3; both would use the platform_driver named serial. While my_rtc/-1 would be bus_id my_rtc (no instance id) and use the platform_driver called my_rtc. */ if (pdev-id ! -1) snprintf(pdev-dev.bus_id, BUS_ID_SIZE, %s.%u, pdev-name, pdev-id); else //-1 indicate theres only one strlcpy(pdev-dev.bus_id, pdev-name, BUS_ID_SIZE); for (i 0; i pdev-num_resources; i) { //遍历设备资源个数如LCD的两个资源控制器和IRQ struct resource *p, *r pdev-resource[i]; if (r-name NULL) r-name pdev-dev.bus_id; p r-parent; if (!p) { //resources分为两种IORESOURCE_MEM和IORESOURCE_IO //CPU对外设IO端口物理地址的编址方式有两种I/O映射方式和内存映射方式 if (r-flags IORESOURCE_MEM) p iomem_resource; else if (r-flags IORESOURCE_IO) p ioport_resource; } if (p insert_resource(p, r)) { printk(KERN_ERR %s: failed to claim resource %d/n, pdev-dev.bus_id, i); ret -EBUSY; goto failed; } } pr_debug(Registering platform device %s. Parent at %s/n, pdev-dev.bus_id, pdev-dev.parent-bus_id); ret device_add(pdev-dev); if (ret 0) return ret; failed: while (--i 0) if (pdev-resource[i].flags (IORESOURCE_MEM|IORESOURCE_IO)) release_resource(pdev-resource[i]); return ret; } 相关参考 device_initialize() /** /drivers/base/core.c * device_initialize - init device structure. * dev: device. * * This prepares the device for use by other layers, * including adding it to the device hierarchy. * It is the first half of device_register(), if called by * that, though it can also be called separately, so one * may use devs fields (e.g. the refcount). */ void device_initialize(struct device *dev) { kobj_set_kset_s(dev, devices_subsys); kobject_init(dev-kobj); klist_init(dev-klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(dev-dma_pools); INIT_LIST_HEAD(dev-node); init_MUTEX(dev-sem); spin_lock_init(dev-devres_lock); INIT_LIST_HEAD(dev-devres_head); device_init_wakeup(dev, 0); set_dev_node(dev, -1); } device_add(struct device *dev) /** * device_add - add device to device hierarchy. * dev: device. * * This is part 2 of device_register(), though may be called * separately _iff_ device_initialize() has been called separately. * * This adds it to the kobject hierarchy via kobject_add(), adds it * to the global and sibling lists for the device, then * adds it to the other relevant subsystems of the driver model. */ 结构体resource /* /include/linux/ioport.h * Resources are tree-like, allowing * nesting etc.. */ struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child; }; 原文地址:http://blog.chinaunix.net/u1/58968/showart_467998.html , 在8250.c(driver/serial/8250.c)的初始化函数serial8250_init中给出了一个很简单的例子 static struct platform_device *serial8250_isa_devs; ...... //create a platform_device serial8250_isa_devs platform_device_alloc(serial8250,PLAT8250_DEV_LEGACY); platform_device_add(serial8250_isa_devs); //add the platform_device to system platform_driver_register(serial8250_isa_driver);//then register the platform_driver 还有另外一个比较类似的比较就是driver_register和platform_driver_register的比较 platform_driver_register(xx_driver) 会向系统注册xx_driver这个驱动程序这个函数会根据 xx_driver中的.name内容搜索系统注册的device中有没有这个platform_device,如果有就会执行 platform_driver(也就是xx_driver的类型)中的.probe函数。 对只需要初始化运行一次的函数都加上__init属性__init 宏告诉编译器如果这个模块被编译到内核则把这个函数放到.init.text段module_exit的参数卸载时同__init类似如果驱动被编译进内核则__exit宏会忽略清理函数因为编译进内核的模块不需要做清理工作显然__init和__exit对动态加载的模块是无效的只支持完全编译进内核。