目录
- 01-1-基本概念之什么是驱动程序()?
- 01-2-驱动程序运行于Linux的内核空间,而不是用户空间中
- 02-Linux系统中常见的设备类型有哪些?
- 03-1-什么叫主设备号、次设备号?
- 03-2-使用宏MKDEV将主设备号和次设备号合成一个数值
- 04-设备文件、类属性文件有什么用?
- 05-以字符设备为例,有了设备的底层操作函数后,怎么样把这些底层操作函数整合进驱动程序中?设备是如何注册进系统的?以下这个流程就完成了这两个任务。
- 06-一个完整的示例代码
- 07-驱动程序编译好后通常是以模块的形式存在的,那么怎么加载这个模块?这个模块加载时从哪里执行?
- 08-为什么驱动程序模块的C文件末尾要加上`MODULE_LICENSE("GPL");`
- 09-驱动程序模块加载完成,完成设备实例化注册并创建了设备文件后,怎么调用设备或对设备进行操作?
01-1-基本概念之什么是驱动程序()?
驱动程序本质上是代码逻辑的集合,通常用于管理、驱动多个设备实例。要想使用某个设备的驱动程序,需要实例化相应的驱动程序,具体实例化的过程请看本文第05点。
这里重点要理解驱动程序只是代码逻辑的集合,它本身并没有主设备号、次设备号,也没有设备文件,主设备号、次设备号、设备文件都是在实例化驱动程序过程中产生的。
01-2-驱动程序运行于Linux的内核空间,而不是用户空间中
驱动程序运行于Linux的内核空间,而不是用户空间中,用户程序是不能直接访问或修改内核中的数据,需要用专门的方式或函数进行数据交换。
关于这个标题的详解见 https://blog.youkuaiyun.com/wenhao_ir/article/details/144950319
02-Linux系统中常见的设备类型有哪些?
在 Linux 系统中,设备可以分为不同的类型,主要根据它们的工作方式、接口和功能进行分类。以下是 Linux 系统中常见的设备类型:
1. 字符设备 (Character Devices)
字符设备是按字符流方式进行数据传输的设备,每次读写都涉及一个字符或字节的数据。例如:
- 终端设备 (
tty
): 用于与用户交互的设备,如串口终端、虚拟终端。 - 串口设备 (
ttyS
): 串口通信设备,如/dev/ttyS0
。 - 键盘 (
kbd
): 连接的键盘设备。 - 鼠标 (
mouse
): 连接的鼠标设备。 - 伪设备:例如
/dev/null
、/dev/random
、/dev/zero
等,这些设备没有硬件实现,提供特殊的功能。
特点:
- 按字符流进行读写,每次读写一个字符(或字节)。
- 通过字符设备文件(如
/dev/ttyS0
)与应用程序交互。
2. 块设备 (Block Devices)
块设备是按块(通常是 512 字节或更大的单位)进行数据传输的设备。它们支持随机访问和缓存功能。例如:
- 硬盘 (
/dev/sda
,/dev/sdb
等): 计算机的主要存储设备。 - 固态硬盘 (SSD) (
/dev/sda1
,/dev/nvme0n1
): 新型存储设备,提供更高的读写速度。 - 光盘驱动器 (
/dev/cdrom
): 用于读取光盘数据的设备。 - USB 存储设备 (
/dev/sdb1
,/dev/sdc
): 连接到系统的 USB 存储设备。 - 虚拟磁盘 (
/dev/loop0
): 用于挂载虚拟磁盘映像的设备。
特点:
- 支持随机读写访问,通常用于文件系统的挂载。
- 设备文件通常位于
/dev/
目录下。
3. 网络设备 (Network Devices)
网络设备是用于网络通信的设备。它们支持网络接口的连接和数据传输。例如:
- 以太网卡 (
eth0
,eth1
): 网络接口,用于连接有线网络。 - 无线网卡 (
wlan0
,wlan1
): 用于连接无线网络。 - 虚拟网络设备 (
lo
): 回环接口,用于与本机进行网络通信。
特点:
- 用于实现计算机与外部网络的通信。
- 可以是有线或无线设备,支持 TCP/IP 等协议。
4. 输入设备 (Input Devices)
输入设备是用于将用户输入传递给计算机的设备。它们通常使用字符设备接口。例如:
- 鼠标 (
/dev/input/mice
): 用于提供用户的指针控制输入。 - 键盘 (
/dev/input/event0
): 用于接收用户的键盘输入。 - 触摸屏 (
/dev/input/eventX
): 用于接收触摸操作。
特点:
- 输入设备通常通过
/dev/input/
目录进行管理,使用字符设备接口。 - 输入事件通过设备文件传递给应用程序。
5. 虚拟设备 (Virtual Devices)
虚拟设备并不对应于物理硬件设备,而是通过软件实现的设备。例如:
- 虚拟终端 (
/dev/tty0
,/dev/tty1
): 提供与系统交互的虚拟控制台。 - 内存设备 (
/dev/mem
): 用于访问物理内存的虚拟设备。 - 随机数设备 (
/dev/random
,/dev/urandom
): 用于生成随机数的设备。
特点:
- 不依赖于硬件,而是由内核或驱动程序模拟。
- 提供系统管理或特殊功能。
6. 特殊设备 (Special Devices)
这些设备通常与硬件密切相关,但它们的作用和用法比较特殊。例如:
- 时钟设备 (
/dev/rtc
): 实时钟设备,用于管理系统时间。 - 伪设备 (
/dev/null
,/dev/zero
): 不涉及实际硬件,而是提供特殊功能,通常用于丢弃或生成数据。
7. USB 设备 (USB Devices)
USB 设备通过 USB 总线连接到计算机,涵盖了各种设备类型。例如:
- USB 存储设备 (
/dev/sda1
,/dev/sdb1
): 连接的 USB 存储。 - USB 摄像头 (
/dev/video0
): 通过 USB 接口连接的摄像头。 - USB 键盘和鼠标 (
/dev/input/eventX
): 连接的 USB 键盘和鼠标。
特点:
- 支持即插即用(plug-and-play),可以动态连接和断开。
8. 串口设备 (Serial Devices)
串口设备通过串行接口进行通信,通常用于较为传统的设备。例如:
- 串口终端 (
/dev/ttyS0
,/dev/ttyS1
): 传统的串口设备。 - 调制解调器 (
/dev/ttyUSB0
): 通过串口连接的调制解调器。
特点:
- 通过串行接口传输数据,通常用于低速数据通信。
9. SCSI 设备 (SCSI Devices)
SCSI 是一种广泛用于硬盘、光驱等设备的标准接口。例如:
- SCSI 硬盘 (
/dev/sda
,/dev/sdb
): SCSI 接口的硬盘设备。 - SCSI 光驱 (
/dev/sr0
): SCSI 接口的光驱设备。
特点:
- 高性能和扩展性,常用于服务器或工作站中。
- 支持多个设备共享同一总线。
10. PCI 设备 (PCI Devices)
PCI 总线上的设备用于高性能数据交换。例如:
- 显卡 (
/dev/video0
): 通过 PCI 接口连接的显卡设备。 - 网卡 (
/dev/eth0
): 通过 PCI 接口连接的网卡设备。 - 音频设备 (
/dev/snd/pcmC0D0p
): 通过 PCI 接口连接的音频设备。
特点:
- 高速数据传输,支持多种设备接口。
- 通过 PCI 总线连接多个设备。
11. I2C 和 SPI 设备 (I2C/SPI Devices)
I2C 和 SPI 是常用于嵌入式设备的通信协议,用于连接传感器、显示器等设备。例如:
- I2C 设备:通过 I2C 总线连接的设备,通常位于
/dev/i2c-X
。 - SPI 设备:通过 SPI 总线连接的设备,通常位于
/dev/spidevX.Y
。
特点:
- 用于短距离、高速的设备间通信。
- 主要用于嵌入式系统中的外围设备。
小结
在 Linux 系统中,设备类型非常丰富,主要包括字符设备、块设备、网络设备、输入设备、虚拟设备、USB 设备、串口设备等。每种设备都有不同的特点和用途,Linux 系统通过设备文件(通常位于 /dev/
目录下)来对这些设备进行管理和访问。
03-1-什么叫主设备号、次设备号?
主设备号、次设备号是Linux系统管理设备的一种结构,通常如果有多个设备,其驱动程序如果一样,那我们为其分配相同的主设备号,内核通过主设备号找到负责处理该设备的驱动程序,也就是说驱动程序和主设备号是绑定的【也可简单粗暴地理解为主设备号是针对驱动程序而言的,看到主设备号,应该想到的是驱动程序,而不是具体的哪个设备,次设备号才对应于具体的设备】,但它们的次设备号不同,通过不同的次设备号来区别它们。
当然如果设备类型相同,驱动程序也相同,你也可以为它们分配不同的主设备号,不过不建议这样做,这不符号Linux的基本设计原则。
不过也有特例,比如不同类型的设备有时会使用相同的驱动程序,但此时我们也会为它们分配不同的主设备号,以便在系统中区分。例如:块设备和字符设备通常使用不同的主设备号,即使它们底层可能由同一个驱动程序管理。
03-2-使用宏MKDEV将主设备号和次设备号合成一个数值
在 Linux 设备驱动编程中,MKDEV(SPIDEV_MAJOR, minor)
宏用于生成一个含主设备号和次设备号信息的 完整设备号(dev_t 类型),用于标识字符设备或块设备。
示例代码:
dev_t dev = MKDEV(SPIDEV_MAJOR, minor);
- SPIDEV_MAJOR:主设备号,代表设备的类别,例如所有 SPI 设备共用一个主设备号。
- minor:次设备号,区分同一主设备号下的不同设备实例。
MKDEV
宏的定义通常如下:
#define MKDEV(major, minor) (((major) << 20) | (minor))
在 Linux 内核中:
- 主设备号占据 高 12-20 位。
- 次设备号占据 低 12-20 位。
04-设备文件、类属性文件有什么用?
请再次理解“在Linux系统中一切皆文件”这句话,驱动程序写好后,编译成模块文件(ko文件),然后加载进内核,加载完成后就在系统的相应目录中生成了设备文件和类属性文件。
设备文件中存储了设备的主设备号和次设备号,通过主设备号能找到这个设备文件的驱动程序,而驱动程序通过次设备号能区分具体是要操作哪个设备。
类属性文件中用于对某一类设备进行统一管理和操作,它里面存储的信息比较丰富,主要是某一类设备的统一读函数(show函数)和写函数(store函数),关于类属性文件的解释和具体例子,请参考博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/144901797