编写设备驱动程序

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 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 语句。


示例 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 指向的应用程序中的数据结构与驱动程序之间传输数据。在示例 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);
}