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

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

1. 实验环境

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

2. 开始第一个内核字符驱动编写和编译运行

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

~/linux_driver/ 03_spinlock$ touch 03_spinlock_virtchardev.c
~/linux_driver/ 03_spinlock$ touch Makefile
~/linux_driver/ 03_spinlock$ vim 03_spinlock_virtchardev.c


   将下面的代码复制到03_spinlock_virtchardev.c, 如下:

/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/cdev.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Cloud Lab");

#define BUF_LEN      0x1000
#define VIRTCHARDEV_NAME "virtdev"

struct virtchardev
{
        dev_t virtdev_id;
        struct cdev virtdev_cdev;
        struct class *class;
        struct device *device;
        int virtdev_major;
        int virtdev_minor;
        spinlock_t lock;

        struct cdev cdev;
        unsigned char mem[BUF_LEN];

};

struct virtchardev *virtdev;


int virtdev_open(struct inode *inode, struct file *filp)
{
        filp->private_data = virtdev;

        return 0;
}

int virtdev_release(struct inode *inode, struct file *filp)
{
        return 0;
}

static ssize_t virtdev_read(struct file *filp, char __user *buf,
                        size_t size, loff_t *ppos)
{
        unsigned long p =  *ppos;
        unsigned int count = size;
        int ret = 0;
        struct virtchardev *dev = filp->private_data;

        if (p >= BUF_LEN)
        {
        printk("*ppos = %d\n", p);
        return count ? -ENXIO : 0;
        }

        if (count > BUF_LEN - p)
        {
        count = BUF_LEN - p;
        }

        if (copy_to_user(buf, (void*)(dev->mem + p), count))
        {
        ret =  - EFAULT;
        }
        else
        {
        *ppos += count;
        ret = count;

        printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);
        }

        return ret;
}

static ssize_t virtdev_write(struct file *filp, const char __user   *buf,
                        size_t size, loff_t *ppos)
{
        unsigned long p =  *ppos;
        unsigned int count = size;
        int ret = 0;
        void *kbuf = NULL;
        struct virtchardev *dev = filp->private_data;

        kbuf = kvmalloc(size, GFP_KERNEL);
        memset(kbuf, 0, count);
        if (copy_from_user(kbuf, buf, count) != 0)
        {
                ret =  - EFAULT;
        }
        spin_lock(&dev->lock);
        strscpy(dev->mem, kbuf, (count > BUF_LEN ? BUF_LEN : count));
        ret = count;
        printk("write bytes: %zu\n", count);
        spin_unlock(&dev->lock);
        return ret;
}

static const struct file_operations virtdev_fops =
{
        .owner = THIS_MODULE,
        .read = virtdev_read,
        .write = virtdev_write,
        .open = virtdev_open,
        .release = virtdev_release,
};


int virtdev_init(void)
{
        virtdev = kmalloc(sizeof(struct virtchardev), GFP_KERNEL);
        memset(virtdev, 0, sizeof(struct virtchardev));

        spin_lock_init(&virtdev->lock);
        alloc_chrdev_region(&virtdev->virtdev_id, 0, 1,
                                VIRTCHARDEV_NAME );
        virtdev->virtdev_major = MAJOR(virtdev->virtdev_id);

        cdev_init(&virtdev->virtdev_cdev, &virtdev_fops);

        virtdev->virtdev_cdev.owner = THIS_MODULE;
        virtdev->virtdev_cdev.ops = &virtdev_fops;

        cdev_add(&virtdev->virtdev_cdev, virtdev->virtdev_id, 1);

        virtdev->class = class_create(THIS_MODULE, VIRTCHARDEV_NAME);

        device_create(virtdev->class, NULL, virtdev->virtdev_id,
                        NULL, VIRTCHARDEV_NAME);


        return 0;
}

void virtdev_exit(void)
{
        cdev_del(&virtdev->virtdev_cdev);
        unregister_chrdev_region(virtdev->virtdev_id, 1);

        device_destroy(virtdev->class, virtdev->virtdev_id);
        class_destroy(virtdev->class);

        kfree(virtdev);

}

module_init(virtdev_init);
module_exit(virtdev_exit); ~

   编写Makefile:

PWD    := $(shell pwd)
KERDIR := /home/test/kernel_build/linux-imx 
obj-m += 03_spinlock_virtchardev.o

all:
        make -C $(KERDIR) M=$(PWD) modules
clean:
        make -C $(KERDIR) M=$(PWD) clean

3. 编译

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

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

~/linux_driver/03_spinlock $ ls
Makefile  modules.order  Module.symvers  03_spinlock_virtchardev.c  03_spinlock_virtchardev.ko  03_spinlock_virtchardev.mod  03_spinlock_virtchardev.mod.c  03_spinlock_virtchardev.mod.o  03_spinlock_virtchardev.o

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. 上传自己编译的字符驱动模块

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

   

6. 运行上面编译出的驱动模块

   执行insmod加载模块,然后执行rmmod卸载模块,log如下,可以看到当加载时,打印出了virtdev_init里的log, 当卸载时打印出了virtdev_exit里的log,执行加载卸载可以多次执行,也没有问题。