Linux驱动开发:字符设备驱动开发实战

一、工程创建

VSCode 创建工程,设置 C/C++ 配置,导入 linux kernel 源码目录,方便 vscode 写代码自动补全,vscode 配置

{"configurations": [{"name": "Linux","includePath": ["/home/jeck/linux/linux_kernel/nxp/linux-imx-rel_imx_4.1.15_2.1.0_ga/include","/home/jeck/linux/linux_kernel/nxp/linux-imx-rel_imx_4.1.15_2.1.0_ga/arch/arm/include","/home/jeck/linux/linux_kernel/nxp/linux-imx-rel_imx_4.1.15_2.1.0_ga/arch/arm/include/generated"],"defines": [],"compilerPath": "/usr/bin/gcc","cStandard": "c11", "cppStandard": "c++17","intelliSenseMode": "linux-gcc-x64"},{"name": "linux_drive","includePath": ["${workspaceFolder}/**"],"defines": [],"compilerPath": "/usr/bin/gcc","cStandard": "c11","cppStandard": "c++17","intelliSenseMode": "linux-gcc-x64"}],"version": 4
}

二、驱动程序编写

新建一个文件 char_drive.c 文件:

驱动程序主要包含:驱动的注册和卸载,以及基本驱动操作,比如打开、关闭、读取、写入等操作,这里使用原子的例程分析:

2.1 驱动操作接口

打开字符设备,因为驱动在 linux 下就是一个文件,我们传入文件的 inode 和文件结构体用于关联设备。

/** @description        : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int chrdevbase_open(struct inode *inode, struct file *filp)
{// printk("chrdevbase open!\r\n");return 0;
}

关闭字符设备,关闭设备就是打开设备的逆操作,断开设备结构体和文件结构体的关联。

/** @description        : 关闭/释放设备* @param - filp   : 要关闭的设备文件(文件描述符)* @return             : 0 成功;其他 失败*/
static int chrdevbase_release(struct inode *inode, struct file *filp)
{// printk("chrdevbase release!\r\n");return 0;
}

读字符设备,传入驱动设备文件,这里我们并没有使用文件指针,模拟进行操作,使用 copy_to_user 内核函数,将数据打印到用户数据空间。

/** @description        : 从设备读取数据* @param - filp   : 要打开的设备文件(文件描述符)* @param - buf    : 返回给用户空间的数据缓冲区* @param - cnt  : 要读取的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 读取的字节数,如果为负值,表示读取失败*/
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 senddata ok!\r\n");}else{printk("kernel senddata failed!\r\n");}// printk("chrdevbase read!\r\n");return 0;
}

写字符设备,和读字符串设备相同,只不过数据读写方向相反。

/** @description        : 向设备写数据* @param - filp    : 设备文件,表示打开的文件描述符* @param - buf     : 要写给设备写入的数据* @param - cnt     : 要写入的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue = 0;/* 接收用户空间传递给内核的数据并且打印出来 */retvalue = copy_from_user(writebuf, buf, cnt);if (retvalue == 0){printk("kernel recevdata:%s\r\n", writebuf);}else{printk("kernel recevdata failed!\r\n");}// printk("chrdevbase write!\r\n");return 0;
}

2.2 注册、注销接口

驱动基本操作程序写话后,编写驱动注册和注销接口:

驱动注册本质上就是将操作函数作为指针传递进去,挂载到调用结构体上,注册时要传入设备号和设备名称

/** @description    : 驱动入口函数* @param       : 无* @return       : 0 成功;其他 失败*/
static int __init chrdevbase_init(void)
{int retvalue = 0;/* 注册字符设备驱动 */retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);if (retvalue < 0){printk("chrdevbase driver register failed\r\n");}printk("chrdevbase init!\r\n");return 0;
}

设备注销就只需要传入设备号和名称就行,注销函数会根据设备号清除相关结构体

/** @description    : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit chrdevbase_exit(void)
{/* 注销字符设备驱动 */unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);printk("chrdevbase exit!\r\n");
}

注册和注销函数还需要在注册一次,关联到系统模块加载和卸载相关的结构体上

module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

除此之外还要作者信息,此处不能缺少

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

三、应用程序编写

编写 main 函数,根据传入参数进行驱动代码的测试,main 函数的参数也叫做命令参数,在 linux 下敲命令的时候就有用了,argv 指的是 char * argc[] 的大小 char * argc[]是一个指针数组,里面存放的是各个传入字符串的首地址,测试代码如下:

int main(int argc, char *argv[])
{int fd, retvalue;char *filename;char readbuf[100], writebuf[100];if (argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开驱动文件 */fd = open(filename, O_RDWR);if (fd < 0){printf("Can't open file %s\r\n", filename);return -1;}if (atoi(argv[2]) == 1){ /* 从驱动文件读取数据 */retvalue = read(fd, readbuf, 50);if (retvalue < 0){printf("read file %s failed!\r\n", filename);}else{/*  读取成功,打印出读取成功的数据 */printf("read data:%s\r\n", readbuf);}}if (atoi(argv[2]) == 2){/* 向设备驱动写数据 */memcpy(writebuf, usrdata, sizeof(usrdata));retvalue = write(fd, writebuf, 50);if (retvalue < 0){printf("write file %s failed!\r\n", filename);}}/* 关闭设备 */retvalue = close(fd);if (retvalue < 0){printf("Can't close file %s\r\n", filename);return -1;}return 0;
}

这段应用程序就是简单的传入设备文件名称,然后再根据后续参数进行读或者写的操作。

四、程序编译

驱动程序使用 makefile 用交叉工具链进行编译:

KERNELDIR := /home/jeck/linux/linux_kernel/nxp/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := char_drive.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

KERNELDIR 为 Linux 内核目录,编译指令:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

编译完成,得到 ko 文件可用于内核加载:

然后直接在 linux 用交叉工具链将测试程序编译链接成可执行文件:

arm-linux-gnueabihf-gcc char_device_app.c -o test_app

模块编译成功后,创建内核模块目录:/lib/modules/4.1.15,其中 4.1.15 是内核版本,可以通过 uname -r 查询。

复制 ko 文件和测试程序到文件系统目录下面,加载 ko 模块:

加载指令:

insmod char_drive.ko

然后使用 test_app 进行测试就行

Linux驱动开发:字符设备驱动开发实战相关推荐

  1. <Linux开发>--驱动开发-- 字符设备驱动(3) 过程详细记录

    <Linux开发>–驱动开发-- 字符设备驱动(3) 过程详细记录 驱动开发是建立再系统之上的,前面作者也记录了系统移植的过程记录,如果有兴趣,可进入博主的主页查看相关文章,这里就不添加链 ...

  2. linux的驱动开发——字符设备驱动

    1.字符设备驱动 \qquad字符设备驱动是最基本,最常用的设备.它将千差万别的硬件设备采用统一的接口封装起来,屏蔽了硬件的差异,简化了应用层的操作. 2.描述所有字符设备的结构体 \qquad描述所 ...

  3. 正点原子-驱动开发-字符设备驱动

    Linux中的三大类驱动:字符设备.块和网络设备驱动 I2C.SPI.音频等都属于字符设备驱动 的类型 EMMC.NAND.SD卡和 U盘等存储都属于块设备 网卡,WIFI等都属于网络驱动 一个设备可 ...

  4. linux用户空间flash驱动,全面掌握Linux驱动框架——字符设备驱动、I2C驱动、总线设备驱动、NAND FLASH驱动...

    原标题:全面掌握Linux驱动框架--字符设备驱动.I2C驱动.总线设备驱动.NAND FLASH驱动 字符设备驱动 哈~ 这几天都在发图,通过这种方式,我们希望能帮大家梳理学过的知识,全局的掌握Li ...

  5. 【linux驱动之字符设备驱动基础】

    linux驱动之字符设备驱动基础 文章目录 linux驱动之字符设备驱动基础 前言 一.开启驱动学习之路 二.驱动预备知识 三.什么是驱动? 3.1 驱动概念 3.2 linux 体系架构 3.3 模 ...

  6. linux PCI驱动调用字符设备驱动方式

    上一篇文章写了字符设备驱动的基本结构及访问方式,在实际应用时首先需要绑定自己的硬件设备.本篇主要描述字符设备驱动与PCI接口类型的设备访问方式(内核为2.6.24及以上的方法,测试内核为2.6.32) ...

  7. Linux驱动之字符设备驱动

    系列文章目录 第一章 Linux入门之驱动框架 第二章 Linux驱动之字符设备驱动 文章目录 系列文章目录 前言 一.认识字符设备驱动 1.基本概念 2.基本概念 二.字符设备旧框架 1.注册和注销 ...

  8. 设备驱动,字符设备驱动、(总线)设备驱动模型、sysfs文件系统、平台设备驱动

    以下内容转载于微信公众号:嵌入式企鹅圈.如有侵权,请告知删除. 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术. 对于初学者来说会非常 ...

  9. linux open函数_Linux驱动开发 / 字符设备驱动内幕 (1)

    哈喽,我是老吴,继续记录我的学习心得. 一.保持专注的几个技巧 将最重要的事放在早上做. 待在无干扰环境下,比如图书馆. 意识到刚坐下开始投入工作前,有点负面小情绪是特别正常的现象. 让"开 ...

  10. STM32MP157驱动开发——字符设备驱动

    一.简介 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节 流进行读写操作的设备,读写数据是分先后顺序的.比如我们最常见的点灯.按键. IIC. SPI, LCD ...

最新文章

  1. Python/Jupyter Notebook以及可视化的运用
  2. 【Android】Mac Android adb 配置
  3. 京东产品负责人:数据如何高效驱动供应链?
  4. 【学术研讨课】雷诺实验2018-06-13
  5. SAP Spartacus产品页面的normalizer
  6. 538B. Quasi Binary
  7. 902. 最短编辑距离
  8. 构建测试的体系化思维(高级篇)
  9. MySQL多实例配置
  10. MyBaties入门
  11. 用cmd 查看本机的IP地址
  12. iptables高性能前端优化-无压力配置1w+条规则
  13. cubic 插值和 bicubic,pytorch, opencv和matlab的实现差异
  14. pandas数据分析给力教程【完整版】(七)
  15. C语言采药动态规划,动态规划_POJ1775:采药
  16. conda删除虚拟环境
  17. 《疯狂Java讲义》读书笔记1
  18. 硬件电路设计入门之三路二选一模拟开关电路芯片--74HCT4053
  19. 紫光展锐t610处理器相当于骁龙多少
  20. linux 硬盘格式化,挂载,反挂载等操作

热门文章

  1. 密盾科技即将推出同态加密隐私计算开源平台Pinsehub!
  2. Iframe 滚动条
  3. URL文件创建方式_艾孜尔江撰
  4. 描述性物理海洋学--第七章学习笔记
  5. win7如何计算机共享的打印机驱动程序,Win7系统连接共享打印机时提示“找不到驱动程序”怎么办...
  6. 华为Mate40国内版价格揭晓
  7. Unity 解决在不同分辨率下正圆或正方形的变形问题
  8. 油腻老爷们,你是有多久没化过妆了?
  9. Excel如何提取括号前内容
  10. 图片随机小角度堆叠效果,可用于相册缩略图叠加样式【长宽最优化】