Definindo códigos de controle de E/S

Ao definir novas IOCTLs, é importante lembrar as seguintes regras:

Um código de controle de E/S é um valor de 32 bits que consiste em vários campos. A figura a seguir ilustra o layout dos códigos de controle de E/S.

diagrama ilustrando o layout do código de controle de e/s.

Use a macro CTL_CODE fornecida pelo sistema, que é definida em Wdm.h e Ntddk.h, para definir novos códigos de controle de E/S. A definição de um novo código IOCTL, quer pretenda ser usado com solicitações de 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 Device indica o tipo de dispositivo e Function indica a operação. Um nome de constante de exemplo é IOCTL_VIDEO_ENABLE_CURSOR.

Forneça os seguintes parâmetros para a macro CTL_CODE :


Identifica o tipo de dispositivo. Esse valor deve corresponder ao valor definido no membro DeviceType da estrutura de DEVICE_OBJECT do driver. (Consulte Especificando tipos de dispositivo). Valores menores que 0x8000 são reservados para a Microsoft. Valores de 0x8000 e superiores podem ser usados por fornecedores. Observe que os valores atribuídos pelo fornecedor definem o bit Comum .

FunctionCode
Identifica a função a ser executada pelo driver. Valores menores que 0x800 são reservados para a Microsoft. Valores de 0x800 e superiores podem ser usados por fornecedores. Observe que os valores atribuídos pelo fornecedor definem o bit Personalizado .

TransferType
Indica como o sistema passará dados entre o chamador de DeviceIoControl (ou IoBuildDeviceIoControlRequest) e o driver que manipula o IRP.

Use uma das seguintes constantes definidas pelo sistema:

METHOD_BUFFERED
Especifica o método de E/S armazenado 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 TransferType .

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 de 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
Não especifica nem E/S em buffer nem direta. 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 puder ser executado no contexto do thread que originou a solicitação de controle de E/S. Apenas um driver de modo kernel de nível mais alto tem garantia de atender a essa condição, portanto, METHOD_NEITHER raramente é usado para os códigos de controle de E/S 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 deseja configurar o acesso em buffer ou direto aos dados do usuário após o recebimento da solicitação, possivelmente deve bloquear o buffer do usuário e 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 pode alterar os dados armazenados em buffer antes que o driver possa usá-los ou o chamador pode ser trocado assim como o driver está acessando o buffer do usuário.

Para obter mais informações, consulte Using nor Buffered nor Direct E/S.

RequiredAccess
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 gerente de E/S criará IRPs e chamará o driver com um código de controle de E/S específico somente se o chamador tiver solicitado os direitos de acesso especificados. RequiredAccess é 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.

FILE_READ_DATA
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.

FILE_WRITE_DATA
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 ORed juntos se o chamador precisar ter direitos de acesso de leitura e gravação.

Alguns códigos de controle de E/S definidos pelo sistema têm um valor RequiredAccess 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 do código de controle de E/S público IOCTL_DISK_SET_PARTITION_INFO mostra que essa solicitação de E/S só pode 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)

Observação

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.

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

Outras macros úteis

As macros a seguir são úteis para extrair os campos DeviceType de 16 bits e TransferType de 2 bits de um código 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.