【linux字符设备驱动-01】创建一个字符设备驱动

目录

  • 一、创建字符设备
    • 1、申请设备号
      • 方法一
      • 方法二
    • 2、创建类
      • 方法一
      • 方法二
    • 3、创建设备
  • 二、创建字符设备驱动
    • 1、初始化:cdev_init
    • 2、添加到内核:cdev_add
  • 三、一个完整的字符设备驱动程序
    • 1、驱动源码
    • 2、测试demo

一、创建字符设备

1、申请设备号

方法一

alloc_chrdev_region:在未指定设备号的情况下使用

dev_t devid;
alloc_chrdev_region(&devid, 0, 1, "test");
major = MAJOR(devid); /* 获取主设备号 */
minor = MINOR(devid); /* 获取次设备号 */

方法二

register_chrdev_region:在给定设备号的情况下使用

int major;
int minor;
dev_t devid;
devid = MKDEV(major, minor);
register_chrdev_region(devid, 1, "test");

通 过 alloc_chrdev_region 或者 register_chrdev_region 申请的设备号,释放函数都是:unregister_chrdev_region。

2、创建类

方法一

class_register:把类注册进内核,类需要提前准备好。与class_unregister配对使用。

//用法一:先申请内存,再初始化
struct class *my_class;my_class= kzalloc(sizeof(*my_class), GFP_KERNEL);
my_class->name = "my_class";
my_class->owner = THIS_MODULE;class_register(my_class);	// 会在/sys/class/下创建"my_class"目录//用法二:定义结构体
struct class my_class= {.name		= "my_class",.owner 		= THIS_MODULE,
};class_register(&my_class);	// 会在/sys/class/下创建"my_class"目录

方法二

class_create:创建类,并注册进内核,返回类结构体指针。与class_destroy配对使用。

struct class *my_class;
my_class= class_create(THIS_MODULE, "my_class");	// 会在/sys/class/下创建"my_class"目录

阅读class_create的源码会发现,class_create和class_register最后都调用了__class_register。两者都会在/sys/class/下创建设备类节点(是个文件夹)。

3、创建设备

device_create:创建设备,需要传入class和设备号,返回device结构体指针。与device_destroy配对使用。

struct device *device;
device = device_create(class, NULL, devid, NULL, "device_name");	// 会在/dev/下创建"device_name"设备节点

内核源码中实际上device_create用的并不多,反而是device_register与device_add更常见,其实它们之间的调用关系是:device_create->device_registe->device_add。在较新版本的内核中是:device_create->device_create_groups_vargs->device_add。

二、创建字符设备驱动

通过上面的第一部分,可以完成一个字符设备的创建,并在/dev/下面生成对应的设备节点。但是此时还不能够对设备节点进行write/read/ioctl等操作,因为并没有为设备注册相应的驱动。cdev是Linux内核中的一个结构体,定义在<linux/cdev.h>中,代表字符设备驱动的实例。cdev结构体中包含了字符设备驱动所需的各种信息,如设备号、设备文件操作函数等。通过cdev可以将设备文件操作函数与设备驱动关联起来,实现对字符设备的操作。

1、初始化:cdev_init

struct cdev my_cdev;// 文件操作描述符
static struct file_operations my_fops = {.owner 			= THIS_MODULE,.open 			= my_open,.release 		= my_release,.read 			= my_read,.write 			= my_write,.unlocked_ioctl = my_ioctl,
};cdev_init(&my_cdev, &my_fops);

2、添加到内核:cdev_add

cdev_add(&my_cdev, devid, 1);

cdev_init完成对cdev结构体的初始化,cdev_add将cdev注册进内核,并与设备号关联起来。当应用层对设备节点进行操作时,会根据设备节点的设备号去内核中匹配对应的文件操作描述符,之后调用其中的回调函数。
卸载驱动时,使用cdev_del函数将字符设备驱动从内核中删除。

三、一个完整的字符设备驱动程序

一个字符设备驱动除了包含必要设备号dev_t、所属类class、设备device、驱动cdev这些信息,此外还会有其它私有数据。为方便管理,将所有信息定义到一个结构体中。

1、驱动源码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/uaccess.h>#define CLASS_NAME 		"my_class"
#define DEVICE_NAME 	"my_device"
#define BUFFER_SIZE 	1024
#define MY_IOCTL_RESET 	_IO('M', 0)struct chardev {char name[32];dev_t devid;struct cdev cdev;struct class *class;struct device *device;int major;int minor;
};static char buffer[BUFFER_SIZE];
static struct chardev my_dev;static int my_open(struct inode *inode, struct file *file)
{pr_info("%s\n", __func__);return 0;
}static int my_release(struct inode *inode, struct file *file)
{pr_info("%s\n", __func__);return 0;
}static ssize_t my_read(struct file *file, char __user *user_buffer, size_t count, loff_t *offset)
{ssize_t bytes_read = 0;int remaining_bytes = 0;remaining_bytes = BUFFER_SIZE - *offset;if (remaining_bytes == 0) {return 0;}bytes_read = min_t(size_t, count, remaining_bytes);if (copy_to_user(user_buffer, buffer + *offset, bytes_read)) {return -EFAULT;}*offset += bytes_read;return bytes_read;
}static ssize_t my_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *offset)
{ssize_t bytes_written = 0;int remaining_bytes = 0;remaining_bytes = BUFFER_SIZE - *offset;if (remaining_bytes == 0) {return -ENOSPC;}bytes_written = min_t(size_t, count, remaining_bytes);if (copy_from_user(buffer + *offset, user_buffer, bytes_written)) {return -EFAULT;}*offset += bytes_written;return bytes_written;
}static long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{switch (cmd) {case MY_IOCTL_RESET:memset(buffer, 0, BUFFER_SIZE);pr_info("buffer reset\n");break;default:return -EINVAL;}return 0;
}static struct file_operations my_fops = {.owner 			= THIS_MODULE,.open 			= my_open,.release 		= my_release,.read 			= my_read,.write 			= my_write,.unlocked_ioctl = my_ioctl,
};static int __init chardev_init(void)
{int ret;// 1. 分配设备号ret = alloc_chrdev_region(&my_dev.devid, 0, 1, "my_devid");if (ret < 0) {pr_err("Failed to allocate device number: %d\n", ret);return ret;}my_dev.major = MAJOR(my_dev.devid);my_dev.minor = MINOR(my_dev.devid);pr_info("major = %d, minor = %d\n", my_dev.major, my_dev.minor);// 2. 初始化cdev结构体、添加到内核cdev_init(&my_dev.cdev, &my_fops);ret = cdev_add(&my_dev.cdev, my_dev.devid, 1);if (ret < 0) {pr_err("Failed to add cdev: %d\n", ret);unregister_chrdev_region(my_dev.devid, 1);	// 记得注销设备号return ret;}// 3. 创建类my_dev.class = class_create(THIS_MODULE, CLASS_NAME);if (IS_ERR(my_dev.class)) {ret = PTR_ERR(my_dev.class);pr_err("Failed to create class: %d\n", ret);return ret;}// 4. 创建设备my_dev.device = device_create(my_dev.class, NULL, my_dev.devid, NULL, DEVICE_NAME);if (IS_ERR(my_dev.device)) {ret = PTR_ERR(my_dev.device);pr_err("Failed to create device: %d\n", ret);class_destroy(my_dev.class);	// 记得注销类return ret;}pr_info("%s done\n", __func__);return 0;
}static void __exit chardev_exit(void)
{device_destroy(my_dev.class, my_dev.devid);class_destroy(my_dev.class);cdev_del(&my_dev.cdev);unregister_chrdev_region(my_dev.devid, 1);pr_info("%s done\n", __func__);
}module_init(chardev_init);
module_exit(chardev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");

2、测试demo

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>#define DEVICE_PATH "/dev/my_device"
#define MY_IOCTL_RESET _IO('M', 0)
static int fd;
static char buffer[256];int file_write(void)
{ssize_t ret;// 打开设备文件fd = open(DEVICE_PATH, O_RDWR);if (fd < 0) {perror("Failed to open device");return -1;}// 写入数据到设备文件ret = write(fd, "Hello, device!", 14);if (ret < 0) {perror("Failed to write to device");close(fd);return -1;}printf("Write data to device: Hello, device!\n");// 关闭设备文件close(fd);return 0;
}int file_read(void)
{ssize_t ret;// 打开设备文件fd = open(DEVICE_PATH, O_RDWR);if (fd < 0) {perror("Failed to open device");return -1;}// 读取设备文件中的数据ret = read(fd, buffer, sizeof(buffer));if (ret < 0) {perror("Failed to read from device");close(fd);return -1;}printf("Read data from device: %s\n", buffer);// 关闭设备文件close(fd);return 0;
}int file_ioctl(void)
{ssize_t ret;// 打开设备文件fd = open(DEVICE_PATH, O_RDWR);if (fd < 0) {perror("Failed to open device");return -1;}// 重置设备文件中的数据ret = ioctl(fd, MY_IOCTL_RESET);if (ret < 0) {perror("Failed to ioctl");close(fd);return -1;}printf("Device buffer reset\n");// 关闭设备文件close(fd);return 0;
}int main(void)
{file_write();file_read();file_ioctl();return 0;
}

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

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

相关文章

腾讯汤道生:超千亿参数 超2万亿tokens 腾讯混元大模型向行业全面开放

9月7日&#xff0c;2023腾讯全球数字生态大会在深圳宝安举行。腾讯集团高级执行副总裁、云与智慧产业事业群CEO汤道生表示&#xff0c;腾讯将迈入“全面拥抱大模型”时代&#xff1a;“以大模型生成技术为核心&#xff0c;人工智能正在成为下一轮数字化发展的关键动力&#xff…

stable diffusion实践操作-SD原理

系列文章目录 本文专门开一节写SD原理相关的内容&#xff0c;在看之前&#xff0c;可以同步关注&#xff1a; stable diffusion实践操作 文章目录 系列文章目录前言一、原理说明1.1、出图原理1.1.1 AI画画不是和人一样&#xff0c;从0开始&#xff0c;而是一个去噪点的过程&am…

华为云云服务器评测|详解 Nacos 安装部署

环境配置 服务器云耀云服务器L操作系统CentOS 7.9 64bit | 公共镜像JDK64 bit JDK 1.8MavenMaven 3.2.xnacos-server2.2.3 下载地址 官方githubRelease 2.2.3 (May 25th, 2023) alibaba/nacos GitHub百度网盘链接&#xff1a;https://pan.baidu.com/s/1K8UE6iJL2ZnosUY83b…

金鸣识别名片识别模块 ,名片扫描仪的神仙“伴侣”

名片扫描仪是现代办公中常见的设备&#xff0c;其作用是将纸质名片转换为电子格式并进行识别。在实现这一功能方面&#xff0c;使用自带OCR功能和金鸣识别两种方式均具有各自的优势。 一方面&#xff0c;自带OCR功能的名片扫描仪具有便捷性和即时性的优势。通过设备内置的OCR技…

Qt Creator使用Clang Format方法

Qt Creator使用Clang Format 习惯性的想格式化代码&#xff0c;发现Qt Creator默认居然是没有代码格式化的&#xff0c;只有一个缩进。 Qt Creater中有个插件&#xff1a;beautifier&#xff0c;在"帮助->关于"插件中&#xff0c;开启“Beautifier”即可&#xf…

CSS内联样式与外联样式

第一种内联样式 通过HTML元素的style属性来设置CSS样式&#xff0c;语法如下: style"css属性:css属性值;"示例代码: <!DOCTYPE html> <html><head><title>01_第一种使用方式.html</title></head><body><!-- style&q…

[PHP]empty一直返回true

class Post {public function __get($key){return true;} }$post new Post(); var_dump(empty($post->a));// bool(true) PHP: 重载 - Manual 读取不可访问&#xff08;protected 或 private&#xff09;或不存在的属性的值时&#xff0c;__get() 会被调用。 当对不可访…

开源库网格算法比较

对于Mesh&#xff0c;我们通常分为结构化网格和非结构化网格。理解很简单&#xff0c;除了四边形和六面体是结构化网格&#xff0c;其它都是非结构化网格 最近在学习网格算法&#xff0c;本人关心的主要是3D网格相关的算法&#xff0c;总结了一下主要包括&#xff1a;网格生成…

删除单链表偶数节点

本题要求实现两个函数&#xff0c;分别将读入的数据存储为单链表、将链表中偶数值的结点删除。链表结点定义如下&#xff1a; struct ListNode { int data; struct ListNode *next; }; 函数接口定义&#xff1a; struct ListNode *createlist(); struct ListNode *deleteeven( …

Git的安装以及基础使用方法

Git是一种分布式版本控制系统&#xff0c;被广泛用于管理代码、文档和任何其他类型的数据。它允许开发者在团队中协作&#xff0c;并且在处理大型项目时可以保持代码的完整性。 这里写目录标题 一、安装和设置二、基本的Git命令三、分支和合并四、标签和远程仓库 一、安装和设置…

架构师spring boot 面试题

spring boot 微服务有哪些特点&#xff1f; Spring Boot 微服务具有以下特点&#xff1a; 独立性&#xff1a;每个微服务都是独立的部署单元&#xff0c;有自己的代码库和数据库。这使得微服务可以独立开发、测试、部署和扩展。 分布式&#xff1a;微服务架构将一个大型应用程…

pdf怎么转换成jpg图片?

随着数字文档的广泛应用&#xff0c;将PDF转换为JPG图片格式成为了一个常见的需求。无论是为了在网页上展示内容&#xff0c;还是为了与他人分享图片&#xff0c;以下是一些简单的方法&#xff0c;帮助您将PDF文件快速转换为高质量的JPG图片。 方法一&#xff1a;在线PDF转JPG…

手写Mybatis:第12章-完善ORM框架,增删改查操作

文章目录 一、目标&#xff1a;完善增删改查二、设计&#xff1a;完善增删改查三、实现&#xff1a;完善增删改查3.1 工程结构3.2 完善增删改查类图3.3 扩展解析元素3.4 新增执行方法3.4.1 执行器接口添加update3.4.2 执行器抽象基类3.4.3 简单执行器 3.5 语句处理器实现3.5.1 …

Nginx - 根据请求参数路由进行不同的响应

文章目录 需求思路 需求 业务有一个统一入口 /api/biz?type1 /api/biz/type2需要对不同的接口实现流控 最常见的是通过location进行路径匹配的时候&#xff0c;但是无法使用正则表达一起捕获这个路径和querstring的参数。如果我们想通过URL里面的Query String进行不同的rew…

ChatGPT 插件 “Consensus“ 实现论文搜索功能;数据工程在语言建模中的重要性

&#x1f989; AI新闻 &#x1f680; ChatGPT 插件 “Consensus” 实现论文搜索功能 摘要&#xff1a;OpenAI 推出了一个名为 “Consensus” 的插件&#xff0c;可在 ChatGPT 上进行论文搜索。用户只需用一句话描述自己想了解的问题&#xff0c;插件就能从 2 亿篇论文中搜索并…

关于unordered_map中元素的插入顺序与遍历顺序问题

最近做一个项目遇到一个问题困扰我两天,差点自闭。经排查是 插入到unordered_map中的键值对的顺序和实际的键值对存在的顺序没有关系 众所周知,unordered_map插入元素后,键值不会自动排序,但是顺序却不一定和插入顺序一致。msvc和gcc编译器也各不相同。 std::unordered_map&l…

使用Puppeteer爬取地图上的用户评价和评论

导语 在互联网时代&#xff0c;获取用户的反馈和意见是非常重要的&#xff0c;它可以帮助我们了解用户的需求和喜好&#xff0c;提高我们的产品和服务质量。有时候&#xff0c;我们需要从地图上爬取用户对某些地点或商家的评价和评论&#xff0c;这样我们就可以分析用户对不同…

Java方法的使用

目录 一、方法的概念及使用 二、方法的重载 三、递归 一、方法的概念及使用 1、方法的概念 2、方法定义 3、方法调用的执行过程 4、实参和形参的关系(重要) 5、没有返回值的方法 二、方法的重载 三、递归 1、递归的概念 一、方法的概念及使用 1、方法的概念 方法…

java八股文面试[数据库]——InnoDB与MyISAM的区别

InnoDB和MyISAM是使用MySQL时最常用的两种引擎类型&#xff0c;我们重点来看下两者区别。 事务和外键 InnoDB支持事务和外键&#xff0c;支持回滚&#xff0c;具有安全性和完整性&#xff0c;适合大量insert或update操作 MyISAM不支持事务和外键&#xff0c;它提供高速存储和…

ElementUI浅尝辄止29:Breadcrumb 面包屑

显示当前页面的路径&#xff0c;快速返回之前的任意页面。 1.如何使用&#xff1f; 在el-breadcrumb中使用el-breadcrumb-item标签表示从首页开始的每一级。Element 提供了一个separator属性&#xff0c;在el-breadcrumb标签中设置它来决定分隔符&#xff0c;它只能是字符串&am…