欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[笔记分享] [SD] 块设备驱动学习小结

程序员文章站 2024-02-22 20:27:10
...

Chipset: msm8926
OS: Android4.4
Kernel: 3.4.0

相关概念:

块设备与字符设备区别:

  1. 以块传输,字符设备以字节为单位。
  2. 有对应缓冲区。
  3. 可以随机访问。可以不按传输顺序访问,比如访问的是1/10/3/2扇区,可以调整为1/2/3/10扇区。此设计是为了机械类块设备如硬盘设计的,对于SD/EMMC可以直接访问。

Struct block_device_operations:

[blkdev.h]
[笔记分享] [SD] 块设备驱动学习小结
此struct和字符设备的file_operations类似。

需要关注的有open/release/ioctl/getgeo.
Ioctl: 块设备的请求通过此接口。
Getgeo: 根据块设备的信息填充hd_geometry, 里面包含磁头,扇区,柱面,俗称CHS。

Struct gendisk:

[genhd.h]
[笔记分享] [SD] 块设备驱动学习小结
Major: 主设备号。
First_minor: 第一个次设备号。
Minors: 最大支持次设备号数。
Part_tbl: 分区信息。
Part0: 磁盘信息
Fops: 块设备操作函数集。
Queue: 请求队列,用户请求通过队列来处理。

Gendisk相关函数:
1. struct gendisk *alloc_disk(int minors)
分配struct gendisk,驱动不能自己分配,需要调用此接口才行。
Minors表示改磁盘最大能支持多少分区。
2. void add_disk(struct gendisk *disk)
注册disk,在驱动初始化完成之后调用。
3. void del_gendisk(struct gendisk *disk)
4. static inline void set_capacity(struct gendisk *disk, sector_t size)
设置disk容量,以扇区为单位。块设备最小寻址单位是扇区,最常见的是512bytes,还
有其他大小,对于内核,与块设备驱动交互的扇区都以512bytes为单位。
许多块设备一次能传输多个扇区。

Struct request:

[blkdev.h]
[笔记分享] [SD] 块设备驱动学习小结
[笔记分享] [SD] 块设备驱动学习小结
用request来表示一个等待I/O的请求。
Queuelist: 构成链表。
Q: 指向对应的queue。
__data_len: 要传送的扇区数。
_sector: 要传送的下个sector.
Bio: 请求的block io,里面包含具体的block信息。
Rq_disk: 对应disk设备.
Part: 对应磁盘。

Struct request_queue:

[blkdev.h]
[笔记分享] [SD] 块设备驱动学习小结
[笔记分享] [SD] 块设备驱动学习小结
[笔记分享] [SD] 块设备驱动学习小结

此结构主要描述设备能够支持的请求类型,扇区大小,对齐要求等参数。
还包含了一个I/O调度器,选使用哪种调度算法来控制当前的I/O请求排列,相邻扇区合并等过程。不过对于SD/EMMC这类设备,就不用I/O调度器,直接现来现写。

相关接口:

  1. struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
    [blk-core.c]初始化请求队列, rfn为对应请求处理函数指针。
  2. struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
    [blk-core.c] sd/ram等这些随机访问的非机械设备,不需要复杂I/O调度,应使用此接口
    ,再调用blk_queue_make_request()将制造函数与队列绑定,这样就直接调用制造函数,而且不会调用请求函数了。
    可参考如下比较:
    [笔记分享] [SD] 块设备驱动学习小结

Struct bio:

[笔记分享] [SD] 块设备驱动学习小结
一个bio对应一个I/O请求。此结构包含具体的I/O请求信息,如扇区索引,传送大小,读写标志等。
Bi_sector: 要传送的第一个sector。
Bi_rw: 读还是写。
Bi_io_vec: 包含对应传输的页,字节数以及偏移地址。

Struct bio_vec:

[笔记分享] [SD] 块设备驱动学习小结

Gedisk/request/queue/bio/bio_vec关系图:

[笔记分享] [SD] 块设备驱动学习小结


注册/注销:

int register_blkdev(unsigned int major, const char *name)
此接口可选,以后可能会被移除,它只完成了两件事:
1. 分配一个主设备号。
2. 在/proc/devices下创建入口。


不使用请求处理函数:

手机都是对sd/emmc这类随机访问设备操作,因此使用请求处理函数的方法就不阐述了。
在此模式下需要提供一个“制造函数”,类似于请求处理函数。
[blkdev.h]
[笔记分享] [SD] 块设备驱动学习小结
虽然q参数还在,实际不包含任何请求。
核心是bio,处理完成之后使用bio_endio()通知处理结束。
[bio.c]

[笔记分享] [SD] 块设备驱动学习小结


Ramdisk举例:

Ramdisk是一种模拟磁盘,数据存在ram中,以块设备的方式访问,对应设备文件一般为/dev/ram%d。
我们来看看上面提到的这些关键数据是如何被使用到的。
[brd.c]

brd_init():

[笔记分享] [SD] 块设备驱动学习小结
559: 调用register_blkdev()注册块设备,主设备号RAMDISK_MAJOR为1.
563: 分配brd,后面会看下它里面做了什么。
572: 在初始化完成之后将disk添加进去。

执行完register_blkdev()之后在/proc/devices下能看到:
[笔记分享] [SD] 块设备驱动学习小结

执行完add_disk()之后能在/dev/block下看到ramx节点,总共有16个。

[笔记分享] [SD] 块设备驱动学习小结
[笔记分享] [SD] 块设备驱动学习小结

Note: /dev/下的节点和是udev监测sysfs里的设备而创建的。

/dev/block对应sysfs的创建是:
[genhd.c]
[笔记分享] [SD] 块设备驱动学习小结
而ramx节点的创建对应的sysfs 执行code是:
[brd.c]
[笔记分享] [SD] 块设备驱动学习小结
[genhd.c]
[笔记分享] [SD] 块设备驱动学习小结

brd_alloc():

[笔记分享] [SD] 块设备驱动学习小结
473: 分配request queue,且表示不是用I/O调度器。所以这里没使用请求处理函数的方法。
476: 对应的“制造函数”是brd_make_request().
483: 分配disk.
488: 表明对应的fops是brd_fops.
493: 设置disk的capacity.

brd_make_request():

[笔记分享] [SD] 块设备驱动学习小结
344: 本次传输是读还是写。
349: 读或者写数据。
356: 结束读写。
[笔记分享] [SD] 块设备驱动学习小结

314/318: 读写分别只是将数据copy到brd或者从brd的sector copy出来。
[笔记分享] [SD] 块设备驱动学习小结

brd_fops:

[笔记分享] [SD] 块设备驱动学习小结
[笔记分享] [SD] 块设备驱动学习小结
399~403: 如果有被打开了,就将device相关的buffer/cache清理掉。

                                                    Kris.Fei
                                                    2014/09/09