硬件软件系统方案应用范例智能车大赛技术支持学习园地关于我们恩智浦官网

在云实验室开发板上实验mutex

1. 实验环境

   在实验本节代码前,准备环境一个ubuntu物理机或者虚拟机,下载并使能交叉编译工具链,然后编译好内核源码,参考《编译内核镜像并在云实验室开发板运行》。

2. 使用mutex保护的字符驱动示例

   在你要写代码的目录下用touch新建两个文件,一个源码文件,一个Makefile。如下:

~/linux_driver/04_mutex$ touch 04_virchardev_mutex.c
~/linux_driver/04_mutex$ touch Makefile
~/linux_driver/04_mutex$ vim 04_virchardev_mutex.c 

   将下面的代码复制到04_virchardev_mutex.c, 如下:

/*
* Copyright 2025 NXP 
* SPDX-License-Identifier: GPL-2.0+
*/
  1 #include <linux/module.h>
  2 #include <linux/types.h>
  3 #include <linux/init.h>
  4 #include <linux/cdev.h>
  5
  6 MODULE_LICENSE("GPL");
  7 MODULE_AUTHOR("Cloud Lab");
  8
  9 #define BUF_LEN      0x1000
 10 #define VIRTCHARDEV_NAME "virtdev"
 11
 12 struct virtchardev
 13 {
 14         dev_t virtdev_id;
 15         struct cdev virtdev_cdev;
 16         struct class *class;
 17         struct device *device;
 18         int virtdev_major;
 19         int virtdev_minor;
 20         struct mutex lock;
 21
 22         struct cdev cdev;
 23         unsigned char mem[BUF_LEN];
 24
 25 };
 26
 27 struct virtchardev *virtdev;
 28
 29
 30 int virtdev_open(struct inode *inode, struct file *filp)
 31 {
 32         filp->private_data = virtdev;
 33
 34         return 0;
 35 }
 36
 37 int virtdev_release(struct inode *inode, struct file *filp)
 38 {
 39         return 0;
 40 }
 41
 42 static ssize_t virtdev_read(struct file *filp, char __user *buf,
 43                         size_t size, loff_t *ppos)
 44 {
 45         unsigned long p =  *ppos;
 46         unsigned int count = size;
 47         int ret = 0;
 48         struct virtchardev *dev = filp->private_data;
 49
 50         if (p >= BUF_LEN)
 51         {
 52                 printk("*ppos = %d\n", p);
 53                 return count ? -ENXIO : 0;
 54         }
 55
 56         if (count > BUF_LEN - p)
 57         {
 58                 count = BUF_LEN - p;
 59         }
 60         mutex_lock(&dev->lock);
 61         if (copy_to_user(buf, (void*)(dev->mem + p), count))
 62         {
 63                 ret =  - EFAULT;
 64         }
 65         else
 66         {
 67                 *ppos += count;
 68                 ret = count;
 69
 70                 printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);
 71         }
 72         mutex_unlock(&dev->lock);
 73         return ret;
 74 }
 75
 76 static ssize_t virtdev_write(struct file *filp, const char __user   *buf,
 77                         size_t size, loff_t *ppos)
 78 {
 79         unsigned long p =  *ppos;
 80         unsigned int count = size;
 81         int ret = 0;
 82         void *kbuf = NULL;
 83         struct virtchardev *dev = filp->private_data;
 84
 85         kbuf = kvmalloc(size, GFP_KERNEL);
 86         memset(kbuf, 0, count);
 87         if (copy_from_user(kbuf, buf, count) != 0){
 88                 ret =  - EFAULT;
 89         }
 90         mutex_lock(&dev->lock);
 91         strscpy(dev->mem, kbuf, (count > BUF_LEN ? BUF_LEN : count));
 92         ret = count;
 93         printk("write bytes: %zu\n", count);
 94         mutex_unlock(&dev->lock);
 95         return ret;
 96 }
 97
 98 static const struct file_operations virtdev_fops =
 99 {
100         .owner = THIS_MODULE,
101         .read = virtdev_read,
102         .write = virtdev_write,
103         .open = virtdev_open,
104         .release = virtdev_release,
105 };
106
107
108 int virtdev_init(void)
109 {
110         virtdev = kmalloc(sizeof(struct virtchardev), GFP_KERNEL);
111         memset(virtdev, 0, sizeof(struct virtchardev));
112
113         mutex_init(&virtdev->lock);
114         alloc_chrdev_region(&virtdev->virtdev_id, 0, 1,
115                                 VIRTCHARDEV_NAME );
116         virtdev->virtdev_major = MAJOR(virtdev->virtdev_id);
117
118         cdev_init(&virtdev->virtdev_cdev, &virtdev_fops);
119
120         virtdev->virtdev_cdev.owner = THIS_MODULE;
121         virtdev->virtdev_cdev.ops = &virtdev_fops;
122
123         cdev_add(&virtdev->virtdev_cdev, virtdev->virtdev_id, 1);
124
125         virtdev->class = class_create(THIS_MODULE, VIRTCHARDEV_NAME);
126
127         device_create(virtdev->class, NULL, virtdev->virtdev_id,
128                         NULL, VIRTCHARDEV_NAME);
129
130
131         return 0;
132 }
133
134 void virtdev_exit(void)
135 {
136         cdev_del(&virtdev->virtdev_cdev);
137         unregister_chrdev_region(virtdev->virtdev_id, 1);
138
139         device_destroy(virtdev->class, virtdev->virtdev_id);
140         class_destroy(virtdev->class);
141
142         kfree(virtdev);
143
144 }
145
146 module_init(virtdev_init);
147 module_exit(virtdev_exit);

3. 编译

~/linux_driver/04_mutex$ export CROSS_COMPILE=~/toolchain/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
~/linux_driver/04_mutex$ export ARCH=arm64
~/linux_driver/04_mutex$ make

   编译完成后当前目录会有如下文件:

04_virchardev_mutex.c   04_virchardev_mutex.mod    04_virchardev_mutex.mod.o  Makefile       Module.symvers 04_virchardev_mutex.ko 04_virchardev_mutex.mod.c  04_virchardev_mutex.o      modules.order

4. 替换云实验室开发板镜像并重新启动

  根据《编译内核镜像并在云实验室开发板运行》替换dtb和image,这里仍然需要这一步,因为内核模块在加载时需要和内核编译版本相匹配。

 上传并替换云实验室板子的dtb和Image,依次点击用户网页端下面1,2,3的位置,在点击位置2时,选择TFTP,点击3后选择文件~/kernel_build/linux-imx/arch/arm64/boot/dts/freescale/imx8mp-evk.dtb和~/kernel_build/linux-imx/arch/arm64/boot/Image。

   

   上传成功后,点击下方PowerReset EVK按钮,重启开发板。

   

   至此,开发板开始运行自己修改编译的linux镜像。

5. 上传自己编译的字符驱动模块

   在3中编译出了关于mutex应用的内核驱动程序04_virchardev_mutex.ko,通过一次点击下图中的1,2,3位置,然后切换到本地对应的文件目录上传。

   

6. 运行字符驱动模块

   执行insmod加载模块,然后cat读取/dev/virtdev是空的,写入welcome(echo welcome > /dev/virtdev)后,再次读取,会显示刚刚写入的内容,log如下: