ToB企服应用市场:ToB评测及商务社交产业平台

标题: Structure of Linux Kernel Device Driver(Part II) [打印本页]

作者: 丝    时间: 2024-7-16 21:01
标题: Structure of Linux Kernel Device Driver(Part II)
Structure of Linux Kernel Device Driver

ref. https://www.youtube.com/watch?v=XoYkHUnmpQo&list=LL&index=1&t=272s
Talk to the hardware

在操作系统中有几种机制能够让CPU与硬件设备举行通信:
在Memory-Mapped I/O(MMIO)机制中,将I/O设备映射到内存地址空间,然后CPU可以通过寄存器与硬件举行通信,以是当用户使用指针向某个地址写入数据,实际上是将数据写入了寄存器中。
使用mmio与硬件设备举行通信的三个步调:
当然也可以使用函数ioremap()返回的指针举行读写,不外推荐使用内核封装的函数对这个指针举行操作,注意使用iounmap()释放掉这些地址空间。完成地址映射后,可以通过cat \proc\iomem来查看I/O设备的地址映射。
下面这段代码用于控制一个LED灯设备的驱动:
  1. #define GPIO1_BASE        0x0209C000
  2. #define GPIO1_SIZE        8
  3. #define LED_OFF        0
  4. #define LED_ON        1
  5. static struct {
  6.         dev_t devnum;
  7.         struct cdev cdev;
  8.         void __iomem *regbase;
  9.     // device datas area
  10. } drvled_data;
  11. static void drvled_setled(unsigned int status)
  12. {
  13.         u32 val;
  14.         /* set value */
  15.         val = readl(drvled_data.regbase);
  16.         if (status == LED_ON)
  17.                 val |= GPIO_BIT;
  18.         else if (status == LED_OFF)
  19.                 val &= ~GPIO_BIT;
  20.         writel(val, drvled_data.regbase);
  21.         /* update status */
  22.         drvled_data.led_status = status;
  23. }
  24. static ssize_t my_write(struct file *file, const char __user *buf,
  25.                             size_t count, loff_t *ppos)
  26. {
  27.         char kbuf = 0;
  28.         if (copy_from_user(&kbuf, buf, 1))
  29.                 return -EFAULT;
  30.         if (kbuf == '1') {
  31.                 drvled_setled(LED_ON);
  32.                 pr_info("LED ON!\n");
  33.         } else if (kbuf == '0') {
  34.                 drvled_setled(LED_OFF);
  35.                 pr_info("LED OFF!\n");
  36.         }
  37.         return count;
  38. }
  39. static const struct file_operations drvled_fops = {
  40.         .owner = THIS_MODULE,
  41.         .write = my_write,
  42. };
  43. static int __init init_module(void)
  44. {
  45.    
  46.         if (!request_mem_region(GPIO1_BASE, GPIO1_SIZE, "my_device_driver")) {
  47.                 // handle error
  48.         }
  49.         drvled_data.regbase = ioremap(GPIO1_BASE, GPIO1_SIZE);
  50.         if (!drvled_data.regbase) {
  51.                 // handle error
  52.         }
  53.     // Device driver initialization and installation
  54.     return 0;
  55. }
  56. static int __exit exit_module(void)
  57. {
  58.     iounmap(drvled_data.regbase);
  59.         release_mem_region(GPIO1_BASE, GPIO1_SIZE);
  60.     // Unloading and unregistering the device driver
  61. }
复制代码
通过上面的这些API,可以通过读写对应了设备文件控制对应的硬件设备。下图是一个LED的驱动,其整体框架如下:

如许的框架存在一些问题:
因此,如许的框架还需要肯定程度的解耦合并举行模块化。
Driver Model

Linux驱动模型提供了多个设备驱动抽象(abstraction to device drivers),这能够使驱动代码更模块化、可重用而且轻易维护。
该驱动模型的组成如下:

使用驱动框架(linux/leds.h for led device)将接口尺度化后,意味着驱动开辟者不再需要定义file_operations来指定回调函数的行为,这些接口以及对应回调函数都由对应的框架完成定义。Users know beforehand the interface provided by a driver based on its class or type.
为了避免硬件资源被一个设备独占,需要使用特定的API进举措态控制,比如对于LED设备,Linux内核中实现了一种生产者/消费者的模型(gpiolib)来管理GPIO资源。
如下,使用了框架将用户接口尺度化,并使用内核接口管理硬件资源:
  1. #include <linux/leds.h>
  2. // other header files
  3. struct drvled_data_st {
  4.         struct gpio_desc *desc;         // change from "void __iomem *regbase;"
  5.         struct led_classdev led_cdev;   // change from "cdev"
  6. };
  7. static struct drvled_data_st *drvled_data;
  8. static void drvled_setled(unsigned int status)
  9. {
  10.     // not use the writel()
  11.         if (status == LED_ON)
  12.                 gpiod_set_value(drvled_data->desc, 1);
  13.         else
  14.                 gpiod_set_value(drvled_data->desc, 0);
  15. }
  16. static void drvled_change_state(struct led_classdev *led_cdev,
  17.                                 enum led_brightness brightness)
  18. {
  19.         if (brightness)
  20.                 drvled_setled(LED_ON);
  21.         else
  22.                 drvled_setled(LED_OFF);
  23. }
  24. static int __init drvled_init(void)
  25. {
  26.         int result = 0;
  27.     // no need for driver initialization
  28.     // no need for iommp for device
  29.         drvled_data = kzalloc(sizeof(*drvled_data), GFP_KERNEL);
  30.         if (!drvled_data) {
  31.                 // handle error
  32.         }
  33.         result = gpio_request(GPIO_NUM, DRIVER_NAME);
  34.         if (result) {
  35.                 // handle error
  36.         }
  37.         drvled_data->desc = gpio_to_desc(GPIO_NUM);
  38.         drvled_data->led_cdev.name = "ipe:red:user";
  39.         drvled_data->led_cdev.brightness_set = drvled_change_state;
  40.         result = led_classdev_register(NULL, &drvled_data->led_cdev);
  41.         if (result) {
  42.                 // handle error
  43.         }
  44.     // ...
  45. }
  46. static void __exit drvled_exit(void)
  47. {
  48.         led_classdev_unregister(&drvled_data->led_cdev);
  49.     // not use the iounmap()       
  50.     gpio_free(GPIO_NUM);
  51.         release_mem_region(GPIO1_BASE, GPIO1_SIZE);
  52.         kfree(drvled_data);
  53. }
  54. // ...
复制代码
使用设备框架对驱动举行重组后,用户不再直接与/dev/目录下的设备文件举行交互,而是在目录/sys/class/led目录下找到全部的LED类的设备,进入所注册的驱动目录下,可以找到控制LED设备的接口,这些接口仍然以文件的形式存在,但是和之前所定义的接口相比会更加尺度化,也就是险些全部的LED设备都可以使用如许的接口举行控制。
Bus infrastructure

末了,可以使用总线框架实现设备与驱动的解耦合。总线框架组成如下:
总线框架如下图所示:

解耦合的实现:在总线框架中,驱动相当于是一种类(class),当用户在总线上注册设备时,将会产生这个类的实例。以I2C总线设备为例,下图演示了这个过程:

这个过程可以大致分为三步:
有很多种方法向总线注册一个设备:

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4