回答

收藏

[评测分享] 【米尔 MYD-YM62X 开发板入门评测】 7.字符设备驱动框架验证

#板卡评测 #板卡评测 1388 人阅读 | 0 人回复 | 2023-11-23

本帖最后由 andeyqi 于 2023-11-23 22:29 编辑

简介:
linux 的驱动程序一般是在内核态,用户在用户态通过虚拟文件系统VFS创建的文件节点的,open/read/write 等方法访问驱动程序,字符设备驱动程序是最基本的驱动模型,用户态通过系统调用访问内核态的驱动程序,那开始我们的主题字符驱动程序的学习。

1.内核模块代码编写

用户态调用内核态的驱动程序流程如下:

我们编写内核态测试代码通过模块加载的方式进行加载,open 函数只是打印log 用于确认代码已经被调用到,代码如下:

  1. #include <linux/module.h>
  2. #include <linux/fs.h>
  3. #include <linux/device.h>
  4. #include <linux/cdev.h>
  5. #include <linux/types.h>
  6. #include <linux/kdev_t.h>
  7. #include <linux/uaccess.h>

  8. #define DEV_MEM_SIZE 1024
  9. char device_buffer[DEV_MEM_SIZE];
  10. static int pseudo_chr_dev_open(struct inode *inode, struct file *filp)
  11. {
  12.     pr_info("open was successful\n");
  13.     return 0;
  14. }
复制代码


wirte 函数将用户态态的数据copy 至缓冲区域device_buffer保存

  1. static ssize_t pseudo_chr_dev_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos)
  2. {
  3.     pr_info("write requested for %zu bytes\n", count);

  4.     if((*f_pos + count) > DEV_MEM_SIZE)
  5.     {
  6.         count = DEV_MEM_SIZE - *f_pos;
  7.     }

  8.     if(!count)
  9.     {
  10.         return -ENOMEM;
  11.     }

  12.     if(copy_from_user(&device_buffer[*f_pos], buff, count))
  13.     {
  14.         return -EFAULT;
  15.     }

  16.     pr_info("Number of bytes successfully written = %zu\n", count);
  17.     return count;
  18. }
复制代码


read 函数将缓冲区device_buffer的数据返回至用户空间

  1. static ssize_t pseudo_chr_dev_read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos)
  2. {
  3.     pr_info("read requested for %zu bytes \n", count);
  4.     if((*f_pos + count) > DEV_MEM_SIZE)
  5.     {
  6.         count = DEV_MEM_SIZE - *f_pos;
  7.     }
  8.     if(copy_to_user(buff, &device_buffer[*f_pos], count))
  9.     {
  10.         return -EFAULT;
  11.     }
  12.     pr_info("Number of bytes successfully read = %zu\n", count);
  13.     return count;
  14. }
复制代码



模块加载及卸载代码

  1. static int pseudo_chr_dev_release(struct inode *inode, struct file *filp)
  2. {
  3.     pr_info("close was successful\n");
  4.     return 0;
  5. }


  6. struct file_operations device_fops={
  7.     .owner = THIS_MODULE,
  8.     .open = pseudo_chr_dev_open,
  9.     .release = pseudo_chr_dev_release,
  10.     .read = pseudo_chr_dev_read,
  11.     .write = pseudo_chr_dev_write,
  12. };

  13. dev_t device_number;

  14. struct cdev chr_dev;
  15. struct class *pseudo_class;
  16. struct device *pseudo_char_device;

  17. static int __init pseudo_chrdev_init(void)
  18. {
  19.     int ret = 0;
  20.     ret = alloc_chrdev_region(&device_number, 0, 1, "pseudo_chr_dev");
  21.     if(ret < 0)
  22.     {
  23.         goto fail_dev_num;
  24.     }
  25.     pr_info("Device number <major>:<minor> = %d:%d", MAJOR(device_number),MINOR(device_number));

  26.     cdev_init(&chr_dev, &device_fops);
  27.     chr_dev.owner = THIS_MODULE;
  28.     cdev_add(&chr_dev, device_number, 1);
  29.     pseudo_class = class_create(THIS_MODULE, "pseudo_class");
  30.     pseudo_char_device = device_create(pseudo_class, NULL, device_number, NULL,"pseudo_chrdev");
  31.     pr_info("Module init was successful\n");
  32.     return 0;
  33. fail_dev_num:
  34.     return ret;
  35. }

  36. static void __exit pseudo_chrdev_exit(void)
  37. {
  38.     device_destroy(pseudo_class, device_number);
  39.     class_destroy(pseudo_class);
  40.     cdev_del(&chr_dev);
  41.     unregister_chrdev_region(device_number, 1);
  42.     pr_info("module unloaded\n");
  43. }
  44. module_init(pseudo_chrdev_init);
  45. module_exit(pseudo_chrdev_exit);

  46. MODULE_LICENSE("GPL");
  47. MODULE_AUTHOR("Andyqi");
  48. MODULE_DESCRIPTION("This is pseudo driver module of character device");
复制代码


内核态测试代码已经ok,我们编写makefile,KERN_DIR 指定内核代码路径,将驱动程序编译为模块。
  1. KERN_DIR = /home/rlk/ym625x/myd-ym62x-bsp/myir-ti-linux

  2. all:
  3.         make -C $(KERN_DIR) M=`pwd` modules

  4. clean:
  5.         make -C $(KERN_DIR) M=`pwd` modules clean
  6.         rm -rf modules.order

  7. obj-m        += char_drv.o
复制代码

执行make 命令编译,编译通过生成char_drv.ko 文件




2.命令行读取验证

我们已经编译好了ko 文件,模块加载函数内部包含了在文件系统中创建class 并在class 创建设备我们可以通过文件节点来访问驱动程序,对应的节点名称规则如下:



将编译好的ko 文件上传到开发板通过insmode 命令加载至内核,通过log可知模块加载成功分配了239:0的设备号



通过cat /sys/class/pseudo_class/pseudo_chrdev/dev 也可以读取到device 对应的设备号为239:0



先尝试使?echo命令写入数据到字符设备/dev/pseudo_chrdev   echo "Hello world!" > /dev/pseudo_chrdev,以下log 可以看出我们的write 函数及open函数已经被调用,从上?的命令可以看到整个文件操作的过程,从open到close。


我们尝试通过cat 读取文件 cat /dev/pseudo_chrdev ,我们可以看到read 函数已经被调用并且读到了我们写入的"Hello world!"




3.App程序验证

通过echo,cat 已经验证了文件的基本操作,我们编写如下测试代码在app 空间访问字符驱动程序
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <unistd.h>
  5. #include <errno.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #define MAX 1048
  9. char buffer[MAX];
  10. int main(int argc, char **argv)
  11. {
  12.     int fd = 0;
  13.     int ret = 0;
  14.     int count = 0;
  15.     /* 两个参数 */
  16.     if(argc != 2)
  17.     {
  18.         printf("Wrong usage, Please try the way: <file> <number toread>\n");
  19.         goto exit;
  20.     }
  21.     /* 字符转换整数, main函数的参数 */
  22.     count = atoi(argv[1]);
  23.     /* 打开字符设备文件 */
  24.     fd = open("/dev/pseudo_chrdev", O_RDWR);
  25.     if(fd < 0)
  26.     {
  27.         perror("open fail");
  28.         goto exit;
  29.     }
  30.     printf("Open operation was successful\n");
  31.     /* 从字符设备缓冲读取数据 */
  32.     ret = read(fd, &buffer[MAX], count);
  33.     if(!ret)
  34.     {
  35.         printf("read failure or end of file\n");
  36.         goto exit;
  37.     }
  38.     else
  39.     {
  40.         printf("read %d bytes data from pseudo character device\n", ret);
  41.     }
  42.     /* 从用户空间写数据到字符设备 */
  43.     ret = write(fd, &buffer[MAX], count);
  44.     if(!ret)
  45.     {
  46.         printf("write failure or character device full\n");
  47.         goto exit;
  48.     }
  49.     else
  50.     {
  51.         printf("write %d bytes date to pseudo character device\n", ret);
  52.     }
  53.     return 0;
  54. exit:
  55.     close(fd);
  56.     return 0;
  57. }
复制代码
将测试程序编译通过后,传送至开发板运行结果如下,可以看出内核态的open read write 也已经被调用到了


分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

3854 积分
6 主题
+ 关注
热门推荐
关闭

站长推荐上一条 /3 下一条