韦东山嵌入式教程第四篇Linux驱动基础知识学习笔记(1)——Hello驱动程序

2023-11-10

驱动入门——Hello驱动程序

1、怎么写出一个驱动程序?

  1. 确定主设备号
  2. 定义自己的file_operations结构体
  3. 实现对应的open、read、write函数,填入file_operations结构体
  4. 实现入口函数。安装驱动程序时,就会调用入口函数,把file_operations结构体告诉内核,注册驱动程序
  5. 实现出口函数。卸载驱动时就会调用该出口函数unregister_chrdev()
  6. 其他完善,提供设备信息,自动创建设备节点
    说明:
    1、file_operations结构体怎么注册?
    通过函数register_chrdev(… , file_operations,…)来注册到内核中的“数组”chrdevs[主设备号](可以自定义主设备号,也可以传入0,让内核给驱动程序分配主设备号)。应用程序在调用open、read、write函数时会根据主设备号选择对应的驱动程序,即从chrdevs[ ]中去去找到对应的file_operations结构体。那么谁来调用这个注册函数register_chrdev()?答案是入口函数。安装驱动时就会调用入口函数来注册file_operations结构体。

应用程序在调用open打开一个文件时会返回一个整数,

m = open ("/dev/xxx" , flags,mode);
这个整数m 对应结构体 struct file{ }

struct file{
	...
	const struct file_operations  *f_op;
	...
	unsigned int       f_flags;  //open中的flags和mode都被保存在这里
	fomde_t            f_mode;
	...
}

去读写文件时,文件的当前偏移地址也会保存在struct file结构体的f_pos成员里。
file_operations结构体中有驱动程序提供的open等函数

struct file_operations{
ssize_t (*read)(......);
ssize_t (*write)(......);
long (unlocked_ioctl)(......);
int(*open)();
int(*release)();
}

2、开始code

可参考内核linux-4.4_xxx中的/drivers/char/misc.c驱动的写法
Hello驱动程序的作用:APP调用write时,传入数据保存到驱动程序中,read把数据再读出来。
以下是hello驱动的代码

//头文件参考misc字符驱动代码copy的
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

/* 1. 确定主设备号                                                                 */
static int major = 0;   //设备号可以自定义,0号是由OS帮我们设定
static char kernel_buf[1024];
static struct class *hello_class;


#define MIN(a, b) (a < b ? a : b)

/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t hello_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)  
//__user是一个空的宏,表示这个buf数组是来自用户空间,不能随意访问
//size_t是一个为了增强程序可移植性而设立的新数据类型,相当于一个无符号整数,在32位机器中是32位,在64位机器中是64位(int在64位机器中也是32位的)。 size_t类型通常用于循环、数组索引、大小的存储和地址运算。
//loff_t用于维护当前读写位置
//ssize_t是signed的
{
	int err;    //定义这个变量是为了用上copy_to_user的返回值,否则就会报错
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_to_user(buf, kernel_buf, MIN(1024, size));//把内核里面的数组内容拷贝到用户的数组里面,第三个参数是拷贝的大小
	return MIN(1024, size);
}

static ssize_t hello_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(kernel_buf, buf, MIN(1024, size));
	return MIN(1024, size);
}

static int hello_drv_open (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int hello_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* 2. 定义自己的file_operations结构体  */
static struct file_operations hello_drv = {
	.owner	 = THIS_MODULE,
	.open    = hello_drv_open,
	.read    = hello_drv_read,
	.write   = hello_drv_write,
	.release = hello_drv_close,
};

/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init hello_init(void)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);//调试信息
	major = register_chrdev(0, "hello", &hello_drv);  /* /dev/hello */
	//第一个参数是默认主设备号,第二个是名字,第三个是file_operations结构体的指针
	hello_class = class_create(THIS_MODULE, "hello_class");
	err = PTR_ERR(hello_class);
	if (IS_ERR(hello_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "hello");
		return -1;
	}
	device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */
	
	return 0;
}

/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit hello_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(hello_class, MKDEV(major, 0));
	class_destroy(hello_class);
	unregister_chrdev(major, "hello");
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

应用程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/*
 * ./hello_drv_test -w abc  写数据
 * ./hello_drv_test -r 	    读数据
 */
int main(int argc, char **argv)
{
	int fd;
	char buf[1024];
	int len;
	
	/* 1. 判断参数 */
	if (argc < 2) 
	{
		printf("Usage: %s -w <string>\n", argv[0]);
		printf("       %s -r\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open("/dev/hello", O_RDWR);   //读写打开
	if (fd == -1)
	{
		printf("can not open file /dev/hello\n");
		return -1;
	}

	/* 3. 写文件或读文件 */
	if ((0 == strcmp(argv[1], "-w")) && (argc == 3))//如果是-w写文件
	{
		len = strlen(argv[2]) + 1;
		len = len < 1024 ? len : 1024;
		write(fd, argv[2], len);  //往设备文件里面写数据
	}
	else
	{
		len = read(fd, buf, 1024);		
		buf[1023] = '\0';
		printf("APP read : %s\n", buf);
	}
	close(fd);
	return 0;
}

简单来说写驱动就是注册设备,实现fops,实现init和exit函数,然后应用程序就能往设备文件中读写传输数据了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

韦东山嵌入式教程第四篇Linux驱动基础知识学习笔记(1)——Hello驱动程序 的相关文章

  • ioctl 命令的用户权限检查

    我正在实现 char 驱动程序 Linux 并且我的驱动程序中有某些 IOCTL 命令仅需要由 ADMIN 执行 我的问题是如何在 ioctl 命令实现下检查用户权限并限制非特权用户访问 IOCTL 您可以使用bool capable in
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • Linux 中 m 标志和 o 标志将存储在哪里

    我想知道最近收到的路由器通告的 m 标志和 o 标志的值 从内核源代码中我知道存储了 m 标志和 o 标志 Remember the managed otherconf flags from most recently received R
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • ALSA:snd_pcm_writei 调用时缓冲区不足

    当运行我最近从灰烬中带回来的旧程序时 我遇到了缓冲区不足的情况 该程序将原始声音文件完全加载到内存中 2100 字节长 525 帧 并准备 ALSA 进行输出 44 1khz 2 通道 有符号 16 位 if err snd pcm set
  • 在 C 中使用单个消息队列是否可以实现双向通信

    我希望服务器向客户端发送一些消息 并让客户端确认它 我被分配了这个任务 我可以在 C linux 中使用单个消息队列来完成它还是我需要创建两个 谢谢 是的 可以使用 sysV 消息队列来做到这一点 从您之前的问题来看 您正在使用该队列 您可
  • tcpdump 是否受 iptables 过滤影响?

    如果我的开发机器有iptables规则到FORWARD一些数据包 这些数据包是否被 tcpdump 捕获 我有这个问题 因为我知道存在其他链称为INPUT如果数据包路由到 它会过滤发往应用程序的数据包FORWARD链 它会到达吗tcpdum
  • 从 ttyUSB0 写入和读取,无法得到响应

    我对 Linux tty 不太有经验 我的环境是带有丰富 USB 串行的 Raspbian 什么有效 stty F dev ttyUSB0 38400 cu l dev ttyUSB0 s 38400 cu to dev ttyUSB0作品
  • 并行运行 shell 脚本

    我有一个 shell 脚本 打乱大型文本文件 600 万行和 6 列 根据第一列对文件进行排序 输出 1000 个文件 所以伪代码看起来像这样 file1 sh bin bash for i in seq 1 1000 do Generat
  • 内核的panic()函数是否完全冻结所有其他进程?

    我想确认内核的panic 功能和其他类似kernel halt and machine halt 一旦触发 保证机器完全冻结 那么 所有的内核和用户进程都被冻结了吗 是panic 可以被调度程序中断吗 中断处理程序仍然可以执行吗 用例 如果
  • 与 pthread 的进程间互斥

    我想使用一个互斥体 它将用于同步对两个不同进程共享的内存中驻留的某些变量的访问 我怎样才能做到这一点 执行该操作的代码示例将非常感激 以下示例演示了 Pthread 进程间互斥体的创建 使用和销毁 将示例推广到多个进程作为读者的练习 inc
  • 如何在 Linux 中使用 C 语言使用共享内存

    我的一个项目有点问题 我一直在试图找到一个有据可查的使用共享内存的例子fork 但没有成功 基本上情况是 当用户启动程序时 我需要在共享内存中存储两个值 当前路径这是一个char and a 文件名这也是char 根据命令参数 启动一个新进
  • 为什么opencv videowriter这么慢?

    你好 stackoverflow 社区 我有一个棘手的问题 我需要你的帮助来了解这里发生了什么 我的程序从视频采集卡 Blackmagic 捕获帧 到目前为止 它工作得很好 同时我用 opencv cv imshow 显示捕获的图像 它也工
  • 使用 python 脚本更改 shell 中的工作目录

    我想实现一个用户态命令 它将采用其参数之一 路径 并将目录更改为该目录 程序完成后 我希望 shell 位于该目录中 所以我想实施cd命令 但需要外部程序 可以在 python 脚本中完成还是我必须编写 bash 包装器 Example t
  • 在生产服务器上使用 Subversion 使文件生效的最佳方法是什么?

    目前我已经设置了 subversion 这样当我在 Eclipse PDT 中进行更改时 我可以提交更改 它们将保存在 home administrator 中项目文件 该文件具有 subversion 推荐的 branches tags
  • Linux 为一组进程保留一个处理器(动态)

    有没有办法将处理器排除在正常调度之外 也就是说 使用sched setaffinity我可以指示线程应该在哪个处理器上运行 但我正在寻找相反的情况 也就是说 我想从正常调度中排除给定的处理器 以便只有已明确调度的进程才能在那里运行 我还知道
  • linux下如何从文本文件中获取值

    我有一些文本格式的文件 xxx conf 我在这个文件中有一些文本 disablelog 1 当我使用 grep r disablelog oscam conf 输出是 disablelog 1 但我只需要值1 请问你有什么想法吗 一种方法
  • 我什么时候应该编写 Linux 内核模块?

    有些人出于某种原因想要将 Linux 中的代码从用户空间移动到内核空间 很多时候 原因似乎是代码应该具有特别高的优先级 或者只是 内核空间更快 这对我来说似乎很奇怪 我什么时候应该考虑编写内核模块 有一套标准吗 我怎样才能激励将代码保存在
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的
  • 无法显示 Laravel 欢迎页面

    我的服务器位于 DigitalOcean 云上 我正在使用 Ubuntu 和 Apache Web 服务器 我的家用计算机运行的是 Windows 7 我使用 putty 作为终端 遵循所有指示https laracasts com ser

随机推荐

  • 【C】PTA期末分数排序(归并排序)

    考试结束了 全班同学的分数都出来了 老师需要对分数做一次排序 看看从高到低 分数的排列是怎样的 输入格式 第一行是一个n 表示班级同学的人数 1 lt n lt 500000 第二行开始有n个分数 0 lt 分数 lt 100 分数都是整数
  • [从零开始学习FPGA编程-23]:进阶篇 - 基本组合电路-门电路级组合逻辑运算(Verilog语言)

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 125246093 目录 前言 第1章
  • 排序函数 dense_rank()

    dense rank 对结果集进行排序 排名值没有间断 特定行的排名等于该特定行之前不同排名值的数量加一 语法 DENSE RANK OVER
  • error when starting dev server: Error: Cannot find module ‘node:url‘

    需要升级node版本 直接在官网 下载 Node js 上下载最新的msi 安装到原来node的安装目录下即可
  • 降雨预测方法

    降雨预测方法 DBNPF Deep Belief Network for Precipitation Forecast 来源 张雷师兄论文 A deep learning based precipitation forecasting 模型
  • centos 7 安装总结

    CentOS Community ENterprise Operating System 社区企业操作系统 Linux发行版之一 来源于Red Hat Enterprise Linux依照开放代码规定释出的源代码所编译而成 安装注意 安装c
  • 如何用python爬取大量博客

    如果要使用 Python 爬取大量博客 可以使用爬虫框架 例如 Scrapy 首先 需要对目标网站进行分析 确定数据的 URL 格式和数据的 HTML 标签 然后 可以使用 Scrapy 来编写爬虫代码 自动访问目标网站并提取需要的数据 具
  • 在 CLion 中进行整体替换代码

    突然在打题的时候发现int型无法满足题目要求 想要批量换成long long 使用快捷键 Ctrl Shift R Windows Linux 或 Command Shift R Mac 来打开 Find and Replace 对话框 在
  • 第一章 python初窥 课后练习题

    目录 1 输入1 127的ascii码并输出对应字符 2 输入a b c d4个整数 计算a b c d的结果 3 计算一周有多少分钟 多少秒钟 4 3个人在餐厅吃饭 想分摊饭费 总共花费35 27美元 他们还想给15 的小费 每个人该怎么
  • SpringBoot: Mybatis配置事务管理

  • awk 正则表达式、正则运算符详细介绍

    http www cnblogs com chengmo archive 2010 10 11 1847772 html 前言 使用awk作为文本处理工具 正则表达式是少不了的 要掌握这个工具的正则表达式使用 其实 我们不必单独去学习它的正
  • STM32CubeMX—串口空闲中断+DMA接收

    一 实验说明 实验平台 STM32F103C8T6 实验内容 使用串口一空闲中断结合DMA 完成不定长数据接收 STM32的串口接收数据的方式 1 轮询接收 所谓轮询 就是在主函数中判断接收完成的标志位 举个不太恰当例子 就比如 此时你正在
  • 【8086汇编】字符串逆序的实现,附代码

    目录 引言 题目 程序思想 程序例程 程序结果 改进思路 引言 之前有本科的同学需要做一个汇编程序 简单补了补发现自己会嘻嘻嘻 题目 题目 从键盘输入任一字符串 字符数 gt 1 在下一行以相反的次序显示出来 如 输入字符串123abc 在
  • 企业微信三方应用开发(三)企业微信第三方应用开发常见问题

    加我微信li570467731 拉你进二百多人企业微信开发同行群 文末有二维码 企业微信开发三部曲 企业微信应用开发概述篇 免费 已完结 企业微信开发第三方应用开发篇 更新中 企业微信开发自建内部应用开发 筹备中 关注公众号 ToB Dev
  • 详细解析Python爬虫代理的使用方法

    嗨 大家好 作为一名专业的代理IP供应商 我想和你们聊一聊爬虫中常用的代理IP类型以及如何在Python中使用代理IP 相信这篇文章会让你对Python爬虫代理IP的使用有更深入的了解 那么 不多说 让我们开始吧 首先 让我们来了解一下爬虫
  • Java基础——环境变量配置、注释、关键字、标识符

    目录 01 01 计算机基础知识 计算机概述 01 02 计算机基础知识 软件开发和计算机语言概述 01 03 计算机基础知识 人机交互 01 04 计算机基础知识 键盘功能键和快捷键 01 05 计算机基础知识 如何打开DOS控制台 01
  • mes选型与实施指南_中小制造企业,如何选型信息系统?

    一 前言 最近走访客户和讲课比较多 接触到了大量的中小制造企业 深刻体会到了制造企业的焦虑 也深刻体会到了中小企业期望进行数字化转型 期望实现智能制造的迫切愿望 但是受限于人才 受限于知识 受限于经验 对实现企业信息化的过程中 过多受到乙方
  • Revit 2011二次开发之得到选择的对象

    start Transaction TransactionMode Manual Regeneration RegenerationOption Manual public class Document Selection IExterna
  • 微前端介绍

    提到微前端 稍微懂微前端的同学 可能会这样问 为什么不用iframe方案呢 其实 如果不考虑体验问题 iframe方案几乎是最完美的微前端解决方案 iframe最大的特性就是提供了浏览器原生的硬隔离方案 样式隔离 js隔离 但它最大的问题也
  • 韦东山嵌入式教程第四篇Linux驱动基础知识学习笔记(1)——Hello驱动程序

    驱动入门 Hello驱动程序 1 怎么写出一个驱动程序 确定主设备号 定义自己的file operations结构体 实现对应的open read write函数 填入file operations结构体 实现入口函数 安装驱动程序时 就会