字符设备驱动创建流程

驱动程序是一个软件组件,可让操作系统和设备彼此通信。

例如,假设应用程序需要从设备中读取某些数据。 应用程序会调用由操作系统实现的函数,操作系统会调用由驱动程序实现的函数。 驱动程序(由设计和制造该设备的同一公司编写)了解如何与设备硬件通信以获取数据。 当驱动程序从设备获取数据后,它会将数据返回到操作系统,操作系统会将数据返回至应用程序。

功能:向上提供接口向下管理硬件

Linux常见设备驱动分为三种:字符设备驱动,块设备驱动,网络设备驱动

字符设备驱动:按照字节流来访问,并且只能顺序访问,不能无需访问的设备就是字符设备驱动。

块设备驱动:按照block(512字节)来访问,可以顺序访问也可以无序访问的设备(随机访问)就是块设备驱动

网络设备驱动:网卡设备驱动没有设备节点(历史问题),控制网卡硬件进行网络数据数据收发代码就是网络设备驱动

设备驱动是为更好的管理设备而编写的程序,而linux内核管理这些驱动,所以编写驱动会与内核打交道。

以下是一个驱动的最基本框架代码

#include 
#include 

// 入口函数
// 在安装驱动模块的时候执行的函数
// static:限定作用域
// int   :返回值类型
//__init:告诉编译器将demo_init函数放在.init.text段中,__init是给编译器使用的。
// demo_init:驱动的入口函数的名字
//(void) :函数没有参数
static int __init demo_init(void)
{
    return 0;
}

// 出口函数
// 在卸载驱动模块的时候执行的函数
// static:限定作用域
// void  :返回值类型
//__exit:告诉编译器将demo_exit函数放在.exit.text段中,__exit是给编译器使用的。
// demo_exit:驱动的出口函数的名字
//(void) :函数没有参数
static void __exit demo_exit(void)
{
}
module_init(demo_init); // 告诉内核入口地址
module_exit(demo_exit); // 告诉内核出口地址
MODULE_LICENSE("GPL"); // 许可证,驱动遵从开源的协议

第1步要在内核注册字符设备驱动

        为什么注册:

  1. 声明设备编号:内核需要知道每个设备在系统中的唯一编号,以便将设备与相应的驱动程序进行匹配。通过注册字符设备驱动,驱动程序可以向内核声明设备所需的编号范围,并指定设备名称。

  2. 确定设备操作函数:字符设备驱动需要实现一些函数来处理设备的读写、打开、关闭等操作。在注册设备驱动时,驱动程序需要向内核提供这些函数的地址,以便内核可以在需要时调用它们。

  3. 实现热插拔:通过注册字符设备驱动,内核可以实现对设备的热插拔支持。当新设备插入系统时,内核会检测并自动加载相应的驱动程序,以便对设备进行管理

第2步创建设备结点

        在上步注册后会获得驱动设备号用设备号来关联设备节点

设备节点是一种特殊的文件。它们用来表示系统中存在的硬件设备或其他实体,并提供标准的访问接口。设备节点一般位于文件系统的 /dev 

为什么要创建设备节点:

        1.绑定设备与驱动程序

设备节点起到了将设备与驱动程序进行绑定的作用。当用户空间打开一个设备节点时,内核会自动将其与相应的驱动程序进行匹配,并调用驱动程序提供的设备操作函数来处理相应的请求

        2.提供对应设备的用户空间访问接口

创建相应的设备节点,驱动程序可以向用户空间暴露设备的访问接口。用户空间可以通过该接口来访问和控制设备。

第3步编写逻辑程序

字符设备驱动的内部实现

:用户层调用IO函数打开设备节点,每一个文件都有一个属于自己的inode号也是索引当前文件的inode结构体的索引号,inode结构体里面有一个成员存储着设备号,还有一个共用体成员表明设备类型,通过这两个成员可以追溯到字符设备驱动结构体对象,该结构体里面包含一个操作方法结构体,由于IO函数的读写都需要文件描述符参数,通过该文件描述符可以定位驱动设备文件节点,从而通过操作方法结构体指针再调用你自己写的读写函数。

        

你可能感兴趣的:(linux,驱动)