当前位置: 首页 > article >正文

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.kler.cn/news/283018.html

相关文章:

  • Nginx: 缓存, 不缓存特定内容和缓存失效降低上游压力策略及其配置示例
  • 基于python文案转语音并输出-自媒体等职业副业均可使用,不受他人限制
  • 从“云、边、端”的统一管理,为传统工厂数字化转型赋能的智慧地产开源了
  • OpenAI API Error: Resource not found - Text Summarization in NodeJS
  • QT中引入SQLITE3数据库
  • 自定义全局变量在SpringBoot的应用
  • 后端完成api顺序
  • 外卖霸王餐项目是什么?怎么搭建属于自己的外卖霸王餐小程序 ?
  • 灰度发布-介绍-全链路灰度实现
  • 由浅入深学习 C 语言:Hello World【提高篇】
  • 浏览器缓存
  • 网络安全-安全渗透简介和安全渗透环境准备
  • 【CSP:202109-2】非零段划分(Java)
  • 4.sklearn-K近邻算法、模型选择与调优
  • MySQL集群技术1——编译部署mysql
  • “重启就能解决一切问题”,iPhone重启方法大揭秘
  • 解决:无法从域控制器读取配置信息
  • 2024.8.29 C++
  • C#面:ASP.NET MVC 中还有哪些注释属性用来验证?
  • RKNPU2从入门到实践 ---- 【8】借助 RKNN Toolkit lite2 在RK3588开发板上部署RKNN模型
  • 设计模式--装饰器模式
  • 理解torch.argmax() ,我是错误的
  • 融资和融券分别是什么意思,融资融券开通后能融到多少资金?
  • Datawhale X 李宏毅苹果书 AI夏令营_深度学习基础学习心得Task2.2
  • Java 入门指南:Java NIO —— Selector(选择器)
  • 【hot100篇-python刷题记录】【搜索二维矩阵】
  • 分布式锁的实现:ZooKeeper 的解决方案
  • hive数据迁移
  • 低代码革命:JNPF平台如何简化企业应用开发
  • Linux 中的中断响应机制