zoukankan      html  css  js  c++  java
  • platform_device和platform_driver

    platform_device和platform_driver

     

    platform_deviceplatform_driver(一)

    首先介绍一下注册一个驱动的步骤:

    1、定义一个platform_driver结构

    2、初始化这个结构,指定其proberemove等函数,并初始化其中的driver变量

    3、实现其proberemove等函数

    platform_driver结构,定义于include/linux/platform_device.h文件中:

    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;

    };

     

        可见,它包含了设备操作的几个功能函数,同样重要的是,它还包含了一个device_driver结构。刚才提到了驱动程序中需要初始化这个变量。下面看一下这个变量的定义,位于include/linux/device.h中:

     

    struct device_driver {

        const char        * name;

        struct bus_type        * bus;

        struct kobject        kobj;

        struct klist        klist_devices;

        struct klist_node    knode_bus;

        struct module        * owner;

        const char         * mod_name;  /* used for built-in modules */

        struct module_kobject    * mkobj;

        int    (*probe)    (struct device * dev);

        int    (*remove)    (struct device * dev);

        void    (*shutdown)    (struct device * dev);

        int    (*suspend)    (struct device * dev, pm_message_t state);

        int    (*resume)    (struct device * dev);

    };

     

        需要注意这两个变量:nameowner。那么的作用主要是为了和相关的platform_device关联起来,owner的作用是说明模块的所有者,驱动程序中一般初始化为THIS_MODULE

        下面是一个platform_driver的初始化实例: 

    static struct platform_driver s3c2410iis_driver = { 

      .probe = s3c2410iis_probe, 

      .remove = s3c2410iis_remove,

      .driver = {

        .name = "s3c2410-iis",

        .owner = THIS_MODULE,

      },

    }; 

     

        上面的初始化是一个音频驱动的实例。注意其中的driver这个结构体,只初始化了其nameowner两个量。接着看一下和driver相关的另一个结构,定义如下:

    struct platform_device {

        const char    * name;

        int        id;

        struct device    dev;

        u32        num_resources;

        struct resource    * resource;

    };

    该结构中也有一个name变量。platform_driver从字面上来看就知道是设备驱动。设备驱动是为谁服务的呢?当然是设备了。platform_device就描述了设备对象。下面是一个具体的实例:

    struct platform_device s3c_device_iis = {

        .name         = "s3c2410-iis",

        .id         = -1,

        .num_resources     = ARRAY_SIZE(s3c_iis_resource),

        .resource     = s3c_iis_resource,

        .dev = {

            .dma_mask = &s3c_device_iis_dmamask,

            .coherent_dma_mask = 0xffffffffUL

        }

    };

         它的name变量和刚才上面的platform_drivername变量是一致的,内核正是通过这个一致性来为驱动程序找到资源,即platform_device中的resource。这个结构的定义如下,位于include/linux/ioport.h中:

     struct resource {

        resource_size_t start;

        resource_size_t end;

        const char *name;

        unsigned long flags;

        struct resource *parent, *sibling, *child;

    };

         下面是一个具体的实例:

    static struct resource s3c_iis_resource[] = {

        [0] = {

            .start = S3C24XX_PA_IIS,

            .end = S3C24XX_PA_IIS + S3C24XX_SZ_IIS -1,

            .flags = IORESOURCE_MEM,

        }

    };

        这个结构的作用就是告诉驱动程序设备的起始地址和终止地址和设备的端口类型。这里的地址指的是物理地址。

        另外还需要注意platform_device中的device结构,它详细描述了设备的情况,定义如下:

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

    };

         上面把驱动程序中涉及到的主要结构都介绍了,下面主要说一下驱动程序中怎样对这个结构进行处理,以使驱动程序能运行。

        由于篇幅限制,下面的内容见下一篇:

    http://blog.chinaunix.net/u1/57747/showart.php?id=1074059

    platform_deviceplatform_driver(二)

    紧接上面介绍的数据结构,介绍驱动程序的具体实现过程。

     

        相信大家都知道module_init()这个宏。驱动模块加载的时候会调用这个宏。它接收一个函数为参数,作为它的参数的函数将会对上面提到的platform_driver进行处理。看一个实例:假如这里module_init要接收的参数为s3c2410_uda1341_init这个函数,下面是这个函数的定义:

    static int __init s3c2410_uda1341_init(void) { 

     memzero(&input_stream, sizeof(audio_stream_t)); 

     memzero(&output_stream, sizeof(audio_stream_t)); 

     return platform_driver_register(&s3c2410iis_driver); 

     

    意函数体的最后一行,它调用的是platform_driver_register这个函数。这个函数定义于driver/base/platform.c中,原型如下:

    int platform_driver_register(struct platform_driver *drv)

    它的功能就是为上面提到的plarform_driver中的driver这个结构中的proberemove这些变量指定功能函数

        到目前为止,内核就已经知道了有这么一个驱动模块。内核启动的时候,就会调用与该驱动相关的probe函数。我们来看一下probe函数实现了什么功能。

        probe函数的原型为

        int xxx_probe(struct platform_device *pdev)

        即它的返回类型为int,接收一个platform_device类型的指针作为参数。返回类型就是我们熟悉的错误代码了,而接收的这个参数呢,我们上面已经说过,驱动程序为设备服务,就需要知道设备的信息。而这个参数,就包含了与设备相关的信息。

        probe函数接收到plarform_device这个参数后,就需要从中提取出需要的信息。它一般会通过调用内核提供的platform_get_resourceplatform_get_irq等函数来获得相关信息。如通过platform_get_resource获得设备的起始地址后,可以对其进行request_mem_regionioremap等操作,以便应用程序对其进行操作。通过platform_get_irq得到设备的中断号以后,就可以调用request_irq函数来向系统申请中断。这些操作在设备驱动程序中一般都要完成。

        在完成了上面这些工作和一些其他必须的初始化操作后,就可以向系统注册我们在/dev目录下能看在的设备文件了。举一个例子,在音频芯片的驱动中,就可以调用register_sound_dsp来注册一个dsp设备文件,lcd的驱动中就可以调用register_framebuffer来注册fb设备文件。这个工作完成以后,系统中就有我们需要的设备文件了。而和设备文件相关的操作都是通过一个file_operations 来实现的。在调用register_sound_dsp等函数的时候,就需要传递一个file_operations 类型的指针。这个指针就提供了可以供用户空间调用的writeread等函数file_operations结构的定义位于include/linux/fs.h中,列出如下:

    struct file_operations {

        struct module *owner;

        loff_t (*llseek) (struct file *, loff_t, int);

        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

        int (*readdir) (struct file *, void *, filldir_t);

        unsigned int (*poll) (struct file *, struct poll_table_struct *);

        int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

        int (*mmap) (struct file *, struct vm_area_struct *);

        int (*open) (struct inode *, struct file *);

        int (*flush) (struct file *, fl_owner_t id);

        int (*release) (struct inode *, struct file *);

        int (*fsync) (struct file *, struct dentry *, int datasync);

        int (*aio_fsync) (struct kiocb *, int datasync);

        int (*fasync) (int, struct file *, int);

        int (*lock) (struct file *, int, struct file_lock *);

        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

        int (*check_flags)(int);

        int (*dir_notify)(struct file *filp, unsigned long arg);

        int (*flock) (struct file *, int, struct file_lock *);

        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);

        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);

        int (*setlease)(struct file *, long, struct file_lock **);

    };

         到目前为止,probe函数的功能就完成了。

        当用户打开一个设备,并调用其readwrite等函数的时候,就可以通过上面的file_operations来找到相关的函数。所以,用户驱动程序还需要实现这些函数,具体实现和相关的设备有密切的关系,这里就不再介绍了。

    /********************************************************************/

    实际我看Linux源代码中却大量使用platform_device

    struct platform_device {       

            const char        * name;        

            u32                id;                

            struct device        dev;       

            u32                num_resources;     

            struct resource        * resource;

    };

    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;                                      

    };

    从结构体可以看出,platform_devicedevice派生出,platform_driverdevice_driver派生出

    同样添加驱动PlatformDrvA,初始化platform_driver结构体的driver域时,没有初始化其bus_type域,而实际将该驱动添加在sys/bus/platform/drivers目录下,

    在源代码中哪里可以看到这部分代码。

    还有

    struct miscdevice  {                                  

            int minor;                         

            const char *name;                  

            const struct file_operations *fops;

            struct list_head list;             

            struct device *parent;             

            struct device *this_device;        

    }; 

    与字符型设备

    struct cdev {

            struct kobject kobj;

            struct module *owner;

            const struct file_operations *ops;

            struct list_head list;

            dev_t dev;

            unsigned int count;

    };

    从结构体可以看出,miscdevicedevice派生出,它与platform_device区别:

    1platform_device中有设备使用的资源的信息resource

    2miscdevice中有该设备的使用方法file_operations

    从设备驱动源代码中:

    第一步

    static struct platform_device at91sam9260_adc_device = {

        .name       = "at91_adc",

        .id     = -1,

        .dev        = {

                    .platform_data  = &adc_data,

        },  

        .resource   = adc_resources,

        .num_resources  = ARRAY_SIZE(adc_resources),

    };

    static struct resource spi0_resources[] = {

        [0] = {

            .start  = AT91SAM9260_BASE_SPI0,

            .end    = AT91SAM9260_BASE_SPI0 + SZ_16K - 1,

            .flags  = IORESOURCE_MEM,

        },

        [1] = {

            .start  = AT91SAM9260_ID_SPI0,

            .end    = AT91SAM9260_ID_SPI0,

            .flags  = IORESOURCE_IRQ,

        },

    };

    //向系统添加此设备,注册设备的资源

    platform_device_register(&at91sam9260_adc_device);

    第二步:

    static struct file_operations at91_adc_fops = {

            .owner =        THIS_MODULE,

           .ioctl =        at91_adc_ioctl,

           .read =        at91_adc_readdata,       

           .open =                at91_adc_open,        

           .release =        at91_adc_release,        

    };

    static struct miscdevice at91_adc_dev = {        

            .minor =        MISC_DYNAMIC_MINOR,

            .name =                "adc",

            .fops =                &at91_adc_fops,

    };

    //向系统添加此设备,注册设备的使用方法

    misc_register(&at91_adc_dev);

    第三步:

    static struct platform_driver at91_i2c_driver = {

            .probe                = at91_adc_probe,

            .remove                = __devexit_p(at91_adc_remove),

            .suspend        = at91_adc_suspend,

            .resume                = at91_adc_resume,

            .driver                = {

                    .name        = "at91_adc",

                    .owner        = THIS_MODULE,

            },

    };

    //注册此设备驱动

    platform_driver_register(&at91_i2c_driver);

    这三个结构体关系:

                         (基类)

                         kobject --------------------

                        /               /                     /

                       /                 /                      /

                    device        cdev                   driver

                   /          / (设备驱动操作方法)           /

                  /            /                                        /

        miscdevice         platform_device               platform_driver

    (设备驱动操作方法)    (设备的资源)                   (设备驱动

    我的疑问:

    1、当写字符型设备驱动时,我一般只使用cdev结构体,使用此种方式,好像无法在sysfs中显示出该设备。

    2miscdevice是否支持字符设备和块设备,如果使用它怎么辨别块设备或字符设备。

    3miscdeviceplatform_deviceplatform_driver是否可以作为通用的设备驱动方法,由platform_device注册设备资源

    platform_driver注册设备驱动、miscdevice注册设备使用方法。


    http://blogold.chinaunix.net/u1/57747/showart.php?id=1074059

    紧接上面介绍的数据结构,介绍驱动程序的具体实现过程。

     
        相信大家都知道module_init()这个宏。驱动模块加载的时候会调用这个宏。它接收一个函数为参数,作为它的参数的函数将会对上面提到的platform_driver进行处理。看一个实例:假如这里module_init要接收的参数为s3c2410_uda1341_init这个函数,下面是这个函数的定义:

    static int __init s3c2410_uda1341_init(void) {
     memzero(&input_stream, sizeof(audio_stream_t));
     memzero(&output_stream, sizeof(audio_stream_t));
     return platform_driver_register(&s3c2410iis_driver);
    }

    注意函数体的最后一行,它调用的是platform_driver_register这个函数。这个函数定义于driver/base/platform.c中,原型如下:

    int platform_driver_register(struct platform_driver *drv)

    它的功能就是为上面提到的plarform_driver中的driver这个结构中的probe、remove这些变量指定功能函数。

        到目前为止,内核就已经知道了有这么一个驱动模块。内核启动的时候,就会调用与该驱动相关的probe函数。我们来看一下probe函数实现了什么功能。

        probe函数的原型为

        int xxx_probe(struct platform_device *pdev)

        即它的返回类型为int,接收一个platform_device类型的指针作为参数。返回类型就是我们熟悉的错误代码了,而接收的这个参数呢,我们上面已经说过,驱动程序为设备服务,就需要知道设备的信息。而这个参数,就包含了与设备相关的信息。

        probe函数接收到plarform_device这个参数后,就需要从中提取出需要的信息。它一般会通过调用内核提供的platform_get_resource和platform_get_irq等函数来获得相关信息。如通过platform_get_resource获得设备的起始地址后,可以对其进行request_mem_region和ioremap等操作,以便应用程序对其进行操作。通过platform_get_irq得到设备的中断号以后,就可以调用request_irq函数来向系统申请中断。这些操作在设备驱动程序中一般都要完成。

        在完成了上面这些工作和一些其他必须的初始化操作后,就可以向系统注册我们在/dev目录下能看在的设备文件了。举一个例子,在音频芯片的驱动中,就可以调用register_sound_dsp来注册一个dsp设备文件,lcd的驱动中就可以调用register_framebuffer来注册fb设备文件。这个工作完成以后,系统中就有我们需要的设备文件了。而和设备文件相关的操作都是通过一个file_operations 来实现的。在调用register_sound_dsp等函数的时候,就需要传递一个file_operations 类型的指针。这个指针就提供了可以供用户空间调用的write、read等函数。file_operations结构的定义位于include/linux/fs.h中,列出如下:

    struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        int (*readdir) (struct file *, void *, filldir_t);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, struct dentry *, int datasync);
        int (*aio_fsync) (struct kiocb *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
        int (*check_flags)(int);
        int (*dir_notify)(struct file *filp, unsigned long arg);
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
        int (*setlease)(struct file *, long, struct file_lock **);
    };

        到目前为止,probe函数的功能就完成了。

        当用户打开一个设备,并调用其read、write等函数的时候,就可以通过上面的file_operations来找到相关的函数。所以,用户驱动程序还需要实现这些函数,具体实现和相关的设备有密切的关系,这里就不再介绍了。


  • 相关阅读:
    C++中的名字重整技术
    Linux下C++开发常用命令
    《Effective C++(第三版)》 的55条建议
    我也介绍下sizeof与strlen的区别
    POJ
    HDU
    HDU-1754-I Hate It(单点更新+区间查询)
    HDU多校1003-Divide the Stones(构造)
    Just an Old Puzzle(2019多校1007)
    Idiomatic Phrases Game(最短路+注意坑点)
  • 原文地址:https://www.cnblogs.com/yuzaipiaofei/p/4124680.html
Copyright © 2011-2022 走看看