Linux字符设备驱动开发一

linux字符设备驱动

    • 0 驱动介绍
    • 1 字符设备驱动
      • 1.1 字符设备相关概念和结构体
      • 1.2 实现简单的字符设备模块
      • 1.3 创建字符设备
      • 1.4 总结

应用程序调用文件系统的API(open、close、read、write) -> 文件系统根据访问的设备类型,调用对应设备的驱动API -> 驱动对硬件进行操作。
请添加图片描述

0 驱动介绍

驱动类型:

  1. 字符设备驱动:以一个字节一个字节读写的硬件对应的驱动。比如串口、watchdog、rtc、鼠标、触摸屏等。字符设备必须以串行顺序依次进行访问。
  2. 块设备驱动:以一个扇区一个扇区(512kb、4096kb)读写的硬件对应的驱动。比如硬盘。块设备可以按任意顺序进行访问。
  3. 网络设备驱动:走内核里的协议栈内存。比如网卡驱动、can驱动。网络设备面向数据包的接收和发送而设计。

除网络设备外,字符设备与块设备都被映射到Linux文件系统的文件和目录,通过文件系统的系统调用接口open()、write()、read()、close()等即可访问字符设备和块设备。

驱动的动态加载和静态加载区别:

  1. 编译方式不同:静态加载–obj-y,动态加载–obj-m
  2. 存在位置不同:静态加载,则直接编译到linux内核镜像中uimag;动态加载,则是独立的ko,驱动代码不在内核镜像中,而是独立存在于文件系统中
  3. 加载时机不同:静态加载,内核启动时就加载;动态加载,在系统启动的时候首先uboot的启动,然后linux内核启动,最后文件系统启动。静态加载,随linux内核一起加载启动,动态加载则需要等文件系统启动后手动insmod加载。

动态加载优势:

  1. 实现热插拔机制:硬件要实现热插拔,则驱动必须使用动态加载。设备插入时,加载驱动;设备移除时,卸载驱动。
  2. 驱动调试:调试更方便。单独编译驱动,通过insmod、rmmod加载和卸载进行单独调试。
  3. 开机优化:有的驱动加载非常耗时,影响设备启动速度。动态加载,可以等用户的图形界面起来后,再加载驱动,让用户感觉系统启动速度加快了。

1 字符设备驱动

字符设备必须以串行顺序依次进行访问,常见的字符设备:串口、watchdog、rtc、鼠标、触摸屏等。字符设备的读写以字节为单位。

1.1 字符设备相关概念和结构体

了解字符设备相关概念和结构体,为之后阅读字符设备驱动代码打下坚实基础。

1 设备号dev_t:类似一个人的身份证号。内核中通过dev_t来描述设备号,其实质是unsigned int 32位整数,其中高12位为主设备号,低20位为次设备号。

  • 为每一个外设设置一个独一无二的ID,让内核能够区分每一个设备。
  • 高12区分不同类别的设备
  • 低20位,区分同一类别的不同设备

linux如何为每一个设备生成设备号呢?
可以是驱动自行构造一个设备号,然后调用函数register_chrdev_region向内核注册。如果注册成功,该函数返回0;注册失败,返回一个负数,就不能用这个设备号。

int register_chrdev_region(dev_t from, unsigned count, const char *name);

2 设备描述cdev、file_operations:
一个linux设备,需要通过cdev结构体来描述设备信息,通过file_operations来描述如何操作设备。最后要通过cdev_add函数来注册设备,让linux内核知道这个设备。

/*设备信息的描述*/
struct cdev {struct kobject kobj; /**/struct module *owner;const struct file_operations *ops; /*一组函数集*/struct list_head list; /*链表*/dev_t dev; /*设备号*/unsigned int count; /*已支持的设备*/
}/*设备行为的描述:驱动设备开发,就要实现这些函数*/
struct file_operations {struct module *owner;ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);int (*open) (struct inode *, struct file *);int (*release) (struct inode *, struct file *);...
};/*相关注册函数*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);

1.2 实现简单的字符设备模块

#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/fs.h>
#define OK (0)
#define ERROR (-1)
#define DEV_NUM (10)/*创建设备ID:MKDEV注册设备ID:给设备行为结构体的成员函数赋值初始化设备:建立设备行为结构体file_operations和设备信息结构体cdev的关系;添加设备:向内核注册设备,使内核知道设备,建立设备ID和设备信息cdev的关系;加载设备:insmod创建设备文件:建立设备文件与设备ID的关系:mknod /dev/my_chr_dev c 232 0测试设备行为结构体的成员函数功能
*/dev_t g_dev_num; /*设备号*/
struct cdev *g_cdev; /*设备信息*/
struct file_operations *g_fops; /*设备行为信息*/
const int g_major = 232; /*主设备号*/
const int g_minor = 0; /*次设备号*/
unsigned g_dev_count = 3;
const char g_dev_name[] = "my_char_dev"; /*设备名称,内核管理用的,随便写*/ssize_t my_dev_read(struct file *my_file, char __user *my_user, size_t my_size, loff_t *my_loff)
{printk(KERN_EMERG "read my dev success\n");return OK;
}ssize_t my_dev_write(struct file *my_file, const char __user *my_user, size_t my_size, loff_t *my_loff)
{printk(KERN_EMERG "write my dev success\n");return OK;
}int my_dev_open(struct inode *my_inode, struct file *my_file)
{printk(KERN_EMERG "open my dev success\n");return OK;
}int my_dev_release(struct inode *my_inode, struct file *my_file)
{printk(KERN_EMERG "release my dev success\n");return OK;
}static int __init chr_init(void)
{g_dev_num = MKDEV(g_major, g_minor); /*生成设备号*/printk("dev id:%d\n", g_dev_num);/*注册设备号*/if (register_chrdev_region(g_dev_num, g_dev_count, g_dev_name) != 0) {printk(KERN_EMERG "register dev id failed\n");return ERROR;}/*内核函数:自动生成设备函数, 避免自定义设备号冲突,导致注册失败if (alloc_chrdev_region(&g_dev_num, g_major, g_dev_count, g_dev_name) != OK) {printk(KERN_EMERG "register dev id failed\n");return ERROR;}*//*为设备信息结构体cdev和设备行为结构体file_operations申请内存;kzalloc相比kmalloc,会将申请的内存地址置为0,*/g_cdev = kzalloc(sizeof(struct cdev), GFP_KERNEL);g_fops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);/*为设备行为结构体成员函数赋值*/g_fops->read = my_dev_read;g_fops->write = my_dev_write;g_fops->open = my_dev_open;g_fops->release = my_dev_release;g_fops->owner = THIS_MODULE; /*一般就赋值为THIS_MODULE宏*/cdev_init(g_cdev, g_fops); /*初始化设备:建立cdev和file_operations的联系*/cdev_add(g_cdev, g_dev_num, g_dev_count); /*向内核注册设备:建立cdev和设备号的联系*/printk(KERN_EMERG "add my_chr_dev success\n");return OK;
}static void __exit chr_exit(void)
{cdev_del(g_cdev); /*删除设备*/unregister_chrdev_region(g_dev_num, g_dev_count); /*注销设备号*/printk(KERN_EMERG "unregister my_chr_dev success\n");
}module_init(chr_init);
module_exit(chr_exit);
MODULE_LICENSE("GPL");

make编译模块后,insmod my_chr_dev加载字符设备驱动。
makefile文件解读:

  • ifneq,首先判断是否定义了内核宏,第一次执行由于没有定义内核宏,先执行else分支,然后执行all。
  • make -C,切换到内核目录内,-M指定编译的模块。此时进入-M指定的模块内,再次执行makefile文件。
  • 此时,内核宏定义了,执行if分支,编译内核模块。
ifneq ($(KERNELRELEASE),)obj-m := my_chr_dev.o
elseKERDIR ?="/lib/modules/$(shell uname -r)"/buildPWD := $(shell pwd)all:$(MAKE) -C $(KERDIR) M=$(PWD)
clean:rm -rf *.o *.ko *mod.c 9.module.o *.order
endif

1.3 创建字符设备

mknod /dev/my_chr_dev c 232 0232 0为主、次设备号。

  • mknod,实际上就是建立设备文件my_chr_dev与设备号232 0之间的联系。

驱动测试程序:

#include <stdio.h>
#include <fcntl.h> /*O_RDWR*/
#include <unistd.h> /*read open write close usleep*/
#define DATA_NUM (10)int main()
{int fd;int wd, rd;char my_buffer[DATA_NUM] = "12345";fd = open("/dev/my_chr_dev", O_RDWR);printf("fd=%d\n", fd);if (fd == -1){printf("open failed\n");return -1;}wd = write(fd, my_buffer, DATA_NUM);rd = read(fd, my_buffer, DATA_NUM);printf("wd=%d, rd=%d\n", wd, rd);return 0;
}

该程序用于测试驱动程序的open、write、read、release。编译执行该驱动程序后,执行dmesg,可以查看内核调用驱动程序对应的open、write、read、release函数时日志信息。

1.4 总结

开发字符设备驱动主要有7步:

  1. 创建设备号MKDEV、注册设备号register_chrdev_region;
  2. 编写设备行为结构体的成员函数:成员函数实现硬件的open/close/read/write等操作。
  3. 建立设备信息结构体cdev与设备行为结构体file_operations之间的联系:cdev_init
  4. 向内核注册设备,建立设备信息结构体与设备号之间的联系:cdev_add
  5. make编译、insmod加载设备驱动。
  6. 创建设备文件,建立设备文件与设备号之间的联系:mknod /dev/chrDev c 主设备号 次设备号
  7. 编写测试脚本,测试驱动程序的open/close/read/write功能。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用微信小程序开启桶装水订购业务

在当今的数字化时代&#xff0c;微信小程序已经成为一种非常流行的在线购物方式。对于桶装水配送行业&#xff0c;利用微信小程序可以提供更加方便快捷的服务&#xff0c;同时也可以提高门店的管理效率。本文将介绍如何制作一个微信小程序&#xff0c;用于支持桶装水配送门店多…

Flask开发类似jenkins构建自动化测试任务工具

1、自动化 某一天你入职了一家高大上的科技公司&#xff0c;开心的做着软件测试的工作&#xff0c;每天点点点&#xff0c;下班就走&#xff0c;晚上陪女朋友玩王者&#xff0c;生活很惬意。 但是美好时光一般不长&#xff0c;这种生活很快被女主管打破。为了提升公司测试效率…

模拟电子技术实验(三)

单选题 1.本实验的实验目的中&#xff0c;输出电阻测量是第几个目的&#xff1f; A. 1个。 B. 2个。 C. 3个。 D. 4个。 答案&#xff1a;C 评语&#xff1a;10分 单选题 2. 有一定输出功率的放大器的 “功率”下面理解正确的是&#xff1f; A. 能…

车辆路径优化问题(VRP)变体及数学模型

车辆路径优化问题变体及数学模型 一、旅行商问题&#xff08;Travelling salesman problem&#xff0c;TSP&#xff09;TSP问题数学模型TSP问题求解 二、车辆路径问题&#xff08;Vehicle Routing Problem&#xff0c;VRP&#xff09;三、带容量约束的车辆路径优化问题&#xf…

【Web】浅聊XStream反序列化本源之恶意动态代理注入

目录 简介 原理 复现 具体分析之前 我们反序列化了个什么&#xff1f; XStream反序列化的朴素通识 具体分析 第一步&#xff1a;unmarshal解组 第二步&#xff1a;readClassType获取动态代理类的Class对象 第三步&#xff1a;调用convertAnother对动态代理类进行实例…

自适应窗口图片轮播HTML代码

自适应窗口图片轮播HTML代码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 代码下载地址 自适应窗口图片轮播HTML代码

Python之requests实现github模拟登录

文章目录 github 模拟登录前言模拟登录流程抓包操作查看登录表单的内容登录操作 模拟登录操作在 main函数的调用获得 auth_token调用/session接口登录处理检测登录是否成功 总结&#xff1a; github 模拟登录 前言 前面学习了requests模块的基础学习后&#xff0c;接下来做一个…

Vulnhub - DC-1

希望和各位大佬一起学习&#xff0c;如果文章内容有错请多多指正&#xff0c;谢谢&#xff01; 个人博客链接&#xff1a;CH4SER的个人BLOG – Welcome To Ch4sers Blog DC-1 靶机下载地址&#xff1a;DC: 1 ~ VulnHub 0x01 信息收集 Nmap扫描目标主机&#xff0c;发现开…

链路聚合实验(思科)

华为设备参考&#xff1a; 一&#xff0c;技术简介 网络设备的链路聚合技术&#xff08;Link Aggregation&#xff09;是一种将多个物理链路捆绑在一起&#xff0c;形成一个逻辑链路的技术。这样做可以增加带宽、提高可靠性和实现负载均衡。 二&#xff0c;实验目的 橙色的阻…

【C++】—— 代理模式

目录 &#xff08;一&#xff09;什么是代理模式 &#xff08;二&#xff09;为什么使用代理模式 &#xff08;三&#xff09;代理模式实现步奏 &#xff08;四&#xff09;代码示例 &#xff08;五&#xff09;代理模式优缺点 &#xff08;一&#xff09;什么是代理模式 …

【代码随想录 | 链表 03】两两交换链表中的节点

文章目录 3.两两交换链表中的节点3.1题目3.2解法&#xff1a;虚拟头节点 3.两两交换链表中的节点 3.1题目 24.两两交换链表中的节点——力扣链接 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下…

数字孪生与智慧城市:实现城市治理现代化的新路径

随着信息技术的迅猛发展&#xff0c;智慧城市已成为城市发展的必然趋势。数字孪生技术作为智慧城市建设的重要支撑&#xff0c;以其独特的优势为城市治理现代化提供了新的路径。本文将探讨数字孪生技术在智慧城市中的应用&#xff0c;以及如何实现城市治理的现代化。 一、数字…