Linux——驱动——自动设备

news/2024/9/13 20:20:12 标签: linux, 运维, 服务器

自动创建设备节点是Linux设备驱动开发中的一个重要环节,它允许设备驱动程序在内核中注册后,自动在/dev目录下创建对应的设备文件,从而使得用户空间程序可以通过标准的文件操作接口(如open、read、write等)与硬件设备进行交互。 ❤

一、自动创建设备节点的概念

在Linux中,一切皆文件,设备驱动程序也不例外。设备驱动程序通过设备文件的形式向上层程序提供接口。设备文件通常位于/dev目录下,包括字符设备文件、块设备文件、网络设备文件等。这些特殊类型的文件使用统一的文件操作函数(如open、read、write等)进行访问。

二、相关函数和宏定义

(1)class_create宏定义

#define class_create(owner, name)		\  
({						\  
	static struct lock_class_key __key;	\  
	__class_create(owner, name, &__key);	\  
})

实际上,class_create是一个宏定义,它内部调用了__class_create函数。

参数

  • owner:指向模块所有者的指针,通常为THIS_MODULE宏,表示当前模块。
  • name:设备类的名称,该名称将用于在/sys/class/目录下创建对应的目录。

返回值:返回指向新创建的struct class结构体的指针,如果创建失败则返回ERR_PTR错误码。

(2)class_destroy

/**
 * class_destroy - destroys a struct class structure
 * @cls: pointer to the struct class that is to be destroyed
 *
 * Note, the pointer to be destroyed must have been created with a call
 * to class_create().
 */
void class_destroy(struct class *cls)
{
	if ((cls == NULL) || (IS_ERR(cls)))
		return;

	class_unregister(cls);
}

功能class_destroy函数用于销毁之前通过class_create创建的设备类。它会删除/sys/class/目录下对应的目录,并清理相关的资源。

参数cls是指向要销毁的设备类的指针。

返回值:无返回值

(3)device_create

功能device_create函数用于在指定的设备类下创建一个新的设备,并自动在/dev目录下创建对应的设备文件(如果udev或mdev配置正确的话)。此外,它还会在/sys/devices//sys/class/<类名>/目录下创建相应的设备目录。

struct device *device_create(struct class *class, struct device *parent,  
			    dev_t devt, void *drvdata,  
			    const char *fmt, ...);

参数

  • class:设备所属的类。
  • parent:设备的父设备,如果设备没有父设备,则传递NULL
  • devt:设备的设备号,由主设备号和次设备号组成。
  • drvdata:传递给设备的私有数据,通常传递NULL
  • fmt:设备文件的名称格式字符串,后续可变参数将根据这个格式字符串生成设备文件的名称。

返回值:返回指向新创建的struct device结构体的指针,如果创建失败则返回ERR_PTR错误码

(4)device_destroy

功能device_destroy函数用于销毁之前通过device_create创建的设备。它会删除/sys/devices//sys/class/<类名>/目录下对应的设备目录,并尝试删除/dev目录下的设备文件(这通常依赖于udev或mdev的配置)

void device_destroy(struct class *class, dev_t devt);

参数

  • class:设备所属的类。
  • devt:设备的设备号。

返回值:无返回值。

三、实现方式

(1)设置class—create()

(2)出错处理

(3)实现步骤

①. 使用udev

udev通过读取sysfs中的设备信息,结合udev规则文件(通常位于/etc/udev/rules.d/目录下),来决定如何创建设备文件。但是,在设备驱动代码中,通常需要执行以下步骤来配合udev自动创建设备节点:

  • 使用class_create函数创建一个设备类(class),这个类会在/sys/class/目录下创建一个对应的目录。

  • 使用device_create函数在创建的设备类下创建一个设备,这个函数会在/sys/class/<类名>/目录下创建一个设备目录,并且udev会根据规则文件在/dev目录下创建对应的设备文件。

②. 使用mdev(嵌入式系统)

在嵌入式Linux系统中,由于资源限制,可能会使用mdev作为udev的简化版。mdev同样能够监听内核的设备事件,并自动创建或删除设备文件。但是,与udev不同,mdev的配置可能更加简单直接,通常通过配置文件或命令行参数来指定行为。

(4)具体代码

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/string.h>
#include <asm/uaccess.h>
#include <linux/device.h>

#define MAJOR_NUM 253
#define MINOR_NUM 0
#define DEV_NAME "led"
#define DEV_NUM 1
#define GPBCON 0x56000010
#define GPBDAT 0x56000014

static volatile unsigned long * gpbcon;
static volatile unsigned long * gpbdat;

static void init_led(void)						
{
	// 配置GPB5引脚功能为输出
	*gpbcon &= ~(0xff << 10);
	*gpbcon |= (0x55 << 10);

	// 将GPB5引脚电平置高
	*gpbdat |= (0xf << 5);
}

static void led_on(void)
{
	// 将GPB5引脚电平置低
	*gpbdat &= ~(0xf << 5);
}

static void led_off(void)
{
	// 将GPB5引脚电平置高
	*gpbdat |= (0xf << 5);
}

static int open (struct inode * inode, struct file * file)
{
	init_led();
	printk("led open ...\n");
	return 0;
}

static ssize_t read (struct file * file, char __user * buf, size_t len, loff_t * offset)
{
	//copy_to_user(buf, data, len);
	printk("led read ...\n");
	return 0;
}

static ssize_t write (struct file * file, const char __user * buf, size_t len, loff_t * offset)
{
	unsigned char data[12] = {0};
	size_t len_cp = sizeof(data) < len ? sizeof(data) : len;
	copy_from_user(data, buf, len_cp);

	if(!strcmp(data, "ledon"))
		led_on();
	else if(!strcmp(data, "ledoff"))
		led_off();
	else
		 return -1;

	printk("led write ...\n");
	return len_cp;
}

static int close (struct inode * inode, struct file * file)
{
	printk("led close ...\n");
	return 0;
}

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = open,
	.read = read,
	.write = write,
	.release = close
};
static struct cdev cdev;
static dev_t dev;
struct class * pclass;
struct device * pdev;

static int __init led_init(void) 
{
	int ret = 0;
	dev = MKDEV(MAJOR_NUM, MINOR_NUM);

	cdev_init(&cdev, &fops);

	ret = cdev_add(&cdev, dev, DEV_NUM);
	if(ret < 0)
		goto err_cdev_add;

	ret = register_chrdev_region(dev, DEV_NUM, DEV_NAME);
	if(ret < 0)
		goto err_register_chrdev_region;

	pclass = class_create(THIS_MODULE, "led_class");
	if(pclass == NULL)
		goto err_class_create;

	pdev = device_create(pclass, NULL, dev, NULL, DEV_NAME);
	if(pdev == NULL)
		goto err_device_create;

	gpbcon = ioremap(GPBCON, sizeof(*gpbcon));
	gpbdat = ioremap(GPBDAT, sizeof(*gpbdat));

	printk("led_init  ...\n");

	return ret;

err_cdev_add:
	cdev_del(&cdev);
	printk("led cdev_add failed\n");
	return ret;

err_register_chrdev_region:
	unregister_chrdev_region(dev, DEV_NUM);
	cdev_del(&cdev);
	printk("led register_chrdev_region failed\n");	
	return ret;

err_class_create:
	class_destroy(pclass);
	unregister_chrdev_region(dev, DEV_NUM);
	cdev_del(&cdev);
	printk("led class_create failed\n");	
	return -1;

err_device_create:
	device_destroy(pclass, dev);
	class_destroy(pclass);
	unregister_chrdev_region(dev, DEV_NUM);
	cdev_del(&cdev);
	printk("led device_create failed\n");	
	return -1;
}

static void __exit led_exit(void)
{
	iounmap(gpbcon);
	iounmap(gpbdat);
	device_destroy(pclass, dev);
	class_destroy(pclass);
	unregister_chrdev_region(dev, DEV_NUM);
	cdev_del(&cdev);
	printk("led_exit  ###############################\n");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");


http://www.niftyadmin.cn/n/5622113.html

相关文章

使用MVC设计模式根据软件系统设计建设高校数字化教育教学资源共享平台

目录 案例 【题目】 【问题 1】(9 分) 【问题 2】(6 分) 【问题 3】(10 分) 答案 【问题 1】答案 【问题 2】答案 【问题 3】解析 相关推荐 案例 阅读以下关于软件系统设计的叙述&#xff0c;在答题纸上回答问题 1 至问题 3。 【题目】 某软件企业受该省教育部门委托…

VMware上Linux系统报错--mount: 在 /dev/sr0 上找不到媒体【解决办法】

问题背景 在VMware上使用CentOS7系统&#xff0c;想要在CentOS7系统上挂载一个镜像文件&#xff0c;在系统下使用mount命令挂载报错如下所示&#xff1a; 解决办法 用lsblk命令查看sr0设备是存在的 查看虚拟机侧问题&#xff0c;右键虚拟机&#xff0c;点击设置 镜像文件已选…

笔记整理—uboot启动过程(5)BL2板级初始化

上一章说到了uboot在BL2阶段大概都要干什么&#xff0c;也说到了为了实现这些要先进行内存排布&#xff0c;实现了这些后便可实现BL2部分的板级初始化。首先先来看一下init_fnc_ptr函数指针。 for(init_fnc_ptrinit_sequence;*init_fnc_ptr;init_fnc_ptr){if((*init_fnc_ptr)(…

【AI大模型】提示词(Prompt)全面解析

文章目录 前言前置准备&#xff08;非常重要&#xff09;一、Prompt 提示词介绍1.1 Prompt 的重要性 二、Prompt 提示词元素构成与实践2.1 关键字2.2 上下文2.3 格式要求2.4 实践示例 三、Prompt 提示词编写原理3.1 清晰性3.2 具体性3.3 适应性 四、Prompt 提示词编写常用的分隔…

C#中的压缩与解压缩:掌握内置功能的艺术

标题&#xff1a;C#中的压缩与解压缩&#xff1a;掌握内置功能的艺术 在软件开发中&#xff0c;数据压缩是一种常见的需求&#xff0c;无论是为了节省存储空间还是减少网络传输时间。C#提供了一套内置的压缩和解压缩功能&#xff0c;使得开发者能够轻松实现这些功能。本文将详…

OpenCV 图像处理基础算法介绍c++

VS2022配置OpenCV环境 关于OpenCV在VS2022上配置的教程可以参考&#xff1a;VS2022 配置OpenCV开发环境详细教程 图像处理 图像处理是一个广泛的领域&#xff0c;它涉及到对图像数据进行分析、修改和改进的各种技术。以下是一些基本的图像处理操作&#xff0c;这些操作通常可…

游戏开发设计模式之中介者模式

目录 中介者模式的定义和结构 中介者模式的意图和主要解决的问题 中介者模式的使用场景 中介者模式的优点和缺点 中介者模式的应用实例 中介者模式的实现方式 中介者模式与观察者模式的关系 中介者模式在游戏开发中的具体应用案例是什么&#xff1f; 如何在大型项目中…

Python算法工程师面试整理-线性代数

1. 向量和矩阵 ● 向量:表示一个n维空间中的点,通常以列向量或行向量表示。 ○ 向量运算:加法、标量乘法、点积(内积)、叉积(外积)。 ● 矩阵:由行和列组成的二维数组。 ○ 矩阵运算: ■ 矩阵加法:元素对应相加。