查看: 577|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    3 天前
  • 签到天数: 128 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2023-11-23 20:32:45 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 andeyqi 于 2023-11-23 22:29 编辑

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

    1.内核模块代码编写

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

    我们编写内核态测试代码通过模块加载的方式进行加载,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 文件
    make.png




    2.命令行读取验证

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



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

    insmode.png


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

    cat_dev.png


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


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


    read.png read.png


    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 也已经被调用到了
    appread.png


    回复

    使用道具 举报

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

    本版积分规则

    关闭

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

    手机版|小黑屋|与非网

    GMT+8, 2024-5-20 19:01 , Processed in 0.104354 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.