1. Linux设备分类
- 字符设备: 指应用程序按字节/字符来读写数据的设备。通常为传真、虚拟终端和串口调制解调器、键盘之类设备提供流通信服务,通常不支持随机存取数据。字符设备在实现时大多不使用缓存器。系统直接从设备读/写每一个字符。
- 块设备: 通常支持随机存取和寻址,并使用缓存器。操作系统为输入输出分配了缓存以存储一块数据。当程序向设备发送读或写数据的请求时,系统把数据中的每一个字符存储在适当的缓存中。当缓存填满时,会采取适当的操作(把数据传走),而后系统清空缓存。与字符设备不同的是,是否支持随机存储。字符型是流形式,逐一存储。典型的块设备有硬盘、 SD卡、闪存等,应用程序可以寻址磁盘上的任何位置,并由此读取数据。此外,数据的读写只能以块的倍数进行。
- 网络设备: 是一种特殊设备,它并不存在于/dev 下面,主要用于网络数据的收发。
Linux 内核中处处体现面向对象的设计思想,为了统一形形色色的设备, Linux 系统将设备抽象为 struct cdev, struct block_device,struct net_devce 三个对象,具体的设备都可以包含着三种对象从而继承和三种对象属性和操作,并通过各自的对象添加到相应的驱动模型中,从而进行统一的管理和操作
2. 字符设备抽象
Linux 内核将字符设备抽象成一个数据结构 (struct cdev), 字符设备对象cdev 记录了字符设备的相关信息(设备号、内核对象),字符设备的打开、读写、关闭等操作接口(file_operations),在我们想要添加一个字符设备时,就是将这个对象注册到内核中,通过创建一个文件(设备节点)绑定对象的 cdev,当我们对这个文件进行读写操作时,就可以通过虚拟文件系统,在内核中找到这个对象及其操作接口,从而控制设备。
- 硬件层,通过查看硬件的原理图、芯片的数据手册,确定底层需要配置的寄存器,这类似于裸机开发。将对底层寄存器的配置,读写操作放在文件操作接口里面,也就是实现file_operations 结构体。
- 驱动层,将文件操作接口注册到内核,内核通过内部散列表来登记记录主次设备号。在文件系统层,新建一个文件绑定该文件操作接口,应用程序通过操作指定文件的文件操作接口来设置底层寄存器
3. 相关概念及数据结构
linux 中使用设备编号来表示设备,主设备号区分设备类别,次设备号标识具体的设备。cdev 结构体被内核用来记录设备号,在使用设备时通常会打开设备节点,通过设备节点的 inode 结构体、 file 结构体最终找到 file_operations 结构体,并从 file_operations 结构体中得到操作设备的具体方法
3.1 设备号
对于字符的访问是通过文件系统的名称进行的,这些名称被称为特殊文件、设备文件, Linux 根目录下有/dev 这个文件夹,专门用来存放设备中的驱动程序,使用 ls -l /dev以列表的形式列出系统中的所有设备。其中,每一行表示一个设备,每一行的第一个字符表示设备的类型。
如图:’ c’用来标识字符设备,’ b’用来标识块设备。如 autofs 是一个字符设备 c, 它的主设备号是 10,次设备号是 235; loop0 是一个块设备,它的主设备号是 7,次所备案为 0,同时可以看到 loop0-loop3 共用一个主设备号,次设备号由 0 开始递增 ,一般来说,主设备号指向设备的驱动程序,次设备号指向某个具体的设备。如图, I2C-0, I2C-1属于不同设备但是共用一套驱动程序
-
内核中设备编号的含义
typedef u32 __kernel_dev_t; typedef __kernel_dev_t dev_t; /* 表示设备号 */ #define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define MKDEV