• 【linux驱动开发】-gpiolib概念与实践


    1.什么是gpiolib?

           在平常的硬件驱动过程中,很多的硬件都要用到GPIO,GPIO会复用,如果同一时刻GPIO被两个驱动同时控制了,那么就会出现bug,所以内核提供了gpiolib来统一管理系统中所有的GPIO,gpiolib本身就是属于驱动框架的一部分!

    在这部分的学习过程中,我们会分析gpiolib应该如何去学习,如何编程实现gpiolib,并且通过上一节中对LED的程序进行扩展,增加gpiolib控制来实现灯的变化!

    2.如何学习gpiolib

    gpiolib的架构(建立)

    在内核启动,静态映射自动建立的时候,有一个函数xxx_gpiolib_init()会被自动加载!

    然后进入到xxx_gpiolib_init()这个函数中,依次进行分析!

    接下来就顺着主线分析s3c_gpio_chip这个结构体,首先可以看出来,这个结构体是一个gpio端口的抽象,这个结构体的一个变量就可以完全的描述一个IO端口!

    这里区分端口和IO口: 一个Soc有很多的IO口,这些IO口首先被分成N个端口,然后每个端口有包含了M个IO口。譬如GPA0是一个端口,里面包含了8个IO口,我们一般记作GPA0_0(GPA0.0)、GPA0_1;

    这个东西是一个结构体数组,数组里包含了很多个struct s3c_gpio_chip类型的变量!一个变量对应一个端口,重点是描述.chip这个元素!

    其中.base 、.ngpio、.label成了GPIO中的一些属性:

     通过上面的分析,我们已经大概知道了如何分析一个gpiolib的建立,并且在init函数的最后一句,我们有向内核建立gpiolib的函数。

    这个函数才是具体进行gpiolib的注册的。这个函数接受的参数是我们当前文件定义好的结构体数组xxx_gpio_4bit(其实两个参数分别是数组名和数组元素个数),这个数组包含了当前系统中所有IO端口的信息。

    gpiolib的使用方法

            在这里我们不考虑,内核部分是如何实现的,只考虑如果我们作为厂商的驱动工程师,我们应该怎样使用gpiolib!也是比较重要的一部分!

    gpiochip_add:是框架开出来的接口,给厂商驱动工程师用,用于向内核注册我们的gpiolib。

    gpio_request:是框架开出来的接口,给使用gpiolib来编写自己的驱动的驱动工程师用的,驱动中想使用某一个gpio,就必须先调用gpio_request接口来向内核的gpiolib部分申请,得到允许后才能使用这个gpio。

    gpio_free:对应gpio_request,用来释放申请后用完了的gpio。

    gpio_request_one/gpio_request_array:这两个是gpio_request的变种。

    gpiochip_is_requested:接口用来判断某一个gpio是否已经被申请了。

    gpio_direction_input/gpio_direction_output:接口用来设置GPIO为输入/输出模式,注意该函数内部实际并没有对硬件进行操作,只是通过chip结构体变量的函数指针调用了将来Soc厂商的驱动工程师真正写的操作gpio设置成输出模式的那个函数!

    3.通过编程实践测试gpiolib

    第一步:使用gpio_request申请要是用的一个GPIO。

    第二步:gpio_direction_input/gpio_direction_output设置成输入/输出模式。

    第三步:设置输出值gpio_set_value,获取gpio_get_value.

    通过以上三步和上一章节LED驱动框架编写LED的驱动程序!

    1. #include // module_init module_exit
    2. #include // __init __exit
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. ///****此驱动使用的是led框架和gpiolib框架进行编写***
    11. #define GPIO_LED1 S5PV210_GPJ0(3)
    12. #define GPIO_LED2 S5PV210_GPJ0(4)
    13. #define GPIO_LED3 S5PV210_GPJ0(5)
    14. #define X210_LED_OFF 1 // X210中LED是正极接电源,负极节GPIO
    15. #define X210_LED_ON 0 // 所以1是灭,0是亮
    16. static struct led_classdev mydev1; // 定义结构体变量
    17. static struct led_classdev mydev2; // 定义结构体变量
    18. static struct led_classdev mydev3; // 定义结构体变量
    19. // 这个函数就是要去完成具体的硬件读写任务的
    20. static void s5pv210_led1_set(struct led_classdev *led_cdev,
    21. enum led_brightness value)
    22. {
    23. printk(KERN_INFO "s5pv210_led1_set\n");
    24. //writel(0x11111111, GPJ0CON);
    25. // 在这里根据用户设置的值来操作硬件
    26. // 用户设置的值就是value
    27. if (value == LED_OFF)
    28. {
    29. // 用户给了个0,希望LED灭
    30. //writel(0x11111111, GPJ0CON);
    31. // 读改写三部曲
    32. //writel((readl(GPJ0DAT) | (1<<3)), GPJ0DAT);
    33. gpio_set_value(GPIO_LED1, X210_LED_OFF);
    34. }
    35. else
    36. {
    37. // 用户给的是非0,希望LED亮
    38. //writel(0x11111111, GPJ0CON);
    39. //writel((readl(GPJ0DAT) & ~(1<<3)), GPJ0DAT);
    40. gpio_set_value(GPIO_LED1, X210_LED_ON);
    41. }
    42. }
    43. static void s5pv210_led2_set(struct led_classdev *led_cdev,
    44. enum led_brightness value)
    45. {
    46. printk(KERN_INFO "s5pv2102_led_set\n");
    47. //writel(0x11111111, GPJ0CON);
    48. // 在这里根据用户设置的值来操作硬件
    49. // 用户设置的值就是value
    50. if (value == LED_OFF)
    51. {
    52. // 用户给了个0,希望LED灭
    53. //writel(0x11111111, GPJ0CON);
    54. // 读改写三部曲
    55. //writel((readl(GPJ0DAT) | (1<<4)), GPJ0DAT);
    56. }
    57. else
    58. {
    59. // 用户给的是非0,希望LED亮
    60. //writel(0x11111111, GPJ0CON);
    61. //writel((readl(GPJ0DAT) & ~(1<<4)), GPJ0DAT);
    62. }
    63. }
    64. static void s5pv210_led3_set(struct led_classdev *led_cdev,
    65. enum led_brightness value)
    66. {
    67. printk(KERN_INFO "s5pv210_led3_set\n");
    68. //writel(0x11111111, GPJ0CON);
    69. // 在这里根据用户设置的值来操作硬件
    70. // 用户设置的值就是value
    71. if (value == LED_OFF)
    72. {
    73. // 用户给了个0,希望LED灭
    74. //writel(0x11111111, GPJ0CON);
    75. // 读改写三部曲
    76. //writel((readl(GPJ0DAT) | (1<<5)), GPJ0DAT);
    77. }
    78. else
    79. {
    80. // 用户给的是非0,希望LED亮
    81. //writel(0x11111111, GPJ0CON);
    82. //writel((readl(GPJ0DAT) & ~(1<<5)), GPJ0DAT);
    83. }
    84. }
    85. static int __init s5pv210_led_init(void)
    86. {
    87. // 用户insmod安装驱动模块时会调用该函数
    88. // 该函数的主要任务就是去使用led驱动框架提供的设备注册函数来注册一个设备
    89. int ret = -1;
    90. // 在这里去申请驱动用到的各种资源,当前驱动中就是GPIO资源
    91. if (gpio_request(GPIO_LED1, "led1_gpj0.3")) //申请内存,如果成功返回0,失败返回1 如果返回0 说明成功,直接跳到else执行
    92. {
    93. printk(KERN_ERR "gpio_request failed\n");//返回1 说明失败,打印此句
    94. }
    95. else
    96. {
    97. // 设置为输出模式,并且默认输出1让LED灯灭
    98. gpio_direction_output(GPIO_LED1, 1);
    99. }
    100. // led1
    101. mydev1.name = "led1";
    102. mydev1.brightness = 0;
    103. mydev1.brightness_set = s5pv210_led1_set;
    104. ret = led_classdev_register(NULL, &mydev1);//led_classdev_register就是注册设备
    105. if (ret < 0) {
    106. printk(KERN_ERR "led_classdev_register failed\n");
    107. return ret;
    108. }
    109. // led2
    110. mydev2.name = "led2";
    111. mydev2.brightness = 0;
    112. mydev2.brightness_set = s5pv210_led2_set;
    113. ret = led_classdev_register(NULL, &mydev2);
    114. if (ret < 0) {
    115. printk(KERN_ERR "led_classdev_register failed\n");
    116. return ret;
    117. }
    118. // led3
    119. mydev3.name = "led3";
    120. mydev3.brightness = 0;
    121. mydev3.brightness_set = s5pv210_led3_set;
    122. ret = led_classdev_register(NULL, &mydev3);
    123. if (ret < 0) {
    124. printk(KERN_ERR "led_classdev_register failed\n");
    125. return ret;
    126. }
    127. return 0;
    128. }
    129. static void __exit s5pv210_led_exit(void)
    130. {
    131. led_classdev_unregister(&mydev1);
    132. led_classdev_unregister(&mydev2);
    133. led_classdev_unregister(&mydev3);
    134. gpio_free(GPIO_LED1);
    135. }
    136. module_init(s5pv210_led_init);
    137. module_exit(s5pv210_led_exit);
    138. // MODULE_xxx这种宏作用是用来添加模块描述信息
    139. MODULE_LICENSE("GPL"); // 描述模块的许可证
    140. MODULE_AUTHOR("aston <1264671872@qq.com>"); // 描述模块的作者
    141. MODULE_DESCRIPTION("s5pv210 led driver"); // 描述模块的介绍信息
    142. MODULE_ALIAS("s5pv210_led"); // 描述模块的别名信息

  • 相关阅读:
    5米DEM高程数据分析和对比
    ubuntu 安装 notepad++
    高考假期预习指南:开启你的IT学习之旅
    14条最佳JavaScript代码编写技巧
    Python爬虫入门:如何设置代理IP进行网络爬取
    使用RNN联合注意力机制实现机器翻译
    屡破纪录|Hubble数据库又获2022全球数字经济大会背书 数据库赛道同类选优入选 “数字经济产业创新成果”
    uni-app接入mPaas扫码
    Unity中全局光照GI的总结
    【Android Jetpack】Hilt的理解与浅析
  • 原文地址:https://blog.csdn.net/weixin_49176627/article/details/126454883