Linux驱动开发(1)-最简单的字符设备驱动开发例子

1.简介

字符设备驱动:按照字节流进行读写操作的设备,例如点灯、按键、IIC、SPI、LCD。

Linux系统中一切皆文件,驱动加载成功,就会在/dev目录生成文件,对文件操作,则可实现对硬件操作。应用程序运行在用户空间,驱动运行在内核空间,用户空间不能直接对内核操作,因此借助系统调用实现。
image.png

2.字符设备驱动开发

2.1 内核驱动操作函数集合

include/linux/fs.h 中 file_operations 结构体

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
	ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
	int (*iterate) (struct file *, struct dir_context *);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*mremap)(struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **, void **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
					  loff_t len);
	void (*show_fdinfo)(struct seq_file *m, struct file *f);
	#ifndef CONFIG_MMU
	unsigned (*mmap_capabilities)(struct file *);
	#endif
};

owner:该结构体的模块的指针,一般为THIS_MODULE
llseek:修改文件读写位置
read:读取设备文件
write:写入设备文件
poll:查询设备是否可以进行非阻塞读写
unlocked_ioctl:对应ioctl,控制设备
campat_ioctl: 64 位系统上, 32 位的应用程序调用将会使用此函数。在 32 位的系统上运行 32 位的应用程序调用的是 unlocked_ioctl
mmap:设备内存映射到用户空间,一般用于帧缓冲设备,这样应用程序可以直接操纵内核空间,避免数据在用户空间和内核空间来回复制
open:打开设备文件
release:关闭设备文件,对应close函数
fasync:刷新待处理数据
aio_fsync:异步刷新待处理和数据

2.2 驱动开发步骤

1)模块加载和卸载

module_init(xxx_init); ///模块加载函数
module_exit(xxx_exit); ///模块卸载函数

2)字符设备注册与注销

static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
	
static inline void unregister_chrdev(unsigned int major, const char *name)

major:模块的主设备号
主设备号由32个bit组成,高12个bit为主设备号,低20个为次设备号。
设备号可以静态分配或动态分配,静态分配则代表自己去指定,可以使用cat /proc/devices查看系统中已使用的设备号,也可以通过上面函数的方式由系统动态分配,推荐是动态分配,静态指定容易造成冲突

fops:file_operations文件操作集合结构体指针
name:设备名
3)实现字符设备操作函数

static struct file_operations user_fops = {
    .owner = THIS_MODULE,
    .open = user_open,
    .release = user_close,
    .read = user_read,
    .write = user_write
};

4)LICENSE、作者信息

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xuzhangxin"); 

2.3 实战

2.3.1 vscode工程配置

ctrl+shift+p,添加一个Cpp工程配置文件,其中设置包含头文件的目录,便于查找函数声明
image.png
会自动创建好.vscode,其中有一个c_cpp_properties.json,添加linux kernel源码的头文件路径

{
	"configurations": [
		{
			"name": "Linux",
			"includePath": [
				"${workspaceFolder}/**",
				"/home/xzx/share/project_ipc/study_linux_project/linux_bsp/linux-imx-4.1.15-2.1.0-g3dc0a4b-v2.7/include",
				"/home/xzx/share/project_ipc/study_linux_project/linux_bsp/linux-imx-4.1.15-2.1.0-g3dc0a4b-v2.7/arch/arm/include",
				"/home/xzx/share/project_ipc/study_linux_project/linux_bsp/linux-imx-4.1.15-2.1.0-g3dc0a4b-v2.7/arch/arm/include/generated/"
			],
			"defines": [],
			"compilerPath": "/usr/bin/gcc",
			"cStandard": "c11",
			"cppStandard": "gnu++14",
			"intelliSenseMode": "linux-gcc-x64"
		}
	],
	"version": 4
}

2.3.2 全部源码

2.3.2.1 驱动部分

1)驱动源码

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/uaccess.h>

#define USER_MAJOR 201
#define USER_NAME "user_chrdev"



char pri_buf[1024] = {0};

static int user_open(struct inode *node, struct file *file)
{
	printk("user driver open");
}

static int user_close(struct inode *node, struct file *file)
{
	printk("user driver close");
}

static ssize_t user_read (struct file *file, char __user *data, size_t cnt, loff_t *off)
{
	printk("user driver read");

	int ret = 0;
	ret = copy_to_user(data, pri_buf, cnt);
	if (ret < 0)
	{
		printk("user driver read failed:%d", ret);
		return ret;
	}

	printk("user driver read sucess, ret:%d", ret);
	return ret;
}
static ssize_t user_write(struct file *file, const char __user *data, size_t cnt, loff_t *off)
{
	printk("user driver write");

	int ret = 0;
	ret = copy_from_user(pri_buf, data, cnt);
	if (ret < 0)
	{
		printk("user driver write failed:%d", ret);
		return ret;
	}

	printk("user driver write sucess, ret:%d", ret);
	return ret;
}


static struct file_operations user_fops = {
	.owner = THIS_MODULE,
	.open = user_open,
	.release = user_close,
	.read = user_read,
	.write = user_write
	};

static int __init user_init(void)
{
	/// 注册驱动
	int ret = register_chrdev(USER_MAJOR, USER_NAME, &user_fops);
	if (ret < 0)
	{
		printk("user driver registration failed");
	}
	printk("user driver init");

	return 0;
}

static int __exit user_exit(void)
{
	/// 注销驱动
	unregister_chrdev(USER_MAJOR, USER_NAME);
	printk("user driver exit");
}


module_init(user_init);
module_exit(user_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xuzhangxin");

2)驱动编译Makefile
这里要根据自己的路径等更改

KERNELDIR := /home/xzx/share/project_ipc/study_linux_project/linux_bsp/linux-imx-4.1.15-2.1.0-g3dc0a4b-v2.7
CURRENT_PATH := $(shell pwd)
    obj-m := user_chrdev.o 

build: kernel_modules 
kernel_modules:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

3)加载驱动
编译生成user_chrdev_ko,随后拷贝到板子上,使用tftp、nfs等都可以。
使用insmod user_chrdev_ko加载驱动,
同时使用lsmod或者cat /proc/devices能看到驱动是否加载成功。
4)创建设备节点文件
mknod /dev/user_chrdev c 201 0
创建/dev/user_chrdev节点,c代表节点为字符设备,201为主设备号,0为次设备号
至此,驱动部分就准备完毕了,开始用应用代码去测试驱动啦!
5)卸载驱动
在不用的时候可以用rmmod卸载驱动,当然不是现在哈

2.3.2.2 应用测试部分

1)源码

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"



int main(int argc, char **argv)
{
    char w_buf[1024] = {0};
    char r_buf[1024] = {0};

    int fd = open("/dev/user_chrdev", O_RDWR);
    if (fd < 0)
    {
        return -1;
    }
    printf("open success\n");

    snprintf(w_buf, sizof(w_buf) / sizeof(w_buf[10]), "hello world");

    write(fd, w_buf, 10);
    read(fd, r_buf, 10);

    printf("r_buf :%s", r_buf);

    close(fd);


    return 0;
}

2)验证
gcc 编译后拷贝到板子上运行,查看有没有驱动内添加的打印即可

3. 源码地址

哈喽~我是Embedded-Xin,沪漂嵌入式开发工程师一枚,立志成为嵌入式全栈开发工程师,成为优秀博客创作者,共同学习进步。
以上代码全部放在我私人的github地址,其中有许多自己辛苦敲的例程源码,供大家参考、批评指正,有兴趣还可以直接提patch修改我的仓库~:
https://github.com/Xuzhangxin/study_linux_project.git
觉得不错的话可以点个收藏和star~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/298393.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue新手村(一)

目录 1、Vue简介——Vue的特点 2、Vue的第一个页面 3.Vue的简单使用介绍 3.1、{{ }}的使用 3.2、v-text和v-html 3.2.1、v-text和{{ }}的区别 3.2.2、v-html和v-text的区别 3.3、v-on【事件绑定】 3.3.1、绑定事件的语法 3.3.2、语法简化 3.3.3、传参 3.4、v-show和…

excel统计分析——两因素有重复方差分析

参考资料&#xff1a;生物统计学 无重复观测值的两因素方差分析只能研究两个因素的主效应&#xff0c;不能考察因素间的交互作用&#xff0c;只有在确定因素间不存在交互作用时才能进行无重复观测值的试验和分析。为了准确估计因素的主效应、交互作用和随机误差&#xff0c;每个…

设计模式之原型模式【创造者模式】

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

第121场双周赛题解:揭秘算法竞赛中的数位挑战与解题策略

需要多掌握解题套路 比赛地址 100157. 大于等于顺序前缀和的最小缺失整数 class Solution:def missingInteger(self, nums: List[int]) -> int:# Step 1: Find the longest consecutive prefixi 0 for i in range(1, len(nums)):if nums[i] ! nums[i - 1] 1:breakelse:…

Zookeeper三节点搭建

一、安装前准备 安装JDK&#xff08;之前已经安装了&#xff09; 拷贝apache-zookeeper-3.5.7-bin.tar.gz安装包到Linux系统下 解压到指定目录 在/opt/module/zookeeper-3.5.7/这个目录下创建zkData&#xff0c;在/opt/module/zookeeper-3.5.7/zkData目录下创建一个myid的…

第 121 场 LeetCode 双周赛题解

A 大于等于顺序前缀和的最小缺失整数 模拟&#xff1a;先求最长顺序前缀的和 s s s &#xff0c;然后从 s s s 开始找没有出现在 n u m s nums nums 中的最小整数 class Solution { public:int missingInteger(vector<int> &nums) {unordered_set<int> vis(…

MySQL数据管理(二)

DML语言 DML &#xff08;数据操作语&#xff09;&#xff1a;用于操作数据库对象中所包含的数据 DML包括&#xff1a; INSERT&#xff08;添加数据语句&#xff09;UPDATE&#xff08;更新数据语句&#xff09;DELETE&#xff08;删除数据语句&#xff09; 一、添加数据 …

Matlab三维绘图

绘制三维图plot3 t0:pi/50:10*pi; xsin(t); ycos(t); zt; plot3(x,y,z); 产生栅格数据点meshgrid 这个接口在绘制三维图像里面相当重要&#xff0c;很多时候要将向量变成矩阵才能绘制三维图。 x0:0.5:5; y0:1:10; [X,Y]meshgrid(x,y); plot(X,Y,o); x和y是向量&#xff0c;…

qt三大控件

1.QListWidget控件 先在ui界面将 QListWidget拖出来竖直对齐 再去代码中实现文本插入 两种插入方式 方法1 //listWidget使用 有左右中间对齐需求QListWidgetItem * itemnew QListWidgetItem("床前明月光"); // //上面只是独立的一句话,没有关联起来ui-&g…

Android AAudio

文章目录 基本概念启用流程基本流程HAL层对接数据流计时模型调试 基本概念 AAudio 是 Android 8.0 版本中引入的一种音频 API。 AAudio 提供了一个低延迟数据路径。在 EXCLUSIVE 模式下&#xff0c;使用该功能可将客户端应用代码直接写入与 ALSA 驱动程序共享的内存映射缓冲区…

HarmonyOS4.0系统性深入开发15Want概述

Want概述 Want的定义与用途 Want是对象间信息传递的载体&#xff0c;可以用于应用组件间的信息传递。其使用场景之一是作为startAbility()的参数&#xff0c;包含了指定的启动目标以及启动时需携带的相关数据&#xff0c;如bundleName和abilityName字段分别指明目标Ability所…

模拟算法(模拟算法 == 依葫芦画瓢)万字

模拟算法 基本思想引入算法题替换所有的问号提莫攻击Z字形变换外观数列数青蛙 基本思想 模拟算法 依葫芦画瓢解题思维要么通俗易懂&#xff0c;要么就是找规律&#xff0c;主要难度在于将思路转换为代码。 特点&#xff1a;相对于其他算法思维&#xff0c;思路比较简单&#x…

线性代数 --- 为什么LU分解中L矩阵的行列式一定等于正负1?

以下是关于下三角矩阵L的行列式一定等于-1的一些说明 笔者的一些话(写在最前面)&#xff1a; 这是一篇小文&#xff0c;是我写的关于求解矩阵行列式的一篇文章中的一部分。之所以把这一段专门提溜出来&#xff0c;是因为这一段相对于原文是可以完全独立的&#xff0c;也是因为我…

YOLOv5改进 | 检测头篇 | 增加辅助检测头利用AFPN改进Head(附详细修改教程)

一、本文介绍 本文给大家带来的改进机制是利用今年新推出的AFPN(渐近特征金字塔网络)来优化检测头,AFPN的核心思想是通过引入一种渐近的特征融合策略,将底层、高层和顶层的特征逐渐整合到目标检测过程中。这种渐近融合方式有助于减小不同层次特征之间的语义差距,提高特征…

在VM下使用Composer完成快照方式的软件制作

Composer允许您构建软件、应用程序、偏好设置文件或是文档的安装包&#xff0c;安装包可以部署到远程电脑或是作为镜像流程的一部分。构建软件包的第一步就是创建包源&#xff0c;根据要打包的软件&#xff0c;Composer允许您监视软件的安装和使用驱动器上已存在的文件来创建包…

python豆瓣实例,抓取多页数据-应用到知识点:随时数,xpath,间隔请求sleep

源代码: <!DOCTYPE html> <html lang="zh-CN" class="ua-windows ua-webkit"> <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="renderer" content=&q…

计算机网络-VLAN原理与配置

之前我们学习了以太网的基础知识&#xff0c;了解了网络交换设备的发展&#xff0c;交换机的工作原理&#xff0c;广播域和冲突域。 一、概述 还简单了解了以太网的CSMA/CD通讯机制&#xff0c;以太网是建立在CSMA/CD (Carrier Sense Multiple Access/Collision Detection&…

【LMM 014】NExT-GPT:能够输入和生成任意模态的多模态大模型

论文标题&#xff1a;NExT-GPT:Any-to-Any Multimodal Large Language Model 论文作者&#xff1a;Shengqiong Wu, Hao Fei*, Leigang Qu, Wei Ji, Tat-Seng Chua 作者单位&#xff1a; NExT Lab, National University of Singapore 论文原文&#xff1a;https://arxiv.org/abs…

学习笔记——C++运算符之逻辑运算符

作用&#xff1a;用于根据表达式的真值返回真值或假值 逻辑运算符有以下符号&#xff1a; #include<bits/stdc.h> using namespace std; int main(){// 逻辑运算符 非 !int a10;//在c中&#xff0c;除了0均是真 cout<<!a<<endl;//0 cout<<!!a<<…

《MySQL系列-InnoDB引擎06》MySQL锁介绍

文章目录 第六章 锁1 什么是锁2 lock与latch3 InnoDB存储引擎中的锁3.1 锁的类型3.2 一致性非锁定读3.3 一致性锁定读3.4 自增长与锁3.5 外键和锁 4 锁的算法4.1 行锁的三种算法4.2 解决Phantom Problem 5 锁问题5.1 脏读5.2 不可重复读5.3 丢失更新 6 阻塞7 死锁 第六章 锁 开…
最新文章