Linux 驱动开发基础知识——Hello驱动程序(一)

这篇具有很好参考价值的文章主要介绍了Linux 驱动开发基础知识——Hello驱动程序(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 个人名片:

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件

🦁作者简介:一名喜欢分享和记录学习的在校大学生
🐯个人主页:妄北y

🐧个人QQ:2061314755

🐻个人邮箱:2061314755@qq.com
🦉个人WeChat:Vir2021GKBS
🐼本文由妄北y原创,首发CSDN🎊🎊🎊
🐨座右铭:大多数人想要改造这个世界,但却罕有人想改造自己。

专栏导航:

妄北y系列专栏导航:

C/C++的基础算法:C/C++是一种常用的编程语言,可以用于实现各种算法,这里我们对一些基础算法进行了详细的介绍与分享。🎇🎇🎇

C/C++刷题库:分享一些关于编程的练习基础题,也会后续加入一系列的算法题,分享自己的解题思路和方法。🥰🥰🥰

计算机网络:对计算机网络的基础知识框架有一个简单的学习与认识,对计算机网络中常见的题型进行一个总结与归纳。🍾🍾🍾

QT基础入门学习:对QT的基础图形化页面设计进行了一个简单的学习与认识,利用QT的基础知识进行了翻金币小游戏的制作🤹🤹🤹

Linux基础编程:初步认识什么是Linux,为什么学Linux,安装环境,进行基础命令的学习,入门级的shell编程。🍻🍻🍻

Linux的系统编程+网络编程:IO编程、进程、线程、进程间通讯(包括管道、信号、信号量、共享内存等)网络编程主要就是socket,poll,epoll,以及对TCP/IP的理解,同时要学会高并发式服务器的编写。🙌🙌🙌

Linux应用开发基础开发:分享Linux的基本概念、命令行操作、文件系统、用户和权限管理等,网络编程相关知识,TCP/IP 协议、套接字(Socket)编程等,可以实现网络通信功能。💐💐💐

Linux项目开发:Linux基础知识的实践,做项目是最锻炼能力的一个学习方法,这里我们会学习到一些简单基础的项目开发与应用,而且都是毕业设计级别的哦。🤸🤸🤸


非常期待和您一起在这个小小的互联网世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨ 

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件

文章介绍:

🎉本篇文章对Linux驱动基础学习的相关知识进行分享!🥳🥳🥳

如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加油,一起奔跑,让我们顶峰相见!!!💪💪💪

🎁感谢大家点赞👍收藏⭐评论✍️

一、Hello 驱动(不涉及硬件操作)

我们选用的内核都是 4.x 版本,操作都是类似的:

rk3399 linux 4.4.154

rk3288 linux 4.4.154

imx6ul linux 4.9.88

am3358 linux 4.9.168

1.如何编写驱动程序

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件

 2.APP 打开的文件在内核中如何表示

        APP 打开文件时,可以得到一个整数,这个整数被称为文件句柄。对于 APP 的每一个文件句柄,在内核里面都有一个“struct file”与之对应。

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件

        可以猜测,我们使用 open 打开文件时,传入的 flags、mode 等参数会被记录在内核中对应的 struct file 结构体里(f_flags、f_mode): 

int open(const char *pathname, int flags, mode_t mode);

        去读写文件时,文件的当前偏移地址也会保存在 struct file 结构体的 f_pos 成员里。

3. 打开字符设备节点时,内核中也有对应的 struct file

        注意这个结构体中的结构体:struct file_operations *f_op,这是由驱动程序提供的。

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件

结构体 struct file_operations 的定义如下:Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件 

二、编写代码 

1.写驱动程序

参考 driver/char 中的程序,包含头文件,写框架,传输数据:

(1)驱动中实现 open, read, write, release,APP 调用这些函数时,都打印内核信息

(2)APP 调用 write 函数时,传入的数据保存在驱动中

(3)APP 调用 read 函数时,把驱动中保存的数据返回给 APP

2.hello_drv.c:

#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;
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)
{
	int err;
	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 */


	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");


 参考Linux的内核进行编写程序D:\linux\Linux-4.9.88\Linux-4.9.88\drivers\char(字符驱动程序)

第18~24行:确定主设备号

        第19行:主设备号

        第20行:将write得到的数据传入数组  kernel_buf

        第24行:得到数据长度的最小值

第26~53行:实现对应的open/read/write等函数,填入file_operations结构体 

        第31行:读取 kernel_buf中的信息

        第39行:将信息写入 kernel_buf

第55~62行:定义自己的file_operations结构体 

        在结构体内设置open, read, write, release这四个成员

第66~85行:得有一个入口函数:安装驱动程序时,就会去调用这个入口函数

        第82行:创造设备  

第87~94行:得到一个出口函数:卸载驱动程序时,就会去调用这个出口函数

        第92行:销毁设备

第99~102行:提供设备信息,自动创建设备节点

        第102行:增加GPL协议

        阅读一个驱动程序,从它的入口函数开始,第 66 行就是入口函数。它的主要工作就是第 71 行,向内核注册一个 file_operations 结构体:hello_drv, 这就是字符设备驱动程序的核心。        

        file_operations 结构体 hello_drv 在第 56 行定义,里面提供了 open/read/write/release 成员,应用程序调用 open/read/write/close 时就会导致这些成员函数被调用。 

        file_operations 结构体 hello_drv 中的成员函数都比较简单,大多数只是打印而已。要注意的是,驱动程序和应用程序之间传递数据要使用 copy_from_user/copy_to_user 函数。

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件

3.写测试程序

测试程序要实现写、读功能:

./hello_drv_test -w www.100ask.net // 把字符串“www.100ask.net”发给驱动程序
./hello_drv_test -r // 把驱动中保存的字符串读回来

#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))
	{
		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;
}


 三、测试

编写驱动程序的 Makefile

         驱动程序中包含了很多头文件,这些头文件来自内核,不同的 ARM 板它的某些头文件可能不同。所以编译驱动程序时,需要指定板子所用的内核的源码路径。、

        要编译哪个文件?这也需要指定,设置 obj-m 变量即可,怎么把.c 文件编译为驱动程序.ko?这要借助内核的顶层 Makefile。 本驱动程序的 Makefile 内容如下:


# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册

KERN_DIR = KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f hello_drv_test

obj-m	+= hello_drv.o

        先设置好交叉编译工具链,编译好你的板子所用的内核,然后修改 Makefile 指定内核源码路径,最后即可执行 make 命令编译驱动程序和测试程序。 

        注意:我们是在 Ubuntu 中编译程序,但是需要在 ARM 板子上测试。所以需要把程序放到 ARM 板子上。

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件

        启动单板后,可以通过 NFS 挂载 Ubuntu 的某个目录,访问该目录中的程序。

cp *.ko hello_drv_test ~/nfs_rootfs/

        在 ARM 板上测试:

     //安装驱动程序

[root@100ask:/mnt]# insmod hello_drv.ko

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件

// 驱动程序会生成设备节点 

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件

// 查看测试程序的用法 

//往驱动程序中写入字符串

// 从驱动程序中读出字符串

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件 

大佬觉得有用的话点个赞 👍🏻 呗。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥任务在无形中完成,价值在无形中升华,让我们一起加油吧!🌙🌙🌙

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件

Linux 驱动开发基础知识——Hello驱动程序(一),Linux 驱动开发基础知识,linux,运维,服务器,驱动开发,库函数,嵌入式硬件文章来源地址https://www.toymoban.com/news/detail-804390.html

到了这里,关于Linux 驱动开发基础知识——Hello驱动程序(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 1. 驱动开发--基础知识

    该文内容源于朱有鹏老师的课程,按照自己的理解进行汇总,方便查阅。如有侵权,请告知删除。 驱动一词的字面意思 物理上的驱动 硬件中的驱动 linux内核驱动   软件层面的驱动广义上就是指:这一段代码操作硬件去动,所以这一段代码就叫硬件的驱动程序。( 本质上

    2024年02月09日
    浏览(40)
  • 【IMX6ULL驱动开发学习】02.hello驱动程序之cdev注册字符设备驱动程序和设置次设备号

    目录 一、register_chrdev 二、解决方法 2.1 alloc_chrdev_region函数:注册一系列字符设备编号 2.2 cdev_init函数:初始化cdev结构体  2.3  cdev_add函数:将字符设备添加到系统中  三、驱动程序 【IMX6ULL驱动开发学习】01.编写第一个hello驱动+自动创建设备节点(不涉及硬件操作)_阿龙还

    2024年02月14日
    浏览(40)
  • Linux驱动开发—最详细应用程序调用驱动程序解析

    Linux下进行驱动开发,完全将驱动程序与应用程序隔开,中间通过 C标准库函数 以及 系统调用 完成驱动层和应用层的数据交换。 驱动加载成功以后会在“/dev”目录下生成一个相应的文件,应用程序通过 对“/dev/xxx” (xxx 是具体的驱动文件名字) 的文件进行相应的操作 即可实

    2024年02月16日
    浏览(47)
  • 嵌入式Linux驱动开发 02:将驱动程序添加到内核中

    在上一篇文章 《嵌入式Linux驱动开发 01:基础开发与使用》 中我们已经实现了最基础的驱动功能。在那篇文章中我们的驱动代码是独立于内核代码存放的,并且我们的驱动编译后也是一个独立的模块。在实际使用中将驱动代码放在内核代码中,并将驱动编译到内核中也是比较

    2023年04月09日
    浏览(73)
  • Linux设备驱动开发学习笔记(等待队列,锁,字符驱动程序,设备树,i2C...)

    container_of函数可以通过结构体的成员变量检索出整个结构体 函数原型: 内核开发者只实现了循环双链表,因为这个结构能够实现FIFO和LIFO,并且内核开发者要保持最少代码。 为了支持链表,代码中要添加的头文件是linux/list.h。内核中链表实现核心部分的数据结构 是struct li

    2024年01月22日
    浏览(55)
  • 在windows通过VS Code开发Linux内核驱动程序

    最近在看Linux设备驱动程序第三版,为了在windows系统上练手操作,先是下载VMware Workstation安装了Linux系统虚拟机。然后在vscode上编写简单的示例程序,通过ftp把源文件发送到Linux虚拟机后,再在虚拟机上make编译测试内核驱动程序。这样即使是在内核日志中打印个简单的hello w

    2024年02月06日
    浏览(54)
  • 基础篇010.1 STM32驱动RC522 RFID模块之一:基础知识

    目录 1. RFID概述 1.1 RFID工作原理 1.2 RFID分类 1.3 RFID模块 1.4 RFID卡片 1.5 IC卡和ID卡介绍 1.6 IC卡和ID的区分 2. Mifare卡结构原理 2.1 Mifare卡概述 2.2 Mifare非接触式 IC 卡性能简介(M1) 2.2.1 Mifare S50与Mifare S70 2.2.2 S50存储结构 2.2.3 M1射频卡工作原理 2.2.4 M1射频卡与读写器的通讯 2.3 MRF52

    2024年02月09日
    浏览(49)
  • 信息系统项目管理基础知识学习笔记 - IT 治理基础 - IT治理的驱动因素

    信息系统项目管理基础知识学习笔记 - IT 治理基础 - IT治理的驱动因素 IT治理的驱动因素 组织的IT战略 驱动组织开展高质量IT治理因素 IT治理的内涵 IT 治理体系

    2024年02月11日
    浏览(50)
  • ARM开发基础知识

    概念:寄存器是处理器内部的存储器,没有地址 作用:一般用于暂时存储参与运算的数据和运算结果 分类: 通用寄存器、专用寄存器、 状态 寄存器 注意:有标签(带三角光标)的是独有的寄存器 总结: ARM7,9,11 有37个寄存器 30 个通用寄存器 1 个用作PC( program counter) 1个

    2024年02月02日
    浏览(39)
  • Linux底层基础知识

    汇编,C语言,C++可以通过不同的编译器,编译成机器码。而java只能由Java虚拟机识别。Java虚拟机可以看成一个操作系统,Java虚拟机是由汇编,C,Linux等编写而成的一个操作系统(面向os) 不同的芯片,底层的CISC指令集不同,所以其机器码有区别,因此汇编不能跨平台。特定

    2024年02月07日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包