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

news2025/2/28 2:50:54

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.coloradmin.cn/o/1361536.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

奋楫扬帆,奔赴新程 | 2023 年图扑大事记回顾,与您携手共迎 2024

2023.01 工信部公示了 2022 年度智能制造示范工厂揭榜单位和优秀场景名单。图扑软件和上海洲邦合作建设的宁波甬友数字孪生工厂被评为优秀场景&#xff0c;全国共有 369 个智能制造典型场景入选。 2023.01 在第十一届中国创新创业大赛全国赛&#xff08;新一代信息技术&#…

leetcode12 整数转罗马数字

题目描述&#xff1a;给定一个整数&#xff0c;将其转换为罗马数字。罗马数字由七个字符表示&#xff1a;I&#xff08;1&#xff09;、V&#xff08;5&#xff09;、X&#xff08;10&#xff09;、L&#xff08;50&#xff09;、C&#xff08;100&#xff09;、D&#xff08;5…

100行代码搭建一个IO泄露监测框架

大家好&#xff0c;最近由于项目原因&#xff0c;对IO资源泄漏的监测进行了一番调研深入了解&#xff0c;发现IO泄漏监测框架实现成本比较低&#xff0c;效果很显著&#xff1b;同时由于IO监测涉及到反射&#xff0c;还了解到了通过一种巧妙的方式实现Android P以上非公开api的…

locust 快速入门--一次接口压测

背景&#xff1a; 使用locust&#xff0c;借助webUI&#xff0c;完成一次接口压测 实现步骤&#xff1a; 完成locust环境配置 准备一个locustfile&#xff08;current_limiting_test.py&#xff09; from locust import HttpUser, task, events from locust.env import Envi…

(Python + Selenium4)Web自动化测试自学Day1

目录 文章声明⭐⭐⭐让我们开始今天的学习吧&#xff01;自动打开Chrome浏览器实现自动搜索元素定位常用的元素定位方式By.IDBy.CLASS_NAMEBy.TAG_NAMEBy.NAMEBy.LINK_TEXTBy.PARTIAL_LINK_TEXTBy.CSS_SELECTOR根据id定位根据class定位根据属性定位组合定位 By.XPATH 文章声明⭐…

Spring中的工厂类ApplicationContext和BeanFactory

1.ApplicationContext ApplicationContext的实现类&#xff0c;如下图 ClassPathXmlApplicationContext&#xff1a;加载类路径下 Spring 的配置文件 FileSystemXmlApplicationContext&#xff1a;加载本地磁盘下 Spring 的配置文件 ApplicationContext由BeanFactory派生而…

文件归类妙招:用关键字替换改扩展名方法,文件重命名技巧

在日常工作中&#xff0c;文件的数量会随着时间的推移不断增加。如果文件没有得到适当的归类和整理&#xff0c;就会导致很难找到所需的文件。所以文件归类是非常重要的任务。现在来看云炫文件管理器一些实用的文件归类妙招&#xff1a;用关键字替换修改文件扩展名的方法&#…

【计算机毕业设计】SSM在线化妆品网站

项目介绍 本项目为前后台项目&#xff0c;前台为普通用户登录&#xff0c;后台为管理员登录&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,分类管理,产品管理,用户管理,订单管理等功能。 用户角色包含以下功能&#xff1a; 提交订单,用户登录,用户首页,查看…

程序性能优化全能手册

本文聊一个程序员都会关注的问题&#xff1a;性能。 当大家谈到“性能”时&#xff0c;你首先想到的会是什么&#xff1f; 是每次请求需要多长时间才能返回&#xff1f; 是每秒钟能够处理多少次请求&#xff1f; 还是程序的CPU和内存使用率高不高&#xff1f; 这些问题基本上…

3d全景怎么拍摄?应用领域有哪些?

3d全景技术是综合了VR技术和全景拍摄的一种新型应用技术&#xff0c;通过3D全景技术可以为用户带来720度无死角的观看方式和真实的观看体验&#xff0c;那么3d全景是怎么拍摄制作的呢&#xff1f;应用领域又有哪些呢&#xff1f; 3d全景拍摄制作流程其实不难&#xff0c;常见的…

基于日照时数计算逐日太阳辐射

基于日照时数计算逐日太阳辐射

ffmpeg.c(4.3.1)源码剖析

文章目录 前言一、FFmpeg 源码结构图二、ffmpeg.h 头文件详解三、main 函数主要流程分析四、ffmpeg_parse_options1、命令行例子①、解析命令行 split_commandline()②、parse_optgroup()③、MATCH_PER_XXX_OPT() 2、vf 选项解析①、filters②、vf 术语③、avfilter_graph_pars…

分布式协调系统

分布式协调系统 分布式协调系统解决的进程间的通信和协作&#xff0c;根据是否在同一时间和是否相互引用分为四个模型。 示例系统Chubby 主功能&#xff1a;让客户端实现同步&#xff0c;方法是加锁服务 介绍一下系统&#xff1a; 系统由五台服务器构成&#xff0c;通过pax…

Java:Lambda表达式、方法引用

文章目录 1、Lambda表达式1.1 Lambda表达式体验1.2 Lambda表达式的省略形式1.3 Lambda表达式练习 2、方法引用体验3、方法引用符4、引用静态方法5、引用对象的实例方法6、引用类的实例方法7、引用构造方法8、引用数组的构造方法9、方法引用练习9.1 练习19.2 练习29.3 练习3 10、…

CAN通信的基本原理与实现方法

一. CAN协议概念 1.1 CAN 协议简介 CAN 是控制器局域网络 (Controller Area Network) 的简称&#xff0c;它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的&#xff0c;并最终成为国际标准(ISO11519以及ISO11898),是国际上应用最广泛的现场总线之一。差异点如下&…

​三子棋(c语言)

前言&#xff1a; 三子棋是一种民间传统游戏&#xff0c;又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏规则是双方对战&#xff0c;双方依次在9宫格棋盘上摆放棋子&#xff0c;率先将自己的三个棋子走成一条线就视为胜利。但因棋盘太小&#xff0c;三子棋在很多时候会出现和…

【CentOS 7.9】安装搜狗输入法教程

总览 1.如何在 centos7.x 中使用 搜狗输入法 一、安装依赖 1.切换至 root 用户 su root2.更新 yum yum update3.卸载 ibus rpm -e --nodeps ibus4.安装 epel 源 yum -y install epel-release5.安装 fcitx 环境、qtwebkit包 和 alien 转换工具等&#xff08;可以将 .deb …

静态网页设计——旅游景点介绍(HTML+CSS+JavaScript)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a; https://www.bilibili.com/video/BV1f64y1N7uH/?vd_source5f425e0074a7f92921f53ab87712357b 使用技术&#xff1a;HTMLCSSJS&#xff08;…

系列十一、(三)Sentinel控制台

一、Sentinel控制台 二、实时监控 2.1、概述 实时监控&#xff0c;顾名思义是用来实时监控的&#xff0c;具体监控的是接口请求通过的QPS和拒绝的QPS&#xff0c;默认情况下没有访问记录&#xff0c;所以看不到任何记录&#xff0c;需要访问接口才会有记录。另外需要注意&…

[4K80 AI ISP IPC芯片]

4K80 AI ISP IPC芯片 Hi3403V100是一颗面向监控市场推出的专业 Ultra-HD Smart IP Camera SOC&#xff0c;该芯片最高支持四路sensor输入&#xff0c;支持最高4K60的ISP图像处理能力&#xff0c;支持3F WDR加粗样式、多级降噪、六轴防抖、硬件拼接等多种图像增强和处理算法&am…