精华内容
参与话题
问答
  • 参考: http://blog.csdn.net/qq_28992301/article/details/52385518 http://blog.csdn.net/zoe6553/article/details/6372445 ... 1:什么是platform总线?platform...

    参考:

    http://blog.csdn.net/qq_28992301/article/details/52385518

    http://blog.csdn.net/zoe6553/article/details/6372445

    http://blog.chinaunix.net/uid-25014876-id-111745.html

     

    1:什么是platform总线?
    platform总线是区别于实体总线USB、 I2C、SPI 、PIC总线的虚拟总线,一些usb设备选址的话需要通过USB总线来进行寻址,

    而有些类似于SoC内部外设如led 看门狗 定时器是直接通过内存的寻址空间来进行寻址的,cpu与这些设备通信是不需要总线的,2.6内核以后要

    对所有设备进行统一管理,通过kset、kobject来建立层次关系,对这些直接通过内存寻址的设备虚拟了一种总线即platform总线,在硬件上

    实际是没有这个总线;platform内核纯软件的总线,所有的直接通过内存寻址的设备都映射到这条总线上;

    2:platform总线的优点

      a:可以通过platform总线,可以遍历所有的platform总线设备;platform本质其实也是kset、kobject,具有kobject的特性

      b:实现设备与驱动的分离,通过platform总线,设备与驱动是分开注册的,通过platform总线的probe来随时检测与设备匹配的驱动,如匹配上即进行这个设备的驱动注册;

      c:由于上面这个优势,一个驱动可以供同类的几个设备使用;

    3:platform总线以及platform总线设备驱动的实现流程

      a:platform总线注册

      b:platform_device注册

      c:platform_driver注册

      d:设备与驱动的匹配

      e:驱动的注册

    platform总线的工作流程如下图:

     

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------

     1:根据上面的流程我们来分析一下具体代码:
      platform总线的注册:platform的注册是linux内核工程师已经设注册好的;重点看一下.match = platform_match函数;platform_driver和platform_device就是通过这个函数

    来匹配的

    1 struct bus_type platform_bus_type = {
    2     .name        = "platform",
    3     .dev_attrs    = platform_dev_attrs,
    4     .match        = platform_match,
    5     .uevent        = platform_uevent,
    6     .pm        = &platform_dev_pm_ops,
    7 };
     1 int __init platform_bus_init(void)
     2 {
     3     int error;
     4 
     5     early_platform_cleanup();
     6 
     7     error = device_register(&platform_bus);
     8     if (error)
     9         return error;
    10     error =  bus_register(&platform_bus_type);
    11     if (error)
    12         device_unregister(&platform_bus);
    13     return error;
    14 }

     

     1 static int platform_match(struct device *dev, struct device_driver *drv)
     2 {
     3     struct platform_device *pdev = to_platform_device(dev);
     4     struct platform_driver *pdrv = to_platform_driver(drv);
     5 
     6     /* match against the id table first */
     7     if (pdrv->id_table)
     8         return platform_match_id(pdrv->id_table, pdev) != NULL;
     9 
    10     /* fall-back to driver name match */
    11     return (strcmp(pdev->name, drv->name) == 0);
    12 }

    由platform_match_id函数来进行匹配的,如果id_table不为空,则通过id_table来pdev_name匹配,如果为空,则drv->name与pdev->name来进行匹配,

    匹配上以后再执行probe函数,这个函数即注册这个设备的驱动;

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------

    2:platform_device的注册

    在arch/arm/mach-s3c2440/mach-mini2440.c文件中

     这里注意.name、.dev.platform_data 这两个变量 

    platform_driver和platform_device就是通过name来匹配的。name一致则匹配上;

    .dev.platform_data这个元素是中的内容是name、gpio flag def_trigger四个元素

     

     1 static struct platform_device mini2440_led1 = {
     2     .name        = "s3c24xx_led",      
     3     .id        = 1,
     4     .dev        = {
     5         .platform_data    = &mini2440_led1_pdata,
     6     },
     7 };
     8 
     9 static struct platform_device mini2440_led2 = {
    10     .name        = "s3c24xx_led",
    11     .id        = 2,
    12     .dev        = {
    13         .platform_data    = &mini2440_led2_pdata,
    14     },
    15 };

     

     设置好platform_device 结构体以后就可以注册platform_device设备了,把我们设置好的platform_device结构体放到mini2440这个结构体数组指针中;

     1 static struct platform_device *mini2440_devices[] __initdata = {
     2     &s3c_device_ohci,
     3     &s3c_device_wdt,
     4     &s3c_device_i2c0,
     5     &s3c_device_rtc,
     6     &s3c_device_usbgadget,
     7     &mini2440_device_eth,
     8     &mini2440_led1,
     9     &mini2440_led2,
    10     &mini2440_led3,
    11     &mini2440_led4,
    12     &mini2440_button_device,
    13     &s3c_device_nand,
    14     &s3c_device_sdi,
    15     &s3c_device_iis,
    16     &mini2440_audio,
    17 };

    在arch/arm/mach-s3c2440/mach-mini2440.c

    mini2440_init 函数下

      platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));

    使用的platform_add_devices这个函数把mini2440的所有设备注册到内核中;内核会自动查找platform_device链表以及platform_driver链表,当match以后字自动执行platform_driver的probe函数;

    在整理一下platform_device的注册过程:

    1:设置好platform_device结构体(对于led驱动来说关键是name、dev->platform_data两个元素)

    2:初始化好dev->platform_data结构体,这里主要涉及到led驱动所要用到的gpio,

    这里我们可以看到linux内核platform驱动框架的设计思想:首先设备和驱动是分开的,同类设备有共性的部分,不同的部分,不同的部分在初始化的即被设置好;共性的部分内核工程师以及设置好;然后在通过一个匹配函数如果内核链表的设备与驱动链表的驱动匹配,则会自动安装驱动,否则不会安装驱动;

    3:把设置好的platform_device设备加入到mini2440_devices中

    4:在mini2440_device初始化的时候通过platform_add_devices函数把platform设备注册上去;注册以后再/sys/bus/platform/devices目录下会看到dev.name的文件夹

    ---------------------------------------------------------------------------------------------------------------------------------------------------------

    3:platform_driver的注册

    1 struct platform_driver {
    2     int (*probe)(struct platform_device *);
    3     int (*remove)(struct platform_device *);
    4     void (*shutdown)(struct platform_device *);
    5     int (*suspend)(struct platform_device *, pm_message_t state);
    6     int (*resume)(struct platform_device *);
    7     struct device_driver driver;
    8     const struct platform_device_id *id_table;
    9 };
     1 static struct platform_driver s3c24xx_led_driver = {
     2     .probe        = s3c24xx_led_probe,
     3     .remove        = s3c24xx_led_remove,
     4     .driver        = {
     5         .name        = "s3c24xx_led",
     6         .owner        = THIS_MODULE,
     7     },
     8 };
     9 
    10 static int __init s3c24xx_led_init(void)
    11 {
    12     return platform_driver_register(&s3c24xx_led_driver);
    13 }

    设置好platform_driver  结构体,使用platform_driver_register注册即可,这里关键的是probe、remove、driver.name 三个变量;

    platform_driver_register  使用这个函数注册以后再 /sys/bus/platform/drivers目录下会看到 dev.name的文件夹

    内核会自动检测匹配以后会自动执行probe函数;

    -----------------------------------------------------------------------------------------------------------------------------------------------------------

    代码实战:

    led_driver.c driver注册;

      1 #include <linux/module.h> // module_init  module_exit
      2 #include <linux/init.h>   // __init   __exit
      3 #include <linux/fs.h>
      4 #include <asm/uaccess.h>
      5 #include <plat/map-base.h>
      6 #include <plat/map-s5p.h>
      7 #include <mach/regs-gpio.h>
      8 #include <mach/gpio-bank.h>
      9 #include <linux/ioport.h>
     10 #include <linux/string.h>
     11 #include <asm/io.h>
     12 #include <linux/cdev.h>
     13 #include <linux/device.h>
     14 #include <linux/leds.h>
     15 #include <linux/gpio.h>
     16 #include <linux/slab.h>
     17 #include <linux/platform_device.h>
     18 #include <mach/leds-gpio.h>
     19 
     20 struct led_classdev *led_device;
     21 struct s5pv210_led_platdata *pdata;
     22 
     23 
     24 #define x210_led_on     0
     25 #define x210_led_off     1
     26 
     27 static void s5pv210_led_set(struct led_classdev *led_cdev,
     28                    enum led_brightness value)
     29 {
     30     
     31     //真正控制硬件的函数
     32     if (value == LED_OFF) {        
     33         gpio_set_value(pdata->gpio, x210_led_off);
     34         printk(KERN_INFO "LED1 OFF...");
     35     }
     36     else {
     37         
     38         gpio_set_value(pdata->gpio, x210_led_on);
     39         printk(KERN_INFO "LED1 ON...");
     40     }
     41 
     42 }
     43 
     44 
     45 
     46 
     47 // 模块安装函数
     48 static int s5pv210_led_probe(struct platform_device *dev)
     49 {
     50     int ret = -1;
     51     printk(KERN_INFO "led_device init\n");
     52 
     53 
     54     led_device = kzalloc(sizeof(struct led_classdev), GFP_KERNEL);
     55     if (led_device == NULL)
     56     {
     57     printk(KERN_ERR "No memory for led_device\n");
     58     return -ENOMEM;
     59     }
     60 
     61     pdata = dev->dev.platform_data;
     62     
     63     led_device->name = pdata->name;
     64     led_device->brightness_set = s5pv210_led_set;
     65 
     66     
     67 
     68     //在这里进行注册驱动;
     69     ret = led_classdev_register(NULL, led_device);
     70     if (ret < 0)
     71     {
     72     printk(KERN_ERR "led_classdev_register failed\n");
     73     kfree(led_device);
     74     return ret;
     75     }
     76 
     77    
     78 
     79     //初始化gpio
     80     ret = gpio_request(pdata->gpio, pdata->name);
     81     if (ret < 0) {
     82         printk(KERN_ERR "couldn't claim card detect pin \n");
     83         return -1;
     84     }
     85     gpio_direction_output(pdata->gpio, 1);
     86 
     87     return 0;
     88 }
     89 
     90 // 模块删除函数
     91 static int s5pv210_led_remove(struct platform_device *dev)
     92 {
     93     printk(KERN_INFO "leddev_dev  exit\n");
     94 
     95     //注销led设备驱动
     96     led_classdev_unregister(led_device);
     97     kfree(led_device);    
     98 
     99     //删除gpiolib库中引脚
    100     gpio_free(pdata->gpio);    
    101 
    102     printk(KERN_INFO "leddev_dev  unregist success\n");
    103 
    104     return 0;
    105 }
    106 
    107 static struct platform_driver s5pv210_led_driver = {
    108     .probe        = s5pv210_led_probe,
    109     .remove        = s5pv210_led_remove,
    110     .driver        = {
    111         .name        = "s5pv210_led",
    112         .owner        = THIS_MODULE,
    113     },
    114 };
    115 
    116 static int __init s5pv210_led_init(void)
    117 {
    118     return platform_driver_register(&s5pv210_led_driver);
    119 }
    120 
    121 static void __exit s5pv210_led_exit(void)
    122 {
    123     platform_driver_unregister(&s5pv210_led_driver);
    124 }
    125 
    126 
    127 module_init(s5pv210_led_init);
    128 module_exit(s5pv210_led_exit);
    129 
    130 // MODULE_xxx这种宏作用是用来添加模块描述信息
    131 MODULE_LICENSE("GPL");        // 描述模块的许可证
    132 MODULE_AUTHOR("BHC <BHC>");     // 描述模块的作者
    133 MODULE_DESCRIPTION("led test"); // 描述模块的介绍信息
    134 MODULE_ALIAS("alias xxx");      // 描述模块的别名信息

    device设备注册

     1 static struct s5pv210_led_platdata s5pv210_led1_pdata = {
     2     .name        = "led0",
     3     .gpio        = S5PV210_GPJ0(3),
     4     .flags        = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
     5     .def_trigger    = "",
     6 };
     7 static struct s5pv210_led_platdata s5pv210_led2_pdata = {
     8     .name        = "led1",
     9     .gpio        = S5PV210_GPJ0(4),
    10     .flags        = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
    11     .def_trigger    = "",
    12 };
    13 static struct s5pv210_led_platdata s5pv210_led3_pdata = {
    14     .name        = "led2",
    15     .gpio        = S5PV210_GPJ0(5),
    16     .flags        = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
    17     .def_trigger    = "",
    18 };
    19 
    20 static struct platform_device s5pv210_led0 = {
    21     .name        = "s5pv210_led",
    22     .id        = 1,
    23     .dev        = {
    24         .platform_data    = &s5pv210_led1_pdata,
    25     },
    26 };
    27 
    28 static struct platform_device s5pv210_led1 = {
    29     .name        = "s5pv210_led",
    30     .id        = 2,
    31     .dev        = {
    32         .platform_data    = &s5pv210_led2_pdata,
    33     },
    34 };
    35 
    36 static struct platform_device s5pv210_led2 = {
    37     .name        = "s5pv210_led",
    38     .id        = 3,
    39     .dev        = {
    40         .platform_data    = &s5pv210_led3_pdata,
    41     },
    42 };

     

     static struct platform_device *smdkc110_devices[] __initdata 

    把下面代码加入到smdkc110_devices 这个结构体中;即可注册设备和驱动了

    1     //led device 
    2     &s5pv210_led1,
    3     &s5pv210_led2,
    4     &s5pv210_led0,

     

    转载于:https://www.cnblogs.com/biaohc/p/6667529.html

    展开全文
  • platform驱动模型使用总结

    千次阅读 2018-07-21 18:13:17
    platform平台总线驱动的编写主要分为两个部分: 一个是platform_device部分,主要是提供设备本身对驱动处理所要求的参数。 另一个是platform_driver部分,主要是利用platform_device这边传递过来的参数提供对硬件...

    platform平台总线驱动的编写主要分为两个部分:

    一个是platform_device部分,主要是提供设备本身对驱动处理所要求的参数。

    另一个是platform_driver部分,主要是利用platform_device这边传递过来的参数提供对硬件的初始化,以及构建sys文件系统接口,方便应用程序操作驱动。

     

    首先我们看platform_device部分要提供的数据封装和传递。

    platform驱动模型抽象出了一个通用的结构体struct platform_device。

    struct platform_device {
    	const char	* name;
    	int		id;
    	struct device	dev;
    	u32		num_resources;
    	struct resource	* resource;
    
    	const struct platform_device_id	*id_entry;
    
    	/* arch specific additions */
    	struct pdev_archdata	archdata;
    };

    name:则是这个设备的名称,将来的驱动会和设备通过名字做匹配。

    id:因为可能会有同一类型的多个设备需要注册,但他们的驱动可以共用,可以理解为,只靠名字并不能完全区分到底是哪一个,所以有了id,默认id是从0开始增长的。填成-1表示需要系统自动分配。

    dev:这是所有设备所都具有的一个结构体,用来存放设备相关的信息。所有驱动模型都会有这个设备结构体。

    resource    :主要是资源,包括寄存器,中断,DMA,内存等都属于资源。该设备用到哪些,都要在platform_device里面填充好。

    platform_device_id :主要是放置两个数据,一个name字符串,一个ulong类型的driver_data数据,这两个三星平台在name做设备名称区分,和处理器名称区分。driver_data主要是通过一个枚举来区分。主要是为了驱动的通用性。

    pdev_archdata  :从注释上也可以看到,这个是为特殊的硬件架构增加的,目前用不到。

     

    我们先来看一下struct resource结构体

    /*
     * 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; //指针用来构成链表
    };

        struct device   结构体

    
    struct device {
    	struct device		*parent;
    
    	struct device_private	*p;
    
    	struct kobject kobj;
    	const char		*init_name; /* initial name of the device */
    	struct device_type	*type;
    
    	struct mutex		mutex;	/* mutex 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		*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;
    #ifdef CONFIG_OF
    	struct device_node	*of_node;
    #endif
    
    	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
    
    	spinlock_t		devres_lock;
    	struct list_head	devres_head;
    
    	struct klist_node	knode_class;
    	struct class		*class;
    	const struct attribute_group **groups;	/* optional groups */
    
    	void	(*release)(struct device *dev);
    };
    

    device结构体是为所有的设备设计的通用结构体,通常我们根据自己需要填充和使用,不一定需要全部填充初始化。

    比如我们使用了dma,那么初始化的时候,就需要填写dma相关的参数。

    作为通用性的,我们更多的使用是void        *platform_data;  这个指针。

    因为它是一个通用接口,所以我们根据我们具体的设备,定义一个自己的和具体设备用到数据相关的结构体,填充好后,绑定到platform_data,待将来驱动初始化等使用。

    platform_device这边主要是对struct platform_device这个结构体的填充,通常不需要写复杂的代码。

     

    带填充完后,调用platform_add_devices函数使其加入设备这边即可。

     

    接下来则是platform_driver部分的编写:

    首先是编写整体的入口函数和LICENSE

    static int __init xxxx_register(void)
    {
    	platform_driver_register(&xxxx_driver);
    
    	return 0;
    }
    static void __exit xxxx_unregister(void)
    {
    	platform_driver_unregister(&xxxx_driver);
    }
    
    module_init(xxxx_register);
    module_exit(xxxx_unregister);
    MODULE_LICENSE("GPL");
    

    上面这个是每个platform_driver驱动编写的固定格式。

     

    接下来要编写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;
    	const struct platform_device_id *id_table;
    };

    通常必须要写的有 probe,remove,以及driver。

    其它的则根据驱动 需要编写。

    基本实现方式如下。

    而xxxx_driver则是我们要实现的重点。

    static struct platform_driver xxxx_driver = {
    	.probe = xxxx_probe,
    	.remove = __devexit_p(xxxx_remove),
    	.driver = {
    		   .name = "xxxx",		
    		   .owner = THIS_MODULE,
    	},
    };

    其中probe通常是作为初始化函数,同时也要在它里面使用platform_device传过来的参数。主要是void *platform_data参数。

    因为这个参数是我们根据具体设备定义的。所以在初始化时我们就可以很方便的使用了。

     

    同时,驱动里面还有其特有的一些参数,驱动 程序可以自定义一个结构体对他们进行封装(比如需要用到的虚拟文件系统的接口函数集合struct file_operations 或struct fb_ops等,申请的内存的虚拟地址,寄存器通过动态映射的地址、范围,以及为各种设备alloc的设备信息)。

    以上这些都需要进行统一管理,这个时候最好的方式就是通过自定义一个包含上面所有说到的信息的结构体,用来封装这些信息。

    而把这个结构体如何放在那里呢?

    当初设计device结构体架构的大神们早就想好了。

    struct device的第二个变量struct device_private    *p;就是用来设计放置驱动私有数据的

    /**
     * struct device_private - structure to hold the private to the driver core portions of the device structure.
     *
     * @klist_children - klist containing all children of this device
     * @knode_parent - node in sibling list
     * @knode_driver - node in driver list
     * @knode_bus - node in bus list
     * @driver_data - private pointer for driver specific info.  Will turn into a
     * list soon.
     * @device - pointer back to the struct class that this structure is
     * associated with.
     *
     * Nothing outside of the driver core should ever touch these fields.
     */
    struct device_private {
    	struct klist klist_children;
    	struct klist_node knode_parent;
    	struct klist_node knode_driver;
    	struct klist_node knode_bus;
    	void *driver_data;
    	struct device *device;
    };

    无论是从名字上,还是上面的注释中 我们都可以看到    void *driver_data;就是为设计用来放置不同驱动数据的。

     

     

    为什么一定要放置在这里面,放置成全局变量那不行吗?

    理论上是可以的,在驱动程序比较简单,在一个文件中编写,只需要把这个结构体定义为为本文件可见的静态的。但实际上,一些驱动程序的实现是比较复杂的,需要使用多个文件编写,这样如果写成全局变量,将会成为软件开发的噩梦。

     

    probe函数编写的步骤

    static int __devinit xxxx_probe(struct platform_device *pdev)
    {
        
    
        /* 申请内存,用来存放驱动程序用到的数据结构pdata */
    
    
        /* 把申请到的数据结构pdata 的地址绑定到platform_device->dev->p->driver_data上 */
    
    
    
        /* 填充申请到的数据结构pdata ,可能用到 platform_device->dev.platform_data 传过来的数据,经行动态映射等,传参,换算等等*/
    
    
        /* 通过allo函数申请的设备资源,继续填充完数据结构pdata */
    
    
        /* 填充和应用层交互的sysfs文件系统的接口函数到,数据结构pdata */
    
    
        /* register_xxxx注册设备 */
    
        /* 初始化设备 */
    
    
        /* 填充完所有的数据 */
    
       
        /* device_create_file创建和应用层交互的sysfs虚拟文件系统 */
    }
    
    
    

    其中   /* 填充和应用层交互的sysfs文件系统的接口函数到,数据结构pdata */

    需要驱动层根据具体的设备情况编写,应用层用到的open,read等函数。

     

    probe做完了主要的大多数工作。remove则处理的和probe刚好相反。

    static int __devexit xxxx_remove(struct platform_device *pdev)
    {
    
         /* device_remove_file卸载sysfs虚拟文件系统 */
    
    
         /* 取消初始化设备(如果设备开着的话要先关掉[最简单的是关掉其时钟]再取消初始化) */
    
    
         /* unregister_xxxx卸载设备 */
    
    
         /* 通过allo函数申请的设备资源,release掉 */
    
    
         /* 申请的资源如io,中断,内存,dma等释放掉 */
    
    
         /* 释放申请的内存pdata */
    }

     

    platform_deiver驱动编写部分经常用到下面几个宏,我对其进行简要分析:

    #define to_platform_device(x)             container_of((x), struct platform_device, dev)
    #define platform_get_drvdata(_dev)        dev_get_drvdata(&(_dev)->dev)
    #define platform_set_drvdata(_dev,data)   dev_set_drvdata(&(_dev)->dev, (data))

    第一个宏很简单,已知 struct platform_device结构体中的struct device结构体的地址,找到对应的struct platform_device结构体的地址。

    使用的是linux内核应用最广的数据结构推算宏来实现的

    /* 该宏有连个参数,第一个是结构体类型,第二个是其成员变量的名称
     * 改宏大的作用是的得到结构体成员地址相对于结构体起始地址的偏移 
     * 方法也很经典,即假设该结构体位于0地址处,而成员变量的地址则就是该成员变量相对该结构体的偏移地址
     */
    define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
    
    /**
     * container_of - cast a member of a structure out to the containing structure
     * @ptr:	the pointer to the member.
     * @type:	the type of the container struct this is embedded in.
     * @member:	the name of the member within the struct.
     *
     */
    /* 作用和参数上面都有解释,typeof是一个关键字,其作用是通过一个变量得到其类型 */
    #define container_of(ptr, type, member) ({			\
    	const typeof(((type *)0)->member) * __mptr = (ptr);	\   /* 该句的作用是防止编译器出现告警 */
    	(type *)((char *)__mptr - offsetof(type, member)); })  /* offsetof宏看懂的话,这里面的减法就很简单了。    要注意的是(char *)__mptr 必须要把__mptr 强转为char*,否指针相减不是以字节为单位*/

     

    第2、3个宏是一组宏。其中platform_set_drvdata是把参数data放进某个位置,platform_get_drvdata是从某个位置取出data.

    #define platform_get_drvdata(_dev)        dev_get_drvdata(&(_dev)->dev)
    #define platform_set_drvdata(_dev,data)   dev_set_drvdata(&(_dev)->dev, (data))

    先看platform_set_drvdata的实现

    void dev_set_drvdata(struct device *dev, void *data)
    {
    	int error;
    
    	if (!dev)
    		return;
    	if (!dev->p) {
    		error = device_private_init(dev);  
    		if (error)
    			return;
    	}
    	dev->p->driver_data = data;
    }

    可以看到,这个宏前面主要是做参数有效性检查,如果给p没有分配内存的话,这里也会为p分配内存的。如果分配了的话,直接把data放到p里面的driver_data.

    看到这里,有没有想起来driver_data是什么鬼啊?

    就是probe函数刚进来,申请的自定义类型结构体的一个数据结构,这个数据结构超级重要,什么open,read之类都要使用里面的信息,所以platform驱动框架直接给我们把这两个给做成标准接口了,方便我们使用。

     

    既然platform_set_drvdata已经清楚了,那dev_get_drvdata就一目了然了。

    void *dev_get_drvdata(const struct device *dev)
    {
    	if (dev && dev->p)
    		return dev->p->driver_data;
    	return NULL;
    }

     

    展开全文
  • platform驱动之probe函数

    2016-03-28 13:23:05
    probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用platform设备的probe函数完成驱动注册最后工作。资源、中断调用函数以及其他相关工作。下面是probe被调用...
    驱动注册的probe函数

    probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用platform设备的probe函数完成驱动注册最后工作。资源、中断调用函数以及其他相关工作。下面是probe被调用的一些程序流程。

    从driver_register看起:

    [cpp] view plain
    1. int driver_register(struct device_driver * drv)  
    2. {  
    3.     klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);  
    4.     init_completion(&drv->unloaded);  
    5.     return bus_add_driver(drv);  
    6. }  
    klist_init与init_completion没去管它,可能是2.6的这个设备模型要做的一些工作。直觉告诉我要去bus_add_driver。

    bus_add_driver中:
    都是些Kobject 与 klist 、attr等。还是与设备模型有关的。但是其中有一句:
    driver_attach(drv);
    单听名字就很像:
    [cpp] view plain
    1. void driver_attach(struct device_driver * drv)  
    2. {  
    3.     bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);  
    4. }  
    这个熟悉,遍历总线上的设备并设用__driver_attach。
    在__driver_attach中又主要是这样:
    driver_probe_device(drv, dev);

    跑到driver_probe_device中去看看:

    有一段很重要:
    if (drv->bus->match && !drv->bus->match(dev, drv))
    goto Done;
    明显,是调用的驱动的总线上的match函数。如果返回1,则可以继续,否则就Done了。
    继承执行的话:
    [cpp] view plain
    1. if (drv->probe)  
    2.     ret = drv->probe(dev);  
    3. if (ret) {  
    4.     dev->driver = NULL;  
    5.     goto ProbeFailed;  
    6. }  
    只要probe存在则调用之。至此就完成了probe的调用

    这个过程链的关键还是在drv->bus->match ,因为其余的地方出错的话就是注册失败,而只要注册不失败且match返回1,那么就铁定会调用驱程的probe了。你可以注册一个总线类型和总线,并在 match中总是返回 1, 会发现,只要struct device_driver中的bus类型正确时,probe函数总是被调用.


    ---------------------------------------------------------------------------------------------------------------------------------------------------


    有两个重要的链表挂在bus上,一个是设备device链表,一个是驱动driver链表。

    每当我们向一根bus注册一个驱动driver时,套路是这样的

    driver_register(struct device_driver * drv) -> bus_add_driver() -> driver_attach() ->

    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

    bus_for_each_dev遍历该总线上所有的device,执行一次__driver_attach(),看能不能将驱动关联(attach)到某个设备上去。

    __driver_attach()

    ->driver_probe_device()


    ->drv->bus->match(dev, drv), // 调用bus的match函数,看device和driver匹不匹配。如果匹配上,

    继续执行really_probe()。

    ->really_probe()

    ->driver->probe()。(如果bus->probe非空,则调用bus->probe)


    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------



    而每当我们向一根bus添加一个硬件时时,套路是这样的

    device_add()

    \\ device_add 中有很多操作kobject,注册sysfs,形成硬件hiberarchy结构的代码。

    如果您忘记了,先回头去参考参考"我是sysfs"

    ->bus_attach_device() -> device_attach() ->bus_for_each_drv()

    bus_for_each_drv与bus_for_each_dev类似,遍历该总线上所有的driver,执行一次__device_attach(),看能不能将设备关联(attach)到某个已登记的驱动上去。

    __device_attach()

    ->driver_probe_device() //后面与上面一样

    总结一些,一句话,注册一个某个bus的驱动就是先把驱动自己链入到bus驱动链表中去,在从bus的设备链表中一一寻找,看有没有自己可以关联上的设备。找到就probe,再把二者bind起来。反之,添加设备道理也是一样的。


    -------------------------------------------------------------------------------------------------------------------------------------------------------------------------


    论坛讨论帖:

    1. 在看platform驱动是,发现一个很低能的问题,static int __devinitdm9000_probe(struct platform_device *pdev) 中的struct platform_device *pdev是从那里来的device,跟踪platform_driver_register(&dm9000_driver);一直没有发现platform_device的出现,但是现在probe函数中突然出现了这个变量,那么这个变量是从和而来?

    2. platform驱动分为platform_driver和platform_device两个结构体表示,前者表示平台驱动,后者表示平台设备,这个struct platform_device *pdev就是在平台相关文件里注册的。平台下会定义一个platform_device,然后platform_device_register(&platform_device),所以你注册的平台驱动,当在platform_bus上探测到有个设备的时候,这个你之前在平台文件里注册的platform_device就会传过来。

    3. "platform_bus上探测到有个设备的时候"此句怎么理解,是怎么探测到的呢?

    4. 驱动肯定是自己注册了。至于设备的注册,有些设备所使用的协议有枚举过程,从这处过程中可以得到设备信息,进而生成相应设备结构体,然后注册在相应总线上。

    在总线上注册设备时,会遍历该总线上已注册的驱动,用总线的match方法判断是否有匹配的驱动,如果有,则调用驱动的probe函数;在总线上注册驱动时,会遍历该总线上已注册的设备,用总线的match方法判断是否有匹配的设备,如果有,则调用驱动的probe函数。即,不管是先注册设备还是先注册驱动,总线的match方法会作用于所有组合,如果匹配了,则调用驱动的probe方法,这样就探测到了。

    platform_bus是虚拟的总线,没有具体的协议。上面所讲设备的结构体是在枚举时动态生成的,设备的注册也是在枚举时触发的,而platform_device的定义与注册是在平台初始化文件里手动做的。后面的情况就是一样的了。其它一些没有类似枚举之类过程的总线,如I2C,也是这种流程。

    5. 内核启动的时候, platform_device 是优先于 platform_driver 注册的。 比如 platform_device A , 是在arch/arm/mach-XXXX/mach-XXXX.c 文件里注册的, 而这个文件的代码是 优先于 platform_driver_register 执行的。
    所以在你platform_driver_register执行的时候, platform_device A已经被挂在platform_bus总线上了, 而platform_driver_register()有个功能是到platform_bus上去挨个找寻,找寻挂在上面的platform_bus上的platform_device。找到了就执行probe()。

    6. 可不可以这样,我没有试过。
    但是因为驱动中有 开发者实现的 probe 函数。 如果先注册驱动, 驱动就找不到设备, 从而不执行probe函数,而驱动中最重要的就是probe函数,硬件的初始化,寄存器的配置,时钟的使能都在probe函数里完成。从这一点来说,驱动先于设备注册,应该不可行。

    7. 你好,关于I2C驱动,目前有三个问题想请教下:   
        1,请问用i2c_add_driver注册I2C的client端的驱动的时候,这个client是怎么和哪个adapter attach的(在此之前,系统中注册了四个bit-style的adapter)?注意,我的i2c_driver中,并没有赋值attach_adapter,但是赋值了probe
        2,注册adapter是用的 i2c_add_adapter,跟了下这个函数,发现它call的是->i2c_register_adapter->
                                       adap->dev.bus = &i2c_bus_type;
                               adap->dev.type = &i2c_adapter_type;
                               res = device_register(&adap->dev);
              这里是注册的是一个device,那么根据device-driver的模型,那adapter的driver是怎么被赋值的呢?
       3,i2c_add_driver是向哪跟总线上注册的,是i2c_bus_type吗?i2c_register_adapter又是向哪跟总线注册的呢? 也是i2c_bus_type吗?
       谢谢

    8. i2c_driver并不是要跟adapter绑定,而是要和i2c_client绑定 。注册一个i2c_driver时并不知道它支持的设备在哪条总线上。例如一个系统有两个i2c控制器,每个控制器上有一个相同型号的EEPROM,它们只需一个i2c_driver。关键在于如何发现哪条总线上有i2c_driver所支持设备。

    老I2C框架里,由i2c_driver负责发现设备,每注册一个adapter遍历已注册i2c_driver,每注册一个i2c_driver遍历已注册的i2c_adapter,不让他们错过任何一次组合,让i2c_driver在adapter上探测一下是否有它支持的设备,如果有,则生成一个i2c_client实例。当然由于i2c协议很弱,这种探测很不可靠。

    现在(尤其在SOC系统中)倾向于静态定义这些信息:如果我知道哪条总线上有哪个设备,我就犯不着让i2c_driver去找它们了。于是在平台初始化函数里注册i2c_board_info,这个结构体就是一个i2c_client模板。i2c_board_info里有个bus_num作为匹配的凭证,如果bus_num与i2c_adapter的bus_num相同,那就匹配上了。所有注册的i2c_board_info会放在一个链表里,每注册一个i2c_adapter就去扫描这个链表,如果匹配就生成i2c_client。

    在老框架,i2c_client肯定是在i2c_driver时调用attach_adapter时出现的;新框架里则可以在没有i2c_driver的情况下根据静态信息生成i2c_client。另外让i2c_driver探测设备时,顶多能知道设备地址,而欲查询设备其它属性,i2c协议里没有通用的方法。而i2c_board_info静态了提供了这些属性。 

    而对于旧框架,新框架一方面在接口上保持兼容,另一方面在功能上也有更新。新框架里通过bus_num来区别是扫描静态的i2c_board_info链表。如果不是,那就又归到老式的方法:让i2c_driver去找设备。这个过程跟上面讲的老框架的过程一样,添加adapter或者driver时相互遍历已注册的对方。新方法里有个i2c_detect,看起来是把以前散落在各个i2c_driver->attach_adapter中的代码抽象出来了,最后再调用i2c_driver->detect,这个的功能应该和i2c_driver->attach_adapter是一样的。只不过它只负责是否检测到驱动支持的设备,而i2c_client的生成则放在外面框架代码里了。最后再调用i2c_driver->attach_adapter,兼容旧驱动。注释里说这个方法以后可能会消失。其实在SOC系统里,一般到静态扫描就完了,也不会去调用detect方法。

    不管通过什么方法生成i2c_client,注册它后,下面就是经典设备模型里的match/probe了。

    i2c_driver与i2c_adapter都是注册在i2c_bus_type上,至于i2c_adapter的driver,我以前看代码时也没找着。现在看来,它根本没有另一半。其实看i2c_bus_type->match方法,它是通过i2c_verify_client把device转成i2c_client的,这里只验证device->type是不是i2c_client_type,也就是说,这条总线根本没准备为i2c_adapter匹配任何东西。

    展开全文
  •  platform设备和驱动与linux设备模型密切相关。platform在linux设备模型中,其实就是一种虚拟总线没有对应的硬件结构。它的主要作用就是管理系统的外设资源,比如io内存,中断信号线。现在大多数处理器芯片都是soc,...

    一. 概述
        platform设备和驱动与linux设备模型密切相关。platform在linux设备模型中,其实就是一种虚拟总线没有对应的硬件结构。它的主要作用就是管理系统的外设资源,比如io内存,中断信号线。现在大多数处理器芯片都是soc,如s3c2440,它包括处理器内核(arm920t)和系统的外设(lcd接口,nandflash接口等)。linux在引入了platform机制之后,内核假设所有的这些外设都挂载在platform虚拟总线上,以便进行统一管理。
    二. platform 总线
       1. 在系统中platform对应的文件drivers/base/platform.c,它不是作为一个模块注册到内核的,关键的注册总线的函数由系统初始化部分,对应/init/main.c中的do_basic_setup函数间接调用。这里可以看出platform非常重要,要在系统其他驱动加载之前注册。下面分析platform总线注册函数
    1. int __init platform_bus_init(void)  
    2. {  
    3.     int error;  
    4.     early_platform_cleanup();  
    5.     error = device_register(&platform_bus);  
    6.         //总线也是设备,所以也要进行设备的注册  
    7.     if (error)  
    8.         return error;  
    9.     error =  bus_register(&platform_bus_type);  
    10.         //注册platform_bus_type总线到内核  
    11.     if (error)  
    12.         device_unregister(&platform_bus);  
    13.     return error;  
    14. }  
        这个函数向内核注册了一种总线。他首先由/drivers/base/init.c中的driver_init函数调用,driver_init函数由/init/main.c中的do_basic_setup函数调用,do_basic_setup这个函数由kernel_init调用,所以platform总线是在内核初始化的时候就注册进了内核。
       2. platform_bus_type 总线结构与设备结构
        (1) platform总线 设备结构
    1. struct device platform_bus = {  
    2.     .init_name  = "platform",  
    3. };  
        platform总线也是一种设备,这里初始化一个device结构,设备名称platform,因为没有指定父设备,所以注册后将会在/sys/device/下出现platform目录。
        (2) platform总线 总线结构
    1. struct bus_type platform_bus_type = {  
    2.     .name       = "platform",  
    3.     .dev_attrs  = platform_dev_attrs,   
    4.     .match      = platform_match,  
    5.     .uevent     = platform_uevent,  
    6.     .pm     = &platform_dev_pm_ops,  
    7. };  
        platform_dev_attrs    设备属性
        platform_match        match函数,这个函数在当属于platform的设备或者驱动注册到内核时就会调用,完成设备与驱动的匹配工作。
        platform_uevent       热插拔操作函数
    三. platform 设备
       1. platform_device 结构
    1. struct platform_device {  
    2.     const char  * name;  
    3.     int     id;  
    4.     struct device   dev;  
    5.     u32     num_resources;  
    6.     struct resource * resource;  
    7.     struct platform_device_id   *id_entry;  
    8.     /* arch specific additions */  
    9.     struct pdev_archdata    archdata;  
    10. };  
        (1)platform_device结构体中有一个struct resource结构,是设备占用系统的资源,定义在ioport.h中,如下
    1. struct resource {  
    2.     resource_size_t start;  
    3.     resource_size_t end;  
    4.     const char *name;  
    5.     unsigned long flags;  
    6.     struct resource *parent, *sibling, *child;  
    7. };  
        (2) num_resources 占用系统资源的数目,一般设备都占用两种资源,io内存和中断信号线。这个为两种资源的总和。
       2. 设备注册函数 platform_device_register
    1. int platform_device_register(struct platform_device *pdev)  
    2. {  
    3.     device_initialize(&pdev->dev);  
    4.     return platform_device_add(pdev);  
    5. }  
        这个函数首先初始化了platform_device的device结构,然后调用platform_device_add,这个是注册函数的关键,下面分析platform_device_add:
    1. int platform_device_add(struct platform_device *pdev)  
    2. {  
    3.     int i, ret = 0;  
    4.   
    5.     if (!pdev)  
    6.         return -EINVAL;  
    7.   
    8.     if (!pdev->dev.parent)  
    9.         pdev->dev.parent = &platform_bus;  
    10.         //可以看出,platform设备的父设备一般都是platform_bus,所以注册后的platform设备都出现在/sys/devices/platform_bus下  
    11.     pdev->dev.bus = &platform_bus_type;  
    12.         //挂到platform总线上  
    13.     if (pdev->id != -1)  
    14.         dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);  
    15.     else  
    16.         dev_set_name(&pdev->dev, "%s", pdev->name);  
    17.         //设置设备名字,这个名字与/sys/devices/platform_bus下的名字对应  
    18.     for (i = 0; i < pdev->num_resources; i++) { //下面操作设备所占用的系统资源  
    19.         struct resource *p, *r = &pdev->resource[i];  
    20.   
    21.         if (r->name == NULL)  
    22.             r->name = dev_name(&pdev->dev);  
    23.   
    24.         p = r->parent;  
    25.         if (!p) {  
    26.             if (resource_type(r) == IORESOURCE_MEM)  
    27.                 p = &iomem_resource;  
    28.             else if (resource_type(r) == IORESOURCE_IO)  
    29.                 p = &ioport_resource;  
    30.         }  
    31.   
    32.         if (p && insert_resource(p, r)) {  
    33.             printk(KERN_ERR  
    34.                    "%s: failed to claim resource %d\n",  
    35.                    dev_name(&pdev->dev), i);  
    36.             ret = -EBUSY;  
    37.             goto failed;  
    38.         }  
    39.     }  
    40.        //上面主要是遍历设备所占用的资源,找到对应的父资源,如果没有定义,那么根据资源的类型,分别赋予iomem_resource和ioport_resource,然后调用insert_resource插入资源。  
    41.        //这样系统的资源就形成了一个树形的数据结构,便于系统的管理  
    42.     pr_debug("Registering platform device '%s'. Parent at %s\n",  
    43.          dev_name(&pdev->dev), dev_name(pdev->dev.parent));  
    44.   
    45.     ret = device_add(&pdev->dev);  
    46.         //注册到设备模型中  
    47.     if (ret == 0)  
    48.         return ret;  
    49.  failed:  
    50.     while (--i >= 0) {  
    51.         struct resource *r = &pdev->resource[i];  
    52.         unsigned long type = resource_type(r);  
    53.         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)  
    54.             release_resource(r);  
    55.     }  
    56.     return ret;  
    57. }  
       3. mini2440内核注册platform设备过程
        因为一种soc确定之后,其外设模块就已经确定了,所以注册platform设备就由板级初始化代码来完成,在mini2440中是mach-mini2440.c的mini2440_machine_init函数中调用platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))来完成注册。这个函数完成mini2440的所有platform设备的注册:
        (1) platform_add_devices函数是platform_device_register的简单封装,它向内核注册一组platform设备
        (2) mini2440_devices是一个platform_device指针数组,定义如下:
    1. static struct platform_device *mini2440_devices[] __initdata = {  
    2.     &s3c_device_usb,  
    3.     &s3c_device_rtc,  
    4.     &s3c_device_lcd,  
    5.     &s3c_device_wdt,  
    6.     &s3c_device_i2c0,  
    7.     &s3c_device_iis,  
    8.     &mini2440_device_eth,  
    9.     &s3c24xx_uda134x,  
    10.     &s3c_device_nand,  
    11.     &s3c_device_sdi,  
    12.     &s3c_device_usbgadget,  
    13. };  
        这个就是mini2440的所有外设资源了,每个外设的具体定义在/arch/arm/plat-s3c24xx/devs.c,下面以s3c_device_lcd为例说明,其他的类似。s3c_device_lcd在devs.c中它定义为:
    1. struct platform_device s3c_device_lcd = {  
    2.     .name         = "s3c2410-lcd",  
    3.     .id       = -1,  
    4.     .num_resources    = ARRAY_SIZE(s3c_lcd_resource),  
    5.     .resource     = s3c_lcd_resource,  
    6.     .dev              = {  
    7.         .dma_mask       = &s3c_device_lcd_dmamask,  
    8.         .coherent_dma_mask  = 0xffffffffUL  
    9.     }  
    10. };  
        可以看出,它占用的资源s3c_lcd_resource,定义如下:
    1. static struct resource s3c_lcd_resource[] = {  
    2.     [0] = {  
    3.         .start = S3C24XX_PA_LCD,  
    4.         .end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,  
    5.         .flags = IORESOURCE_MEM,  
    6.     },  
    7.     [1] = {  
    8.         .start = IRQ_LCD,  
    9.         .end   = IRQ_LCD,  
    10.         .flags = IORESOURCE_IRQ,  
    11.     }  
    12. };  
       这是一个数组,有两个元素,说明lcd占用了系统两个资源,一个资源类型是IORESOURCE_MEM代表io内存,起使地址S3C24XX_PA_LCD,这个是LCDCON1寄存器的地址。另外一个资源是中断信号线。
    四. platform设备驱动
       如果要将所写的驱动程序注册成platform驱动,那么所做的工作就是初始化一个platform_driver,然后调用platform_driver_register进行注册。
       1. 基本数据机构platform_driver
    1. struct platform_driver {  
    2.     int (*probe)(struct platform_device *);  
    3.     int (*remove)(struct platform_device *);  
    4.     void (*shutdown)(struct platform_device *);  
    5.     int (*suspend)(struct platform_device *, pm_message_t state);  
    6.     int (*resume)(struct platform_device *);  
    7.     struct device_driver driver;  
    8.     struct platform_device_id *id_table;  
    9. };  
        这是platform驱动基本的数据结构,在驱动程序中我们要做的就是声明一个这样的结构并初始化。下面是lcd驱动程序对它的初始化:
    1. static struct platform_driver s3c2412fb_driver = {  
    2.     .probe      = s3c2412fb_probe,  
    3.     .remove     = s3c2410fb_remove,  
    4.     .suspend    = s3c2410fb_suspend,  
    5.     .resume     = s3c2410fb_resume,  
    6.     .driver     = {  
    7.         .name   = "s3c2412-lcd",  
    8.         .owner  = THIS_MODULE,  
    9.     },  
    10. };  
        上面几个函数是我们要实现的,它将赋值给device_driver中的相关成员,probe函数是用来查询特定设备是够真正存在的函数。当设备从系统删除的时候调用remove函数。
       2. 注册函数platform_driver_register
    1. int platform_driver_register(struct platform_driver *drv)  
    2. {  
    3.     drv->driver.bus = &platform_bus_type;  
    4.     if (drv->probe)  
    5.         drv->driver.probe = platform_drv_probe;  
    6.     if (drv->remove)  
    7.         drv->driver.remove = platform_drv_remove;    
    8.     if (drv->shutdown)  
    9.         drv->driver.shutdown = platform_drv_shutdown;  
    10.     return driver_register(&drv->driver);  
    11. }  
        这个函数首先使驱动属于platform_bus_type总线,将platform_driver结构中的定义的probe,remove,shutdown赋值给device_driver结构中的相应成员,以供linux设备模型核心调用,然后调用driver_regster将设备驱动注册到linux设备模型核心中。
    五. 各环节的整合
        前面提到mini2440板级初始化程序将它所有的platform设备注册到了linux设备模型核心中,在/sys/devices/platform目录中都有相应的目录表示。platform驱动则是由各个驱动程序模块分别注册到系统中的。但是他们是如何联系起来的呢,这就跟linux设备模型核心有关系了。在ldd3中的linux设备模型的各环节的整合中有详细的论述。这里简要说明一下platform实现的方法。每当注册一个platform驱动的时候就会调用driver_register,这个函数的调用会遍历设备驱动所属总线上的所有设备,并对每个设备调用总线的match函数。platform驱动是属于platform_bus_type总线,所以调用platform_match函数。这个函数实现如下:
    1. static int platform_match(struct device *dev, struct device_driver *drv)  
    2. {  
    3.     struct platform_device *pdev = to_platform_device(dev);  
    4.     struct platform_driver *pdrv = to_platform_driver(drv);  
    5.   
    6.     /* match against the id table first */  
    7.     if (pdrv->id_table)  
    8.         return platform_match_id(pdrv->id_table, pdev) != NULL;  
    9.     /* fall-back to driver name match */  
    10.     return (strcmp(pdev->name, drv->name) == 0);  
    11. }  
        这个函数将device结构转换为platform_devcie结构,将device_driver结构转换为platform_driver结构,并调用platform_match_id对设备与驱动相关信息进行比较。如果没有比较成功会返回0,以便进行下一个设备的比较,如果比较成功就会返回1,并且将device结构中的driver指针指向这个驱动。然后调用device_driver中的probe函数,在lcd驱动中就是s3c2412fb_probe。这个函数是我们要编写的函数。这个函数检测驱动的状态,并且测试能否真正驱动设备,并且做一些初始化工作。
    展开全文
  • audio驱动之platform驱动

    2020-04-24 19:06:00
    audio驱动相关结构体 注释 snd_soc_codec_driver 音频编解码芯片描述及操作函数,如控件/微件/音频路由的描述信息、时钟配置、IO 控制等 snd_soc_dai_driver 音频数据接口描述及操作函数,根据 codec 端和 soc...
  • platform驱动详解

    千次阅读 2018-01-04 11:21:55
    platform总线是虚拟的平台总线,是linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。...platform驱动工作流程: 1. 系统开机内核初始化阶段,初始化platform总线; 2. platform_device初始化调用
  • platform 驱动详解

    2015-04-15 16:31:40
    平台总线是内核实现的一条虚拟总线,Linux设备模型包含三个重要的元素,总线、设备和驱动,那看看平台总线又是怎样去实现的。 首先看平台总线的定义: [cpp] view plaincopy 946 struct...
  • platform驱动模型

    千次阅读 2011-04-29 16:41:00
    内核为例,分析了基于platform总线的驱动模型。首先介绍了Platform总线的基本概念,接着介绍了platform device和platform driver的定义和加载过程,分析了其与基类device 和driver的派生关系及在此过程中面向...
  • platform驱动编写

    2019-08-14 17:36:07
    平台总线是一条虚拟总线。 一边的“device”结构体和另一边的“较稳定的 drivice 代码”的联系: “device_add()”除了将“devcie”结构放到 bus 的“dev 链表”之外,还会从另一边的“drv” 链表中取表元即某个...
  • platform 驱动实验

    2018-05-19 10:37:33
    //设备注册 #include &lt;linux/module.h&gt; #include &lt;linux/kernel.h&...linux/platform_device.h&gt; MODULE_LICENSE("GPL"); static void device_release(...
  • platform驱动学习

    2016-08-03 00:57:29
    platform实现的是一种总线设备驱动模型,所谓的总线是虚拟的总线,挂在这个虚拟总线上的设备和驱动可以被内核以platform的机制探测到并使用probe进行初始化驱动platform的概念和字符设备驱动,块设备驱动,网卡...
  • platform驱动简介

    千次阅读 2013-10-24 21:52:29
    目的:说白了就是为了将设备与驱动分离,通过platform总线进行连接 废话不多说: 相关结构介绍: 1.platform设备 结构体structplatform_device{ const charname;/*设备名*/ u32 id;/*设备id*/ struct device ...
  • platform驱动分离

    2018-12-03 09:56:00
    目录 platform驱动分离 框架结构 与输入子系统联系 设备描述 驱动算法 注册机制 程序 测试 platform驱动分离 框架结构 与输入子系统联系 ...
  • Platform 驱动架构

    2015-06-09 17:47:41
    自从2.6以后版本的内核,出现了一个platform架构的驱动子系统,里面有两个基本的对象:platform_device,platform_driver; Linux platformdriver机制和传统的device_driver机制相比,一个十分明显的优势在于platform...
  • platform驱动结构体

    2016-05-17 23:04:57
    struct platform_device {  const char * name;  int id;  struct device dev;  u32 num_resources;  struct resource * resource;
  • Platform驱动程序

    2013-01-18 01:47:50
    Platform 驱动与传统的设备驱动模型相比,优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性。   平台设备描述 平台设
  • platform驱动分析

    2012-08-11 08:48:04
    platform驱动程序设计路线:注册设备(platform_device)->编写设备驱动。 注册设备: struct platform_device {  const char * name;  int id;  struct device dev;  u32 num_resources;  struct ...
  • platform驱动看驱动模型

    千次阅读 2010-12-06 22:27:00
    platform驱动看驱动模型
  • 从Linux2.6开始linux加入一套驱动管理和注册机制-—platform平台总线驱动模型,platform平台总线是一条虚拟总线,platform_device为相应的设备,platform_driver为相应的驱动,在这种设备模型中,需关心总线,设备和驱动这...
  • 上一篇博文《platform设备驱动框架搭建分析》主要是根据内核源码来分析platform驱动模型工作的原理,在实际的驱动开发中如何使用Linux的这么一种模型来管理这种类型的设备呢?把tq2440开发板上的LED1当做是平台设备...
  • Linux Platform驱动模型

    2019-05-28 17:53:13
    Linux Platform驱动模型(一) _设备信息:https://www.cnblogs.com/xiaojiang1025/p/6367061.html Linux Platform驱动模型(二) _驱动方法:...Linux Platform驱动模型(三) _platform+cdev:h...

空空如也

1 2 3 4 5 ... 20
收藏数 5,197
精华内容 2,078
关键字:

platform驱动