当前位置: 首页 > news >正文

正点原子RK3568学习日志19- Linux错误处理 字符驱动框架完全体

1. Linux错误处理

内核错误码保存在errno-base.h文件

使用goto语句处理的时候,应该遵循“先进后出”的原则


用了class_create()和device_create()函数,必须使用IS_ERR()函数判断返回的指针是否是有效的,如果是无效的,需要调用PTR_ERR()函数将无效指针转换为错误码,并进行错误码的返回

对于任何一个指针来说,必然存在三种情况,一种是合法指针,一种是NULL(也就是空指针),一种是错误指针(也就是无效指针)

错误指针已经指向了64位系统内核空间的最后一页0xfffffffffffff000~0xffffffffffffffff,指针落在这段地址之内,说明是错误的无效指针

错误返回1   IS_ERR  同时该函数返回的错误地址对应一个linux的错误码

如果想知道这个指针是哪个错误码,使用PTR_ERR函数转化。0xfffffffffffff000~0xffffffffffffffff这段地址和Linux错误码是一一对应的

/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_H#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */
#endif

dev1.class = class_create(THIS_MODULE, "test"); //创建类,名字为testif(IS_ERR(dev1.class)){ret = PTR_ERR(dev1.class);goto err_class_create;}dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test"); //创建设备,名字为testif(IS_ERR(dev1.device)){ret = PTR_ERR(dev1.device);goto err_device_create;}err_device_create:class_destroy(dev1.class);   //删除类
err_class_create:cdev_del(&dev1.cdev_test);      //删除cdev
err_chr_add:unregister_chrdev_region(dev1.dev_num, 1);  //注销设备号
err_chrdev:return ret;

2.实验:12_err Linux错误处理实验

err.c

#include <linux/init.h>  //初始化头文件
#include <linux/module.h>  //基本模块文件
#include <linux/kdev_t.h>  //包含设备号相关宏和函数
#include <linux/cdev.h>  //字符设备结构体头文件cdev
#include <linux/fs.h>   //注册设备节点的文件结构体
#include <linux/uaccess.h> //用户空间和内核空间数据传输头文件struct device_test{dev_t dev_num;int major;int minor;struct cdev cdev_test;struct class *class;struct device *device;char kbuf[32];};struct device_test dev1;//定义设备结构体变量dev1//open函数实现,在那个file结构体找
static int cdev_test_open(struct inode *inode, struct file *file)
{file->private_data = &dev1; //将设备结构体变量地址赋值给file的private_data字段printk("THis is cdev_test_open\r\n");return 0;
}//read函数,从设备读取数据
static ssize_t cdev_test_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{struct device_test *test_dev = (struct device_test *)file->private_data; //获取设备结构体变量地址if (copy_to_user(buf, test_dev->kbuf, strlen(test_dev->kbuf)) != 0){printk("copy_to_user is error\n");return -1;}printk("This is cdev_test_read\n");return 0;
}//write函数,向设备写入数据	
static ssize_t cdev_test_write (struct file *file, const char __user *buf, size_t size, loff_t *off)
{struct device_test *test_dev = (struct device_test *)file->private_data; //获取设备结构体变量地址if (copy_from_user(test_dev->kbuf, buf, size) != 0){printk("copy_from_user is error\n");return -1;}printk("This is cdev_test_write\n");printk("kbuf = %s\r\n", test_dev->kbuf);return 0;
}static int cdev_test_release (struct inode *inode, struct file *file)
{printk("This is cdev_test_release\n");return 0;
}//设备操作函数
static struct file_operations cdev_test_fops  = {  //file_operations结构体.owner = THIS_MODULE,    //指向本模块.open = cdev_test_open,  //指向cdev_test_open函数.read = cdev_test_read,  //指向cdev_test_read函数.write = cdev_test_write, //指向cdev_test_write函数.release = cdev_test_release, //指向cdev_test_release函数
};static int __init chr_fops_init(void)
{//注册字符驱动int ret; 
//1.创建设备号//动态申请设备号ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "alloc_name"); if(ret < 0) //申请失败{goto err_chrdev;}printk("alloc_chrdev_region is ok\n");       dev1.major = MAJOR(dev1.dev_num);  //获取主设备号dev1.minor = MINOR(dev1.dev_num);  //获取次设备号printk("major = %d\n", dev1.major);  //打印设备号printk("minor = %d\n", dev1.minor); //2.注册字符设备cdev //初始化cdevcdev_init(&dev1.cdev_test, &cdev_test_fops); dev1.cdev_test.owner = THIS_MODULE; //将owner字段指向本模块,防止模块被卸载//添加cdevret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1); //注册字符设备到内核if (ret < 0) //注册失败{goto err_chr_add;}printk("cdev_add is ok\n");//3.创建设备节点//创建类和设备节点dev1.class = class_create(THIS_MODULE, "test"); //创建类,名字为testif(IS_ERR(dev1.class)){ret = PTR_ERR(dev1.class);goto err_class_create;}dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test"); //创建设备,名字为testif(IS_ERR(dev1.device)){ret = PTR_ERR(dev1.device);goto err_device_create;}return 0;err_device_create:class_destroy(dev1.class);   //删除类
err_class_create:cdev_del(&dev1.cdev_test);      //删除cdev
err_chr_add:unregister_chrdev_region(dev1.dev_num, 1);  //注销设备号
err_chrdev:return ret;}static void __exit chr_fops_exit(void)
{device_destroy(dev1.class, dev1.dev_num); //删除设备节点class_destroy(dev1.class); //删除类        cdev_del(&dev1.cdev_test); //注销字符设备 unregister_chrdev_region(dev1.dev_num, 1); //释放设备号   
}module_init(chr_fops_init);
module_exit(chr_fops_exit);
MODULE_LICENSE("GPL v2");   //声明模块许可证
MODULE_AUTHOR("AFANFAN");   //声明模块作者

Makefile

export ARCH=arm64#设置平台架构
export CROSS_COMPILE=/opt/atk-dlrk3568-5_10_sdk-toolchain/bin/aarch64-buildroot-linux-gnu-#交叉编译器前缀
obj-m += err.o    #此处要和你的驱动源文件同名
KDIR :=/home/alientek/rk3568_linux5.10_sdk/kernel    #这里是你的内核目录                                                                                                                            
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modules    #make操作
clean:make -C $(KDIR) M=$(PWD) clean    #make clean操作

app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char *argv[])
{int fd;  //文件描述符char buf1[32] = "nihao,afanfan"; //读取缓冲区buffd = open("dev/test", O_RDWR); //以读写方式打开设备文件if(fd < 0) //打开失败{perror("open file error\n");return -1;}write(fd, buf1, sizeof(buf1)); //将buf2缓冲区的数据写入到dev/test设备文件中close(fd); //关闭设备文件 对取消文件描述符到文件的映射return 0;
}///opt/atk-dlrk3568-5_10_sdk-toolchain/bin/aarch64-buildroot-linux-gnu-gcc -o app app.c -static

驱动编译成驱动模块ko 

使用命令“make”进行驱动的编译,编译完生成 err.ko目标文件

编译测试程序app

生成的app文件就是之后放在开发板上运行的可执行文件

/opt/atk-dlrk3568-5_10_sdk-toolchain/bin/aarch64-buildroot-linux-gnu-gcc -o app app.c -static

运行驱动编译成模块insmod

开发板启动之后, insmod err.ko

运行测试程序    ./app

3.问题:

1.perror

perror是 C 语言标准库中的一个函数,用于​​打印系统错误信息​​。它的名字是 "print error" 的缩写。当系统调用或库函数失败时,它会设置一个全局变量 errno来指示具体的错误原因,而 perror就是用来将这个错误代码转换成可读的错误描述信息


2.正确的 chr_fops_exit 函数

正确的顺序应该是您初始化顺序的严格逆序

初始化顺序是:

alloc_chrdev_region (申请设备号)

cdev_init / cdev_add (注册字符设备)

class_create (创建类)

device_create (创建设备节点)

因此,正确的释放顺序应该是:

device_destroy (销毁设备节点)

class_destroy (销毁类)

cdev_del (注销字符设备)

unregister_chrdev_region (释放设备号)

3.inline内联函数

将函数内联展开​​,以减少函数调用的开销

http://www.dtcms.com/a/521334.html

相关文章:

  • MySQL基本查询:从增删改查到复杂应用
  • 青海省城乡建设厅网站网站情况建设说明书
  • 沈阳制作公司网站和apphtml做静态网站
  • nginx-1.9.1.tar.gz 安装教程(详细步骤,从解压到启动)
  • 崇州 网站建设 有限公司wordpress做网站容易吗
  • 免费注册网址广东做seo的公司
  • KVM热迁移安装部署 ESXi
  • 【MRI脑网络构建专题】包含但不限于以下指标的分析
  • 未来的 AI 操作系统(十)——终极形态:当智能系统超越意识
  • 上海网站建设工资多少行业门户网站营销案例
  • 东莞网站制作网站推广任务怎么做
  • 网站维护一次一般要多久做网站寄生虫需要哪些东西
  • 镇江网站建站无锡科技网站建设
  • LINE 账号安全与权重提升实战指南:行为模拟与风控规避策略
  • 天硕全国产工业级固态硬盘如何突破边缘计算存储瓶颈?
  • 诺基亚官方网站四平专业网站设计
  • 修改langgraph-checkpoint-mysql插件兼容Tidb
  • 网页制作模板的网站element推荐几个响应式网站做参考
  • 【市政给排水】城乡供水管网CAD绘制步骤
  • golang学习笔记:标准库path
  • 建设制作外贸网站的公司南平抖音搜索排名seo软件
  • 网站加载效果怎么做的会员系统网站制作
  • 网站开发总结性报告开发网站通过第三方微信认证登录开发费用
  • 详解 Ceph 存储——CRUSH 算法
  • 淘宝商品规格API接口:快速查询商品SKU价格及优惠信息
  • 深圳做网站乐云seo费用优惠wordpress如何设置分类目录
  • 全球蜂窝物联网模组市场格局与区域需求分析
  • 公司怎么做网站企业自建网站平台有哪些
  • 安徽网站开发费用住总集团公司宣传册设计样本
  • 【03】C语言 强制类型转换 与 进制转换