A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

platform总线通常使用基于两个部分,一个部分是device,一个部分是driver.


我以一个简单的led的例子为例总结platform总线的使用。

我使用的是s5pv210处理器,所以我以该处理器为例总结,其实linux驱动,对任何处理器都一样。


首先以实现device部分。

device主要是数据。

首先我在自定义一个结构体,最好是自己创建一个头文件,方便驱动部分好包含。

我定义的放在对应处理器的mach目录下面,因为这个目录下都是放的和处理器相关的头文件。



  • #ifndef __ASM_ARCH_LEDSGPIO_H



  • #define __ASM_ARCH_LEDSGPIO_H "leds-gpio.h"







  • #define S5PV210_LEDF_ACTLOW (1<<0)      /* LED is on when GPIO low */



  • #define S5PV210_LEDF_TRISTATE   (1<<1)      /* tristate to turn off */







  • struct s5pv210_led_platdata {



  •     unsigned int         gpio;



  •     unsigned int         flags;







  •     char            *name;



  •     char            *def_trigger;



  • };







  • #endif /* __ASM_ARCH_LEDSGPIO_H */


很简单,主要就是led的一些信息

接下来就是填充这个结构体了。



  • /***************run add leds*******************/



  • static struct s5pv210_led_platdata s5pv210_led0_pdata = {



  •     .name       = "led0",



  •     .gpio       = S5PV210_GPJ0(3),



  •     .flags      = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,



  •     .def_trigger    = "",



  • };











  • static struct s5pv210_led_platdata s5pv210_led1_pdata = {



  •     .name       = "led1",



  •     .gpio       = S5PV210_GPJ0(4),



  •     .flags      = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,



  •     .def_trigger    = "",



  • };







  • static struct s5pv210_led_platdata s5pv210_led2_pdata = {



  •     .name       = "led2",



  •     .gpio       = S5PV210_GPJ0(5),



  •     .flags      = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,



  •     .def_trigger    = "",



  • };



  • static struct platform_device s5pv210_led0 =



  • {



  •     .name = "s5pv210_led",



  •     .id = 0,



  •     .dev =



  •     {



  •         .platform_data = &s5pv210_led0_pdata,



  •     },



  • };











  • static struct platform_device s5pv210_led1 =



  • {



  •     .name = "s5pv210_led",



  •     .id = 1,



  •     .dev =



  •     {



  •         .platform_data = &s5pv210_led1_pdata,



  •     },



  • };







  • static struct platform_device s5pv210_led2 =



  • {



  •     .name = "s5pv210_led",



  •     .id = 2,



  •     .dev =



  •     {



  •         .platform_data = &s5pv210_led2_pdata,



  •     },



  • };



我的板子上有三个led,我就定义了三个相对应的结构体。

struct s5pv210_led_platdata这个结构体里面的gpio我使用了gpiolib库的定义。

name自己定义,flags可以自定义其作用,比如输入输出,上下拉等。

def_trigger可以表示该gpio可以表示某个设备运行等,这里我没有使用。


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是表示区分同类设备。

我们这里关注struct device dev;



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



  • };


struct device这个结构体里面数据很多,我们主要关注void        *platform_data;

因为这个平台数据指针,就是设计为了给驱动传参用的(看注释)。void *可以串任何类型的指针就更方便了。

而我这里就是把每个led自定义的结构体struct s5pv210_led_platdata的地址放了进去。


自己做一个结构体,把自己创建的平台设备数据放进去。



  • static struct platform_device *smdkv210_devices[] __initdata = {



  •     &s5pv210_led0,



  •     &s5pv210_led1,



  •     &s5pv210_led2,







  • };


利用下面这个函数注册进入,device这边就已经结束了。platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));当然,比较简单的方式是把自己的设备数据,添加到原来的设备后面,就可以不用自己添加了。


接下来看驱动部分。

大的部分看就是平台数据的注册和卸载。



  • /* 平台总线模型驱动部分 */



  • static struct platform_driver s5pv210_led_driver =



  • {



  •     .probe = s5pv210_led_probe,



  •     .remove = s5pv210_led_remove,



  •     .driver =



  •     {



  •         .name = "s5pv210_led",



  •         .owner = THIS_MODULE,



  •     },



  • };







  • /* 平台总线驱动注册 */



  • static int __init s5pv210_led_init(void)



  • {



  •     return platform_driver_register(&s5pv210_led_driver);



  • }







  • /* 平台总线驱动取消 */



  • static void __exit s5pv210_led_exit(void)



  • {



  •     platform_driver_unregister(&s5pv210_led_driver);



  • }











  • module_init(s5pv210_led_init);



  • module_exit(s5pv210_led_exit);







  • MODULE_LICENSE("GPL");



  • MODULE_AUTHOR("run");



  • MODULE_DESCRIPTION("leds-s5pv210");


驱动的注册和卸载是固定的比较简单。但里面填充的驱动模型,这个最主要的。

下面看一下平台驱动模型的原型。



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



  • };


上面这个驱动模型比较全面。而我的led的需要很简单,只需要填充probe和remove函数即可。

还有一个比较中要的是driver,这个里面的东西是和设备注册那时候的做匹配的。(名字匹配)



  • struct device_driver {



  •         const char                *name;



  •         struct bus_type                *bus;







  •         struct module                *owner;



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







  •         bool suppress_bind_attrs;        /* disables bind/unbind via sysfs */







  • #if defined(CONFIG_OF)



  •         const struct of_device_id        *of_match_table;



  • #endif







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



  •         const struct attribute_group **groups;







  •         const struct dev_pm_ops *pm;







  •         struct driver_private *p;



  • };


这里我们主要关注char *naem和 driver_private *p。

其中name主要是这个驱动的名字。为了和对应设备匹配,驱动名字必须和设备名字一致。

driver_private *p的使用可以看我的上一个博客。

接下来我们看最主要的连个函数probe和remove。

其中probe是负责设备初始化的。remove是负责卸载设备的时候做收尾工作的。


因为我们这里是led,所以使用了led的设备驱动模型。


所以初始化部分用的是led的设备模型初始化的。



  • /* 想当于初始化函数 */



  • static int s5pv210_led_probe(struct platform_device *pdev)



  • {



  •     /* 得到设备传过来来的数据 */



  •     struct s5pv210_led_platdata *pdata = pdev->dev.platform_data;



  •     struct s5pv210_gpio_led *led = NULL;



  •     int ret = -1;







  •     printk(KERN_INFO"-----------s5pv210_led_probe-------------\n");







  •     led = kzalloc(sizeof(struct s5pv210_gpio_led),GFP_KERNEL);



  •     if(NULL == led)



  •     {



  •         dev_err(&pdev->dev, "no memoey for device \n");



  •         return -ENOMEM;



  •     }



  •     /* 使用gpiolib库,申请gpio */



  •     if(gpio_request(pdata->gpio,pdata->name))



  •     {



  •         printk(KERN_ERR"gpio_request fail\n");



  •         kfree(led);



  •         return -EINVAL;



  •     }



  •     else



  •     {



  •         /* 初始化gpio为输出 */



  •         gpio_direction_output(pdata->gpio,1);



  •     }







  •     /* 把动态申请到的driver data绑定到相应的设备的私有数据中 */



  •     platform_set_drvdata(pdev, led);







  •     led->cdev.name = pdata->name;



  •     led->cdev.brightness_set = s5pv210_led_brightness_set;



  •     led->cdev.brightness = 0;



  • //  led->cdev.flags = pdata->flags;     //这个标志必须用内核定义的,自己定义的可能会和内核的重复



  •     led->pdata = pdata;







  •     /* 利用led设备类模型注册该设备 */



  •     ret = led_classdev_register(&pdev->dev,&led->cdev );



  •     if(ret)



  •     {



  •         dev_err(&pdev->dev, "led classdev_register fail\n");



  •         kfree(led);



  •         gpio_free(pdata->gpio);



  •         return ret;



  •     }







  •     return 0;



  • }


这个函数里注释比较明确,唯一需要说明的是flags,因为pdata->flags是我们子定义的功能,前面在设备部分说明已的是做输入输出标志或输出的默认电平标志。这里我们led是做输出的,默认给的高电平。所以没用这个标志,但千万不能赋值和led->cdev.flags.因为这个cdev是led的设备模型定义的标志,其作用已经固定了。我就是刚在开始赋值的,导致led被挂起。不能操纵。

可以看到下面led类里面就用到



  • static int led_suspend(struct device *dev, pm_message_t state)



  • {



  •         struct led_classdev *led_cdev = dev_get_drvdata(dev);







  •         if (led_cdev->flags & LED_CORE_SUSPENDRESUME)



  •                 led_classdev_suspend(led_cdev);







  •         return 0;



  • }







  • static int led_resume(struct device *dev)



  • {



  •         struct led_classdev *led_cdev = dev_get_drvdata(dev);







  •         if (led_cdev->flags & LED_CORE_SUSPENDRESUME)



  •                 led_classdev_resume(led_cdev);







  •         return 0;



  • }


它们的定义如下。



  • struct led_classdev {



  •         const char                *name;



  •         int                         brightness;



  •         int                         max_brightness;



  •         int                         flags;







  •         /* Lower 16 bits reflect status */



  • #define LED_SUSPENDED                (1 << 0)



  •         /* Upper 16 bits reflect control information */



  • #define LED_CORE_SUSPENDRESUME        (1 << 16)







  •         /* Set LED brightness level */



  •         /* Must not sleep, use a workqueue if needed */



  •         void                (*brightness_set)(struct led_classdev *led_cdev,



  •                                           enum led_brightness brightness);



  •         /* Get LED brightness level */



  •         enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);







  •         /* Activate hardware accelerated blink, delays are in



  •          * miliseconds and if none is provided then a sensible default



  •          * should be chosen. The call can adjust the timings if it can't



  •          * match the values specified exactly. */



  •         int                (*blink_set)(struct led_classdev *led_cdev,



  •                                      unsigned long *delay_on,



  •                                      unsigned long *delay_off);







  •         struct device                *dev;



  •         struct list_head         node;                        /* LED Device list */



  •         const char                *default_trigger;        /* Trigger to use */







  • #ifdef CONFIG_LEDS_TRIGGERS



  •         /* Protects the trigger data below */



  •         struct rw_semaphore         trigger_lock;







  •         struct led_trigger        *trigger;



  •         struct list_head         trig_list;



  •         void                        *trigger_data;



  • #endif



  • };


probe函数里注册led的驱动模型的时候需要用到一个brightness_set函数,这个函数就是led的设备模型中干活的函数,用来设置led的量灭的。



  • /* 设置led灯的亮灭 */



  • static void s5pv210_led_brightness_set(struct led_classdev *led_cdev,



  •                       enum led_brightness brightness)



  • {



  •     struct s5pv210_gpio_led * led = to_gpio(led_cdev);



  •     printk(KERN_INFO"led brightness set\n");







  •     if(LED_OFF == brightness)



  •     {



  •         gpio_set_value(led->pdata->gpio, 1);



  •     }



  •     else



  •     {



  •         gpio_set_value(led->pdata->gpio, 0);



  •     }







  • }


下面几个就是上面用到的一些的数据类型和工具函数。



  • /* 自定义结构体,主要是想包含平台数据 */



  • struct s5pv210_gpio_led



  • {



  •     struct led_classdev cdev;



  •     struct s5pv210_led_platdata *pdata;



  • };











  • /* 得到platform_device里面的platform_data */



  • static inline struct s5pv210_gpio_led *pdev_to_gpio(struct platform_device *dev)



  • {



  •     return platform_get_drvdata(dev);



  • }







  • /* 通过container_of宏,利用s5pv210_gpio_led 结构体里面的led_classdev结构体得到s5pv210_gpio_led */



  • static inline struct s5pv210_gpio_led *to_gpio(struct led_classdev *led_cdev)



  • {



  •     return container_of(led_cdev,struct s5pv210_gpio_led,cdev);



  • }


总结:上面设备部分用到了palaform_device的一些知识。

下面驱动部分用到了platform_driver和led设备模型,以及gpiolib库的简单应用。

几个知识点算是对上一周学习的总结。


【转载】原文地址: https://blog.csdn.net/qq_16777851/article/details/80850526




2 个回复

倒序浏览
回复 使用道具 举报
棒棒哒
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马