Linux Platform devices 平台设备驱动

发布时间:2024-07-16  

platform平台设备驱动是基于设备总线驱动模型的,它只不过是将 device 进一步封装成为 platform_device,将 device_driver 进一步封装成为 platform_device_driver,前面已经分析过设备总线驱动模型,关于device 与 device_driver 的注册过程以及它们在sysfs文件系统中的层次关系就不在分析,本文重点分析platform平台设备驱动与设备总线驱动模型相比较新增添的那些东西。


在Linux设备模型的抽象中,存在着一类称作“Platform Device”的设备,内核是这样描述它们的(Documentation/driver-model/platform.txt):

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设备包括:基于端口的设备(已不推荐使用,保留下来只为兼容旧设备,legacy);连接物理总线的桥设备;集成在SOC平台上面的控制器;连接在其它bus上的设备(很少见)。等等。


这些设备有一个基本的特征:可以通过CPU bus直接寻址(例如在嵌入式系统常见的“寄存器”)。因此,由于这个共性,内核在设备模型的基础上(device和device_driver),对这些设备进行了更进一步的封装,抽象出paltform bus、platform device和platform driver,以便驱动开发人员可以方便的开发这类设备的驱动。


可以说,paltform设备对Linux驱动工程师是非常重要的,因为我们编写的大多数设备驱动,都是为了驱动plaftom设备。



platform_bus_type

我们知道,在设备总线驱动模型的中,BUS像一个月老一样,通过它的match函数,将注册到bus中的device与driver进行配对,那么每一个不同的bus 都有自己的match函数,我们来看看platform_bus_type.

struct bus_type platform_bus_type = {  

    .name       = "platform",  

    .dev_attrs  = platform_dev_attrs,  

    .match      = platform_match,  

    .uevent     = platform_uevent,  

    .pm     = &platform_dev_pm_ops,  

};  

static int platform_match(struct device *dev, struct device_driver *drv)  

{  

    struct platform_device *pdev = to_platform_device(dev);  

    struct platform_driver *pdrv = to_platform_driver(drv);  

  

    /* match against the id table first */  

    if (pdrv->id_table)  

        return platform_match_id(pdrv->id_table, pdev) != NULL;  

  

    /* fall-back to driver name match */  

    return (strcmp(pdev->name, drv->name) == 0);  

}  

    如果platform_device_driver中定义了id_table,则调用 platform_match_id 进行匹配


    举个例子:


static struct platform_device_id s3c24xx_driver_ids[] = {  

    {  

        .name       = "s3c2410-i2c",  

        .driver_data    = TYPE_S3C2410,  

    }, {  

        .name       = "s3c2440-i2c",  

        .driver_data    = TYPE_S3C2440,  

    }, { },  

};  

struct platform_device s3c_device_i2c0 = {  

    .name         = "s3c2410-i2c",  

#ifdef CONFIG_S3C_DEV_I2C1  

    .id       = 0,  

#else  

    .id       = -1,  

#endif  

    .num_resources    = ARRAY_SIZE(s3c_i2c_resource),  

    .resource     = s3c_i2c_resource,  

};  

static const struct platform_device_id *platform_match_id(struct platform_device_id *id, struct platform_device *pdev)  

{  

    while (id->name[0]) {  

        if (strcmp(pdev->name, id->name) == 0) {  

            pdev->id_entry = id;  

            return id;  

        }  

        id++;  

    }  

    return NULL;  

}  

    显然,platform_match_id 的作用就是遍历整个 Id_table 数组,寻找是否有与 platform_device->name 同名的,如果有,则返回这个 Platform_device_id ,使用Id_table 打破了原本设备总线驱动模型,一个 device 只能用与一个 device_driver 配对的局限性。现在一个platform_device_driver 可以与多个platform_device配对。


    如果没有,则只是根据 platform_device_driver->name 与 platform_device->name 进行比较,这也就是老师为啥在写平台设备驱动程序的时候经常说,“将驱动注册到内核中去,如果有同名设备,则调用driver->probe函数....”。




pletform_device 中的 id 的作用:


    if (pdev->id != -1)      /* 如果不是-1 对name编号 */  

        dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);  

    else                             /* -1时直接是名字 */

        dev_set_name(&pdev->dev, pdev->name); 



从device封装而来的platform_device

struct platform_device {  

    const char  * name;  

    int     id;  

    struct device   dev;  

    u32     num_resources;  

    struct resource * resource;  

  

    struct platform_device_id   *id_entry;  

  

    /* arch specific additions */  

    struct pdev_archdata    archdata;  

};    

    name,设备的名称,该名称在设备注册时,会拷贝到dev.init_name中。

    dev,真正的设备,通过 container_of ,就能找到整个platform_device ,访问其它成员,如后面要提到的 resource 

    num_resources、resource,该设备的资源描述,由struct resource(include/linux/ioport.h)结构抽象。 

    在Linux中,系统资源包括I/O、Memory、Register、IRQ、DMA、Bus等多种类型。这些资源大多具有独占性,不允许多个设备同时使用,因此Linux内核提供了一些API,用于分配、管理这些资源。 

    当某个设备需要使用某些资源时,只需利用struct resource组织这些资源(如名称、类型、起始、结束地址等),并保存在该设备的resource指针中即可。然后在设备probe时,设备需求会调用资源管理接口,分配、使用这些资源。而内核的资源管理逻辑,可以判断这些资源是否已被使用、是否可被使用等等。

struct resource {  

    resource_size_t start;  

    resource_size_t end;  

    const char *name;  

    unsigned long flags;  

    struct resource *parent, *sibling, *child;  

};  

static struct resource led_resource[] = {   //jz2440的参数,驱动未测试   

    [0] = {  

        .start = 0x56000010,  

        .end   = 0x56000010 + 8 - 1,  

        .flags = IORESOURCE_MEM,  

    },  

    [1] = {  

        .start = 5,  

        .end   = 5,  

        .flags = IORESOURCE_IRQ,  

    },  

};  

static struct platform_device led_dev = {  

    .name = "myled",    //设备名字 与 驱动相匹配  

    .id   = -1,  

    .num_resources = ARRAY_SIZE(led_resource),  

    .resource = led_resource,  

      

    .dev = {  

        .release = led_release,  

        //.devt = MKDEV(252, 1),  

    },  

};  


从 device_driver 封装而来的platform_device_dirver

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 (*resume)(struct platform_device *);  

    struct device_driver driver;  

    struct platform_device_id *id_table;  

};  

int platform_driver_register(struct platform_driver *drv)  

{  

    drv->driver.bus = &platform_bus_type;  

    if (drv->probe)  

        drv->driver.probe = platform_drv_probe;  

    if (drv->remove)  

        drv->driver.remove = platform_drv_remove;  

    if (drv->shutdown)  

        drv->driver.shutdown = platform_drv_shutdown;  

  

    return driver_register(&drv->driver);  

}  

    struct platform_driver结构和struct device_driver非常类似,上边的platform_drv_probe、platform_drv_remove、platform_drv_shutdown,只不过稍作转换调用platform_driver中的probe、remove、shutdown函数,举个例子稍微看一下

static int platform_drv_probe(struct device *_dev)  

{  

    struct platform_driver *drv = to_platform_driver(_dev->driver);  

    struct platform_device *dev = to_platform_device(_dev);  

  

    return drv->probe(dev);  

}  


Platform Device提供的API

/* include/linux/platform_device.h */  

extern int platform_device_register(struct platform_device *);  

extern void platform_device_unregister(struct platform_device *);  

   

extern void arch_setup_pdev_archdata(struct platform_device *);  

extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);  

extern int platform_get_irq(struct platform_device *, unsigned int);  

extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, const char *);  

extern int platform_get_irq_byname(struct platform_device *, const char *);  

extern int platform_add_devices(struct platform_device **, int);  

   

extern struct platform_device *platform_device_register_full(const struct platform_device_info *pdevinfo);  

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>