建设应用型网站的意义,零食公司建立网站的原因,网站新闻更新怎么设计,一个外国人做的汉子 网站概述
本文介绍了platform框架下的设备驱动开发流程和方法#xff0c;主要包括设备树、驱动程序和应用程序的开发。以随机数驱动为例#xff0c;实现了应用程序调用库函数#xff0c;通过系统调用陷入内核#xff0c;最后执行硬件驱动#xff0c;获取真随机数的过程。
添…概述
本文介绍了platform框架下的设备驱动开发流程和方法主要包括设备树、驱动程序和应用程序的开发。以随机数驱动为例实现了应用程序调用库函数通过系统调用陷入内核最后执行硬件驱动获取真随机数的过程。
添加设备树节点
在soc节点下添加名为trng子节点内容如下
trng: trng0x53030000 {compatible acme,trng;reg 0x00 0x53030000 0x00 0x1000;interrupts 0x34 IRQ_TYPE_LEVEL_HIGH;interrupt-parent plic;
};编译设备树dts生成相应的dtb文件
make dtbs使用新的dtb启动Linux内核。Linux启动成功之后查看是否有trng这个节点
ls /proc/device-tree/soc
# trng0x53030000进入trng目录查看属性相关的文件
/proc/device-tree/soc/trng0x53030000# ls
compatible interrupts phandle
interrupt-parent name reg编写设备驱动
Makefile
新建trng/driver目录并创建Makefile内如如下
# 内核架构
ARCH : riscv
# 交叉工具链
CROSS_COMPILE : /path/to/riscv32-linux-
# 内核目录
KERNELDIR : /path/to/linux/linux-6.1
# 当前目录
PWD : $(shell pwd)
# 目标文件
obj-m : trng.o# 目标
build: kernel_modules# 编译模块
kernel_modules:$(MAKE) ARCH$(ARCH) CROSS_COMPILE$(CROSS_COMPILE) -C $(KERNELDIR) M$(PWD) modules# 清理
clean:$(MAKE) ARCH$(ARCH) CROSS_COMPILE$(CROSS_COMPILE) -C $(KERNELDIR) M$(PWD) clean驱动
在trng/driver下新建trng.c文件内容如下
#include linux/kernel.h
#include linux/module.h
#include linux/interrupt.h
#include linux/fs.h
#include linux/of.h
#include linux/cdev.h
#include linux/io.h
#include linux/iopoll.h
#include linux/platform_device.h/* TRNG寄存器 */
#define TRNG_CTRL (0x0)#define TRNG_CTRL_CMD_MASK (0x07)#define TRNG_CTRL_CMD_RNG (0x01)#define TRNG_CTRL_CMD_SEED (0x02)
#define TRNG_STAT (0x4)#define TRNG_STAT_SEEDED BIT(9)
#define TRNG_MODE (0x8)#define TRNG_MODE_R256 BIT(3)
#define TRNG_ISTAT (0x14)#define TRNG_ISTAT_RAND_RDY BIT(0)#define TRNG_ISTAT_SEED_DONE BIT(1)
#define TRNG_RAND0 (0x20)#define TRNG_TIMEOUT (50000)#define DRIVER_NAME trngstruct trng_dev {dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *dev; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */int irq; /* 中断号 */void __iomem *base; /* 基地址 */
};static int trng_init(void *base)
{int ret;unsigned int value;/* 模式 */value readl(base TRNG_MODE);value | TRNG_MODE_R256;writel(value, base TRNG_MODE); /* 播种 */value readl(base TRNG_CTRL);value ~TRNG_CTRL_CMD_MASK;value | TRNG_CTRL_CMD_SEED;writel(value, base TRNG_CTRL);/* 等待播种完成 */ret readl_relaxed_poll_timeout_atomic(base TRNG_ISTAT,value, (value TRNG_ISTAT_SEED_DONE),10, TRNG_TIMEOUT);if (ret 0) {value | TRNG_ISTAT_SEED_DONE;writel(value, base TRNG_ISTAT);}return ret;
}static int trng_generate_random(void *base, unsigned char *buf)
{int ret;unsigned int value;/* 启动生成随机数 */value readl(base TRNG_CTRL);value ~TRNG_CTRL_CMD_MASK;value | TRNG_CTRL_CMD_RNG;writel(value, base TRNG_CTRL);/* 等待随机数准备好 */ret readl_relaxed_poll_timeout_atomic(base TRNG_ISTAT,value, (value TRNG_ISTAT_RAND_RDY),10, TRNG_TIMEOUT); if (ret) {return ret;}/* 清除准备好标志 */value readl(base TRNG_ISTAT);value ~TRNG_ISTAT_RAND_RDY;writel(value, base TRNG_ISTAT);/* 读取随机数 */for (int i 0; i 8; i) {*(unsigned int*)buf readl(base TRNG_RAND0 i*4);buf 4;}return 0;
}static irqreturn_t trng_irq_handler(int irq, void *dev_id)
{struct trng_dev *trng;trng (struct trng_dev*)dev_id;dev_dbg(trng-dev, TRNG interrupt received\n);return IRQ_HANDLED;
}static int trng_open(struct inode *inode, struct file *filp)
{int ret;struct trng_dev *trng;trng container_of(inode-i_cdev, struct trng_dev, cdev);filp-private_data trng;dev_dbg(trng-dev, Open trng\n);ret trng_init(trng-base);if (ret) {dev_err(trng-dev, Failed to init trng, ret%d\n, ret);return ret;}return 0;
}static int trng_release(struct inode *inode, struct file *filp)
{struct trng_dev *trng;trng filp-private_data;dev_dbg(trng-dev, Release trng\n);return 0;
}static ssize_t trng_read(struct file *filp, char __user *buffer, size_t len, loff_t *offset)
{ int ret;unsigned char random[32];size_t copyed_len, len_to_copy;struct trng_dev *trng;trng filp-private_data;dev_info(trng-dev, Read trng\n);copyed_len 0;while (len) {ret trng_generate_random(trng-base, random);if (ret) {dev_err(trng-dev, Failed to generate random, ret%d\n, ret);return ret;}// print_hex_dump(KERN_INFO, random: , DUMP_PREFIX_NONE, 16, 1, random, sizeof(random), false);len_to_copy (len sizeof(random)) ? len : sizeof(random);ret copy_to_user(buffer, random, len_to_copy);if (ret) {dev_err(trng-dev, Failed to copy to user\n);return -EFAULT;}copyed_len len_to_copy;buffer len_to_copy;len - len_to_copy;}return copyed_len;
} static ssize_t trng_write(struct file *filp, const char __user *buffer, size_t len, loff_t *offset)
{struct trng_dev *trng;trng filp-private_data;dev_dbg(trng-dev, Write trng\n);return len;
}static long trng_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct trng_dev *trng;trng filp-private_data;dev_dbg(trng-dev, Ioctl trng\n);switch (cmd) {default:dev_err(trng-dev, Unknown ioctl 0x%x\n, cmd);break;}return -ENOTTY;
}static int trng_mmap(struct file *filp, struct vm_area_struct *vma)
{struct trng_dev *trng;trng filp-private_data;dev_dbg(trng-dev, Mmap trng\n);return 0;
}static const struct file_operations trng_fops {.owner THIS_MODULE,.open trng_open,.release trng_release,.read trng_read,.write trng_write,.unlocked_ioctl trng_ioctl,.mmap trng_mmap,
};static int trng_probe(struct platform_device *pdev)
{struct device *dev pdev-dev;struct trng_dev *trng;int ret;/* 分配内存 */trng devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL);if (!trng) {dev_err(dev, Failed to allocate memory\n);return -ENOMEM;}/* 将设备的资源映射到内存空间 */trng-base devm_platform_ioremap_resource(pdev, 0);if (IS_ERR(trng-base)) {dev_err(dev, Failed to map device registers\n);return PTR_ERR(trng-base);}/* 获取设备的中断号 */trng-irq platform_get_irq(pdev, 0);if (trng-irq 0) {dev_err(dev, Failed to get irq %d\n, trng-irq);return trng-irq; }/* 请求中断 */ret devm_request_irq(dev, trng-irq, trng_irq_handler, 0,DRIVER_NAME, trng);if (ret) {dev_err(dev, Failed to request IRQ\n);return ret;}/* 申请设备号 */ret alloc_chrdev_region(trng-devid, 0, 1, DRIVER_NAME); if (ret 0) { dev_err(dev, Failed to allocate device number\n); return ret; }trng-major MAJOR(trng-devid); trng-minor MINOR(trng-devid); /* 初始化cdev */trng-cdev.owner THIS_MODULE;cdev_init(trng-cdev, trng_fops);/* 添加一个cdev */ret cdev_add(trng-cdev, trng-devid, 1);if (ret 0) {dev_err(dev, Failed to add cdev\n);unregister_chrdev_region(trng-devid, 1);return ret;} /* 创建类 */trng-class class_create(THIS_MODULE, DRIVER_NAME);if (IS_ERR(trng-class)) {cdev_del(trng-cdev);unregister_chrdev_region(trng-devid, 1);dev_err(dev, Failed to create class\n);return PTR_ERR(trng-class);}/* 创建设备 */trng-dev device_create(trng-class, NULL, trng-devid, NULL, DRIVER_NAME);if (IS_ERR(trng-dev)) {cdev_del(trng-cdev);unregister_chrdev_region(trng-devid, 1);class_destroy(trng-class);dev_err(dev, Failed to create device\n);return PTR_ERR(trng-dev);}/* 保存设备私有结构体 */platform_set_drvdata(pdev, trng);dev_info(dev, TRNG platform driver probed\n);return 0;
}static int trng_remove(struct platform_device *pdev)
{struct trng_dev *trng;/* 获取设备私有结构体 */trng platform_get_drvdata(pdev);/* 删除cdev */cdev_del(trng-cdev);/* 释放设备号 */unregister_chrdev_region(trng-devid, 1);/* 删除设备 */device_destroy(trng-class, trng-devid);/* 删除类 */class_destroy(trng-class);/* 释放设备内存 */// devm_kfree(pdev-dev, trng); // devm_kzalloc为设备分配的内存在设备移除时会自动释放dev_info(pdev-dev, TRNG platform driver removed\n);return 0;
}static const struct of_device_id trng_of_match[] {{ .compatible acme,trng },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, trng_of_match);static struct platform_driver trng_driver {.driver {.name DRIVER_NAME,.of_match_table trng_of_match,},.probe trng_probe,.remove trng_remove,
};module_platform_driver(trng_driver);MODULE_AUTHOR(Author);
MODULE_DESCRIPTION(Trng driver);
MODULE_LICENSE(GPL);使用platform平台驱动设备模型编写trng的驱动程序。 当设备树中的节点与驱动匹配成功会执行trng_probe函数完成驱动的加载。 当应用需要获取随机数时读取这个trng设备陷入内核调用函数trng_read进而调用函数trng_generate_random完成从硬件获取随机数。 如果需要释放设备会调用trng_remove函数卸载设备驱动。
执行make编译驱动程序编译成功生成trng.ko。
Linux启动成功之后可以挂载nfs将trng.ko拷贝到trng目录
mkdir trng
mount -t nfs -o nolock xx.xx.xx.xx:/nfs/trng /root/trng执行如下命令加载设备驱动
insmod trng.ko
# [ 177.821055] trng 53030000.trng: TRNG platform driver probed如果设备驱动加载成功可以在/dev下找到设备
ls /dev/trng另外可以查看trng的设备号
cat /proc/devices
# 249 trng如果需要卸载设备驱动执行
rmmod trng.ko
# [ 2947.495906] trng 53030000.trng: TRNG platform driver removed应用App
Makefile
新建trng/app目录并创建Makefile内如如下
# 交叉工具链
CROSS_COMPILE ? /opt/andestech/nds32le-linux-glibc-v5d/bin/riscv32-linux-# 指定C编译器
CC : $(CROSS_COMPILE)gcc# 目标文件名
TARGET : trng# 源文件名
SRC : trng.c# 默认目标
all: $(TARGET)# 编译并链接
$(TARGET): $(SRC)$(CC) $(SRC) -o $(TARGET)# 清理
clean:rm -f $(TARGET)应用
在trng/app下新建trng.c文件内容如下
#include stdio.h
#include stdint.h
#include stdlib.h
#include unistd.h
#include fcntl.h #define TRNG_DEVICE /dev/trngstatic void hexdump(const char *name, const unsigned char *buffer, unsigned int len)
{printf(****************%s****************\n, name);for (unsigned int i 0; i len; i) {printf(%02x , buffer[i]);if ((i 1) % 16 0) {printf(\n);}}if (len % 16 ) {printf(\n);}
}int main(int argc, char *argv[])
{uint8_t *buf NULL;size_t num;int ret, fd;if (argc 2) {printf(Usage: trng num\n);return -1;}num atoi(argv[1]);buf malloc(num);if (buf NULL) {printf(Failed to malloc\n);return -1;}/* 打开设备 */fd open(TRNG_DEVICE, O_RDONLY);if (fd 0) {printf(Failed to open trng device\n);goto exit;}/* 读取随机数 */ret read(fd, buf, num);if (ret 0) {printf(Failed to read random, ret%d\n, ret);goto exit;}hexdump(random, buf, num);
exit:close(fd);free(buf);return ret;
}执行make编译应用程序编译成功生成trng。
同理将trng应用拷贝到trng目录并执行
./trng 16
# ****************random****************
# 6c 95 ea 3c a0 1f e8 c2 03 db 66 f6 19 4b 07 e3
# c0 96 a3 93 20 a9 68 c5 9f 1f a1 55 c0 9c 24 c9
# 5f 06 47 45 be 2c 21 b5 11 23 23 e6 36 94 3f d6
# 9a 30 68 91 da c4 6d ff af 46 26 c9 ab f8 79 7c 如果随机数获取成功说明驱动和应用程序运行正常。
编译进内核
在开发前期阶段一般将驱动编译成模块方便调试。当驱动开发完成后可以将其编译进内核。
驱动
在linux-6.1/drivers下新建trng目录并创建Makefile和Kconfig文件内容分别如下
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the TRNG device drivers.
#obj-$(CONFIG_TRNG) : trng.o# SPDX-License-Identifier: GPL-2.0-only
#
# TRNG device configuration
#config TRNGtristate TRNG supporthelpThis driver provides support for TRNG in SoCs.To compile this driver as a module, choose M here: the modulewill be called acme-trng.If unsure, say Y.在linux/drivers/Makefile中添加
obj-$(CONFIG_TRNG) trng/在linux/drivers/Kconfig中添加
source drivers/trng/Kconfig将trng.c驱动文件拷贝到trng目录最终目录文件如下
$ tree linux-6.1/drivers/trng/
├── Kconfig
├── Makefile
└── trng.c内核配置
配置内核输入命令
make menuconfig选择Device Drivers-TRNG support选择将trng编译进内核这里可以有三种选择
*将该功能编译进内核空不编译该功能M将该功能编译成内核中的模块
运行
编译Linux并启动在启动日志中如果打印如下说明TRNG驱动运行正常
[ 4.974450] trng 53030000.trng: TRNG platform driver probed可以执行命令获取随机数
cat /dev/trng | hexdump -n 32
0000000 df01 f5bc de33 2509 8d16 7b5f 8868 8bea
0000010 40f3 00f2 97a4 324d 03c2 10c8 b943 3d6d
0000020问题解决 加载KO报异常trng: loading out-of-tree module taints kernel. 原因在于没有将此驱动模块加入到Kconfig导致。 使用函数devm_kzalloc为设备分配的内存在设备移除时会自动释放可以不进行显示释放devm_kfree 宏container_of用于从结构体的某个成员的地址反推出整个结构体的地址尤其注意第一个参数必须为成员的地址如果结构体成员为指针变量需要取该指针变量的地址。