首页 > 系统相关 >Linux驱动开发

Linux驱动开发

时间:2023-06-13 09:11:54浏览次数:40  
标签:开发 file Linux 驱动 include retvalue 设备 chrdevbase

  本文为一个简单的字符设备驱动,涉及驱动编写、测试程序编写、Makefile编写、驱动加载/卸载,运行于Linux虚拟机,不涉及底层配置。撰写本文的主要目的为记录一下驱动的开发流程,参考了正点原子的驱动开发指南。   驱动代码   创建文件夹 1_chrdevbase/ ,下属 APP/ 与 Driver/ 两个文件夹,前者放测试程序,后者放驱动代码。     在 Driver/ 下创建 chrdevbase.c,驱动代码如下   /*   * file name : chrdevbase.c  * description : 一个简单的字符设备demo  * author : 今朝无言  */   #include<linux/types.h> #include<linux/kernel.h> #include<linux/delay.h> #include<linux/ide.h> #include<linux/init.h> //引入module_init()以及module_exit() #include<linux/module.h> //与module相关的宏   MODULE_LICENSE("GPL"); MODULE_AUTHOR("今朝无言");   #define CHRDEVBASE_MAJOR 200 //主设备号,可通过 cat /proc/devices 查看所有设备及其主设备号 #define CHRDEVBASE_NAME "chrdevbase" //设备名   static char readbuf[100]; //读缓冲区 static char writebuf[100]; //写缓冲区 static char kerneldata[] = {"kernel data!"}; //内核数据,用于传递给测试APP,进行读取测试   /*   * description : 打开设备  * @param - inode : 传递给设备的inode  * @param - filp : 设备文件  * @return : 0 success;other failed  */ static int chrdevbase_open(struct inode *inode, struct file *filp){ printk("chrdevbase open!\n"); return 0; }   /*   * description : 从设备读取数据  * @param - filp : 设备文件  * @param - buf : 返回给用户空间的数据缓冲区  * @param - cnt : 要读取的数据长度  * @param - offt : 相对文件首地址的偏移  */ static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){ int retvalue = 0;   memcpy(readbuf, kerneldata, sizeof(kerneldata)); retvalue = copy_to_user(buf, readbuf, cnt);   if(retvalue == 0){ printk("kernel send data ok!\n"); } else { printk("kernel send data failed!\n"); }   return 0; }   /*   * description : 向设备写数据  * @param - filp : 设备文件  * @param - buf : 要写入设备的数据  * @param - cnt : 要写入的数据长度  * @param - offt : 相对文件首地址的偏移  */ static ssize_t chrdevbase_write(struct file *filp, const char *buf, size_t cnt, loff_t *offt){ int retvalue = copy_from_user(writebuf, buf, cnt);   retvalue = copy_from_user(writebuf, buf, cnt); if(retvalue == 0){ printk("kernel receive data: %s \n",writebuf); } else { printk("kernel receive data failed!\n"); }   return 0; }   /*   * description : 关闭设备  * @param - filp : 设备文件描述符  * @return : 0 success;other failed  */ static int chrdevbase_release(struct inode *inode, struct file *filp){ printk("chrdevbase release! \n"); return 0; }   /*  * chrdevbase的file_operations结构体  * file_operations的定义见Kernel/include/linux/fs.h  * 注意函数定义一定要相同,否则报`initialization from incompatible pointer type [-Werror=incompatible-pointer-types]`错  */ static struct file_operations chrdevbase_fops = { .owner= THIS_MODULE, .open= chrdevbase_open, .read= chrdevbase_read, .write= chrdevbase_write, .release= chrdevbase_release };   /*   * description : 驱动入口函数  */ static int __init chrdevbase_init(void){ //若函数没有参数,要加void,否则报`function declaration isn’t a prototype [-Werror=strict-prototypes]`错 int retvalue = 0;   retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops); if(retvalue < 0){ printk("chrdevbase driver register failed!\n"); } else { printk("chrdevbase driver register success!\n"); }   return 0; }   /*   * description : 驱动出口函数  */ static void __exit chrdevbase_exit(void){ unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME); printk("chrdevbase exit!\n");   return; }   //指定驱动入口和出口函数 module_init(chrdevbase_init); module_exit(chrdevbase_exit);   驱动代码Makefile   在 Driver/ 下创建 Makefile,内容如下   KERNELDIR := /lib/modules/4.15.0-189-generic/build #本机编译就/lib/modules/`uname -r`/build #交叉编译就使用对应的Kernel源码目录   CURRENT_PATH := $(shell pwd)   #要生成的模块名 obj-m := chrdevbase.o   build: kernel_modules   kernel_modules: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules clean: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean   测试程序代码   在 APP/ 下创建 chrdevbaseAPP.c,代码如下   /*   * file name : chrdevbaseAPP.c  * description : chedevbase驱动的测试程序  * author : 今朝无言  */   #include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdlib.h> #include<string.h>   static char usrdata[] = {"usr data!"}; //用户数据,用于传递给驱动,进行写入测试   // 用法:./chrdevbaseAPP /dev/chrdevbase arg int main(int argc, char *argv[]){ int fd, retvalue; char *filename; char readbuf[100], writebuf[100];   //检查参数 if(argc != 3){ printf("Error Usage!\n"); return -1; }   filename = argv[1];   //打开驱动文件 fd = open(filename, O_RDWR); if(fd < 0){ printf("Can't open file %s!\n", filename); return -2; }   //arg=1,从驱动文件读取数据 if(atoi(argv[2]) == 1){ retvalue = read(fd, readbuf, 50); if(retvalue < 0){ printf("read file %s failed!\n", filename); } else { printf("read data: %s\n", readbuf); } }   //arg=2,向驱动写数据 if(atoi(argv[2]) == 2){ memcpy(writebuf, usrdata, sizeof(usrdata)); retvalue = write(fd, writebuf, 50); if(retvalue < 0){ printf("write file %s failed!\n", filename); } else { printf("write file success!\n"); } }   //关闭设备 retvalue = close(fd); if(retvalue < 0){ printf("Can't close file %s!\n", filename); return -3; }   return 0; }   测试程序Makefile   在 APP/ 下创建 Makefile,内容如下   build: gcc chrdevbaseAPP.c -o chrdevbaseAPP   clean: rm chrdevbaseAPP   编译测试程序   编译驱动   驱动加载   使用 insmod 命令加载刚刚生成的驱动模块   sudo insmod chrdevbase.ko   执行   cat /proc/devices 查看驱动,如下图,可以看到驱动已经加载     创建设备节点文件   使用 mkmod 命令创建驱动节点   sudo mknod /dev/chrdevbase c 200 0 则创建字符设备文件/dev/chrdevbase,对该文件进行读写操作即可使用驱动,其中 ‘c’ 表示字符设备,200为主设备号,0为次设备号。   测试   进入 APP/ 文件夹,执行   sudo ./chrdevbaseAPP /dev/chrdevbase 1 进行设备读取测试,结果如下   可以看到用户接收到了从内核传递来的数据 ‘kernel data’ 。     执行   sudo ./chrdevbaseAPP /dev/chrdevbase 2 进行设备写入测试,结果如下     查看最后6条日志消息:   dmesg | tail -6   其中前三条是前面进行读取测试的日志输出,后三条是进行写入测试的日志输出,可以看到内核接收到了用户发送来的数据 ‘usr data’ 。   驱动卸载   使用 rmmod 命令卸载驱动:   sudo rmmod chrdevbase.ko   再使用 cat /proc/devices 查看,将发现 chrdevbase 设备已被卸载。     加载/卸载模块时的日志如下:    

标签:开发,file,Linux,驱动,include,retvalue,设备,chrdevbase
From: https://www.cnblogs.com/kn-zheng/p/17476548.html

相关文章

  • 乘风破浪,遇见最美Windows 11之现代Windows桌面应用开发 - .NET反编译神器dotPeek,免费
    什么是dotPeekhttps://www.jetbrains.com/decompiler/dotPeek是由鼎鼎有名的JetBrains开发的免费.Net反编译软件。其界面和VisualStudio一样现代。获取dotPeekhttps://www.jetbrains.com/decompiler/download/download-thanks.html?platform=windowsWebJetBrains.dotP......
  • linux内存回收策略
    1、64位操作系统进程可以最大申请多大的虚拟内存?64位操作系统的进程可以最大申请的虚拟内存取决于操作系统的架构和其对虚拟内存的管理方式。在理论上,64位操作系统可以支持非常大的虚拟内存空间。在大多数64位操作系统中,每个进程最大可以申请的虚拟内存大小通常为2^64字节,也就是......
  • Linux基础命令
    Linux基础命令(1)ctrlc:取消命令,并且换行(2)ctrlu:清空本行命令(3)tab键:可以补全命令和文件名,如果补全不了快速按两下tab键,可以显示备选选项(4)ls:列出当前目录下所有文件,蓝色的是文件夹,白色的是普通文件,绿色的是可执行文件(5)pwd:显示当前路径(6)cdXXX:进入X......
  • Linux解压缩常用命令
    01-.tar格式解包:[*******]$tarxvfFileName.tar打包:[*******]$tarcvfFileName.tarDirName(注:tar是打包,不是压缩!)02-.gz格式解压1:[*******]$gunzipFileName.gz解压2:[*******]$gzip-dFileName.gz压缩:[*******]$gzipFileName03-.tar.gz格式解压:[*******]$tarzxvfFileNa......
  • 音视频技术开发周刊 | 238
    整个世界都是你的绿幕:这个视频抠图换背景的方法着实真假难辨绿幕是影视剧中抠图、换背景的利器,但如果不在绿幕前拍摄,我们还能完美地转换背景吗?华盛顿大学的研究者最近就上传了这样一份论文,不在绿幕前拍摄也能完美转换视频背景,让整个世界都变成你的绿幕。使用边缘计算来增强流传输本......
  • 音视频技术开发周刊 | 237
    瘦脸、瘦腿太初级,揭秘「亚洲秘术」美颜美体特效在机器之心最新一期技术分享中,机器之心机动组以「揭秘人体美化技术」为主题,邀请到了快手、淘宝、火山引擎的技术专家,为大家解密这些特效背后的相关技术。UGC视频质量评价由于内容和质量的高度多样性,用户生成内容(UGC)的视频质量非......
  • 音视频技术开发周刊 | 230
    Opera视频出海非洲面临的技术挑战及应对Opera是一个主要业务在海外的公司。非洲地区,基础设施差,网络带宽小,人均收入低。如何在非洲地区做好视频分发传输是需要一定的市场、技术深耕。本次LiveVideoStackCon2021音视频技术大会北京站,我们邀请到了Opera的技术副总监——张建磊,为我们......
  • 音视频技术开发周刊 | 231
    面向在线教育业务的流媒体分发演进几年前,很多人对在线网课还非常陌生。随着移动设备的普及和音视频技术的发展,如今在线教育产品百花齐放。而在线教育产品能服务千万学子离不开流媒体分发技术的支撑。本次LiveVideoStackCon2021音视频技术大会北京站邀请到了网易有道研发工程师周......
  • 【Linux】——安装Anaconda后默认不进入conda环境方法
    1安装conda后取消命令行前出现的base,取消每次启动自动激活conda的基础环境。通过将auto_activate_base参数设置为false实现:condaconfig--setauto_activate_basefalse那要进入的话通过condaactivatebase如果反悔了还是希望base一直留着的话通过condaconfig--setau......
  • 关于Digispark安装驱动后插入在设备管理器的“通用串行总线设备”分类下显示为“micro
    依据micronucleus的github和查询到的另一些资料解决了该问题,在此处备份防止遗忘。前往https://zadig.akeo.ie/下载Zadig,然后从micronucleus的github下载micronucleus.cfg。打开Zadig,在菜单栏中选择Device-LoadPresetDevice,选择刚刚下载的cfg,然后在下方点击小箭头选择libusb-w......