HDIO_ ioctl 调用摘要

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[];否则,原始值将不变地写回。

  1. 驱动器命令失败 (EIO)。

  2. out_flags 中设置了一个或多个位。

  3. 请求的 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 控制器忙