1 IO端口映射

IO端口与CPU直连,CPU读取IO端口时需要一个地址,就是IO端口物理地址的映射。

IO映射有两种方式:

  • 内存映射:ARM架构芯片只有一个物理地址空间,因此IO端口直接被内存的一部分,内核像访问内存一样访问端口
  • IO空间映射:X86架构有专门的IO空间,CPU通过设立专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元(也即I/O端口)

 

 

2 IO端口模型

2.1 IO端口寄存器

内核对IO编程提供统一的方法,每个设备的IO端口都被组织成一组专用寄存器:

  • 控制寄存器
  • 状态寄存器
  • 输入寄存器
  • 输出寄存器

 

2.2 IO设备树

IO设备驱动程序使用资源(struct resource)记录分配给每个硬件的IO端口,以免资源冲突!

  • char *name:资源拥有者的描述
  • unsigned long start:资源范围的开始
  • unsigned long end:资源范围的结束
  • unsigned long flags:各种标志
  • struct resource *parent:指向资源树中的父节点
  • struct resource *parent:指向资源树中的兄弟节点
  • struct resource *parent:指向资源树中的子节点,子节点的范围(start->end)一定包含在主节点范围内

主节点和子节点的地址范围类似harden和subdev、subdev和component的关系。

可以从/proc/ioports文件中获取当前分配给IO设备的所有IO地址树。比如这里:

 

 

3 IO接口

3.1 概念

IO端口就是带有地址的port口,IO接口则是处于一组IO端口和对应设备控制器之间的一种硬件电路;

它起到翻译作用:

  • IO端口的值 -> IO接口 -> 设备需要的数据和命令
  • 设备状态变化 -> IO接口 -> 更新IO端口寄存器

IO接口还可以通过一条IRQ线把电路连接到可编程中断控制器,代表硬件设备发起中断请求。

 

3.2 分类

① 专用IO接口:键盘接口,图形接口(GPU),磁盘接口,总线鼠标接口,网络接口,等

② 通用IO接口:并口,串口,PCMCIA接口,SCSI接口,通用串行总线(USB),等

 

 

4 设备驱动程序模型的组成

Linux2.6提供了一些数据结构和辅助函数,为系统中所有总线、设备以及驱动程序提供了一个统一的视图 —— 设备驱动程序模型。在/sys目录下有:

  • block:块设备
  • devices:被内核识别的硬件设备
  • bus:系统中用于连接设备的总线
  • class:系统中设备的类型(声卡、网卡、显卡等)
  • power:处理一些硬件设备电源状态的文件
  • firmwarm:处理一些硬件设备的固件的文件

 

 

5 struct kobject和kset容器

5.1 struct kobject

  • char *k_name:容器名称
  • struct kref:容器的引用计数
  • struct kset *kset:kobject嵌入的容器
  • struct kobj_type *ktype:指向kobject的类型描述符
  • struct list_head entry:kobject所插入链表的指针
  • struct kobject *parent:指向父kobject

每个kobject对应于sysfs文件系统中的一个目录kobject被嵌入一个叫做容器的更大的对象中:这个容器叫做kset,kset是具有相同类型的kobject的集合。kobject是一个抽象的结构体,并不包含任何实际的设备信息,它被嵌入容器中,其他重要结构体(struct device或struct device_driver)包含kobject,用于结构体本身的层级描述,比如device的层级关系描述存放在其包含的kobject中。

设备驱动模型之Kobject和Kset详解

 

5.2 struct kset

  • struct subsystem *subsys:指向subsystem描述符
  • struct kobj_type *ktype:指向set的kobject类型描述符(kset->ktype == kset->kobj->ktype)
  • struct list_head list:包含在kset中的kobject链表的首部
  • struct kobject *kobj:嵌入的kobject结构
  • struct kset_hotplug_ops *hotplug_ops:只想用于处理kobject结构的过滤和热插拔操作的回到函数表

kset最重要的是建立上层(sub-system)和下层的(kobject)的关联性。kset是一组kobject的集合,当某一件事发生时,可以同时由kset通知到集合中的kobject。

 

 

6 设备和设备驱动程序

6.1 设备struct device

  • struct kobject *kobj:内嵌的kobject结构
  • struct device_driver *driver:指向控制设备驱动程序的指针

device对象全部收集在device_subsys子系统中,对应的目录为/sys/devices;

每个设备驱动程序都保持一个device对象链表,链接了所有可被管理的设备;

 

6.2 设备驱动程序struct device_driver

  • char *name:设备驱动程序的名称
  • struct kobject kobj:内嵌的kobject结构
  • struct list_head devices:驱动程序支持的所有设备的链表
  • struct module *owner:标识实现设备驱动程序的模块
  • int (*)(struct device *) probe:探测设备的方法
  • int (*)(struct device *) remove:移走设备的方法
  • int (*)(struct device *) suspend:设置设备低功率状态的方法
  • int (*)(struct device *) resume:设备恢复正常时所调用的方法

device_driver被嵌入到一个更大的描述符中,比如相机驱动程序由数据结构vcam_driver描述,其包含一个device_driver对象;

device_register()/device_unregister():往设备驱动模型中插入/移除一个新的device_driver。

设备驱动程序注册的时机:静态被编译进内核,则在内核初始化的时候进行;如果作为一个内核模块(ko),则在模块装入(insmod)的时候注册。

设备驱动程序可以早些注册,方便用户态程序使用,但是需要尽可能晚的初始化(分配内存,IRQ等资源),以节省资源。

 

 

7 总线

7.1 struct bus_type

  • char *name:总线类型的名称
  • struct subsustem subsys:与总线类型相关的kobject子系统,与/sys/bus/*是对应的,比如/sys/bus/pci表示总线类型为pci,子系统就是pci总线子系统
  • struct kset drivers:驱动程序的kobject集合,包含描述符struct device_driver
  • struct kset devices:设备的kobject集合,包含描述符struct device

内核所支持的每一种总线类型都由bus_type对象描述。

 

 

8 设备文件

设备文件的索引节点包含硬件设备的标识符,对应字符或块设备;

设备标识符 = 设备类型 + 主设备号 + 次设备号,具有相同设备类型(字符设备或块设备)和主设备号的所有设备文件可以由同一个设备驱动程序处理,次设备号标识了具体的设备。

早期版本Linux主设备号和次设备号都是8位长,Linux2.6增加了设备号的位长:主12位 + 次20位.

动态分配主次设备号:alloc_chrdev_region()注册设备,传入次设备号、分配设备数量和设备名;该函数会把分配的到的设备号保存在dev中。cdev_init()和cdev_add()注册设备到驱动设备模型,输入参数需要cdev而不是设备号,与动态分配设备号呼应。

动态创建设备文件:udev工具集扫描/sys/class子目录寻找dev文件,在/dev目录下创建对应的设备文件。这样/dev目录下只会创建系统内核支持的设备文件,没有其他多余文件。

创建时机:加载设备驱动程序,或热拔插设备。

普通文件和设备文件在用户层看来没有区别,是因为VFS屏蔽了操作细节,并根据设备类型实现不同函数的调用(open,read,write,lseek,ioctl)。

Linux字符驱动中动态分配设备号与动态生成设备节点

 

 

9 DMA

CPU可以读写RAM,所以RAM和IO设备通信的时候需要CPU参与;DMA电路就是替代CPU实现RAM和IO设备通信的电路。

DMA一旦被激活,可以自行传输数据,当传输完成,会触发一个中断;

当CPU和DMA同时访问RAM,由内存仲裁器解决冲突。

DMA的设置时间比较长,因此传输数据量较少时不如直接使用CPU效率高。

 

 

10 内核对设备驱动程序的支持

① 内核不识别端口:使用in/out 汇编语言 + 端口物理地址 可以直接与设备的IO端口交互

② 内核仅识别端口:把IO接口视为能够读写的字符流的顺序设备;只有某些操作系统的并口和串口使用了这种方法

③ 内核识别设备文件:通过对设备文件的VFS接口操作设备

 

 

11 字符设备驱动程序

字符设备驱动程序描述符:struct cdev

  • struct kobject kobj: 内嵌的object
  • struct module *owner: 驱动程序模块指针
  • struct file_operations *ops: 驱动程序文件操作表指针
  • struct list_head list: 相同字符设备驱动所对应的字符设备文件的链表
  • dev_t dev: 初始主设备号和次设备号
  • unsigned int count:设备号范围