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