精华内容
下载资源
问答
  • Linux platform设备驱动模型 文章目录Linux platform设备驱动模型前言框架设备与驱动的分离设备(device)驱动(driver)匹配(match)参考 前言 为什么要往平台设备驱动迁移?这里需要引入设备,总线,驱动这三个...

    Linux platform设备驱动模型

    前言

    为什么要往平台设备驱动迁移?这里需要引入设备,总线,驱动这三个概念。上一篇字符型设备驱动的实现实际将设备和驱动集成到同一个文件中实现,如果这里有硬件A的驱动硬件B的驱动硬件C的驱动,然后有三类用户接口E接口F接口G,这里用户接口是提供给用户层调用的接口,每一种接口又必须兼容这三种硬件,按照原来的实现方式,为了适配所有的使用需求,理论上会出现A+EA+FA+GB+EB+FB+GC+EC+FC+G,这几种实现方式,而表现在代码中的则是

    #if A
    #elif B
    #elif C
    #endif
    

    当然,目前接口数量和硬件数量不是很庞大的时候,维护上暂时不会造成太大的问题,所以,这里引入了设备/总线/驱动的机制,实现了驱动和设备之间的解耦,这里我的理解是和设计模式中的中介者模式比较相似。
    在这里插入图片描述

    框架

    大致地整理了一下platform设备驱动模型的整体框架。
    在这里插入图片描述

    设备与驱动的分离

    设备(device)

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/slab.h>
    #include <linux/platform_device.h>
    
    static struct platform_device *character_dev;
    
    static int __init cnc_platform_character_init(void){
    
    	int ret = 0;	
    	character_dev = platform_device_alloc("cnc_platform_character", -1);	
    	if (!character_dev)		
    		return -ENOMEM;	
    	ret = platform_device_add(character_dev);	
    	if (ret) {		
    		platform_device_put(character_dev);	
    		printk("\n\n\n\n\n Success platform_device_put(character_dev)\n\n\n\n\n");
    		return ret;	
    	}
    	printk("\n\n\n\n\n Failed platform_device_put(character_dev)\n\n\n\n\n");
    	return 0;
    }
    module_init(cnc_platform_character_init);
    
    static void __exit cnc_platform_character_exit(void){
    	printk("%s call\n",__func__);
    	platform_device_unregister(character_dev);
    
    }
    module_exit(cnc_platform_character_exit);
    
    MODULE_VERSION("1.0");
    MODULE_LICENSE("GPL");
    

    驱动(driver)

    #include <linux/init.h>
    #include <linux/types.h>
    #include <linux/module.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/slab.h>
    #include <linux/platform_device.h>
    #include <linux/miscdevice.h>
    
    #include <linux/of_device.h>
    
    #define DRIVER_DATA_SIZE 	4096
    
    struct cnc_character_st{
    	struct cdev device;
    	u8	data[DRIVER_DATA_SIZE];
    	struct miscdevice miscdev;
    };
    
    //TODO
    static ssize_t cnc_character_read (struct file * fd, char __user * data, size_t len, loff_t * offset){
    	ssize_t ret = 0;
    	return ret;
    }
    
    //TODO
    static ssize_t cnc_character_write (struct file * fd, const char __user * data, size_t len, loff_t * offset){
    	ssize_t ret = 0;
    	return ret;
    }
    
    //TODO
    static long cnc_character_unlocked_ioctl (struct file * fd, unsigned int data, unsigned long cmd){
    	long ret = 0;
    	return ret;
    }
    
    //TODO
    static int cnc_character_open (struct inode * node, struct file * fd){
    	int ret = 0;
    	return ret;
    }
    //TODO
    static int cnc_character_release (struct inode * node, struct file * fd){
    	int ret = 0;
    	return ret;
    }
    
    
    static const struct file_operations cnc_character_ops = {
    	.owner = THIS_MODULE,
    	.read = cnc_character_read,
    	.write = cnc_character_write,
    	.open = cnc_character_open,
    	.unlocked_ioctl = cnc_character_unlocked_ioctl,
    	.release = cnc_character_release,
    };
    
    
    static int cnc_character_probe(struct platform_device *pdev){
    
    	int ret = 0;
    	struct cnc_character_st *character_dev;
    
    	character_dev = devm_kzalloc(&pdev->dev, sizeof(*character_dev),GFP_KERNEL);
    
    	character_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
    	character_dev->miscdev.name = "cnc_platform_character";
    	character_dev->miscdev.fops = &cnc_character_ops;
    	//ret = misc_register(&character_dev->miscdev);
    	platform_set_drvdata(pdev, character_dev);
    	ret = misc_register(&character_dev->miscdev);
    
    	if(ret < 0){
    		return ret;
    	}
    	return 0;
    	
    }
    
    static int cnc_character_remove(struct platform_device *pdev){
    	
    	struct cnc_character_st *gl = platform_get_drvdata(pdev);
    	printk("%s call\n",__func__);
    	misc_deregister(&gl->miscdev);
    	return 0;
    }
    
    static struct platform_driver cnc_character_driver = {
    	.driver = {
    		.name = "cnc_platform_character",
    		.owner = THIS_MODULE,
    	},
    	.probe = cnc_character_probe,
    	.remove = cnc_character_remove, 
    };
    
    module_platform_driver(cnc_character_driver);
    
    MODULE_VERSION("1.0");
    MODULE_LICENSE("GPL");
    

    匹配(match)

    函数static int platform_match(struct device *dev, struct device_driver *drv)在内核drivers/base/platform.c中,其源代码如下:

    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);
    
    	/* When driver_override is set, only bind to the matching driver */
    	if (pdev->driver_override)
    		return !strcmp(pdev->driver_override, drv->name);
    
    	/* Attempt an OF style match first */
    	if (of_driver_match_device(dev, drv))
    		return 1;
    
    	/* Then try ACPI style match */
    	if (acpi_driver_match_device(dev, drv))
    		return 1;
    
    	/* Then try to match against the id table */
    	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_match主要根据四种情况对设备和驱动进行匹配。
    根据注释可以知道,首先判断是否已经设置driver_override,后面只绑定到匹配的驱动程序。

    • 根据设备树风格的匹配;
    • 根据ACPI风格的匹配;
    • 匹配ID表(即platform_device设备名是否出现在platform_driver的ID表内)
    • 匹配platform_device设备名和驱动的name成员

    参考

    https://blog.csdn.net/clam_zxf/article/details/80675395

    https://www.cnblogs.com/chenfulin5/p/5690661.html

    http://blog.chinaunix.net/uid-25622207-id-2778126.html

    展开全文
  • 44 linux内核里的platform设备驱动模型

    千次阅读 2017-06-26 16:41:39
    内核里的设备驱动模型分成三部分: bus(struct bus_type), device(struct device), driver(struct device_driver)我们最常用的设备驱动模型是基于上面的模型封装出来的,叫platform驱动模型,也是分成三部分: ...

    内核里的设备驱动模型分成三部分: bus(struct bus_type), device(struct device), driver(struct device_driver)

    我们最常用的设备驱动模型是基于上面的模型封装出来的,叫platform驱动模型,也是分成三部分:
    platform_bus_type(struct bus_type) , 内核里已实现好这个总线,并且总线匹配函数也写好的。
    platform_device(基于struct device结构体扩展而来, device在platform_devices结构体里就是一个成员)
    platform_driver(基于device_driver扩展而来)

    /
    platform总线,已在linux内核里实现好相关功能:
    “drivers/base/platform.c”

     824 struct bus_type platform_bus_type = {
     825     .name       = "platform",
     826     .dev_attrs  = platform_dev_attrs,
     827     .match      = platform_match, //匹配函数
     828     .uevent     = platform_uevent,
     829     .pm     = &platform_dev_pm_ops,
     830 };  
     831 EXPORT_SYMBOL_GPL(platform_bus_type);
    
     656 static int platform_match(struct device *dev, struct device_driver *drv)
     657 {
     658     struct platform_device *pdev = to_platform_device(dev); //通过dev的地址获取platform_device对象的首地址
     659     struct platform_driver *pdrv = to_platform_driver(drv);
     660 
     661     
     662     if (of_driver_match_device(dev, drv)) //使用device_driver里的of_match_table数组来匹配设备名.(目录内核里基本不用)
     663         return 1; //匹配成功直接返回, 匹配失败则接着往后执行.
     664 
     665     
     666     if (pdrv->id_table) //如果platform_driver对象有设置id_table成员,则用id_table里指定的设备名来与设备进行匹配. 按名字(字符串)比较来匹配.
     667         return platform_match_id(pdrv->id_table, pdev) != NULL; 
     668 
     669    
     670     return (strcmp(pdev->name, drv->name) == 0); //如果platform_driver没有设置id_table成员的值,则使用platform_device对象的名字与device_driver对象的名字进行匹配
     671 }
    

    ///
    平台设备, struct platform_device每个对象表示一个具体的硬件, 用于提供硬件相关的资源

    #include <linux/platform_device.h>
    
    struct platform_device {
            const char      * name;  //设备名字可以重复. 
            int             id;   //设备的id. 注册时dev.init_name=name.id   
            struct device   dev; //平台设备其于原device类型上封装出来的, dev.platform_data也可以提供信息给设备驱动.  设备驱动里可以使用platform_set_drvdata(pdev, data)设置针对此设备的数据. platform_get_drvdata(pdev)获取挂载的数据
    
            u32             num_resources; //数组元素个数
            struct resource * resource;   //数组首地址, 用于提供信息给设备驱动(中断号, IO口,寄存器地址)
                         //当注册设备时,系统会检查这里设置的资源,独占使用这些资源.
        ...
        const struct platform_device_id *id_entry; //指向platform_driver里的id_table数组里匹配上的元素的地址
    };
    
    
    
    extern int platform_device_register(struct platform_device *pdev); //注册平台设备,注册时会指定pdev->dev.bus为平台总线的地址. 设置pdev->dev.init_name.
    extern void platform_device_unregister(struct platform_device *); //反注册平台设备
    
    //在设备驱动里获取指定的资源的函数:
    struct resource *platform_get_resource(struct platform_device *dev,
                           unsigned int type, unsigned int num)  // type用于指定资源类型, num为指定类型的资源里的第几个.
     如: platform_get_resource(pdev, IORESOURCE_IO, 2); //获取pdev平台设备的关于io口的第2个资源
    int platform_get_irq(struct platform_device *dev, unsigned int num) //用于在设备驱动里获取设备提供的第num个中断号资源
    
    /
    //////// platform_device 里的资源 /////////////////////////////
    include <linux/ioport.h>
    
     18 struct resource {
     19     resource_size_t start;
     20     resource_size_t end;
     21     const char *name;
     22     unsigned long flags; //表示资源的类型(有中断号, IO口,DMA通道号,  内存地址...)
     23     struct resource *parent, *sibling, *child;
     24 };
    
     31 #define IORESOURCE_TYPE_BITS    0x00001f00  /* Resource type */
     32 #define IORESOURCE_IO       0x00000100  //表示io口的资源
     33 #define IORESOURCE_MEM      0x00000200  //表示内存地址
     34 #define IORESOURCE_IRQ      0x00000400   //中断号
     35 #define IORESOURCE_DMA      0x00000800   // DMA通道号
     36 #define IORESOURCE_BUS      0x00001000   //总线号
    
    当资源个数与用到多种类型时,可以直接使用下面函数获取指定类型下的第几个资源.
      60 struct resource *platform_get_resource(struct platform_device *dev,
                              unsigned int type, unsigned int num)
      61 {
      62     int i;
      63 
      64     for (i = 0; i < dev->num_resources; i++) {
      65         struct resource *r = &dev->resource[i];
      66 
      67         if (type == resource_type(r) && num-- == 0)
      68             return r;
      69     }
      70     return NULL;
      71 }

    /
    平台设备驱动, 每个struct platform_driver对象表示一个驱动方法.

    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; //其于原device_driver上封装出来的
            const struct platform_device_id *id_table; //一个数组用于指定哪些设备的名字可以与此设备驱动匹配上
        /*     struct platform_device_id { 
                char name[PLATFORM_NAME_SIZE]; //名字
                kernel_ulong_t driver_data; //当设备与此项的内容匹配上时,设备驱动可用driver_data里的数据对匹配的设备作针对性的初始化工作. 
                    __attribute__((aligned(sizeof(kernel_ulong_t)))); //对齐作用
            };
        */
    
    };
    

    注意device_driver里也有probe,remove函数指针,但它与platform_driver的probe, remove函数原型不一样。device_driver里函数原型的参数为”struct device “, platform_driver的函数原型的参数为”struct platform_device “.

    extern int platform_driver_register(struct platform_driver *); //注册平台设备驱动
    extern void platform_driver_unregister(struct platform_driver *); //反注册平台设备驱动


    linux内核里的驱动模型只是bus, device, device_driver这三者. 当platform_device对象注册时,是仅仅把平台设备对象中的dev成员注册而已。当platform_driver对象注册时,也是仅仅把对象里的driver成员注册. 都可以通过成员的地址获取出对象的首地址. platform_driver是基于device_driver扩展而来, 当总线匹配上时, 应是先触发device_driver的probe函数. 那又怎样触发调用platform_driver的probe函数?

    1 当注册平台设备驱动时:

        int platform_driver_register(struct platform_driver *drv)
        {
            drv->driver.bus = &platform_bus_type; //指定属于平台总线
            if (drv->probe) 
            drv->driver.probe = platform_drv_probe; //当平台设备驱动有probe函数时, device_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);
        }
    

    2 当有设备与设备驱动匹配上时, platform_drv_probe函数就会被触发调用.

        static int platform_drv_probe(struct device *_dev)
        {
            struct platform_driver *drv = to_platform_driver(_dev->driver); //_dev->driver是platform_driver对象里的driver成员的地址。然后根据此成员的地址获取出整个platform_driver对象的首地址.
            struct platform_device *dev = to_platform_device(_dev); //根据platform_device里的dev 成员的地址获取出整个platform_device对象的首地址
    
            return drv->probe(dev); //调用platform_driver对象里的probe函数,并传递platform_device对象的地址.
        }
    

    ///
    一个简单的测试例子:
    mypdev.c

    
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/device.h>
    #include <linux/platform_device.h>
    
    
    struct platform_device mydev = {
        .name = "distancer",
        .id = 0,
        .dev = {
            .platform_data = NULL,
        },
        .resource = NULL,
    };
    
    //生成初始化函数和卸载函数,并用module_init, module_exit指定相应的函数
    module_driver(mydev, platform_device_register, platform_device_unregister);
    
    MODULE_LICENSE("GPL");

    ///
    mypdrv.c

    
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/platform_device.h>
    #include <linux/fs.h>
    
    int myprobe(struct platform_device *pdev)
    {
        printk("in myprobe : %s.%d, driver_data=%p\n", pdev->name, pdev->id, pdev->id_entry->driver_data);
        return 0;
    }
    
    int myremove(struct platform_device *pdev)
    {
        printk("in myremove : %s.%d, driver_data=%p\n", pdev->name, pdev->id, pdev->id_entry->driver_data);
        return 0;
    }
    
    struct platform_device_id ids[] = {
        {"mykey", 0x11},
        {"myir",  0x22},
        {"distancer", 0x33},
        {}, //最后必须给一个空的元素,标明是最后一个
    };  
    
    struct platform_driver mypdrv = {
        .probe = myprobe,
        .remove = myremove, 
    
        .driver = {
            .owner = THIS_MODULE,
            .name = "mydrv",    
        },
        .id_table = ids,
    };
    
    module_platform_driver(mypdrv);
    
    MODULE_LICENSE("GPL");
    展开全文
  • 传统 Platform设备驱动方式 中 SPI 驱动模型

    传统 Platform设备驱动方式 中 SPI 驱动模型

    #include <linux/module.h>
    #include <linux/spi/spi.h>
    
    #define DEVICE_NAME   "sensor"
    #define SENSOR_SPI_BUS 0
    struct spi_device *sensor_spi = NULL;
    
    int sensor_spi_write(void)
    {
        // 调用平台的接口实现 spi_write
        return 0;
    }
    
    int sensor_spi_read(void)
    {
        // 调用平台的接口实现 spi_read
        return 0;
    }
    
    
    static const struct spi_device_id sensor_spi_id[] = {
        { DEVICE_NAME, 0 },
        { }
    };
    
    MODULE_DEVICE_TABLE(spi, sensor_spi_id);
    
    static int  sensor_probe(struct spi_device *spi)
    {
        sensor_spi = spi;
        return 0;
    }
    
    static int  sensor_remove(struct spi_device *spi)
    {
        return 0;
    }
    
    static struct spi_driver sensor_driver = {
        .driver = {
            .name  = DEVICE_NAME,
            .owner = THIS_MODULE,
        },
        .probe    =  sensor_probe,
        .remove   =  sensor_remove,
        .id_table =  sensor_spi_id,
    };
    
    static __init int sensor_spi_init(void)
    {
        int status=-1;
        struct spi_master *master;
        struct spi_device *spi;
        struct spi_board_info chip =
        {
            .modalias     = DEVICE_NAME,
            .mode         = 0x00,
            .bus_num      = 0,
            .chip_select  = 0,
            .max_speed_hz = 2000000,
        };
        spi_register_driver(&sensor_driver);
        if (status<0)
        {
            pr_err("%s: spi_register_driver spi_driver failure. status = %d\n", __func__, status);
        }
        pr_err("%s: spi_register_driver spi_driver success. status = %d\n", __func__, status);
        master = spi_busnum_to_master(SENSOR_SPI_BUS);
        if (!master)
        {
            status = -ENODEV;
            goto error_busnum;
        }
        spi = spi_new_device(master, &chip);
        if (!spi)
        {
            status = -EBUSY;
            goto error_mem;
        }
        return status;
    
    error_mem:
    error_busnum:
        spi_unregister_driver(&sensor_driver);
        return status;
    }
    
    static __exit void sensor_spi_exit(void)
    {
        spi_unregister_driver(&sensor_driver);
        if(sensor_spi!=NULL)
        spi_unregister_device(sensor_spi);
    }
    
    module_init(sensor_spi_init);
    module_exit(sensor_spi_exit);
    MODULE_LICENSE("GPL");

    这是 3.2 版本之前的内核里spi驱动的模型。
    归纳一下:

    1. 先注册 spi_board_info 结构体
    2. 匹配 spi_master,
    3. 创建 spi_device.
    4. 创建完设备之后再到 spi 总线上寻找有没有同名的spi_driver. 如果有的话就调用 spi_drive r的 probe 函数.
    5. 在 probe 函数里 register_chrdev/class_create/device_create, 创建 chrdev 然后通过 fops 里的函数来读写 spi 设备.
    展开全文
  • Linux设备模型Platform驱动 目标 理解Linux设备模型的内容和意义 掌握符合设备模型的驱动编写方法 掌握Platform设备驱动编写方法 第一章:Linux设备模型 第二章:Linux设备模型简单例程 第三章:Platform...
  • Platform设备驱动

    2015-06-15 20:27:59
    在Linux 2.6内核的设备驱动模型中,有总线(bus)、设备(device)和驱动(driver)3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;同样的,在系统每注册一个驱动时,会寻找与之...

    Platform设备驱动

    没有分析platform_device端的注册过程

    1、概述:

    Linux 2.6内核的设备驱动模型中,有总线(bus)、设备(device)和驱动(driver)3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;同样的,在系统每注册一个驱动时,会寻找与之匹配的设备,而匹配由总线完成。

    一个现实的Linux设备和驱动通常都需要挂接在一种总线上,比如:PCIUSBI²CSPI等的设备。但是SOC的片上资源:比如片上集成的外设控制器(如LCD控制器)、WatchdogRTC、挂接在内存空间的外设等却不依赖与此类总线。基于这一背景,从2.6内核起,引入了一套虚拟总线Platform总线,相应的设备称为platform_device,驱动称为platform_driver

    该虚拟机制与传统的总线机制相比,优先在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时,通过platform_device提供的标准接口进行申请并使用。

    1、平台设备platform_device

    1)platform_device涉及的主要结构体


    平台设备注册时,需要实现platform_device结构体。

    struct platform_device {    //include\linux\Platform_device.h

    const char * name;

    int id;

    struct device dev; //设备结构体

    u32 num_resources; //资源数目

    struct resource * resource; //资源结构体

    const struct platform_device_id *id_entry;

    /* MFD cell pointer */

    struct mfd_cell *mfd_cell;

    /* arch specific additions */

    struct pdev_archdata archdata;

    };

    该结构体里面需要定义具体设备占用的硬件资源(如:地址空间、中断号等)。

    struct resource { //\include\linux\Ioport.h

    resource_size_t start; //资源的开始地址

    resource_size_t end; //资源的结束地址

    const char *name; //资源的名字

    unsigned long flags; //资源类型

    struct resource *parent, *sibling, *child;

    };

    Resource结构体中的flags表示资源类型,可以为IORESOURCE_IOIO类资源)、IORESOURCE_MEM(内存资源)、IORESOURCE_IRQ(中断资源)、IORESOURCE_DMADMA资源)。

    2)主要开发步骤

    1)初始化platform_device结构体。

    2)注册该platform_device

    3)典型例子分析

    platform_device设备是板文件相关的。以\arch\arm\mach-s3c24xx\Mach-mini2440.c文件为例进行说明。

     //dm9000以太网设备platform_device结构体初始化。

    static struct platform_device mini2440_device_eth = {

    .name = "dm9000",

    .id = -1,

    .num_resources = ARRAY_SIZE(mini2440_dm9k_resource), //资源数目=3

    .resource = mini2440_dm9k_resource, //资源结构体

    .dev = { //设备结构体

    .platform_data = &mini2440_dm9k_pdata, },//附加的信息

    };

    设备除了定义资源外,还附加了一些数据信息,因为对设备的硬件描述除了中断、内存、DMA通道以外,可能还会有一些配置信息,而这些信息也依赖于板。因此,platform也提供了platform_data的支持,它的形式是自定义的。如对于DM9000网卡来说,platform_data为一个dm9000_plat_data结构体,我们可以将MAC地址、总线宽度、板上有无EEPROM信息放入platform_data

    1)资源结构体结构体中定义了3个资源

    /* DM9000AEP 10/100 ethernet controller */

    static struct resource mini2440_dm9k_resource[] = {

    [0] = { //第一个资源

    .start = MACH_MINI2440_DM9K_BASE,   //内存的开始地址

    .end   = MACH_MINI2440_DM9K_BASE + 3, //内的结束地址

    .flags = IORESOURCE_MEM //内存类资源

    },

    [1] = { //第二个资源

    .start = MACH_MINI2440_DM9K_BASE + 4, //内存的开始地址

    .end   = MACH_MINI2440_DM9K_BASE + 7, //内的结束地址

    .flags = IORESOURCE_MEM //内存类资源

    },

    [2] = {

    .start = IRQ_EINT7, //中断的开始值

    .end   = IRQ_EINT7, //中断的结束值

    .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, //中断类资源

    }

    };

    2)设置DM9000附加描述信息:16位、没有EEPROM

    static struct dm9000_plat_data mini2440_dm9k_pdata = {

    .flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),

    };    

     

    2、platform_driver

    1)结构体定义

    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;

    const struct platform_device_id *id_table;

    };

    该结构体需要驱动编写者自己完成。

    2)典型例子分析

    static struct platform_driver dm9000_driver = {

    .driver = {

    .name    = "dm9000", //名字

    .owner  = THIS_MODULE, 

    .pm  = &dm9000_drv_pm_ops,  

    },

    .probe   = dm9000_probe,        //probe函数

    .remove  = __devexit_p(dm9000_drv_remove),   //remove函数

    };

    此外还有一个id_table结构体,用于更宽泛的匹配(稍后涉及)。

    3platform总线

    struct bus_type platform_bus_type = { //drivers\base\Platform.c

     

    .name = "platform",

    .dev_attrs = platform_dev_attrs,

    .match = platform_match,

    .uevent = platform_uevent,

    .pm = &platform_dev_pm_ops,

    };

    重点关注match()成员函数,它确定了platform_deviceplatform_driver之间是如何匹配的。该函数会顺序匹配of_match_tableid_tablename字段,其中之一匹配成功就返回。DM9000网卡中name字段会匹配成功。

    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);

     

    /* Attempt an OF style match first */

    if (of_driver_match_device(dev, drv)) //匹配of_match_table

    return 1;

     

    /* Then try to match against the id table */

    if (pdrv->id_table)

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

     

    /* fall-back to driver name match */

    return (strcmp(pdev->name, drv->name) == 0);   //匹配名字

    }

    4、注册驱动顺序

    驱动注册时的函数调用过程:

    Platform_driver_register()-->driver_register()-->bus_add_driver()-->driver_attach()-->bus_for_each_dev()。将每个挂在虚拟platform bus的设备driver匹配,如果匹配成功则调用platform_driver->probe(platform_device)

     

    5、总结

    由以上分析可知,设备驱动中引入platform的概念的优势有:

    (1)使得设备被挂接在一个总线上。其结果是配套的sysfs节点、设备电源管理都成为可能。

    (2)隔离BSP和驱动。在BSP中定义platform_device设备和设备使用的资源、具体配置信息。而在驱动中,只需通过通用的API去获取资源和数据,做到开发板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台型。

    可参考《Linux设备驱动开发详解》(第二版)。

    展开全文
  • platform设备驱动框架搭建分析

    千次阅读 2015-04-20 17:45:19
    之前接触到的字符设备驱动是非常单纯的Linux字符设备驱动,他不具备工程中Linux驱动中的设备与驱动...platform驱动模型搭建: (1)platform核心层:为设备层和驱动层提供注册接口、为设备层和驱动层的匹配提供标准 ①
  • 前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 —— platform... platform模型驱动编程,需要实现platform_device(设备)与platform_driver(驱动)在platform(虚拟总线)上的注册、匹配,相互绑定,然后
  • 【linux设备模型】之platform设备驱动

    千次阅读 多人点赞 2014-04-22 13:46:33
    一、platform总线、设备驱动  platform是一种虚拟总线,相应的设备称为platform_device,相应的驱动称为platform_driver。 platform_device定义在中: 1 struct platform_device { 2 const char * ...
  • int platform_device_register(struct platform_device *pdev) void platform_device_unregister(struct platform_device *pdev) /*2.注册平台驱动*/ int platform_driver_register(struct platform_driver *drv) ...
  • linux设备驱动模型 - platform总线

    千次阅读 2017-04-04 12:33:02
    驱动模型的基础上,就可以构建实际的设备驱动了,这里以platform总线来进行介绍,因为platform总线具有代表性,platform不是一个实际的总线,它是虚拟出来的,所以在设备上的任何硬件驱动都可以挂在这条总线上,最...
  • Linux Platform驱动模型

    2019-05-28 17:53:13
    Linux Platform驱动模型(一) _设备信息:https://www.cnblogs.com/xiaojiang1025/p/6367061.html Linux Platform驱动模型(二) _驱动方法:https://www.cnblogs.com/xiaojiang1025/p/6367910.html Linux Platform驱动...
  • Linux中platform设备驱动全透析, 讲述了设备驱动模型的理解
  • 这其中有一个最大的问题:设备驱动高度耦合,设备修改后,驱动也需要修改,牵一发而动全身。这为后续的驱动开发造成了很不好的影响。我们应该做到的是,高内聚低耦合。设备修改后只修改相应的设备
  • platform设备驱动

    2017-04-05 11:13:09
    在linux2.6以后的设备驱动模型中,总线将设备与驱动绑定,它们的匹配由总线完成。 一个现实的linux设备和驱动通常都需要挂接在一种总线上,对本身依附于PCI,USB,I2C,SPI等的设备而言,这自然不是问题。但在...
  • linux总线、设备驱动模型:驱动只管驱动,设备只管设备,总线则负责匹配设备和驱动。 主机控制器驱动和外设驱动分离 分层设计 在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个...
  • 1、 Platform总线  Platform总线是linux2.6内核加入的一种... Platform 驱动与传统的设备驱动模型相比,优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,553
精华内容 9,021
关键字:

platform设备驱动模型