驱动开发(二):创建字符设备驱动

往期文章:

驱动开发(一):驱动代码的基本框架
驱动开发(二):创建字符设备驱动 ←本文

驱动开发(三):内核层控制硬件层

目录

字符驱动设备的作用

函数

字符驱动设备注册和注销

注册

注销

自动创建设备节点

创建class类型对象

注销class类型对象

提交文件信息

注销文件信息

示例代码

驱动代码

Makefile

运行结果


字符驱动设备的作用

驱动开发中创建字符设备驱动的目的是为了提供对字符设备的访问接口,使用户空间的应用程序能够通过文件操作系统调用来与字符设备进行交互。

字符设备驱动的作用包括:

  1. 与用户空间的应用程序进行通信:字符设备驱动允许用户空间的应用程序通过文件操作系统调用(如open、read、write、close)来访问设备。应用程序可以通过这些系统调用来读取设备的数据、向设备发送命令或者将数据写入设备。

  2. 实现设备的读写接口:字符设备驱动提供读和写函数,用于从设备中读取数据或向设备中写入数据。这些函数可以根据设备的特性实现相应的读取和写入操作,并把数据传输到用户空间或者从用户空间接收数据。

  3. 处理设备的控制和管理:字符设备驱动可以实现设备的控制和管理功能,包括设备的初始化、中断处理、传输数据的协议等。驱动程序负责确保设备的正确工作,并提供适当的接口供用户空间的应用程序进行操作。

  4. 提供设备的访问权限和保护:字符设备驱动可以限制对设备的访问权限,并提供访问控制和保护机制。驱动程序可以根据需要实现设备的权限控制,确保只有具备相应权限的用户可以访问设备并执行相应操作。

总而言之,字符设备驱动的作用是为用户空间的应用程序提供对设备的访问接口,并实现设备的读写、控制和管理功能,从而实现设备和应用程序之间的数据交互。

函数

字符驱动设备注册和注销

注册

复制代码
#include <linux/fs.h>  
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);
功能:
    Linux 内核中用于注册字符设备的函数
参数:
    major:主设备号(一般写0)。当没有指定主设备号时(大于0时),内核会自动分配一个(等于0时)。
    name:设备名称,通常是一个字符串,用于在 /dev 目录下创建对应的设备文件。
    fops:一个指向 file_operations 结构体的指针,该结构体定义了一组用于设备操作的函数指针。这些操作包括打开设备、读写设备、释放设备等。
返回值:
    (大于0时)成功,返回 0。等于0时,返回主设备号
    如果失败,返回一个负的错误码。

主设备号:大类的驱动需要一个身份(有可能和别的值重复)
次设备号:大类中的小类

注销

复制代码
void unregister_chrdev(unsigned int major, const char *name)
	功能:注销一个字符设备驱动
	参数:
		@major:主设备号
		@name:名字
	返回值:无

自动创建设备节点

自动创建设备节点,即入口(安装驱动的时候)创建设备文件(自动创建的路径是在/dev 下),所以应用层打开文件的时候要注意是/dev/xxx 文件,不要忘了在出口释放资源。

创建class类型对象

复制代码
#include <linux/device.h>
class_create(owner, name)    
    功能:创建一个class类型的对象,向用户空间提交目录信息(内核目录的创建)
    参数:
      @owner  THIS_MODULE
      @name   类名字(文件的名字)
    返回值
      可以定义一个struct class的指针变量cls接受返回值,然后通过IS_ERR(cls)判断
  是否失败,如果成功这个宏返回0,失败返回非0值(可以通过PTR_ERR(cls)来获得
  失败返回的错误码)

注销class类型对象

复制代码
void class_destroy(struct class *cls)
    功能:注销一个class类型的对象

提交文件信息

复制代码
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
    功能:向用户空间提交文件信息
    参数:
		@class :录名字
		@parent:NULL
		@devt  :设备号    MKDEV(major,0)   
		@drvdata :NULL
		@fmt   :文件的名字
    返回值:成功返回struct device *指针
			失败返回错误码指针 

struct class *class: 指向设备所属的类的指针。

struct device *parent: 指向父设备的指针。在设备树中,一个设备可以有多个父设备。

dev_t devt: 设备的标识符。它是一个在 /dev 目录下用于访问设备的文件名的一部分。

void *drvdata: 一个指向任意数据的指针,该数据将与新创建的设备关联。这个指针可以用于存储与设备相关的私有数据。

const char *fmt, ...: 一个格式字符串,类似于 printf 函数的第一个参数,用于指定如何格式化后续的参数列表。(就是文件的名字)

注销文件信息

复制代码
void device_destroy(struct class *class, dev_t devt)
    功能:注销文件信息

示例代码

驱动代码

功能仅为创建一个字符设备驱动

复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/device.h>

#define MODNAME "lianxi"     //文件的名字
unsigned int major=0;               //主设备号

const struct file_operations fops;
struct class *cls;
struct device *dev;

//入口  申请资源
static int __init hello_init(void)
{
    major = register_chrdev(major, MODNAME, &fops);       //注册字符设备
    if(major<0)
	{
 		printk(KERN_ERR "register chrdev error\n");
		return major;
	}

    cls = class_create(THIS_MODULE, MODNAME);    //创建一个class类型的对象,向用户空间提交目录信息(内核目录的创建)
    if(IS_ERR(cls))
    {
        return PTR_ERR(cls);
    }

    dev = device_create(cls, NULL,MKDEV(major,0), NULL,MODNAME );    //向用户空间提交文件信息
    if(IS_ERR(dev ))
  {
    return PTR_ERR(dev);
  }

    return 0;
}

//出口  释放资源
static void __exit hello_exit(void)
{
    device_destroy(cls, MKDEV(major, 0));         //注销文件信息
    class_destroy(cls);                                                 //注销一个class类型的对象
    unregister_chrdev(major, MODNAME);         //注销字符设备驱动
}

module_init(hello_init);    //入口
module_exit(hello_exit);    //出口
MODULE_LICENSE("GPL");      //许可证

Makefile

复制代码
KERNELDIR = /home/linux/kernel/kernel-3.4.39 //=后写你自己的内核顶层的绝对路径
PWD = $(shell pwd)
all:
	make -C $(KERNELDIR) M=$(PWD) modules

.PHONY:clean
clean:
	make -C $(KERNELDIR) M=$(PWD) clean
	
obj-m += XXX.o //XXX为你的.c文件名

运行结果

反复安装删除,如果第二次安装报错,说明有资源没释放掉

相关推荐
cxr8281 天前
SPARC方法论在Claude Code基于规则驱动开发中的应用
人工智能·驱动开发·claude·智能体
sukalot2 天前
window显示驱动开发—显示适配器的子设备
驱动开发
Evan_ZGYF丶2 天前
【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?
linux·驱动开发·android14·rk3576
sukalot3 天前
window显示驱动开发—视频呈现网络简介
驱动开发
sukalot3 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(二)
驱动开发
zwhSunday3 天前
Linux驱动开发(1)概念、环境与代码框架
linux·运维·驱动开发
sukalot4 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(三)
驱动开发
sukalot4 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(一)
驱动开发
cxr8285 天前
基于Claude Code的 规范驱动开发(SDD)指南
人工智能·hive·驱动开发·敏捷流程·智能体
zwhSunday5 天前
Linux驱动开发(2)进一步理解驱动
linux·驱动开发