文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Linux驱动实践:一步一步编写字符设备驱动程序

2024-12-02 16:59

关注

别人的经验,我们的阶梯!

大家好,我是道哥,今天我们继续讨论: Linux 中字符设备的驱动程序。

在上一篇文章中Linux驱动实践:你知道【字符设备驱动程序】的两种写法吗?我们说过:字符设备的驱动程序,有两套不同的API函数,并且在文中详细演示了利用旧的API函数来编写驱动程序。

这篇文章,我们继续这个话题,实际演示一下:字符设备驱动程序的另一套API函数的使用方法。

API 函数

这里主要关注下面这 3 个函数:

  1. // 静态注册设备 
  2. int register_chrdev_region(dev_t from, unsigned count, const char *name); 
  3.  
  4. // 动态注册设备 
  5. int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name); 
  6.  
  7. // 卸载设备 
  8. void unregister_chrdev_region(dev_t from, unsigned count); 

关于静态和动态注册,主要的区别就在于:主设备号由谁来主导分配!

静态注册:由我们的驱动程序来指定主设备号,即参数1:from;

动态注册:由操作系统来分配,驱动程序提供一个变量来接收该设备号,即参数1: dev 指针;

另外,在Linux 2.6后期的内核版本中,引入了 cdev 结构来描述一个字符设备,它的结构体成员是:

  1. struct cdev { 
  2.     struct kobject kobj;    // 内嵌的kobject对象 
  3.     struct module *owner;   // 所属模块 
  4.     const struct file_operations *ops;//文件操作结构体 
  5.     struct list_head list;  // 链表句柄 
  6.     dev_t dev;              // 设备号 
  7.     unsigned int count
  8. }; 

与这个结构体相关的处理函数有:

编写驱动

按照惯例,我们仍然按照步骤,来讨论如何利用上述的APIs,来手写一个字符设备的驱动程序。

以下所有操作的工作目录,都是与上一篇文章相同的,即:~/tmp/linux-4.15/drivers/。

创建驱动目录和驱动程序

  1. $ cd linux-4.15/drivers/ 
  2. $ mkdir my_driver2 
  3. $ cd my_driver2 
  4. $ touch driver2.c 

driver2.c 文件的内容如下(不需要手敲,文末有代码下载链接):

  1. #include  
  2. #include  
  3. #include  
  4. #include  
  5. #include  
  6.  
  7. static struct cdev my_cdev; 
  8. static dev_t dev_no; 
  9.  
  10. int driver2_open(struct inode *inode, struct file *file) 
  11.     printk("driver2_open is called. \n"); 
  12.     return 0; 
  13.  
  14. ssize_t driver2_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 
  15.     printk("driver2_read is called. \n"); 
  16.     return 0; 
  17.  
  18. ssize_t driver2_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos) 
  19.     printk("driver2_write is called. \n"); 
  20.     return 0; 
  21.   
  22. static const struct file_operations driver2_ops={ 
  23.     .owner = THIS_MODULE, 
  24.     .open  = driver2_open, 
  25.     .read  = driver2_read, 
  26.     .write = driver2_write, 
  27. }; 
  28.   
  29. static int __init driver2_init(void) 
  30.     printk("driver2_init is called. \n"); 
  31.  
  32.     // 初始化cdev结构 
  33.     cdev_init(&my_cdev, &driver2_ops); 
  34.  
  35.     // 注册字符设备 
  36.     alloc_chrdev_region(&dev_no, 0, 2, "driver2"); 
  37.     cdev_add(&my_cdev, dev_no, 2); 
  38.  
  39.     return 0; 
  40.   
  41. static void __exit driver2_exit(void) 
  42.     printk("driver2_exit is called. \n"); 
  43.  
  44.     // 注销设备 
  45.     cdev_del(&my_cdev);  
  46.  
  47.     // 注销设备号 
  48.     unregister_chrdev_region(dev_no, 2); 
  49.   
  50. MODULE_LICENSE("GPL"); 
  51. module_init(driver2_init); 
  52. module_exit(driver2_exit); 

这里看一下加载驱动模块时调用的 driver2_init( ) 函数,其中的 cdev_init 用来把cdev结构体与 file_operations 发生关联。

在调用 alloc_chrdev_region( ) 时,操作系统分配了主设备号,并且保存在 dev_no 变量中,然后 cdev_add() 再把设备号与cdev结构体进行关联。

创建 Makefile 文件

  1. $ touch Makefile 

内容如下:

  1. ifneq ($(KERNELRELEASE),) 
  2.     obj-m := driver2.o 
  3. else 
  4.     KERNELDIR ?= /lib/modules/$(shell uname -r)/build 
  5.     PWD := $(shell pwd) 
  6. default
  7.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
  8. clean: 
  9.     $(MAKE) -C $(KERNEL_PATH) M=$(PWD) clean 
  10. endif 

编译驱动模块

  1. $ make 

得到驱动程序: driver2.ko 。

加载驱动模块

在加载驱动模块之前,先来检查一下系统中,几个与驱动设备相关的地方。

先看一下 /dev 目录下,目前还没有我们的设备节点( /dev/driver2 )。

  1. $ ll /dev/driver2 
  2. ls: cannot access '/dev/driver2'No such file or directory 

再来查看一下 /proc/devices 目录下,也没有 driver2 设备的设备号。

  1. $ cat /proc/devices 

 

/proc/devices 文件: 列出字符和块设备的主设备号,以及分配到这些设备号的设备名称。

为了方便查看打印信息,把dmesg输出信息清理一下:

  1. $ sudo dmesg -c 

执行如下指令,加载驱动模块:

  1. $ sudo insmod driver2.ko 

当驱动程序被加载的时候,通过 module_init( ) 注册的函数 driver2_init() 将会被执行,那么其中的打印信息就会输出。

还是通过 dmesg 指令来查看驱动模块的打印信息:

  1. $ dmesg 

此时,驱动模块已经被加载了!

来查看一下 /proc/devices 目录下显示的设备号:

  1. $ cat /proc/devices 

设备已经注册了,主设备号是: 244 。

但是,此时在/dev目录下,还没有我们需要的设备节点。

在上一篇文章中介绍过,还可以利用 Linux 用户态的 udev 服务来自动创建设备节点。

现在,我们手动创建设备节点:

  1. $ sudo mknod -m 660 /dev/driver2 c 244 0 

主设备号 244 是从 /proc/devices 查到的。

检查一下是否创建成功:

  1. $ ll /dev/driver2 

现在,设备的驱动程序已经加载了,设备节点也被创建好了,应用程序就可以来操作(读、写)这个设备了。

应用程序

应用程序仍然放在 ~/tmp/App/ 目录下。

  1. $ mkdir ~/tmp/App/app_driver2 
  2. $ cd ~/tmp/App/app_driver2 
  3. $ touch app_driver2.c 

文件内容如下:

  1. #include  
  2. #include  
  3. #include  
  4.  
  5.  
  6. int main(void) 
  7.     int ret; 
  8.     int read_data[4] = { 0 }; 
  9.     int write_data[4] = {1, 2, 3, 4}; 
  10.     int fd = open("/dev/driver2", O_RDWR); 
  11.     if (-1 != fd) 
  12.     { 
  13.         ret = read(fd, read_data, 4); 
  14.         printf("read ret = %d \n", ret); 
  15.  
  16.         ret = write(fd, write_data, 4); 
  17.         printf("write ret = %d \n", ret); 
  18.     } 
  19.     else 
  20.     { 
  21.         printf("open /dev/driver2 failed! \n"); 
  22.     } 
  23.  
  24.     return 0; 

接下来就是编译和测试了:

  1. $ gcc app_driver2.c -o app_driver2 
  2. $  
  3. $ sudo ./app_driver2  
  4. [sudo] password for xxx: <输入用户密码> 
  5. read ret = 0  
  6. write ret = 0 

从返回值来看,成功打开了设备,并且调用读函数、写函数都成功了!

继续用dmesg命令查看一下:

卸载驱动模块

卸载指令:

  1. $ sudo rmmod driver2 

此时,/proc/devices 下主设备号 244 的 driver2 已经不存在了。

再来看一下 dmesg的打印信息:

可以看到:驱动程序中的 driver2_exit( ) 被调用执行了!

小结

以上就是利用“新的” API 函数,来编写字符设备的驱动程序。

代码结构还是非常清晰的,这得益于Linux良好的驱动程序架构设计!这也是每一名架构师需要学习、努力模仿的地方。

本文转载自微信公众号「IOT物联网小镇」

 

来源:IOT物联网小镇内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯