CDROM ioctl 调用总结¶
Edward A. Falk <efalk@google.com>
2004 年 11 月
本文档旨在描述 CDROM 层支持的 ioctl(2) 调用。这些调用在 Linux 2.6 版本中主要在 `drivers/cdrom/cdrom.c` 和 `drivers/block/scsi_ioctl.c` 中实现。
ioctl 值列在 `
CDROMPAUSE
暂停音频操作
CDROMRESUME
恢复暂停的音频操作
CDROMPLAYMSF
播放音频 MSF (struct cdrom_msf)
CDROMPLAYTRKIND
播放音频轨道/索引 (struct cdrom_ti)
CDROMREADTOCHDR
读取 TOC 头 (struct cdrom_tochdr)
CDROMREADTOCENTRY
读取 TOC 条目 (struct cdrom_tocentry)
CDROMSTOP
停止光盘驱动器
CDROMSTART
启动光盘驱动器
CDROMEJECT
弹出光盘介质
CDROMVOLCTRL
控制输出音量 (struct cdrom_volctrl)
CDROMSUBCHNL
读取子通道数据 (struct cdrom_subchnl)
CDROMREADMODE2
读取 CDROM 模式 2 数据 (2336 字节) (struct cdrom_read)
CDROMREADMODE1
读取 CDROM 模式 1 数据 (2048 字节) (struct cdrom_read)
CDROMREADAUDIO
(struct cdrom_read_audio)
CDROMEJECT_SW
启用(1)/禁用(0) 自动弹出
CDROMMULTISESSION
获取多段会话光盘的最后一段会话起始地址 (struct cdrom_multisession)
CDROM_GET_MCN
获取“通用产品代码”(如果可用)(struct cdrom_mcn)
CDROM_GET_UPC
已废弃,请改用 CDROM_GET_MCN。
CDROMRESET
硬重置驱动器
CDROMVOLREAD
获取驱动器的音量设置 (struct cdrom_volctrl)
CDROMREADRAW
以原始模式读取数据 (2352 字节) (struct cdrom_read)
CDROMREADCOOKED
以处理模式读取数据
CDROMSEEK
寻址到 msf 地址
CDROMPLAYBLK
仅限 scsi-cd, (struct cdrom_blk)
CDROMREADALL
读取所有 2646 字节
CDROMGETSPINDOWN
返回 4 位 spindown 值
CDROMSETSPINDOWN
设置 4 位 spindown 值
CDROMCLOSETRAY
CDROMEJECT 的对应操作
CDROM_SET_OPTIONS
设置行为选项
CDROM_CLEAR_OPTIONS
清除行为选项
CDROM_SELECT_SPEED
设置光盘驱动器速度
CDROM_SELECT_DISC
选择光盘 (适用于自动点唱机)
CDROM_MEDIA_CHANGED
检查介质是否改变
CDROM_TIMED_MEDIA_CHANGE
检查介质自给定时间以来是否改变 (struct cdrom_timed_media_change_info)
CDROM_DRIVE_STATUS
获取托盘位置等
CDROM_DISC_STATUS
获取光盘类型等
CDROM_CHANGER_NSLOTS
获取插槽数量
CDROM_LOCKDOOR
锁定或解锁仓门
CDROM_DEBUG
打开/关闭调试消息
CDROM_GET_CAPABILITY
获取能力
CDROMAUDIOBUFSIZ
设置音频缓冲区大小
DVD_READ_STRUCT
读取结构
DVD_WRITE_STRUCT
写入结构
DVD_AUTH
认证
CDROM_SEND_PACKET
发送数据包到驱动器
CDROM_NEXT_WRITABLE
获取下一个可写块
CDROM_LAST_WRITTEN
获取光盘上最后写入的块
以下信息是通过阅读内核源代码确定的。随着时间的推移,可能会进行一些更正。
一般
除非另有说明,所有 ioctl 调用在成功时返回 0,在错误时返回 -1 并将 errno 设置为适当的值。(某些 ioctl 返回非负数据值。)
除非另有说明,所有 ioctl 调用在尝试将数据复制到或从用户地址空间复制失败时,返回 -1 并将 errno 设置为 EFAULT。
各个驱动程序可能会返回此处未列出的错误代码。
除非另有说明,所有数据结构和常量均定义在 `
` 中。
- CDROMPAUSE
暂停音频操作
用法
ioctl(fd, CDROMPAUSE, 0);
- 输入
无
- 输出
无
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
- CDROMRESUME
恢复暂停的音频操作
用法
ioctl(fd, CDROMRESUME, 0);
- 输入
无
- 输出
无
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
- CDROMPLAYMSF
播放音频 MSF
(struct cdrom_msf)
用法
struct cdrom_msf msf; ioctl(fd, CDROMPLAYMSF, &msf);
- 输入
`cdrom_msf` 结构,描述要播放的音乐片段
- 输出
无
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
- 注意
MSF 代表分-秒-帧
LBA 代表逻辑块地址
片段以开始和结束时间描述,其中每个时间都以分:秒:帧表示。一帧是 1/75 秒。
- CDROMPLAYTRKIND
播放音频轨道/索引
(struct cdrom_ti)
用法
struct cdrom_ti ti; ioctl(fd, CDROMPLAYTRKIND, &ti);
- 输入
`cdrom_ti` 结构,描述要播放的音乐片段
- 输出
无
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
- 注意
片段以开始和结束时间描述,其中每个时间都以轨道和索引表示。
- CDROMREADTOCHDR
读取 TOC 头
(struct cdrom_tochdr)
用法
cdrom_tochdr header; ioctl(fd, CDROMREADTOCHDR, &header);
- 输入
`cdrom_tochdr` 结构
- 输出
`cdrom_tochdr` 结构
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
- CDROMREADTOCENTRY
读取 TOC 条目
(struct cdrom_tocentry)
用法
struct cdrom_tocentry entry; ioctl(fd, CDROMREADTOCENTRY, &entry);
- 输入
`cdrom_tocentry` 结构
- 输出
`cdrom_tocentry` 结构
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
EINVAL `entry.cdte_format` 不是 CDROM_MSF 或 CDROM_LBA
EINVAL 请求的轨道超出范围
EIO 读取 TOC 时发生 I/O 错误
- 注意
TOC 代表目录 (Table Of Contents)
MSF 代表分-秒-帧
LBA 代表逻辑块地址
- CDROMSTOP
停止光盘驱动器
用法
ioctl(fd, CDROMSTOP, 0);
- 输入
无
- 输出
无
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
- 注意
此 ioctl 的确切解释取决于设备,但大多数设备似乎都会使驱动器停止转动。
- CDROMSTART
启动光盘驱动器
用法
ioctl(fd, CDROMSTART, 0);
- 输入
无
- 输出
无
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
- 注意
此 ioctl 的确切解释取决于设备,但大多数设备似乎都会使驱动器启动和/或关闭托盘。其他设备则完全忽略此 ioctl。
- CDROMEJECT
弹出光盘介质
用法
ioctl(fd, CDROMEJECT, 0);
- 输入
无
- 输出
无
- 错误返回
ENOSYS 光盘驱动器不具备弹出能力
EBUSY 其他进程正在访问驱动器,或仓门已锁定
- 注意
参见下面的 CDROM_LOCKDOOR。
- CDROMCLOSETRAY
CDROMEJECT 的对应操作
用法
ioctl(fd, CDROMCLOSETRAY, 0);
- 输入
无
- 输出
无
- 错误返回
ENOSYS 光盘驱动器不具备关闭托盘的能力
EBUSY 其他进程正在访问驱动器,或仓门已锁定
- 注意
参见下面的 CDROM_LOCKDOOR。
- CDROMVOLCTRL
控制输出音量 (struct cdrom_volctrl)
用法
struct cdrom_volctrl volume; ioctl(fd, CDROMVOLCTRL, &volume);
- 输入
`cdrom_volctrl` 结构,包含多达 4 个通道的音量。
- 输出
无
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
- CDROMVOLREAD
获取驱动器的音量设置
(struct cdrom_volctrl)
用法
struct cdrom_volctrl volume; ioctl(fd, CDROMVOLREAD, &volume);
- 输入
无
- 输出
当前音量设置。
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
- CDROMSUBCHNL
读取子通道数据
(struct cdrom_subchnl)
用法
struct cdrom_subchnl q; ioctl(fd, CDROMSUBCHNL, &q);
- 输入
`cdrom_subchnl` 结构
- 输出
`cdrom_subchnl` 结构
- 错误返回
ENOSYS 光盘驱动器不具备音频功能。
EINVAL 格式不是 CDROM_MSF 或 CDROM_LBA
- 注意
返回时,格式根据用户请求转换为 CDROM_MSF 或 CDROM_LBA
- CDROMREADRAW
以原始模式读取数据 (2352 字节)
(struct cdrom_read)
用法
union { struct cdrom_msf msf; /* input */ char buffer[CD_FRAMESIZE_RAW]; /* return */ } arg; ioctl(fd, CDROMREADRAW, &arg);
- 输入
`cdrom_msf` 结构,指示要读取的地址。
只有起始值是重要的。
- 输出
数据写入用户提供的地址。
- 错误返回
EINVAL 地址小于 0,或 msf 小于 0:2:0
ENOMEM 内存不足
- 注意
截至 2.6.8.1 版本,`
` 中的注释表明此 ioctl 接受 `cdrom_read` 结构,但实际源代码读取的是 `cdrom_msf` 结构并将数据缓冲区写入同一地址。 MSF 值通过此公式转换为 LBA 值
lba = (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
- CDROMREADMODE1
读取 CDROM 模式 1 数据 (2048 字节)
(struct cdrom_read)
- 注意
与 CDROMREADRAW 相同,只是块大小为 CD_FRAMESIZE (2048) 字节
- CDROMREADMODE2
读取 CDROM 模式 2 数据 (2336 字节)
(struct cdrom_read)
- 注意
与 CDROMREADRAW 相同,只是块大小为 CD_FRAMESIZE_RAW0 (2336) 字节
- CDROMREADAUDIO
(struct cdrom_read_audio)
用法
struct cdrom_read_audio ra; ioctl(fd, CDROMREADAUDIO, &ra);
- 输入
`cdrom_read_audio` 结构,包含读取起始点和长度
- 输出
音频数据,返回到由 ra 指示的缓冲区
- 错误返回
EINVAL 格式不是 CDROM_MSF 或 CDROM_LBA
EINVAL `nframes` 不在 [1 75] 范围内
ENXIO 驱动器没有队列 (可能意味着无效的文件描述符 fd)
ENOMEM 内存不足
- CDROMEJECT_SW
启用(1)/禁用(0) 自动弹出
用法
int val; ioctl(fd, CDROMEJECT_SW, val);
- 输入
指定自动弹出标志。
- 输出
无
- 错误返回
ENOSYS 驱动器不具备弹出能力。
EBUSY 仓门已锁定
- CDROMMULTISESSION
获取多段会话光盘的最后一段会话起始地址
(struct cdrom_multisession)
用法
struct cdrom_multisession ms_info; ioctl(fd, CDROMMULTISESSION, &ms_info);
- 输入
`cdrom_multisession` 结构,包含所需
格式。
- 输出
`cdrom_multisession` 结构填充有 `last_session` 信息。
- 错误返回
EINVAL 格式不是 CDROM_MSF 或 CDROM_LBA
- CDROM_GET_MCN
获取“通用产品代码”(如果可用)
(struct cdrom_mcn)
用法
struct cdrom_mcn mcn; ioctl(fd, CDROM_GET_MCN, &mcn);
- 输入
无
- 输出
通用产品代码
- 错误返回
ENOSYS 驱动器不具备读取 MCN 数据的能力。
- 注意
源代码注释说明
The following function is implemented, although very few audio discs give Universal Product Code information, which should just be the Medium Catalog Number on the box. Note, that the way the code is written on the CD is /not/ uniform across all discs!
- CDROM_GET_UPC
CDROM_GET_MCN (已废弃)
截至 2.6.8.1 版本,未实现
- CDROMRESET
硬重置驱动器
用法
ioctl(fd, CDROMRESET, 0);
- 输入
无
- 输出
无
- 错误返回
EACCES 访问被拒绝:需要 CAP_SYS_ADMIN 权限
ENOSYS 驱动器不具备重置能力。
- CDROMREADCOOKED
以处理模式读取数据
用法
u8 buffer[CD_FRAMESIZE] ioctl(fd, CDROMREADCOOKED, buffer);
- 输入
无
- 输出
2048 字节数据,“处理”模式。
- 注意
并非所有驱动器都实现。
- CDROMREADALL
读取所有 2646 字节
与 CDROMREADCOOKED 相同,但读取 2646 字节。
- CDROMSEEK
寻址到 msf 地址
用法
struct cdrom_msf msf; ioctl(fd, CDROMSEEK, &msf);
- 输入
要寻址到的 MSF 地址。
- 输出
无
- CDROMPLAYBLK
仅限 scsi-cd
(struct cdrom_blk)
用法
struct cdrom_blk blk; ioctl(fd, CDROMPLAYBLK, &blk);
- 输入
要播放的区域
- 输出
无
- CDROMGETSPINDOWN
已过时,曾仅限于 ide-cd
用法
char spindown; ioctl(fd, CDROMGETSPINDOWN, &spindown);
- 输入
无
- 输出
当前 4 位 spindown 值。
- CDROMSETSPINDOWN
已过时,曾仅限于 ide-cd
用法
char spindown ioctl(fd, CDROMSETSPINDOWN, &spindown);
- 输入
用于控制 spindown 的 4 位值 (待办:此处需更多细节)
- 输出
无
- CDROM_SET_OPTIONS
设置行为选项
用法
int options; ioctl(fd, CDROM_SET_OPTIONS, options);
- 输入
驱动器选项的新值。以下各项的逻辑“或”:
CDO_AUTO_CLOSE
首次 open(2) 时关闭托盘
CDO_AUTO_EJECT
最后释放时打开托盘
CDO_USE_FFLAGS
在 open 时使用 O_NONBLOCK 信息
CDO_LOCK
在文件打开时锁定托盘
CDO_CHECK_TYPE
在打开数据时检查类型
- 输出
在 ioctl 返回值中返回结果选项设置。错误时返回 -1。
- 错误返回
ENOSYS 所选选项不受驱动器支持。
- CDROM_CLEAR_OPTIONS
清除行为选项
与 CDROM_SET_OPTIONS 相同,只是所选选项被关闭。
- CDROM_SELECT_SPEED
设置光盘驱动器速度
用法
int speed; ioctl(fd, CDROM_SELECT_SPEED, speed);
- 输入
新的驱动器速度。
- 输出
无
- 错误返回
ENOSYS 驱动器不支持速度选择。
- CDROM_SELECT_DISC
选择光盘 (适用于自动点唱机)
用法
int disk; ioctl(fd, CDROM_SELECT_DISC, disk);
- 输入
要加载到驱动器中的光盘。
- 输出
无
- 错误返回
EINVAL 光盘编号超出驱动器容量
- CDROM_MEDIA_CHANGED
检查介质是否改变
用法
int slot; ioctl(fd, CDROM_MEDIA_CHANGED, slot);
- 输入
要测试的插槽编号,除了自动换碟机外始终为零。
也可能是特殊值 CDSL_NONE 或 CDSL_CURRENT
- 输出
ioctl 返回值为 0 或 1,取决于介质是否
已改变,或错误时返回 -1。
- 错误返回
ENOSYS 驱动器无法检测到介质改变
EINVAL 插槽编号超出驱动器容量
ENOMEM 内存不足
- CDROM_DRIVE_STATUS
获取托盘位置等
用法
int slot; ioctl(fd, CDROM_DRIVE_STATUS, slot);
- 输入
要测试的插槽编号,除了自动换碟机外始终为零。
也可能是特殊值 CDSL_NONE 或 CDSL_CURRENT
- 输出
ioctl 返回值将是以下值之一
来自 `
` CDS_NO_INFO
信息不可用。
CDS_NO_DISC
CDS_TRAY_OPEN
CDS_DRIVE_NOT_READY
CDS_DISC_OK
-1
错误
- 错误返回
ENOSYS 驱动器无法检测到驱动器状态
EINVAL 插槽编号超出驱动器容量
ENOMEM 内存不足
- CDROM_DISC_STATUS
获取光盘类型等
用法
ioctl(fd, CDROM_DISC_STATUS, 0);
- 输入
无
- 输出
ioctl 返回值将是以下值之一
来自 `
` CDS_NO_INFO
CDS_AUDIO
CDS_MIXED
CDS_XA_2_2
CDS_XA_2_1
CDS_DATA_1
- 错误返回
目前无
- 注意
源代码注释说明
Ok, this is where problems start. The current interface for the CDROM_DISC_STATUS ioctl is flawed. It makes the false assumption that CDs are all CDS_DATA_1 or all CDS_AUDIO, etc. Unfortunately, while this is often the case, it is also very common for CDs to have some tracks with data, and some tracks with audio. Just because I feel like it, I declare the following to be the best way to cope. If the CD has ANY data tracks on it, it will be returned as a data CD. If it has any XA tracks, I will return it as that. Now I could simplify this interface by combining these returns with the above, but this more clearly demonstrates the problem with the current interface. Too bad this wasn't designed to use bitmasks... -Erik Well, now we have the option CDS_MIXED: a mixed-type CD. User level programmers might feel the ioctl is not very useful. ---david
- CDROM_CHANGER_NSLOTS
获取插槽数量
用法
ioctl(fd, CDROM_CHANGER_NSLOTS, 0);
- 输入
无
- 输出
ioctl 返回值将是 CD 换碟机中的插槽数量。对于非多碟设备,通常为 1。
- 错误返回
无
- CDROM_LOCKDOOR
锁定或解锁仓门
用法
int lock; ioctl(fd, CDROM_LOCKDOOR, lock);
- 输入
仓门锁定标志,1=锁定,0=解锁
- 输出
无
- 错误返回
EDRIVE_CANT_DO_THIS
不支持仓门锁定功能。
EBUSY
当多个用户打开驱动器且不具备 CAP_SYS_ADMIN 权限时尝试解锁
- 注意
截至 2.6.8.1 版本,锁定标志是全局的,意味着所有 CD 驱动器将同时锁定或解锁。这可能是一个错误。
EDRIVE_CANT_DO_THIS 值在 `
` 中定义,目前 (2.6.8.1) 与 EOPNOTSUPP 相同。
- CDROM_DEBUG
打开/关闭调试消息
用法
int debug; ioctl(fd, CDROM_DEBUG, debug);
- 输入
光盘调试标志,0=禁用,1=启用
- 输出
ioctl 返回值将是新的调试标志。
- 错误返回
EACCES 访问被拒绝:需要 CAP_SYS_ADMIN 权限
- CDROM_GET_CAPABILITY
获取能力
用法
ioctl(fd, CDROM_GET_CAPABILITY, 0);
- 输入
无
- 输出
ioctl 返回值是当前设备能力标志。参见 CDC_CLOSE_TRAY, CDC_OPEN_TRAY 等。
- CDROMAUDIOBUFSIZ
设置音频缓冲区大小
用法
int arg; ioctl(fd, CDROMAUDIOBUFSIZ, val);
- 输入
新的音频缓冲区大小
- 输出
ioctl 返回值是新的音频缓冲区大小,或错误时返回 -1。
- 错误返回
ENOSYS 该驱动程序不支持。
- 注意
并非所有驱动程序都支持。
DVD_READ_STRUCT 读取结构
用法
dvd_struct s; ioctl(fd, DVD_READ_STRUCT, &s);
- 输入
`dvd_struct` 结构,包含
类型
指定所需信息,可以是 DVD_STRUCT_PHYSICAL, DVD_STRUCT_COPYRIGHT, DVD_STRUCT_DISCKEY, DVD_STRUCT_BCA, DVD_STRUCT_MANUFACT 之一
physical.layer_num
所需层,从 0 开始索引
copyright.layer_num
所需层,从 0 开始索引
disckey.agid
- 输出
`dvd_struct` 结构,包含
物理
当 `type == DVD_STRUCT_PHYSICAL` 时
版权
当 `type == DVD_STRUCT_COPYRIGHT` 时
disckey.value
当 `type == DVD_STRUCT_DISCKEY` 时
bca.{len,value}
当 `type == DVD_STRUCT_BCA` 时
manufact.{len,valu}
当 `type == DVD_STRUCT_MANUFACT` 时
- 错误返回
EINVAL `physical.layer_num` 超过层数
EIO 从驱动器收到无效响应
DVD_WRITE_STRUCT 写入结构
截至 2.6.8.1 版本,未实现
DVD_AUTH 认证
用法
dvd_authinfo ai; ioctl(fd, DVD_AUTH, &ai);
- 输入
`dvd_authinfo` 结构。参见 `
` - 输出
`dvd_authinfo` 结构。
- 错误返回
ENOTTY `ai.type` 未识别。
- CDROM_SEND_PACKET
发送数据包到驱动器
用法
struct cdrom_generic_command cgc; ioctl(fd, CDROM_SEND_PACKET, &cgc);
- 输入
`cdrom_generic_command` 结构,包含要发送的数据包。
- 输出
无
`cdrom_generic_command` 结构,包含结果。
- 错误返回
EIO
命令失败。
EPERM
操作不允许,原因可能是尝试对只读打开的驱动器执行写入命令,或该命令需要 CAP_SYS_RAWIO 权限
EINVAL
`cgc.data_direction` 未设置
- CDROM_NEXT_WRITABLE
获取下一个可写块
用法
long next; ioctl(fd, CDROM_NEXT_WRITABLE, &next);
- 输入
无
- 输出
下一个可写块。
- 注意
如果设备不直接支持此 ioctl,则
ioctl 将返回 CDROM_LAST_WRITTEN + 7。
- CDROM_LAST_WRITTEN
获取光盘上最后写入的块
用法
long last; ioctl(fd, CDROM_LAST_WRITTEN, &last);
- 输入
无
- 输出
光盘上最后写入的块
- 注意
如果设备不直接支持此 ioctl,则结果将从光盘的目录表中导出。如果目录表无法读取,此 ioctl 将返回错误。