总线——设备——驱动模型实际应用(点灯)

不念不忘少年蓝@ 2023-07-07 15:56 28阅读 0赞

在之前的输入子系统中,我们了解到分层的概念,可以将原来的一个.c文件分为软件层、硬件层和核心层。而输入子系统帮我们写好了软件层和核心层,我们只需要写硬件层即可。那如果我们不使用系统提供的输入子系统呢?那我们就需要自己编写软件层、硬件层和核心层,也就是我们所说的总线——设备——驱动模型。
总线是虚拟的概念,驱动部分就是较为稳定的代码,不会轻易改变,设备就是硬件相关的代码。
总线、驱动和设备都有专用的结构体。
驱动的结构体: struct platform_driver
设备的结构体: struct platform_device
而总线的结构体在平台驱动的device_driver结构体的bus_type中,如下图所示:
平台驱动结构体
包含总线的结构体
平台驱动注册函数
当我们构造好平台驱动结构体,并调用平台驱动注册函数后,驱动的名字就会放入虚拟总线中,也就是bus的结构体中,等待设备和其匹配,当有设备匹配成功,那么就会调用平台驱动中的probe函数,进行一系列操作。

driver.c文件内容

  1. #include <linux/module.h>
  2. #include <linux/version.h>
  3. #include <linux/init.h>
  4. #include <linux/fs.h>
  5. #include <linux/interrupt.h>
  6. #include <linux/irq.h>
  7. #include <linux/sched.h>
  8. #include <linux/pm.h>
  9. #include <linux/sysctl.h>
  10. #include <linux/proc_fs.h>
  11. #include <linux/delay.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/input.h>
  14. #include <linux/irq.h>
  15. #include <asm/uaccess.h>
  16. #include <asm/io.h>
  17. static struct class *led_class;
  18. static volatile unsigned long *gpio_con;
  19. static volatile unsigned long *gpio_dat;
  20. static int pin1;
  21. static int pin2 ;
  22. static int pin3;
  23. static int led_open(struct inode *inode, struct file *file)
  24. {
  25. *gpio_con &= ~((3 << (2*pin1)) | (3 << (2*pin2)) | (3 << (2*pin3)));
  26. *gpio_con |= ((1 << (2*pin1)) | (1 << (2*pin2)) | (1 << (2*pin3)));
  27. return 0;
  28. }
  29. static void delay(int time)
  30. {
  31. volatile int i = time;
  32. while(i--);
  33. }
  34. static void led_light(void) //流水灯函数
  35. {
  36. int val = 0;
  37. int tmp;
  38. int time = 100;
  39. *gpio_dat &= ~((1 << pin1) | (1 << pin2) | (1 << pin3));
  40. delay(10000000);
  41. *gpio_dat |= ((1 << pin1) | (1 << pin2) | (1 << pin3));
  42. delay(10000000);
  43. while (time--)
  44. {
  45. tmp = ~val;
  46. tmp &= 7;
  47. *gpio_dat &= ~(7<<4);
  48. *gpio_dat |= (tmp<<4);
  49. delay(10000000);
  50. val++;
  51. if (val == 8)
  52. val =0;
  53. }
  54. }
  55. static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  56. {
  57. int val;
  58. copy_from_user(&val, buf, count);
  59. if (val == 1) {
  60. *gpio_dat &= ~(1 << pin1);
  61. printk("led is light\n");
  62. }
  63. else if (val == 2){
  64. printk("water lamp\n");
  65. led_light();
  66. }
  67. else {
  68. *gpio_dat |= ((1 << pin1) | (1 << pin2) | (1 << pin3));
  69. printk("led is unlight");
  70. }
  71. return 0;
  72. }
  73. static struct file_operations led_ops = {
  74. //注册file_operations结构体,便于上层应用进行调用
  75. .owner = THIS_MODULE,
  76. .open = led_open,
  77. .write = led_write,
  78. };
  79. int major;
  80. static int led_probe(struct platform_device *pdev) //构造probe函数,当驱动和设备匹配后就会调用probe函数
  81. {
  82. printk("led device is connected\n");
  83. struct resource *res;
  84. res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取设备资源
  85. gpio_con = ioremap(res->start, res->end - res->start + 1); //进行io映射
  86. gpio_dat = gpio_con + 1;
  87. res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  88. pin1 = res->start;
  89. pin2 = pin1 + 1;
  90. pin3 = pin1 + 2;
  91. major = register_chrdev(0, "myled", &led_ops);
  92. led_class = class_create(THIS_MODULE, "myled"); //创建类和设备
  93. class_device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); //在linux系统上就会显示为 /dev/led文件
  94. return 0;
  95. }
  96. static int led_remove(struct platform_device *pdev)
  97. {
  98. printk("led is remove\n");
  99. class_device_destroy(led_class, MKDEV(major, 0));
  100. class_destroy(led_class);
  101. unregister_chrdev(major, "myled");
  102. iounmap(gpio_con);
  103. return 0;
  104. }
  105. static struct platform_driver led_drv = {
  106. .probe = led_probe,
  107. .remove = led_remove,
  108. .driver = {
  109. .name = "myled",
  110. },
  111. };
  112. static void led_dev_init(void)
  113. {
  114. platform_driver_register(&led_drv); //注册平台驱动
  115. }
  116. static void led_dev_exit(void)
  117. {
  118. platform_driver_unregister(&led_drv);
  119. }
  120. module_init(led_dev_init);
  121. module_exit(led_dev_exit);
  122. MODULE_LICENSE("GPL");

device.c内容,硬件方面的,便于改动

  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_resource[] = {
  13. [0] = {
  14. .start = 0x56000050,
  15. .end = 0x56000050 + 8 - 1,
  16. .flags = IORESOURCE_MEM,
  17. },
  18. [1] = {
  19. .start = 4,
  20. .end = 4,
  21. .flags = IORESOURCE_IRQ,
  22. },
  23. };
  24. static void led_release(void)
  25. {
  26. }
  27. static struct platform_device led_dev = {
  28. .name = "myled",
  29. .id = -1,
  30. .num_resources = ARRAY_SIZE(led_resource),
  31. .resource = led_resource,
  32. .dev = {
  33. .release = led_release,
  34. },
  35. };
  36. static void led_dev_init(void)
  37. {
  38. platform_device_register(&led_dev);
  39. }
  40. static void led_dev_exit(void)
  41. {
  42. platform_device_unregister(&led_dev);
  43. }
  44. module_init(led_dev_init);
  45. module_exit(led_dev_exit);
  46. MODULE_LICENSE("GPL");

应用层内容 test.c

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. int main(int argc, char **argv)
  6. {
  7. int fd;
  8. int val = 0;
  9. if ((fd=open("/dev/led", O_RDWR)) < 0) {
  10. //打开设备,实际上调用的是驱动中的led_open函数
  11. printf("can't open /dev/led\n");
  12. }
  13. if (strcmp("on", argv[1]) == 0) {
  14. //根据输入的不同内容变换点灯方式
  15. val = 1;
  16. }
  17. else if (strcmp("water", argv[1]) == 0) {
  18. val = 2;
  19. }
  20. else {
  21. val = 3;
  22. }
  23. printf("val = %d\n", val);
  24. write(fd, &val, 4);
  25. return 0;
  26. }

发表评论

表情:
评论列表 (有 0 条评论,28人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Linux字符设备驱动模型(一)

    从事Linux开发也有几年时间了,期间也写了一些比较简单的驱动,但一直没有做系统的整理,今天终于想静下心来做一些整理,首先就从最基本的字符设备驱动开始。先贴出一个简单的字符设备