• Linux驱动中的platform总线分析


    copy from :https://blog.csdn.net/fml1997/article/details/77622860

    概述

    从Linux2.6内核起,引入一套新的驱动管理和注册机制:platform_device 和 platform_driver 。Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_device 表示;驱动用 platform_driver 进行注册。

    linux_platform_driver 机制和传统的device_driver机制(即:通过 driver_register 函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中用使用这些资源时,通过platform device提供的标准接口进行申请并使用。

    platform 是一个虚拟的地址总线,相比 PCI、USB,它主要用于描述SOC上的片上资源。platform 所描述的资源有一个共同点:在CPU 的总线上直接取址。平台设备会分到一个名称(用在驱动绑定中)以及一系列诸如地址和中断请求号(IRQ)之类的资源。

    platform 总线下驱动的开发步骤是:

    1、 设备

    需要实现的结构体是:platform_device 。

    1)初始化 resource 结构变量

    2)初始化 platform_device 结构变量

    3)向系统注册设备:platform_device_register。

    以上三步,必须在设备驱动加载前完成,即执行platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。platform_driver_register()中添加device到内核最终还是调用的device_add函数。Platform_device_add和device_add最主要的区别是多了一步insert_resource(p, r),即将platform资源(resource)添加进内核,由内核统一管理。

    2、驱动

    驱动注册中,需要实现的结构体是:platform_driver 。

    在驱动程序的初始化函数中,调用了platform_driver_register()注册 platform_driver 。需要注意的是:platform_driver 和 platform_device 中的 name 变量的值必须是相同的 。这样在 platform_driver_register() 注册时,会将当前注册的 platform_driver 中的 name 变量的值和已注册的所有 platform_device 中的 name 变量的值进行比较,只有找到具有相同名称的 platform_device 才能注册成功。当注册成功时,会调用 platform_driver 结构元素 probe 函数指针。

    platform_driver_register()的注册过程:

    
        1 platform_driver_register(&s3c2410fb_driver)
    
        2 driver_register(&drv->driver)
    
        3 bus_add_driver(drv)
    
        4 driver_attach(drv)
    
        5 bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
    
        6 __driver_attach(struct device * dev, void * data)
    
        7 driver_probe_device(drv, dev)
    
        8 really_probe(dev, drv)
    
        在really_probe()中:为设备指派管理该设备的驱动:dev->driver = drv, 调用probe()函数初始化设备:drv->probe(dev)

    platform_device_系列函数及其设备注册的作用

    http://blog.csdn.net/maopig/article/details/7410055
    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
    ",
                       pdev->dev.bus_id, i);
                ret = -EBUSY;
                goto failed;
            }
        }
    
        pr_debug("Registering platform device '%s'. Parent at %s
    ",
             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 we're 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 doesn't 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 dma'able 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 dma'ble) */
    
        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
    ", 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 (1<<7)
    #define S3C2410_LCDCON1_DSCAN4 (0<<5)
    #define S3C2410_LCDCON1_STN4 (1<<5)
    #define S3C2410_LCDCON1_STN8 (2<<5)
    #define S3C2410_LCDCON1_TFT (3<<5)
    --------------------------
    #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 we're 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 we're 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.
    It's 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 there's 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 there's 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 @dev's 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对动态加载的模块是无效的,只支持完全编译进内核。


    probe()函数是什么时候被调用,设备和驱动是怎么联系起来的

    http://blog.csdn.net/charistain_huang/article/details/6612431

    probe()函数是什么时候被调用,设备和驱动是怎么联系起来的??
    platform_add_devices(ldd6410_devices, ARRAY_SIZE(ldd6410_devices)); //这是bsp中添加所有的设备–》 platform_device_register(devs[i]);//注册平台设备—》platform_device_add(pdev);将平台设备加入到platform_bus中—》device_add(&pdev->dev);

    下面是驱动:

    static int __init gpio_led_init(void)
    {
     return platform_driver_register(&gpio_led_driver);   //注册平台驱动
    }
    platform_driver_register(&gpio_led_driver) ----》driver_register(&drv->driver);----》bus_add_driver(drv); //添加驱动到总线 ---》driver_attach(drv);//为驱动寻找相应的设备----》
    
    int driver_attach(struct device_driver *drv)
    {
     return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);   //遍历设备总线寻找驱动
    }
    
    
    
    -----》__driver_attach()
    
    static int __driver_attach(struct device *dev, void *data)
    {
     struct device_driver *drv = data;
    
     /*
      * Lock device and try to bind to it. We drop the error
      * here and always return 0, because we need to keep trying
      * to bind to devices and some drivers will return an error
      * simply if it didn't support the device.
      *
      * driver_probe_device() will spit a warning if there 
      * is an error.
      */
    
     if (drv->bus->match && !drv->bus->match(dev, drv))   //通过match判断驱动和设备是否匹配,这里通过比较dev和drv中的设备名来判断,所以设备名需要唯一
      return 0;
    
     if (dev->parent) /* Needed for USB */
      down(&dev->parent->sem);
     down(&dev->sem);
     if (!dev->driver)
      driver_probe_device(drv, dev);  //   驱动和设备绑定
     up(&dev->sem);
     if (dev->parent)
      up(&dev->parent->sem);
    
     return 0;
    }
    
    
    driver_probe_device(drv, dev);  ---》really_probe(dev, drv);
    
    
    
    
    static int really_probe(struct device *dev, struct device_driver *drv)
    {
     int ret = 0;
    
     atomic_inc(&probe_count);
     pr_debug("bus: '%s': %s: probing driver %s with device %s
    ",
       drv->bus->name, __func__, drv->name, dev->bus_id);
     WARN_ON(!list_empty(&dev->devres_head));
    
     dev->driver = drv;
     if (driver_sysfs_add(dev)) {
      printk(KERN_ERR "%s: driver_sysfs_add(%s) failed
    ",
       __func__, dev->bus_id);
      goto probe_failed;
     }
    
     if (dev->bus->probe) {   
      ret = dev->bus->probe(dev);
      if (ret)
       goto probe_failed;
     } else if (drv->probe) {
      ret = drv->probe(dev);  //这里才真正调用了驱动的probe
      if (ret)
       goto probe_failed;
     }
    
     driver_bound(dev);
     ret = 1;
     pr_debug("bus: '%s': %s: bound device %s to driver %s
    ",
       drv->bus->name, __func__, dev->bus_id, drv->name);
     goto done;
    
    probe_failed:
     devres_release_all(dev);
     driver_sysfs_remove(dev);
     dev->driver = NULL;
    
     if (ret != -ENODEV && ret != -ENXIO) {
      /* driver matched but the probe failed */
      printk(KERN_WARNING
             "%s: probe of %s failed with error %d
    ",
             drv->name, dev->bus_id, ret);
     }
     /*
      * Ignore errors returned by ->probe so that the next driver can try
      * its luck.
      */
     ret = 0;
    done:
     atomic_dec(&probe_count);
     wake_up(&probe_waitqueue);
     return ret;
    }

    http://apps.hi.baidu.com/share/detail/21837006

    Platform Device and Drivers

    从< linux/platform_device.h>我们可以了解Platform bus上面的驱动模型接口:platform_device,platform_driver。和PCI和USB这些大结构的总线不同,虚拟总线Platform bus使用最小结构来集成SOC processer上的各种外设,或者各种“legacy”之间的互联。

    Platform device
    典型的Platform device是系统中的各种自主设备,包括各种桥接在外围总线上的port-based device和host,以及各种集成在SOC platform上的控制器。他们都有一个特点,那就是CPU BUS可以直接寻址,或者特殊的情况platform_device连接在其他总线上,但是它的寄存器是可以被直接寻址的。
    Platform device有一个名字,用来进行driver的绑定;还有诸如中断,地址之类的一些资源列表

    struct platform_device {
    const char *name;
    u32 id;
    struct device dev;
    u32 num_resources;
    struct resource *resource;
    };
    
    
    Platform drivers
    Platform driver满足标准driver model,对driver的discovery/enumeration是在driver外部进行的,driver提供了probe()和 remove()方法.Platfomr dirvers通过标准模型提供power management和shutdown通知。
    
    struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*suspend_late)(struct platform_device *, pm_message_t state);
    int (*resume_early)(struct platform_device *);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
    };

    Probe()函数必须验证指定设备的硬件是否真的存在,probe()可以使用设备的资源,包括时钟,platform_data等,Platform driver可以通过下面的函数完成对驱动的注册:
    int platform_driver_register(struct platform_driver *drv);
    一般来说设备是不能被热插拔的,所以可以将probe()函数放在init段里面来节省driver运行时候的内存开销:
    int platform_driver_probe(struct platform_driver *drv,
    int (probe)(struct platform_device ))

    Device Enumeration
    作为一个规则,平台(一般来说是板级)启动代码会注册所有的Platform device:
    int platform_device_register(struct platform_device *pdev);
    int platform_add_devices(struct platform_device **pdevs, int ndev);
    一般来说只会注册那些实际存在的设备,不过也存在特殊的情况,比如kernel可能需要与一个不在板子上的外部网络适配器工作,或者那些不挂在任何总线上的控制器。
    一般情况下,固件启动的过程会输出一个描述板子上所有存在设备的表。如果没有这个表,系统启动代码建立正确的设备的唯一方法就是为一个特定的板子编译一个kernel。这种board-specific kernel广泛用在嵌入式和一般系统的开发上。
    在大部分情况下,设备的memory和IRQ资源不足够让驱动正常工作。board setup code会用device的platform_data域来为设备提供一些额外的资源。
    嵌入式系统上的设备会频繁地使用一个或者多个时钟,这些时钟因为节电的原因只有在真正使用的时候才会被打开,系统在启动过程中会为设备分配时钟,可以通过clk_get(&pdev->dev, clock_name)来获得需要的时钟。

    Legacy Drivers : Device Probing
    一些driver并不会完全遵守标准driver model,这些driver会去注册自己的platform device,而不是让系统来完成注册。这是不值得推
    荐的,主要用来兼容以前的一些旧设备。可以通过下面的API来支持这些legacy driver,一般这些API使用在不支持热插拔的driver上面:
    struct platform_device *platform_device_alloc(
    const char *name, int id);
    可以使用platform_device_alloc动态地创建一个设备,一个更好的方法是,通过下面的函数动态创建一个设备,并把这个设备注册到系统中:
    struct platform_device *platform_device_register_simple(
    const char *name, int id,
    struct resource *res, unsigned int nres);

    Device Naming and Driver Binding
    platform_device.dev.bus_id是一个设备在总线上的名字,它包含两部分:
    * platform_device.name 设备名字,用来进行driver的匹配
    * platform_device.id 设备实例的标号,如果是-1,表示同样名字的设备只有一个
    举个简单的例子,name/id是“serial/1”则它的bus_id就是serial.1 如果name/id是“serial/0”则它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”则它的bus_id就是serial。
    driver的绑定是通过driver core自动完成的,完成driver和device的匹配后以后会自动执行probe()函数,如果函数执行成功,则driver和device就绑定在一起了,drvier和device匹配的方法有3种:
    * 当一个设备注册的时候,他会在总线上寻找匹配的driver,platform device一般在系统启动很早的时候就注册了
    * 当一个驱动注册[platform_driver_register()]的时候,他会遍历所有总线上的设备来寻找匹配,在启动的过程驱动的注册一般比较晚,或者在模块载入的时候
    * 当一个驱动注册[platform_driver_probe()]的时候, 功能上和使用platform_driver_register()是一样的,唯一的区别是它不能被以后其他的device probe了,也就是说这个driver只能和一个device绑定。

      Platform device 和 Platform driver实际上是cpu总线可以直接寻址的设备和驱动,他们挂载在一个虚拟的总线platform_bus_type上,是一种bus-specific设备和驱动。与其他bus-specific驱动比如pci是一样的。他们都是将device和device_driver加了一个warpper产生,仔细看看platform_device就可以看到它必然包含一个device dev,而platform_driver也一样,它必然包含一个device_driver driver。
      所有的设备通过bus_id挂在总线上,多个device可以共用一个driver,但是一个device不可以对应多个driver。驱动去注册时候会根据设备名寻找设备,没有设备会注册失败,注册的过程会通过probe来进行相应资源的申请,以及硬件的初始化,如果probe执行成功,则device和driver的绑定就成功了。设备注册的时候同样会在总线上寻找相应的驱动,如果找到他也会试图绑定,绑定的过程同样是执行probe。

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yili_xie/archive/2010/01/15/5193609.aspx

    platform_driver_probe与platform_driver_register的区别

    Always Believe Something Beauitful Will Be Happen
  • 相关阅读:
    (转)-为什么分布式一定要有Redis?
    (九)redis使用lua脚本
    (八)redis实现分布式锁
    (七)面试题-Redis比较常见的面试题-转载
    (六)redis缓存穿透,击穿,雪崩以及解决方案
    (五)redis的主从复制
    (四)redis的数据持久化-RDB,AOF
    (三)redis的其他功能-Bitmap,HyperLogLog,GEO
    JsTracker
    Web开发者助手 FeHelper
  • 原文地址:https://www.cnblogs.com/Oude/p/12038751.html
Copyright © 2020-2023  润新知