HDIO_ ioctl 调用摘要¶
Edward A. Falk <efalk@google.com>
2004 年 11 月
本文档旨在描述 HD/IDE 层支持的 ioctl(2) 调用。这些调用主要在 (截至 Linux 5.11) drivers/ata/libata-scsi.c 中实现。
ioctl 值列在 <linux/hdreg.h> 中。截至本文撰写时,它们如下所示:
将参数指针传递到用户空间的 ioctl 调用
HDIO_GETGEO
获取设备几何参数
HDIO_GET_32BIT
获取当前 io_32bit 设置
HDIO_GET_IDENTITY
获取 IDE 标识信息
HDIO_DRIVE_TASKFILE
执行原始任务文件
HDIO_DRIVE_TASK
执行任务和特殊驱动器命令
HDIO_DRIVE_CMD
执行特殊驱动器命令
传递非指针值的 ioctl 调用
HDIO_SET_32BIT
更改 io_32bit 标志
以下信息是通过阅读内核源代码确定的。随着时间的推移,可能会进行一些更正。
一般
除非另有说明,所有 ioctl 调用成功时返回 0,错误时返回 -1 并将 errno 设置为适当的值。
除非另有说明,所有 ioctl 调用在尝试向或从用户地址空间复制数据失败时返回 -1 并将 errno 设置为 EFAULT。
除非另有说明,所有数据结构和常量都在 <linux/hdreg.h> 中定义。
- HDIO_GETGEO
获取设备几何参数
用法
struct hd_geometry geom; ioctl(fd, HDIO_GETGEO, &geom);
- 输入
无
- 输出
包含以下内容的 hd_geometry 结构
磁头数
磁头数量
扇区数
每磁道扇区数量
柱面数
柱面数量,模 65536
起始
此分区的起始扇区。
- 错误返回
EINVAL
如果设备不是磁盘驱动器或软盘驱动器,或者用户传递了空指针
- 注意
对于现代磁盘驱动器而言并非特别有用,因为它们的几何参数无论如何都只是一个礼貌性的虚构。现代驱动器现在纯粹通过扇区号寻址(LBA 寻址),而驱动器几何参数是一个抽象,实际上可能会发生变化。目前(截至 2004 年 11 月),几何参数值是“BIOS”值——大概是 Linux 首次启动时驱动器的值。
此外,hd_geometry 结构中的 cylinders 字段是 unsigned short 类型,这意味着在大多数架构上,对于拥有超过 65535 个磁道的驱动器,此 ioctl 将不会返回有意义的值。
start 字段是 unsigned long 类型,这意味着对于大小超过 219 GB 的磁盘,它将不会包含有意义的值。
- HDIO_GET_IDENTITY
获取 IDE 标识信息
用法
unsigned char identity[512]; ioctl(fd, HDIO_GET_IDENTITY, identity);
- 输入
无
- 输出
ATA 驱动器标识信息。有关完整描述,请参阅 ATA 规范中的 IDENTIFY DEVICE 和 IDENTIFY PACKET DEVICE 命令。
- 错误返回
EINVAL 在分区而不是整个磁盘设备上调用
ENOMSG IDENTIFY DEVICE 信息不可用
- 注意
返回在探测驱动器时获得的信息。其中一些信息可能会发生变化,此 ioctl 不会重新探测驱动器来更新信息。
此信息也可从 /proc/ide/hdX/identify 获取。
- HDIO_GET_32BIT
获取当前 io_32bit 设置
用法
long val; ioctl(fd, HDIO_GET_32BIT, &val);
- 输入
无
- 输出
当前 io_32bit 设置的值
- 注意
0=16 位,1=32 位,2,3 = 32 位+同步
- HDIO_DRIVE_TASKFILE
执行原始任务文件
- 注意
如果您手边没有 ANSI ATA 规范的副本,您可能应该忽略此 ioctl。
通过直接写入驱动器的“任务文件”寄存器来执行 ATA 磁盘命令。需要 ADMIN 和 RAWIO 访问权限。
用法
struct { ide_task_request_t req_task; u8 outbuf[OUTPUT_SIZE]; u8 inbuf[INPUT_SIZE]; } task; memset(&task.req_task, 0, sizeof(task.req_task)); task.req_task.out_size = sizeof(task.outbuf); task.req_task.in_size = sizeof(task.inbuf); ... ioctl(fd, HDIO_DRIVE_TASKFILE, &task); ...
输入
(有关传递给 ioctl 的内存区域的详细信息,请参见下文。)
io_ports[8]
要写入任务文件寄存器的值
hob_ports[8]
高位字节,用于扩展命令。
out_flags
指示哪些寄存器有效力的标志
in_flags
指示应返回哪些寄存器的标志
data_phase
参见下文
req_cmd
要执行的命令类型
out_size
输出缓冲区大小
outbuf
要传输到磁盘的数据缓冲区
inbuf
从磁盘接收的数据缓冲区(参见 [1])
输出
io_ports[]
任务文件寄存器中返回的值
hob_ports[]
高位字节,用于扩展命令。
out_flags
指示哪些寄存器有效力的标志(参见 [2])
in_flags
指示应返回哪些寄存器的标志
outbuf
要传输到磁盘的数据缓冲区(参见 [1])
inbuf
从磁盘接收的数据缓冲区
- 错误返回
EACCES CAP_SYS_ADMIN 或 CAP_SYS_RAWIO 权限未设置。
ENOMSG 设备不是磁盘驱动器。
ENOMEM 无法为任务分配内存
EFAULT req_cmd == TASKFILE_IN_OUT (截至 2.6.8 版本未实现)
EPERM
req_cmd == TASKFILE_MULTI_OUT 且驱动器多重计数尚未设置。
EIO 驱动器命令失败。
注意
[1] 请 仔细 阅读以下注意事项。此 ioctl 充满了陷阱。使用此 ioctl 时应极其谨慎。一个错误很容易损坏数据或导致系统挂起。
[2] 输入和输出缓冲区都从用户空间复制并写回用户空间,即使未使用。
[3] 如果 out_flags 中设置了一个或多个位,并且 in_flags 为零,则以下值将用于 in_flags.all 并在完成后写回 in_flags。
如果驱动器启用了 LBA48 寻址,则为 IDE_TASKFILE_STD_IN_FLAGS | (IDE_HOB_STD_IN_FLAGS << 8)
如果是 CHS/LBA28,则为 IDE_TASKFILE_STD_IN_FLAGS
in_flags.all 与每个启用位字段之间的关联会根据字节序(endianness)翻转;幸运的是,TASKFILE 只使用 inflags.b.data 位并忽略所有其他位。最终结果是,在任何字节序的机器上,除了在完成时修改 in_flags 外,它没有其他影响。
[4] SELECT 的默认值是 (0xa0|DEV_bit|LBA_bit),但每个端口四个驱动器的芯片组除外。对于每个端口四个驱动器的芯片组,第一对的值是 (0xa0|DEV_bit|LBA_bit),第二对的值是 (0x80|DEV_bit|LBA_bit)。
[5] ioctl 的参数是一个指向内存区域的指针,该区域包含一个 ide_task_request_t 结构,后跟一个可选的要传输到驱动器的数据缓冲区,再后跟一个可选的从驱动器接收数据的缓冲区。
命令通过 ide_task_request_t 结构传递给磁盘驱动器,该结构包含以下字段:
io_ports[8]
任务文件寄存器的值
hob_ports[8]
高位字节,用于扩展命令
out_flags
指示 io_ports[] 和 hob_ports[] 数组中哪些条目包含有效值的标志。类型为 ide_reg_valid_t。
in_flags
指示 io_ports[] 和 hob_ports[] 数组中哪些条目在返回时应包含有效值的标志。
data_phase
参见下文
req_cmd
命令类型,参见下文
out_size
输出(用户->驱动器)缓冲区大小,字节
in_size
输入(驱动器->用户)缓冲区大小,字节
当 out_flags 为零时,加载以下寄存器。
HOB_FEATURE
如果驱动器支持 LBA48
HOB_NSECTOR
如果驱动器支持 LBA48
HOB_SECTOR
如果驱动器支持 LBA48
HOB_LCYL
如果驱动器支持 LBA48
HOB_HCYL
如果驱动器支持 LBA48
FEATURE
NSECTOR
SECTOR
LCYL
HCYL
SELECT
首先,如果 LBA48,则与 0xE0 进行掩码操作,否则与 0xEF 进行掩码操作;然后,与 SELECT 的默认值进行或操作。
如果 out_flags 中设置了任何位,则加载以下寄存器。
HOB_DATA
如果 out_flags.b.data 已设置。在小端序机器上,HOB_DATA 将通过 DD8-DD15 传输,在大端序机器上通过 DD0-DD7 传输。
DATA
如果 out_flags.b.data 已设置。在小端序机器上,DATA 将通过 DD0-DD7 传输,在大端序机器上通过 DD8-DD15 传输。
HOB_NSECTOR
如果 out_flags.b.nsector_hob 已设置
HOB_SECTOR
如果 out_flags.b.sector_hob 已设置
HOB_LCYL
如果 out_flags.b.lcyl_hob 已设置
HOB_HCYL
如果 out_flags.b.hcyl_hob 已设置
FEATURE
如果 out_flags.b.feature 已设置
NSECTOR
如果 out_flags.b.nsector 已设置
SECTOR
如果 out_flags.b.sector 已设置
LCYL
如果 out_flags.b.lcyl 已设置
HCYL
如果 out_flags.b.hcyl 已设置
SELECT
与 SELECT 的默认值进行或操作,无论 out_flags.b.select 是否设置都加载。
仅当满足以下条件之一时,任务文件寄存器才会在命令完成后从驱动器读回 {io|hob}_ports[];否则,原始值将不变地写回。
驱动器命令失败 (EIO)。
out_flags 中设置了一个或多个位。
请求的 data_phase 是 TASKFILE_NO_DATA。
HOB_DATA
如果 in_flags.b.data 已设置。在小端序机器上,它将包含 DD8-DD15,在大端序机器上包含 DD0-DD7。
DATA
如果 in_flags.b.data 已设置。在小端序机器上,它将包含 DD0-DD7,在大端序机器上包含 DD8-DD15。
HOB_FEATURE
如果驱动器支持 LBA48
HOB_NSECTOR
如果驱动器支持 LBA48
HOB_SECTOR
如果驱动器支持 LBA48
HOB_LCYL
如果驱动器支持 LBA48
HOB_HCYL
如果驱动器支持 LBA48
NSECTOR
SECTOR
LCYL
HCYL
data_phase 字段描述要执行的数据传输。值为以下之一:
TASKFILE_IN
TASKFILE_MULTI_IN
TASKFILE_OUT
TASKFILE_MULTI_OUT
TASKFILE_IN_OUT
TASKFILE_IN_DMA
TASKFILE_IN_DMAQ
等同于 IN_DMA (不支持队列)
TASKFILE_OUT_DMA
TASKFILE_OUT_DMAQ
等同于 OUT_DMA (不支持队列)
TASKFILE_P_IN
未实现
TASKFILE_P_IN_DMA
未实现
TASKFILE_P_IN_DMAQ
未实现
TASKFILE_P_OUT
未实现
TASKFILE_P_OUT_DMA
未实现
TASKFILE_P_OUT_DMAQ
未实现
req_cmd 字段对命令类型进行分类。它可以是以下之一:
IDE_DRIVE_TASK_NO_DATA
IDE_DRIVE_TASK_SET_XFER
未实现
IDE_DRIVE_TASK_IN
IDE_DRIVE_TASK_OUT
未实现
IDE_DRIVE_TASK_RAW_WRITE
[6] 除了重置所有位之外,不要访问 {in|out}_flags->all。始终访问单独的位字段。->all 值将根据字节序(endianness)翻转。出于同样的原因,不要使用 hdreg.h 中定义的 IDE_{TASKFILE|HOB}_STD_{OUT|IN}_FLAGS 常量。
- HDIO_DRIVE_CMD
执行特殊驱动器命令
注意:如果您手边没有 ANSI ATA 规范的副本,您可能应该忽略此 ioctl。
用法
u8 args[4+XFER_SIZE]; ... ioctl(fd, HDIO_DRIVE_CMD, args);
- 输入
WIN_SMART 之外的命令
args[0]
COMMAND
args[1]
NSECTOR
args[2]
FEATURE
args[3]
NSECTOR
WIN_SMART
args[0]
COMMAND
args[1]
SECTOR
args[2]
FEATURE
args[3]
NSECTOR
- 输出
args[] 缓冲区填充有寄存器值,后跟任何
磁盘返回的数据。
args[0]
状态
args[1]
错误
args[2]
NSECTOR
args[3]
未定义
args[4+]
命令返回的 NSECTOR * 512 字节数据。
- 错误返回
EACCES 拒绝访问:需要 CAP_SYS_RAWIO 权限
ENOMEM 无法为任务分配内存
EIO 驱动器报告错误
注意
[1] 对于 WIN_SMART 之外的命令,args[1] 应等于 args[3]。SECTOR、LCYL 和 HCYL 未定义。对于 WIN_SMART,0x4f 和 0xc2 将分别加载到 LCYL 和 HCYL 中。在这两种情况下,SELECT 都将包含驱动器的默认值。有关 SELECT 的默认值,请参阅 HDIO_DRIVE_TASKFILE 注释。
[2] 如果 NSECTOR 值大于零,并且驱动器在命令中断时设置 DRQ,则从设备读取 NSECTOR * 512 字节到 NSECTOR 后面的区域。在上述示例中,该区域将是 args[4..4+XFER_SIZE]。无论 HDIO_SET_32BIT 设置如何,都使用 16 位 PIO。
[3] 如果 COMMAND == WIN_SETFEATURES && FEATURE == SETFEATURES_XFER && NSECTOR >= XFER_SW_DMA_0 && 驱动器支持任何 DMA 模式,IDE 驱动程序将尝试相应地调整驱动器的传输模式。
- HDIO_DRIVE_TASK
执行任务和特殊驱动器命令
注意:如果您手边没有 ANSI ATA 规范的副本,您可能应该忽略此 ioctl。
用法
u8 args[7]; ... ioctl(fd, HDIO_DRIVE_TASK, args);
- 输入
任务文件寄存器值
args[0]
COMMAND
args[1]
FEATURE
args[2]
NSECTOR
args[3]
SECTOR
args[4]
LCYL
args[5]
HCYL
args[6]
SELECT
- 输出
任务文件寄存器值
args[0]
状态
args[1]
错误
args[2]
NSECTOR
args[3]
SECTOR
args[4]
LCYL
args[5]
HCYL
args[6]
SELECT
- 错误返回
EACCES 拒绝访问:需要 CAP_SYS_RAWIO 权限
ENOMEM 无法为任务分配内存
ENOMSG 设备不是磁盘驱动器。
EIO 驱动器命令失败。
注意
[1] SELECT 寄存器的 DEV 位 (0x10) 被忽略,并使用驱动器的适当值。所有其他位均不变地使用。
- HDIO_SET_32BIT
更改 io_32bit 标志
用法
int val; ioctl(fd, HDIO_SET_32BIT, val);
- 输入
io_32bit 标志的新值
- 输出
无
- 错误返回
EINVAL 在分区而不是整个磁盘设备上调用
EACCES 拒绝访问:需要 CAP_SYS_ADMIN 权限
EINVAL 值超出范围 [0 3]
EBUSY 控制器忙