Linux底层驱动的简单认知
发布日期:2021-05-08 12:50:09 浏览次数:27 分类:精选文章

本文共 2597 字,大约阅读时间需要 8 分钟。

底层驱动:从概念到实践

底层驱动是让设备工作的基本程序,它为用户提供了操作设备的接口。以树莓派为例,想要使用其40Pin中的GPIO口,就需要依赖底层驱动来实现对GPIO寄存器的操作。没有驱动,设备无法正常工作。

为什么要写驱动?

当系统没有提供相应设备的操作库或者没有驱动时,就需要自己编写驱动。例如,树莓派提供的wiringPi库让开发者方便地操作GPIO,但如果换到其他Linux板子(如2440、RK3399、nanoPi等),没有像wiringPi这样的库时,就必须手动编写驱动,否则无法操作底层设备。

如何编写驱动?

在编写驱动之前,需要了解Linux是如何调用驱动的,以及用户如何使用驱动。在Linux系统中,所有设备都以文件形式存在,驱动也不例外。

驱动文件的存放位置

驱动文件通常存放在/dev目录下。编写好的驱动文件在安装时应放置在/dev目录中。当使用驱动时,也应该使用/dev目录下的驱动文件。

驱动的使用及区分方式

一个写好的驱动安装后,可以使用C库中的openwriteread函数来操作驱动,因为驱动本身也是文件。这三个文件操作函数同样可以用于驱动:

  • open("/dev/xxx", O_RDWR) 生成文件描述符fd
  • write(fd, "xxx", size_t);向驱动发送指令;
  • read(fd, char *, size_t);读取驱动返回的数据。
  • 操作方式有了,但如何在/dev目录下找到相应的驱动,系统又如何在众多驱动中找到我们写的驱动?有两种方式:

  • 通过文件名:直接打开/dev目录下的某个驱动文件;
  • 通过设备号:设备号又有两种:
    • 主设备号:用来区分不同的设备,如GPIO、IIC、UART等;
    • 次设备号:用来区分相同设备中的多个设备,如GPIO.0、GPIO.1等。
  • 设备号的查看可用命令:cd /dev && ls -l

    驱动链表 file_operations

    驱动链表的结构体file_operations包含大量函数,其中包括openwriteread等函数。在编写驱动时,需要根据需求为结构体的某个函数编写对应的函数,例如openwriteread

    int pin4_open(struct inode *inode, struct file *file) {    // 实现的操作    return 0;}ssize_t pin4_write(struct file *file, const char __user *buf, size_t count, loff_t *loffs) {    // 实现的操作    return 0;}ssize_t pin4_read(struct file *file, char __user *buf, size_t count, loff_t *loff) {    // 实现的操作    return 0;}struct file_operations pin_pos = {    .owner = THIS_MODULE,    .open = pin4_open,    .write = pin4_write,    .read = pin4_read,};

    完成这些后,只是将驱动链表中的结构体搭建好了,还需要将其插入到链表中。

    驱动注册及自动创建驱动文件

    建立好驱动的结构体后,需要将其插入到驱动链表中。注册驱动的函数为register_chrdev

    int register_chrdev(int major, char *module_name, struct file_operations *file_operation);

    参数说明:

  • major:驱动的主设备号;
  • module_name:驱动在链表中的名字;
  • file_operation:驱动的操作结构体指针。
  • 自动创建驱动文件需要几个函数的配合,包括MKDEV宏和device_create函数:

    #define dev_t MKDEV(int major, int minor)struct class *class_create(struct module *owner, const char *name);struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);

    设备创建函数的使用示例:

    int __init pin4_dev_init(void) {    int ret;    devno = MKDEV(major, minor);    ret = register_chrdev(major, module_name, &pin_pos);    pin4_class = class_create(THIS_MODULE, "myfirstdemo");    pin4_dev = device_create(pin4_class, NULL, devno, NULL, module_name);    return 0;}module_init(pin4_dev_init);

    卸载驱动函数的示例:

    void __exit pin4_exit(void) {    device_destroy(pin4_class, devno);    class_destroy(pin4_class);    unregister_chrdev(major, module_name);}module_exit(pin4_exit);

    最后,别忘了添加许可:

    MODULE_LICENSE("GPL v2");

    总结

    编写驱动的基本框架包括:

  • 构建file_operations结构体;
  • 编写初始化函数,包括:
    • 生成设备号MKDEV
    • 注册驱动register_chrdev
    • 创建设备类class_create
    • 创建设备device_create
  • 编写卸载驱动函数;
  • 添加许可证MODULE_LICENSE
  • 通过以上步骤,可以实现对底层设备的有效控制和管理。

    上一篇:树莓派4B Linux的底层驱动编写体验
    下一篇:Linux内核结构粗解及关于 shell 通俗理解

    发表评论

    最新留言

    路过按个爪印,很不错,赞一个!
    [***.219.124.196]2025年03月27日 15时50分52秒