注册字符设备

news2025/1/12 13:40:38

五、注册字符设备

struct cdev
{
	struct kobject kobj;//表示该类型实体是一种内核对象
	struct module *owner;//填THIS_MODULE,表示该字符设备从属于哪个内核模块
	const struct file_operations *ops;//指向空间存放着针对该设备的各种操作函数地址
	struct list_head list;//链表指针域
	dev_t dev;//设备号
	unsigned int count;//设备数量
};

自己定义的结构体中必须有一个成员为 struct cdev cdev,两种方法定义一个设备:

  1. 直接定义:定义结构体全局变量

  2. 动态申请:

    struct cdev * cdev_alloc()

void cdev_init(struct cdev *cdev,const struct file_operations *fops)

struct file_operations 
{
   struct module *owner;           //填THIS_MODULE,表示该结构体对象从属于哪个内核模块
   int (*open) (struct inode *, struct file *);	//打开设备
   int (*release) (struct inode *, struct file *);	//关闭设备
   ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);	//读设备
   ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);    //写设备
   loff_t (*llseek) (struct file *, loff_t, int);		//定位
   long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);//读写设备参数,读设备状态、控制设备
   unsigned int (*poll) (struct file *, struct poll_table_struct *);	//POLL机制,实现多路复用的支持
   int (*mmap) (struct file *, struct vm_area_struct *); //映射内核空间到用户层
   int (*fasync) (int, struct file *, int); //信号驱动
   //......
};

该对象各个函数指针成员都对应相应的系统调用函数,应用层通过调用系统函数来间接调用这些函数指针成员指向的设备驱动函数:

在这里插入图片描述

一般定义一个struct file_operations类型的全局变量并用自己实现各种操作函数名对其进行初始化

int cdev_add(struct cdev *p,dev_t dev,unsigned int count)
功能:将指定字符设备添加到内核
参数:
	p:指向被添加的设备
	dev:设备号
	count:设备数量,一般填1
void cdev_del(struct cdev *p)
功能:从内核中移除一个字符设备
参数:	
	p:指向被移除的字符设备

小结:

字符设备驱动开发步骤:

  1. 如果设备有自己的一些控制数据,则定义一个包含struct cdev cdev成员的结构体struct mydev,其它成员根据设备需求,设备简单则直接用struct cdev
  2. 定义一个struct mydev或struct cdev的全局变量来表示本设备;也可以定义一个struct mydev或struct cdev的全局指针(记得在init时动态分配)
  3. 定义三个全局变量分别来表示主设备号、次设备号、设备数
  4. 定义一个struct file_operations结构体变量,其owner成员置成THIS_MODULE
  5. module init函数流程:a. 申请设备号 b. 如果是全局设备指针则动态分配代表本设备的结构体元素 c. 初始化struct cdev成员 d. 设置struct cdev的owner成员为THIS_MODULE e. 添加字符设备到内核
  6. module exit函数:a. 注销设备号 b. 从内核中移除struct cdev c. 如果如果是全局设备指针则释放其指向空间
  7. 编写各个操作函数并将函数名初始化给struct file_operations结构体变量

验证操作步骤:

  1. 编写驱动代码mychar.c
  2. make 生成ko文件
  3. sudo insmod mychar.ko 插入内核模块
  4. 查阅字符设备用到的设备号(主设备号):cat /proc/devices | grep <申请设备号时用的名字 (mychar)>
  5. 创建设备文件(设备节点) : mknod /dev/mydev c 上一步查询到的主设备号 代码中指定初始次设备号
  6. 编写app验证驱动(testmychar_app.c)
  7. 编译运行app,dmesg命令查看内核打印信息

在这里插入图片描述

代码实现:

mychar.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>

int major = 11;
int minor = 0;
int mychar_num  = 1;

struct cdev mydev;

int mychar_open(struct inode *pnode,struct file *pfile)
{
	printk("open \n");
	return 0;
}

int mychar_close(struct inode *pnode,struct file *pfile)
{
	printk("close \n");
	return 0;
}


struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mychar_open,
	.release = mychar_close,
};

int __init mychar_init(void)
{
	int ret = 0;
	dev_t devno = MKDEV(major,minor);

	/*申请设备号*/
	ret = register_chrdev_region(devno,mychar_num,"mychar");
	if(ret)
	{
		ret = alloc_chrdev_region(&devno,minor,mychar_num,"mychar");
		if(ret)
		{
			printk("get devno failed\n");
			return -1;
		}
		major = MAJOR(devno);//容易遗漏,注意
	}


	/*给struct cdev对象指定操作函数集*/	
	cdev_init(&mydev,&myops);

	/*将struct cdev对象添加到内核对应的数据结构里*/
	mydev.owner = THIS_MODULE;
	cdev_add(&mydev,devno,mychar_num);

	return 0;
}

void __exit mychar_exit(void)
{
	dev_t devno = MKDEV(major,minor);

	cdev_del(&mydev);

	unregister_chrdev_region(devno,mychar_num);

}


MODULE_LICENSE("GPL");

module_init(mychar_init);
module_exit(mychar_exit);

testmychar_app.c

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

#include <stdio.h>


int main(int argc,char *argv[])
{
	int fd = -1;

	if(argc < 2)
	{
		printf("The argument is too few\n");
		return 1;
	}

	fd = open(argv[1],O_RDWR);
	if(fd < 0)
	{
		printf("open %s failed\n",argv[1]);
		return 2;
	}

	close(fd);
	fd = -1;
	return 0;
}

Makefile

ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/fs4412/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)


modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

xtt-objs = test.o func.o

endif


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

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

相关文章

RAD Installer Crack,集成到RAD Studio IDE支持

RAD & Installer Crack,集成到RAD Studio IDE支持 用于创建NSIS和Inno Setup安装程序的RAD Studio扩展。它将NSIS(Nullsoft Scriptable Install System)和Inno Setup与Embarcadero RAD Studio IDE结合在一起。它允许您在RAD Studio中设计和构建NSIS和Inno Setup项目&#x…

错误的迷宫:探索开发中的异常管理之旅

引言&#xff1a;为什么我们需要谈论错误处理&#xff1f; 在软件开发的世界中&#xff0c;错误是不可避免的。它们是我们编程旅程中的挑战&#xff0c;但也是我们成长的机会。正确地处理错误不仅可以确保软件的稳定性和可靠性&#xff0c;还可以为开发者提供宝贵的反馈。本文…

Icon设计神器!这5个软件一定要试试

在界面设计中&#xff0c;Icon既可以为用户指明用途&#xff0c;又可以提升界面设计的质感&#xff0c;可以说是一种必不可少的设计素材。而市面上可以制作的Icon的设计软件也十分丰富&#xff0c;今天本文将选出了5个好用的与大家分享&#xff0c;它们不仅功能强大&#xff0c…

RunnerGo:高效、易用的性能测试神器

你是否曾经遇到过这样的难题&#xff1a;在测试软件性能时&#xff0c;缺乏高效且易用的测试工具&#xff0c;导致测试过程繁琐&#xff0c;测试用例难以管理&#xff0c;测试报告也不尽人意。这些问题让我们在测试过程中倍感困扰。然而&#xff0c;现在有了RunnerGo这款性能测…

基于SpringBoot高校心理教育辅导设计与实现【附开题|万字文档(LW)和搭建文档】

主要功能 前台界面&#xff1a; ①首页、公告管理、查看更多等 ②心理健康学习、文章标题搜索、试卷列表、考试等 ③公告通知、留言反馈等 ④个人中心、考试记录、错题本等 后台登录&#xff1a; ①学生登录&#xff1a; 个人中心、修改密码、个人信息、辅导预约管理、考试管理…

(详解)数据结构-----------栈与队列 c语言实现

本章将会详细讲解以下知识点&#xff1a; 目录 一&#xff1a;栈 1&#xff1a;栈的定义&#xff0c;栈的特点 2&#xff1a;用什么结构来实现栈与原因的分析? 3: (超详解)栈的常用接口并且附上测试用例 二:队列 1:队列的定义&#xff0c;队列的特点 2&#xff1a;用什么结…

QT6为工程添加资源文件

如果在同一个文件夹 如果不在同一个文件夹 然后浏览资源位置&#xff0c;找到文件就可以了

【超简单】远程服务器使用 plt.show() 和 cv2.imshow() 可视化图像

远程服务器可视化图像 我的配置MobaXterm 远程显示VSCode 远程显示 我的配置 服务器 Ubuntu 20.04.3 LTSAnaconda 本地电脑 Win11MobaXtermVSCode MobaXterm 远程显示 配置好服务器连接&#xff08;此处略&#xff09;&#xff1b; 连接服务器&#xff0c;并激活使用的 A…

对接webservice接口时报错:发送方和接收方 Action 不匹配

趁着早上有时间&#xff0c;赶紧记录一下&#xff0c;哈哈。 错误提示如下&#xff1a; 1、英文版&#xff1a; <s:Envelope xmlns:s“http://schemas.xmlsoap.org/soap/envelope/”><s:Body><s:Fault>a:ActionNotSupportedThe message with Action ‘’ ca…

自然语言处理(四):全局向量的词嵌入(GloVe)

全局向量的词嵌入&#xff08;GloVe&#xff09; 全局向量的词嵌入&#xff08;Global Vectors for Word Representation&#xff09;&#xff0c;通常简称为GloVe&#xff0c;是一种用于将词语映射到连续向量空间的词嵌入方法。它旨在捕捉词语之间的语义关系和语法关系&#…

【USRP】集成化仪器系列2 :示波器,基于labview实现

USRP 示波器 1、设备IP地址&#xff1a;默认为192.168.10.2&#xff0c;请勿 修改&#xff0c;运行阶段无法修改。 2、中心频率&#xff1a;当需要生成不同频率单载波的 时候请直接修改中心频率&#xff0c;在运行的时候您 也可以直接修改中心频率。 3、接收增益&#xff1a;…

线程安全-搞清synchronized的真面目

多线程编程中&#xff0c;最难的地方&#xff0c;也是最重要的一个地方&#xff0c;还是一个最容易出错的地方&#xff0c;更是一个特别爱考的地方&#xff0c;就是线程安全问题。 万恶之源&#xff0c;罪魁祸首&#xff0c;多线程的抢占式执行,带来的随机性. 如果没有多线程,此…

paddle.load与pandas.read_pickle的速度对比(分别在有gpu 何无gpu 对比)

有GPU 平台 测试通用代码 import time import paddle import pandas as pd# 测试paddle.load start_time time.time() paddle_data paddle.load(long_attention_model) end_time time.time() print(f"Paddle load time: {end_time - start_time} seconds")# 测试…

【USRP】调制解调系列4:BPSK、QPSK、8PSK、OQPSK、Pi/4DQPSK,基于labview的实现

PSK Phase Shift Keying – 相移键控 在某些调制解调器中用于数据传输的调制系统&#xff0c;在最简单的方式中&#xff0c;二进制调制信号产生0和1。载波相位来表示信号占和空或者二进制1和O。对于有线线路上较高的数据传输速率&#xff0c;可能发生4个或8个不同的相移&…

系统架构:软件工程

文章目录 资源知识点自顶向下与自底向上形式化方法结构化方法敏捷方法净室软件工程面向服务的方法面向对象的方法快速应用开发螺旋模型软件过程和活动开放式源码开发方法功用驱动开发方法统一过程模型RUP基于构件的软件开发UML 资源 信息系统开发方法 知识点 自顶向下与自底…

uniapp 配置网络请求并使用请求轮播图

由于平台的限制&#xff0c;小程序项目中不支持 axios&#xff0c;而且原生的 wx.request() API 功能较为简单&#xff0c;不支持拦截器等全局定制的功能。因此&#xff0c;建议在 uni-app 项目中使用 escook/request-miniprogram 第三方包发起网络数据请求。 官方文档&#xf…

7. 搭建网络

7.1 神经网络 ① 把网络结构放在Sequential里面&#xff0c;好处就是代码写起来比较简介、易懂。 ② 可以根据神经网络每层的尺寸&#xff0c;根据下图的公式计算出神经网络中的参数。 7.2 搭建神经网络 import torch import torchvision from torch import nn from torch.…

【Day-22慢就是快】代码随想录-二叉树-理论基础

二叉树的种类 满二叉树 如果一棵二叉树只有度为0的结点和度为2的结点&#xff0c;并且度为0的结点在同一层上&#xff0c;则这棵二叉树为满二叉树。 深度为K&#xff0c;有2^k-1个节点。 完全二叉树 在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余…

提升代码可读性与可维护性:利用责任链模式优化你的Spring Boot代码

1. 基本介绍 责任链是一种非常常见的设计模式, 具体我就不介绍了, 本文是讲解如何在SpringBoot中优雅的使用责任链模式 1.1. 代码执行流程 基本步骤如下 : SpringBoot启动时, 需要获取 handler 对应Bean, 不同业务对应着不同的多个处理器, 比如 购票业务, 可能需要检查参数是…

LeetCode第21~25题解

CONTENTS LeetCode 21. 合并两个有序链表&#xff08;简单&#xff09;LeetCode 22. 括号生成&#xff08;中等&#xff09; LeetCode 21. 合并两个有序链表&#xff08;简单&#xff09; 【题目描述】 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两…