linux平台总线驱动设备模型之点亮LED

时间:2021-08-19 06:58:15

这一节里,我们来使用平台驱动设备这一套架构来实现我们之前使用简单的字符设备驱动点亮LED,这里并无实际意义,只是告诉大家如果编写平台总线驱动设备。

问:如何编写平台总线驱动设备这一套架构的设备驱动?

答:分为两个.c文件,一个是drv.c,另一个是dev.c;前者实现平台驱动,后者实现平台设备,平台总线不用我们自己实现。

问:编写平台驱动的核心内容有哪些?

答:分配、设置、注册一个platform_driver

问:如何注册平台驱动?

答:使用platform_driver_register(struct platform_driver *drv)函数,该函数的参数为platform_driver

问:如何定义platform_driver?

答:简单示例

[cpp] view
plain
?
  1. static struct platform_driver led_driver = {
  2. .probe      = led_probe,
  3. .remove     = led_remove,
  4. .driver     = {
  5. .name   = "myled",
  6. .owner  = THIS_MODULE,
  7. }
  8. };

问:probe函数什么时候被调用?

答:当系统中有同名的平台设备和平台驱动时,就会调用probe函数。

问:probe函数有什么作用?

答:该函数可以做什么由你决定,你可以只打印一条语句,也可以做很复杂的事情。例如,led_probe函数就做了获取资源,映射IO,注册字符设备。

led_drv.c源码参考:

[cpp] view
plain
?
  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/fs.h>
  4. #include <linux/interrupt.h>
  5. #include <linux/irq.h>
  6. #include <linux/sched.h>
  7. #include <linux/pm.h>
  8. #include <linux/sysctl.h>
  9. #include <linux/proc_fs.h>
  10. #include <linux/delay.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/input.h>
  13. #include <linux/gpio_keys.h>
  14. #include <asm/uaccess.h>   // copy_from_user
  15. #include <asm/io.h>  // ioremap
  16. static struct class *led_cls;
  17. static volatile unsigned long *gpio_con;
  18. static volatile unsigned long *gpio_dat;
  19. static int pin;
  20. static int major;
  21. static int led_open(struct inode * inode, struct file * filp)
  22. {
  23. *gpio_con &= ~(0x3<<(pin*2));
  24. *gpio_con |= (0x1<<(pin*2));
  25. return 0;
  26. }
  27. static ssize_t
  28. led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  29. {
  30. int val;
  31. copy_from_user(&val, buf, count);
  32. if(val == 1)
  33. {
  34. /* 点灯 */
  35. *gpio_dat  &= ~(1<<pin);
  36. }
  37. else
  38. {
  39. /* 灭灯 */
  40. *gpio_dat  |= (1<<pin);
  41. }
  42. return 0;
  43. }
  44. /* File operations struct for character device */
  45. static const struct file_operations led_fops = {
  46. .owner      = THIS_MODULE,
  47. .open       = led_open,
  48. .write      = led_write,
  49. };
  50. static int __devinit led_probe(struct platform_device *pdev)
  51. {
  52. struct resource *res;
  53. printk("led_probe, found led\n");
  54. /* 根据platform_device的资源进行ioremap */
  55. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  56. gpio_con =  ioremap(res->start, res->end - res->start + 1);
  57. gpio_dat = gpio_con + 1;
  58. res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  59. pin =  res->start;
  60. /* 注册字符设备 */
  61. major = register_chrdev(0, "myled", &led_fops);
  62. led_cls = class_create(THIS_MODULE,"myled");
  63. device_create(led_cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
  64. return 0;
  65. }
  66. static int __devexit led_remove(struct platform_device *pdev)
  67. {
  68. printk("led_remove, remove led\n");
  69. device_destroy(led_cls, MKDEV(major, 0));
  70. class_destroy(led_cls);
  71. unregister_chrdev(major, "myled");
  72. iounmap(gpio_con);
  73. return 0;
  74. }
  75. static struct platform_driver led_driver = {
  76. .probe      = led_probe,
  77. .remove     = led_remove,
  78. .driver     = {
  79. .name   = "myled",
  80. .owner  = THIS_MODULE,
  81. }
  82. };
  83. /* 分配/设置/注册一个platform_driver */
  84. static int led_drv_init(void)
  85. {
  86. return platform_driver_register(&led_driver);
  87. }
  88. static void led_drv_exit(void)
  89. {
  90. platform_driver_unregister(&led_driver);
  91. }
  92. module_init(led_drv_init);
  93. module_exit(led_drv_exit);
  94. MODULE_LICENSE("GPL");
  95. MODULE_AUTHOR("LWJ");
  96. MODULE_DESCRIPTION("Just for Demo");

问:编写平台设备驱动的核心内容有哪些?

答:分配、设置、注册一个platform_device

问:如何注册平台设备?

答:使用platform_device_register(struct platform_device *pdev)函数,该函数的参数为platform_device

问:如何定义platform_device?

答:简单示例:led_device

[cpp] view
plain
?
  1. static struct platform_device led_device = {
  2. .id         = -1,
  3. .name       = "myled",  /* 与led_driver的name一致 */
  4. .resource       = led_resources,
  5. .num_resources  = ARRAY_SIZE(led_resources),
  6. .dev            ={
  7. .release    = led_release,
  8. },
  9. };

问:如何定义resource?

答:简单示例:

[cpp] view
plain
?
  1. static struct resource led_resources[] = {
  2. [0] = {
  3. .start  = 0x56000010,      /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */
  4. .end    = 0x56000010 + 8 -1,
  5. .flags  = IORESOURCE_MEM,
  6. },
  7. [1] = {
  8. .start  = 5,        /* LED1 */
  9. .end    = 5,
  10. .flags  = IORESOURCE_IRQ,
  11. },
  12. };

led_dev.c源码参考:

[cpp] view
plain
?
  1. #include <linux/module.h>
  2. #include <linux/version.h>
  3. #include <linux/init.h>
  4. #include <linux/kernel.h>
  5. #include <linux/types.h>
  6. #include <linux/interrupt.h>
  7. #include <linux/list.h>
  8. #include <linux/timer.h>
  9. #include <linux/init.h>
  10. #include <linux/serial_core.h>
  11. #include <linux/platform_device.h>
  12. static struct resource led_resources[] = {
  13. [0] = {  
    //寄存器的起始地址
  14. .start  = 0x56000010,      /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */
  15. .end    = 0x56000010 + 8 -1,
  16. .flags  = IORESOURCE_MEM,
  17. },
  18. [1] = {  
    //寄存器的哪一个引脚,以后可以修改这儿来操作让哪个led点亮
  19. .start  = 5,        /* LED1 */
  20. .end    = 5,
  21. .flags  = IORESOURCE_IRQ,
  22. },
  23. };
  24. static void led_release(struct device * dev)
  25. {
  26. }
  27. static struct platform_device led_device = {
  28. .id         = -1,
  29. .name       = "myled",  /* 与led_driver的name一致 */
  30. .resource       = led_resources,
  31. .num_resources  = ARRAY_SIZE(led_resources),
  32. .dev            ={
  33. .release    = led_release,
  34. },
  35. };
  36. /* 分配/设置/注册一个platform_device */
  37. static int led_dev_init(void)
  38. {
  39. return platform_device_register(&led_device);
  40. }
  41. static void led_dev_exit(void)
  42. {
  43. platform_device_unregister(&led_device);
  44. }
  45. module_init(led_dev_init);
  46. module_exit(led_dev_exit);
  47. MODULE_LICENSE("GPL");
  48. MODULE_AUTHOR("LWJ");
  49. MODULE_DESCRIPTION("Just for Demo");

应用测试程序源码:

[cpp] view
plain
?
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. /* 9th_led_test on
  6. * 9th_led_test off
  7. */
  8. int main(int argc, char **argv)
  9. {
  10. int fd;
  11. int val = 1;
  12. fd = open("/dev/led", O_RDWR);
  13. if (fd < 0)
  14. {
  15. printf("can't open!\n");
  16. }
  17. if (argc != 2)
  18. {
  19. printf("Usage :\n");
  20. printf("%s <on|off>\n", argv[0]);
  21. return 0;
  22. }
  23. if (strcmp(argv[1], "on") == 0)
  24. {
  25. val  = 1;
  26. }
  27. else
  28. {
  29. val = 0;
  30. }
  31. write(fd, &val, 4);
  32. return 0;
  33. }

测试步骤:

[cpp] view
plain
?
  1. 9th_led_test        first_drv.ko        sddisk
  2. Qt                  first_test          second_drv.ko
  3. TQLedtest           fourth_drv.ko       second_test
  4. app_test            fourth_test         sixth_drv.ko
  5. bin                 home                sixth_test
  6. busybox             led_dev.ko          sixthdrvtest
  7. buttons_all_drv.ko  led_drv.ko          sys
  8. buttons_all_test    lib                 third_drv.ko
  9. buttons_input.ko    linuxrc             third_test
  10. dev                 mnt                 tmp
  11. driver_test         opt                 udisk
  12. etc                 proc                usr
  13. fifth_drv.ko        root                var
  14. fifth_test          sbin                web
  15. [WJ2440]# insmod led_drv.ko
  16. [WJ2440]# insmod led_dev.ko
  17. led_probe, found led
  18. [WJ2440]# rmmod led_dev
  19. led_remove, remove led
  20. rmmod: module 'led_dev' not found
  21. [WJ2440]# lsmod
  22. led_drv 2800 0 - Live 0xbf003000
  23. [WJ2440]# insmod led_dev.ko
  24. led_probe, found led
  25. [WJ2440]# lsmod
  26. led_dev 1444 0 - Live 0xbf009000
  27. led_drv 2800 0 - Live 0xbf003000
  28. [WJ2440]# ls /dev/led -l
  29. crw-rw----    1 root     root      252,   0 Jan  2 07:44 /dev/led
  30. [WJ2440]# ./9th_led_test
  31. Usage :
  32. ./9th_led_test <on|off>
  33. [WJ2440]# ./9th_led_test off
  34. [WJ2440]# ./9th_led_test on

当执行./9th_led_test off时,led1被熄灭;当执行./9th_led_test on时 led1被点亮。如果你需要点亮led2,那么只需要修改led_dev的led_resources改为:

[cpp] view
plain
?
  1. static struct resource led_resources[] = {
  2. [0] = {
  3. .start  = 0x56000010,      /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */
  4. .end    = 0x56000010 + 8 -1,
  5. .flags  = IORESOURCE_MEM,
  6. },
  7. [1] = {
  8. .start  = 6,        /* LED2 */
  9. .end    = 6,
  10. .flags  = IORESOURCE_IRQ,
  11. },
  12. };

这样,应用程序不用更改,即可点亮led2,这样一来就实现了,稳定部分不用修改,只需要修改硬件易变部分,并且应用程序不需要任何更改。