查看: 1642|回复: 0

[基础] exynos4412开发板设备注册与驱动注册剖析

[复制链接]
  • TA的每日心情
    开心
    2014-4-21 09:44
  • 签到天数: 26 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    发表于 2017-8-24 15:48:57 | 显示全部楼层 |阅读模式
    分享到:
    Linux内核的主要组成部分由进程调度(SCHED)、内存管理(MM)、虚拟文件系统(VFS),网络接口(NET),进程间通信(IPC)
    查看linux系统的总线ls /sys/bus
    查看linux系统的设备cat /proc/devices

    Linux驱动和设备的注册过程
    Linux 内核会要求每出现一个设备就要向总线汇报,或者说注册,出现一个驱动,也要向总线汇报,或者叫注册。
    在系统初始化的时候,会扫描连接了哪些设备,并为每一个设备建立一个struct_device 的变量,然后将设备的变量插入到devices 链表系统初始化任意一个驱动程序的时候,就要准备一个struct device_driver 结构的变量,然后将驱动的变量插入到drivers 链表
    Linux 总线是为了将设备和驱动绑定,方便管理。在系统每注册一个设备的时候,会寻找与之匹配的驱动(后面的热拔插设备会用到这个注册流程);相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
    先注册设备再注册驱动
    在注册驱动的时候,系统会通过platform_match 函数匹配设备和驱动。注册设备的结构体为platform_device,注册驱动的结构体为platform_driver。设备和和驱动结构体的成员name 字段,相同则匹配如果匹配了则会调用platform_driver 中的probe 函数,注册驱动。
    设备节点
    上层应用使用设备节点访问对应的设备
    ls /dev
    上层应用有一套标准的接口文件和函数用来和底层通信。Linux 将所有对设备的操作全部都抽象为对文件的操作,常用的文件操作函数有open、close、write、read、write 等。

    设备注册platform结构体
    内核文件include/linux/platform_device.h文件中查看platform_device.
    1. struct platform_device{
    2.     const char *name,//设备名称,在sys/devices会显示
    3.     int id;//设备id,用于插入总线并具有相同的name的设备编号,只有一个则是-1;
    4.     struct device dev;//结构体内嵌device结构体
    5.     u32 num_resources;//设备使用的资源数量
    6.     struct resource *resource;//设备组使用的资源数组
    7.     const struct platform_device_id *id_entry;

    8.     struct mfd_cell *mfd_cell;//维护功能配置单元的指针

    9.     struct pdev_archdata archdata;//具体添加

    10. };
    复制代码
    添加设备到平台总线
    在内核源码中arch/arm/mach-exynos/mach-itop4412.c注册平台设备结构体“platform_device”中,只调用了两个参数*name”和“id”。仿照着已有的leds这段代码在它前面添加一个设备“hello_ctl”,
    1. #ifdef CONFIG_LEDS_CTL
    2. struct platform_device s3c_device_leds_ctl = {
    3.     .name   = "leds",
    4.     .id             = -1,
    5. };

    6. #endif
    7. #ifdef CONFIG_HELLO_CTL
    8. struct platform_device s3c_device_hello_ctl = {
    9. .name   = "hello_ctl",
    10. .id             =-1,
    11.     };
    12. #endif
    复制代码
    在Kconfig中添加宏定义
    在drivers/char/Kconfig中添加
    1. config HELLO_CTL
    2.     tristate "Enable HELLO config"
    3.     default y
    4.     help
    5.         Enable HELLO config
    复制代码
    在内核源码中使用命令“mak menuconfig”,进入“Device Drivers—>”→“Character devices —>”→“Enable HELLO config”进入arch/arm/mach-exynos/mach-itop4412.c中,添加设备初始化代码
    仿照LED添加
    1. #ifdef CONFIG_LEDS_CTL
    2.     &s3c_device_leds_ctl,
    3.     #endif
    4.     #ifdef CONFIG_HELLO_CTL
    5.         &s3c_device_hello_ctl,
    6.     #endif
    复制代码
    编译内核烧写即可开发板启动之后,使用命令“ls /sys/devices/platform/”可以查看到新注册的hello 设备

    驱动注册
    注册驱动的时候需要和设备匹配,简单介绍了将驱动注册到平台设备的结构体“platform_driver_register”在内核源码中include/linux/device.h文件中
    1. extern int platform_driver_register(struct platform_driver *);
    2.     extern void platform_driver_unregister(struct platform_driver *);
    3. platform_driver结构体的调用
    4. struct platform_driver {
    5.     int (*probe)(struct platform_device *);
    6.     int (*remove)(struct platform_device *);
    7.     void (*shutdown)(struct platform_device *);
    8.     int (*suspend)(struct platform_device *, pm_message_t state);
    9.     int (*resume)(struct platform_device *);
    10.     struct device_driver driver;
    11.     const struct platform_device_id *id_table;
    12. };
    复制代码
    该结构中包含了一组操作函数和一个struct device_driver 的对像。在驱动中首先要做的就是定义platform_driver 中的函数,并创建这个结构的一个对象实例, 然后在init()函数中调用platform_driver_register()向系统注册我们的驱动。
    函数int (probe)(struct platform_device );
    主要是进行设备的探测和初始化。例如我们想调用一个GPIO,那么首先需要探测这个GPIO 是否被占用了,如果被占用了那么初始化失败,驱动注册也就失败了;如果没有被占用,那么我们就申明要占用它。函数中一般还会添加生成设备节点的函数,如果初始化成功,那么就会需要添加设备节点。
    函数int(remove)(struct platform_device );
    移除驱动,该函数中一般用于去掉设备节点或者释放软硬件资源。
    接着的三个函数:
    1. void (*shutdown)(struct platform_device *);
    2. int (*suspend)(struct platform_device *, pm_message_t state);
    3. int (*resume)(struct platform_device *);
    复制代码
    从字面上就很好理解了,关闭驱动,悬挂(休眠)驱动以及恢复的时候该驱动要做什么
    结构体struct device_driver driver;
    主要包含两个参数,一个是name参数,驱动名称(需要和设备驱动结构体中的name 参数一样),
    一个是owner,一般是THIS_MODULE。
    1. 1.添加“#include <linux/platform_device.h>”,
    2. 然后定义一个宏变量DRIVER_NAME,定义为“hello_ctl”,需要和前面注册的hello 设备的名称相同。
    3. 2.模块入口和出口调用函数platform_driver_register 和
    4. platform_driver_unregister,如下图所示,先将参数名定义为“&hello_driver”。,定义结构体“hello_driver”。
    5. struct platform_driver hello_driver = {
    6.     .probe = hello_probe,
    7.     .remove = hello_remove,
    8.     .shutdown = hello_shutdown,
    9.     .suspend = hello_suspend,
    10.     .resume = hello_resume,
    11.     .driver = {
    12.         .name = DRIVER_NAME,
    13.         .owner = THIS_MODULE,
    14.     }
    15. };
    16. 3.然后定义函数hello_probe、hello_remove、hello_shutdown、hello_suspend、hello_resume。**(需要在结构体前定义声明)**
    复制代码
    示例代码
    1. #include <linux/init.h>
    2. #include <linux/module.h>
    3. /*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/
    4. #include <linux/platform_device.h>

    5. #define DRIVER_NAME "hello_ctl"

    6. MODULE_LICENSE("Dual BSD/GPL");
    7. MODULE_AUTHOR("chenmiaohong");

    8. static int hello_probe(struct platform_device *pdv){

    9.     printk(KERN_EMERG "\tinitialized\n");

    10.     return 0;
    11. }

    12. static int hello_remove(struct platform_device *pdv){

    13.     return 0;
    14. }

    15. static void hello_shutdown(struct platform_device *pdv){

    16.     ;
    17. }

    18. static int hello_suspend(struct platform_device *pdv){

    19.     return 0;
    20. }

    21. static int hello_resume(struct platform_device *pdv){

    22.     return 0;
    23. }

    24. struct platform_driver hello_driver = {
    25.     .probe = hello_probe,
    26.     .remove = hello_remove,
    27.     .shutdown = hello_shutdown,
    28.     .suspend = hello_suspend,
    29.     .resume = hello_resume,
    30.     .driver = {
    31.     .name = DRIVER_NAME,
    32.     .owner = THIS_MODULE,
    33.     }
    34. };


    35. static int hello_init(void)
    36. {
    37.     int DriverState;

    38.     printk(KERN_EMERG "HELLO WORLD enter!\n");
    39.     DriverState = platform_driver_register(&hello_driver);

    40.     printk(KERN_EMERG "\tDriverState is %d\n",DriverState);
    41.     return 0;
    42. }

    43. static void hello_exit(void)
    44. {
    45.     printk(KERN_EMERG "HELLO WORLD exit!\n");

    46.     platform_driver_unregister(&hello_driver);  
    47. }

    48. module_init(hello_init);
    49. module_exit(hello_exit);
    50. 修改Makefile进行编译运行生成.ko文件通过tftp服务器下载到开发板进行加载
    复制代码
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-4-27 01:26 , Processed in 0.121140 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.