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 控制器忙