嵌入式内核及驱动开发之学习笔记(二) 实现应用控制驱动

举报
王建峰 发表于 2021/11/19 02:55:43 2021/11/19
【摘要】 Linux系统根据驱动程序实现的模型框架将设备驱动分成字符设备驱动、块设备驱动、网络设备驱动三大类。这里简单理解一下概念 字符设备:设备按字节流处理数据,通常用的串口设备、键盘设备都是这种。块设备:设备按块单位对数据处理,通常是存储设备。网络设备:顾名思义,建立在socket接口上的设备。   字符设备驱动框架 &nbs...

Linux系统根据驱动程序实现的模型框架将设备驱动分成字符设备驱动、块设备驱动、网络设备驱动三大类。这里简单理解一下概念

  • 字符设备:设备按字节流处理数据,通常用的串口设备、键盘设备都是这种。
  • 块设备:设备按块单位对数据处理,通常是存储设备。
  • 网络设备:顾名思义,建立在socket接口上的设备。

 

字符设备驱动框架

    作为字符设备驱动要素:
        1,必须有一个设备号,用在众多到设备驱动中进行区分
        2,用户必须知道设备驱动对应到设备节点(设备文件)
        3,对设备操作其实就是对文件操作,应用空间操作open,read,write的时候,实际在驱动代码有对应到open, read,write

 

linux把所有到设备都看成文件,用户对字符设备进行操作实际上就是对驱动进行操作,实际上就是操作对应的设备文件!

而设备号是内核层区别设备的一个标识,我们通过驱动代码程序实现下面 应用程序到设备驱动的控制过程。

     应用层程序 -->  设备结点  -->  设备号  -->  设备驱动  -->  硬件 

 

实现一简易的框架。首先是驱动程序的实现

当用户装载完驱动模块,程序会执行 chr_drv_init 这个回调函数,向系统申请一个主设备号(表示程序为哪一类设备服务),创建设备结点(产生/dev/chr2,linux系统能识别这个设备文件);当用户卸载驱动模块后,程序执行 chr_drv_exit 这个回调函数,将之前申请的设备结点和设备号回收。

结构体类型 file_operations 定义了很多对应用层的接口,这些成员变量都是函数指针。我们将这些指针指向自己定义的函数入口。再去实现我们的函数功能。


  
  1. //chr_drv.c
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/fs.h>
  5. #include <linux/device.h>
  6. static unsigned int dev_major = 250;
  7. static struct class *devcls;
  8. static struct device *dev;
  9. ssize_t chr_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos);
  10. ssize_t chr_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos);
  11. int chr_drv_open(struct inode *inode, struct file *filp);
  12. int chr_drv_close(struct inode *inode, struct file *filp);
  13. const struct file_operations my_fops = {
  14. .open = chr_drv_open,
  15. .read = chr_drv_read,
  16. .write = chr_drv_write,
  17. .release = chr_drv_close,
  18. };
  19. static int __init chr_drv_init(void)
  20. {
  21. printk("-------%s-------------\n", __FUNCTION__);
  22. //向系统申请设备号
  23. int ret;
  24. ret = register_chrdev(dev_major, "chr_dev_test", &my_fops);
  25. if(ret == 0){
  26. printk("register ok\n");
  27. }else{
  28. printk("register failed\n");
  29. return -EINVAL;
  30. }
  31. //创建设备结点
  32. devcls = class_create(THIS_MODULE, "chr_cls");
  33. dev = device_create(devcls, NULL, MKDEV(dev_major, 0), NULL, "chr2");
  34. return 0;
  35. }
  36. static void __exit chr_drv_exit(void)
  37. {
  38. printk("-------%s-------------\n", __FUNCTION__);
  39. //销毁这个设备结点
  40. device_destroy(devcls, MKDEV(dev_major, 0));
  41. class_destroy(devcls);
  42. //释放这个设备号
  43. unregister_chrdev(dev_major, "chr_dev_test");
  44. }
  45. module_init(chr_drv_init);
  46. module_exit(chr_drv_exit);
  47. MODULE_LICENSE("GPL");
  48. // read(fd, buf, size);
  49. ssize_t chr_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
  50. {
  51. printk("-------%s-------\n", __FUNCTION__);
  52. return 0;
  53. }
  54. ssize_t chr_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  55. {
  56. printk("-------%s-------\n", __FUNCTION__);
  57. return 0;
  58. }
  59. int chr_drv_open(struct inode *inode, struct file *filp)
  60. {
  61. printk("-------%s-------\n", __FUNCTION__);
  62. return 0;
  63. }
  64. int chr_drv_close(struct inode *inode, struct file *filp)
  65. {
  66. printk("-------%s-------\n", __FUNCTION__);
  67. return 0;
  68. }

 

应用程序用来测试驱动程序的这些接口函数,实现对驱动的控制。

这里open函数定位到驱动程序的chr_drv_open函数,read函数定位到驱动程序的chr_drv_read函数(参考驱动程序中my_fops结构体的初始化)。就像文件IO函数那样使用,只不过我们驱动程序里对应的函数没有具体的实现,能看到一些打印信息。


  
  1. //chr_test.c
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <unistd.h>
  9. int main(int argc, char *argv[])
  10. {
  11. int fd;
  12. int value = 0;
  13. //打开设备结点
  14. fd = open("/dev/chr2", O_RDWR);
  15. if(fd < 0)
  16. {
  17. perror("open");
  18. exit(1);
  19. }
  20. //读操作
  21. read(fd, &value, 4);
  22. while(1)
  23. {
  24. //写操作
  25. value = 0;
  26. write(fd, &value, 4);
  27. sleep(1);
  28. //写操作
  29. value = 1;
  30. write(fd, &value, 4);
  31. sleep(1);
  32. }
  33. close(fd);
  34. return 0;
  35. }

 

 

Makefile文件负责整个工程的编译与管理

相比于之前,要多编译了chr_test.c这个文件,APP_NAME 指定目标文件名,CROSS_COMPILE 指定交叉编译工具链。下面还要修改添加一下编译规则


  
  1. ROOTFS_DIR = /nfs/rootfs
  2. APP_NAME = chr_test
  3. CROSS_COMPILE = /home/linux/soft/gcc-4.6.4/bin/arm-none-linux-gnueabi-
  4. CC = $(CROSS_COMPILE)gcc
  5. ifeq ($(KERNELRELEASE), )
  6. KERNEL_DIR = /mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412
  7. CUR_DIR = $(shell pwd)
  8. all :
  9. make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
  10. $(CC) $(APP_NAME).c -o $(APP_NAME)
  11. clean :
  12. make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
  13. install:
  14. cp -raf *.ko $(APP_NAME) $(ROOTFS_DIR)/drv_module
  15. else
  16. obj-m += chr_drv.o
  17. endif

 

查看实验结果

编译并移动文件到nfs根目录


  
  1. root@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make
  2. root@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make install

 

开发板加载模块,执行应用程序

 

[root@farsight drv_module]# ls
chr_drv.ko   chr_test     chr_test.ko  hello.ko     math.ko
[root@farsight drv_module]# insmod chr_drv.ko
[ 3036.170000] -------chr_drv_init-------------
[ 3036.175000] register ok
[root@farsight drv_module]# ./chr_test
[ 3041.255000] -------chr_drv_open-------
[ 3041.255000] -------chr_drv_read-------
[ 3041.260000] -------chr_drv_write-------
[ 3042.265000] -------chr_drv_write-------
[ 3043.265000] -------chr_drv_write-------
 

 

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/84035899

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。