int xxioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp);
cmd 参数表明应该执行哪个 ioctl(9E) 命令。根据约定,命令的 8-15 位指示与 I/O 控制命令有关的驱动程序。通常,字符的 ASCII 代码代表该驱动程序。驱动程序特定命令位于 0-7 位。以下示例说明了一些 I/O 命令的创建:
#define XXIOC (`x' << 8) /* `x' is a character representing */ /* device xx */ #define XX_GET_STATUS (XXIOC | 1) /* get status register */ #define XX_SET_CMD (XXIOC | 2) /* send command */
对 arg 的解释视命令而定。在驱动程序文档或手册页中应该介绍了这些 I/O 控制命令。在公共头文件中还会定义命令,以便应用程序能够确定命令的名称、命令执行的操作以及命令以 arg 的形式接受或返回的内容。进出驱动程序的任何 arg 数据传输都必须由驱动程序执行。
特定种类的设备(如帧缓存器或磁盘)必须支持 I/O 控制请求的标准集。这些标准 I/O 控制接口在 Solaris 8 Reference Manual Collection 中进行了介绍。例如,fbio(7I) 介绍了帧缓存器必须支持的 I/O 控制,而 dkio(7I) 则介绍了标准的磁盘 I/O 控制。有关 I/O 控制的更多信息,请参见其他 I/O 控制。
驱动程序必须使用 ddi_copyin(9F) 从用户级别的应用程序向内核级别的应用程序传输 arg 数据。驱动程序必须使用 ddi_copyout(9F) 从内核级别向用户级别传输数据。在这两种情况下,如果无法使用 ddi_copyin(9F) 或 ddi_copyout(9F),则会导致系统出现紧急情况。如果体系结构将内核地址空间和用户地址空间分开,或者用户地址空间被换出,系统都会出现紧急情况。
对于每个支持的 ioctl(9E) 请求,ioctl(9E) 通常是 switch 语句。
static int xxioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) { uint8_t csr; struct xxstate *xsp; xsp = ddi_get_soft_state(statep, getminor(dev)); if (xsp == NULL) { return (ENXIO); } switch (cmd) { case XX_GET_STATUS: csr = ddi_get8(xsp->data_access_handle, &xsp->regp->csr); if (ddi_copyout(&csr, (void *)arg, sizeof (uint8_t), mode) != 0) { return (EFAULT); } break; case XX_SET_CMD: if (ddi_copyin((void *)arg, &csr, sizeof (uint8_t), mode) != 0) { return (EFAULT); } ddi_put8(xsp->data_access_handle, &xsp->regp->csr, csr); break; default: /* generic "ioctl unknown" error */ return (ENOTTY); } return (0); }
cmd 变量识别特定的设备控制操作。如果 arg 包含用户虚拟地址,则会出现问题。ioctl(9E) 必须调用 ddi_copyin(9F) 或 ddi_copyout(9F),以便在 arg 指向的应用程序中的数据结构与驱动程序之间传输数据。在示例 15–12 中,对于 XX_GET_STATUS 请求,xsp->regp->csr 的内容会被复制到 arg 中的地址。ioctl(9E) 可以在 *rvalp 中存储任何作为成功发出请求的 ioctl(2) 系统调用的返回值的整数值。应当避免返回负值,例如 -1。许多应用程序假定负值表示失败。
以下示例说明了使用上一段落中所讨论的 I/O 控制的应用程序。
#include <sys/types.h> #include "xxio.h" /* contains device's ioctl cmds and args */ int main(void) { uint8_t status; /* ... */ /* * read the device status */ if (ioctl(fd, XX_GET_STATUS, &status) == -1) { /* error handling */ } printf("device status %x\n", status); exit(0); }