Compartilhar via


Definindo códigos de controle de E/S

Este artigo descreve como criar um IOCTL (código de controle de E/S) exclusivo. IOCTLs podem ser:

  • IOCTLs públicos, que geralmente são definidos pelo sistema e documentados pela Microsoft.
  • IOCTLs privados, que geralmente devem ser usados de forma exclusiva pelos componentes de software de um fornecedor para se comunicarem entre si. Eles geralmente são definidos no arquivo de cabeçalho de um fornecedor e não são documentados pela Microsoft.

Layout IOCTL

Um IOCTL é um valor de 32 bits que consiste em vários campos. A figura a seguir ilustra o layout bit a bit de um IOCTL:

Diagrama ilustrando o layout bit a bit de um código de controle de E/S de 32 bits.

Cada campo no IOCTL tem uma finalidade específica, conforme descrito na tabela a seguir:

Campo Bits no IOCTL Descrição
Comum 31 Os fornecedores devem definir esse bit quando usam um valor atribuído pelo fornecedor para DeviceType.
DeviceType 16-30 Identifica o tipo de dispositivo. Esse valor deve corresponder ao valor definido no membro DeviceType da estrutura de DEVICE_OBJECT do driver. Os fornecedores devem usar um valor de 32768 a 65535 (0x8000 até 0xffff) e devem definir o bit Comum . Os valores de 0 a 32767 (0x0000 até 0x7fff) são reservados para a Microsoft. Para obter mais informações, consulte Especificando tipos de dispositivo.
Acesso 14-15 Indica o tipo de acesso que um chamador deve solicitar ao abrir o objeto de arquivo que representa o dispositivo (consulte IRP_MJ_CREATE). O gerenciador de E/S criará IRPs e chamará o driver com um IOCTL específico apenas se o chamador solicitar os direitos de acesso especificados. Esse campo é especificado usando as seguintes constantes definidas pelo sistema: FILE_ANY_ACCESS, FILE_READ_DATA e FILE_WRITE_DATA.
Personalizado 13 Quando definido, indica que o IOCTL é um IOCTL definido pelo fornecedor.
Função 2-12 Código exclusivo para o driver que identifica a função a ser executada. Para um IOCTL criado pelo fornecedor, use um valor de 2048 a 4095 (0x800 até 0xfff) e defina o bit Personalizado . Valores inferiores a 2048 (0x000 até 0x7ff) são reservados para a Microsoft.
Método 0-1 Indica como o sistema deve passar dados entre o chamador de DeviceIoControl (ou IoBuildDeviceIoControlRequest) e o driver que manipula o IRP. Para obter mais informações, consulte Diretrizes para definir os bits do Método.

Macro para definir códigos de controle de E/S

Use a macro de CTL_CODE fornecida pelo sistema para definir novos códigos de controle de E/S. Essa macro é definida em devioctl.h da seguinte maneira:

#define CTL_CODE( DeviceType, Function, Method, Access ) (                 \
    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
)

Consulte a seção anterior para obter uma descrição de DeviceType, Function, Method e Access.

Tenha as seguintes regras em mente ao definir novos códigos de controle de E/S:

A definição de um novo código IOCTL, destinado ao uso com solicitações IRP_MJ_DEVICE_CONTROL ou IRP_MJ_INTERNAL_DEVICE_CONTROL, usa o seguinte formato:

#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

Escolha um nome de constante descritivo para o IOCTL, do formulário IOCTL_Device_Function, em que o dispositivo indica o tipo de dispositivo e a Função indica a operação. Por exemplo, a constante IOCTL_VIDEO_ENABLE_CURSOR fornecida pelo sistema usa "VIDEO" para Dispositivo e "ENABLE_CURSOR" para Função.

Diretrizes para definir os bits do Access

Ao definir um novo IOCTL, você deve escolher um valor para o campo de bits do Access que indica o tipo de acesso que um chamador deve solicitar ao abrir o objeto de arquivo que representa o dispositivo. O gerenciador de E/S criará IRPs e chamará o driver com um IOCTL específico apenas se o chamador solicitar os direitos de acesso especificados.

O acesso é especificado usando as seguintes constantes definidas pelo sistema:

  • FILE_ANY_ACCESS

    O gerenciador de E/S envia o IRP para qualquer chamador que tenha um identificador para o objeto de arquivo que representa o objeto de dispositivo de destino. Antes de especificar FILE_ANY_ACCESS para um novo código IOCTL, você deve ter certeza absoluta de que permitir o acesso irrestrito ao seu dispositivo não cria um caminho possível para usuários mal-intencionados comprometerem o sistema.

  • LEITURA_DE_ARQUIVO

    O gerenciador de E/S envia o IRP somente para um chamador com direitos de acesso de leitura, permitindo que o driver de dispositivo subjacente transfira dados do dispositivo para a memória do sistema.

  • ARQUIVO_ESCREVER_DADOS

    O gerenciador de E/S envia o IRP somente para um chamador com direitos de acesso de gravação, permitindo que o driver de dispositivo subjacente transfira dados da memória do sistema para seu dispositivo.

FILE_READ_DATA e FILE_WRITE_DATA podem ser combinados usando a operação OR se o chamador precisar ter direitos de acesso tanto para leitura quanto para gravação.

Alguns códigos de controle de E/S definidos pelo sistema têm um valor de Acesso de FILE_ANY_ACCESS, o que permite que o chamador envie o IOCTL específico, independentemente do acesso concedido ao dispositivo. Exemplos incluem códigos de controle de E/S que são enviados para drivers de dispositivos exclusivos.

Outros códigos de controle de E/S definidos pelo sistema exigem que o chamador tenha direitos de acesso de leitura, direitos de acesso de gravação ou ambos. Por exemplo, a seguinte definição da IOCTL_DISK_SET_PARTITION_INFO IOCTL pública mostra que essa solicitação de E/S só poderá ser enviada a um driver se o chamador tiver direitos de acesso de leitura e gravação:

#define IOCTL_DISK_SET_PARTITION_INFO\
        CTL_CODE(IOCTL_DISK_BASE, 0x008, METHOD_BUFFERED,\
        FILE_READ_DATA | FILE_WRITE_DATA)

Os drivers podem usar IoValidateDeviceIoControlAccess para executar uma verificação de acesso mais rigorosa do que a fornecida pelos bits de Acesso de um IOCTL.

Diretrizes para definir os bits do Método

Ao definir um novo IOCTL, você deve escolher um valor para o campo bit Método que indica como o sistema deve passar dados entre o chamador de DeviceIoControl (ou IoBuildDeviceIoControlRequest) e o driver que manipula o IRP.

Use uma das seguintes constantes definidas pelo sistema para definir o campo Método .

  • MÉTODO_BUFFERED

    Especifica o método de E/S em buffer , que normalmente é usado para transferir pequenas quantidades de dados por solicitação. A maioria dos códigos de controle de E/S para dispositivos e drivers intermediários usa esse valor.

    Para obter informações sobre como o sistema especifica buffers de dados para códigos de controle de E/S METHOD_BUFFERED, consulte Descrições de buffer para códigos de controle de E/S.

    Para obter mais informações sobre E/S em buffer, consulte Usando E/S em buffer.

  • METHOD_IN_DIRECT ou METHOD_OUT_DIRECT

    Especifica o método de E/S direto , que normalmente é usado para ler ou gravar grandes quantidades de dados usando DMA ou PIO que devem ser transferidos rapidamente.

    • Especifique METHOD_IN_DIRECT se o chamador de DeviceIoControl ou IoBuildDeviceIoControlRequest passar dados para o driver.

    • Especifique METHOD_OUT_DIRECT se o chamador de DeviceIoControl ou IoBuildDeviceIoControlRequest receberá dados do driver.

    Para obter informações sobre como o sistema especifica buffers de dados para códigos de controle de E/S METHOD_IN_DIRECT e METHOD_OUT_DIRECT, consulte Descrições de buffer para códigos de controle de E/S.

    Para obter mais informações sobre E/S direta, consulte Usando E/S Direta.

  • METHOD_NEITHER

    Especifica que o método de E/S não é armazenado em buffer ou direto. O gerenciador de E/S não fornece buffers ou MDLs do sistema. O IRP fornece os endereços virtuais do modo de usuário dos buffers de entrada e saída especificados para DeviceIoControl ou IoBuildDeviceIoControlRequest, sem validá-los ou mapeá-los.

    Para obter informações sobre como o sistema especifica buffers de dados para códigos de controle de E/S METHOD_NEITHER, consulte Descrições de buffer para códigos de controle de E/S.

    Esse método só poderá ser usado se o driver tiver a garantia de estar em execução no contexto do thread que originou a solicitação de controle de E/S. Somente um driver de modo kernel de nível mais alto tem garantia de atender a essa condição. Portanto, METHOD_NEITHER raramente é usado para IOCTLs que são passados para drivers de dispositivo de baixo nível.

    Com esse método, o driver de nível mais alto:

    • Deve determinar se é necessário configurar o acesso em buffer ou direto aos dados do usuário no recebimento da solicitação.
    • Talvez seja necessário bloquear o buffer de usuário.
    • Deve encapsular seu acesso ao buffer de usuário em um manipulador de exceção estruturado (consulte Tratamento de Exceções).

    Caso contrário, o chamador do modo de usuário de origem poderá alterar os dados armazenados em buffer antes que o driver possa usá-los, ou o chamador poderá ser trocado no momento em que o driver estiver acessando o buffer do usuário.

    Para obter mais informações, consulte Não usar E/S com buffer nem direta.

Outras macros úteis

As macros a seguir são úteis para extrair os campos devicetype de 16 bits e método de 2 bits de um IOCTL.

#define DEVICE_TYPE_FROM_CTL_CODE(ctrlCode)   (((ULONG)(ctrlCode & 0xffff0000)) >> 16)
#define METHOD_FROM_CTL_CODE(ctrlCode)        ((ULONG)(ctrlCode & 3))

Essas macros são definidas em Wdm.h e Ntddk.h.