编写适用于 Oracle® Solaris 11.2 的设备驱动程序

退出打印视图

更新时间: 2014 年 9 月
 
 

ioctl() 入口点(字符驱动程序)

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 that represents 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 控制的更多信息,请参见Miscellaneous I/O Control

驱动程序必须使用 ddi_copyin(9F) 从用户级别的应用程序向内核级别的应用程序传输 arg 数据。驱动程序必须使用 ddi_copyout(9F) 从内核级别向用户级别传输数据。在这两种情况下,如果无法使用 ddi_copyin(9F)ddi_copyout(9F),则会导致系统出现紧急情况。如果体系结构将内核地址空间和用户地址空间分开,或者用户地址空间被换出,系统都会出现紧急情况。

对于每个支持的 ioctl(9E) 请求,ioctl(9E) 通常是 switch 语句。

示例 15-12  ioctl(9E) 例程
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 指向的应用程序中的数据结构与驱动程序之间传输数据。在Example 15–12中,对于 XX_GET_STATUS 请求,xsp->regp->csr 的内容会被复制到 arg 中的地址。ioctl(9E) 可以在 *rvalp 中存储任何作为成功发出请求的 ioctl(2) 系统调用的返回值的整数值。应当避免返回负值,例如 -1。许多应用程序假定负值表示失败。

以下示例说明了使用上一段落中所讨论的 I/O 控制的应用程序。

示例 15-13  使用 ioctl(9E)
#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);
}