Partilhar via


Rotinas StartIo em drivers de Lowest-Level

A chamada do gerente de E/S para a rotina de expedição de um driver é o primeiro estágio para atender a uma solicitação de E/S do dispositivo. A rotina StartIo é o segundo estágio. Cada driver de dispositivo com uma rotina StartIo provavelmente chamará IoStartPacket de suas rotinas DispatchRead e DispatchWrite e, geralmente, para um subconjunto dos códigos de controle de E/S aos quais ele dá suporte em sua rotina DispatchDeviceControl . A rotina IoStartPacket adiciona o IRP à fila de dispositivos fornecida pelo sistema do dispositivo ou, se a fila estiver vazia, chama imediatamente a rotina StartIo do driver para processar o IRP.

Você pode supor que, quando a rotina StartIo de um driver é chamada, o dispositivo de destino não está ocupado. Isso ocorre porque o gerente de E/S chama StartIo em duas circunstâncias; uma das rotinas de expedição do driver acabou de chamar IoStartPacket e a fila do dispositivo estava vazia ou a rotina DpcForIsr do driver está concluindo outra solicitação e acabou de chamar IoStartNextPacket para remover a fila do próximo IRP.

Antes que a rotina StartIo em um driver de dispositivo de nível mais alto seja chamada, a rotina de expedição desse driver deve ter investigado e bloqueado o buffer do usuário, se necessário, para configurar endereços de buffer mapeados válidos no IRP enfileirado em sua rotina StartIo . Se um driver de dispositivo de nível mais alto configurar seus objetos de dispositivo para E/S direta (ou para E/S não armazenada em buffer nem direta), o driver não poderá adiar o bloqueio de um buffer de usuário para sua rotina StartIo ; cada rotina StartIo é chamada em um contexto de thread arbitrário em IRQL = DISPATCH_LEVEL.

Observação

Qualquer memória de buffer a ser acessada pela rotina StartIo de um driver deve ser bloqueada ou alocada da memória residente do espaço do sistema e deve estar acessível em um contexto de thread arbitrário.

Em geral, qualquer rotina StartIo do driver de dispositivo de nível inferior é responsável por chamar IoGetCurrentIrpStackLocation com o IRP de entrada e, em seguida, fazer qualquer processamento específico da solicitação é necessário para iniciar a operação de E/S em seu dispositivo. O processamento específico da solicitação pode incluir o seguinte:

  • Configurando ou atualizando qualquer informação de estado sobre a solicitação atual que o driver mantém. As informações de estado podem ser armazenadas na extensão do dispositivo do objeto de dispositivo de destino ou em outro lugar no pool nãopagado alocado pelo driver.

    Por exemplo, se um driver de dispositivo mantiver um Boolean InterruptExpected para a operação de transferência atual, sua rotina StartIo poderá definir essa variável como TRUE. Se o driver mantiver um contador de tempo limite para a operação atual, sua rotina StartIo poderá configurar esse valor ou a rotina StartIo poderá enfileirar a rotina CustomTimerDpc do driver.

    Se a rotina StartIo compartilhar o acesso a informações de estado ou recursos de hardware com outras rotinas de driver, as informações de estado ou o recurso deverão ser protegidos por um bloqueio de rotação. (Consulte Bloqueios de rotação.)

    Se a rotina StartIo compartilhar o acesso a informações de estado ou recursos com a rotina InterruptService do driver, StartIo deverá usar KeSynchronizeExecution para chamar uma rotina SynchCritSection que acesse as informações de estado ou recurso. (Consulte Usando seções críticas.)

  • Atribuir um número de sequência ao IRP caso o driver precise registrar um erro de E/S do dispositivo durante o processamento do IRP.

    Consulte Erros de log para obter mais informações.

  • Se necessário, traduzindo os parâmetros no local da pilha de E/S do driver em valores específicos do dispositivo.

    Por exemplo, um driver de disco pode precisar calcular o setor inicial ou o deslocamento de bytes para o endereço de disco físico para uma operação de transferência e se o comprimento solicitado da transferência cruzará um limite de setor específico ou excederá a capacidade de transferência de seu dispositivo físico.

  • Se o driver controlar um dispositivo de mídia removível, verifique se há alterações de mídia antes de programar o dispositivo para E/S e notificar seu sistema de arquivos em excesso se a mídia tiver sido alterada.

    Para obter mais informações, consulte Suporte a mídia removível.

  • Se o dispositivo usar dma, verificar se o Comprimento solicitado (número de bytes a serem transferidos, encontrado no local da pilha de E/S do driver do IRP) deve ser dividido em operações de transferência parcial, conforme explicado em Técnicas de Entrada/Saída, supondo que um driver de nível superior estreitamente acoplado não pré-plit grandes transferências para o driver de dispositivo.

    A rotina StartIo desse driver de dispositivo também pode ser responsável por chamar KeFlushIoBuffers e, se o driver usar o DMA baseado em pacotes, para chamar AllocateAdapterChannel com a rotina AdapterControl do driver.

    Consulte Objetos do Adaptador e DMA e Manutenção da Coerência de Cache para obter detalhes adicionais.

  • Se o dispositivo usar PIO, mapeando o endereço virtual base do buffer, descrito no IRP em Irp-MdlAddress>, para um endereço de espaço do sistema com MmGetSystemAddressForMdlSafe.

    Para solicitações de leitura, a rotina StartIo do driver de dispositivo pode ser responsável por chamar KeFlushIoBuffers antes do início das operações de PIO. Consulte Manutenção da coerência de cache para obter mais informações.

  • Se um driver não WDM usar um objeto de controlador, chamar IoAllocateController para registrar sua rotina ControllerControl .

  • Se o driver manipular IRPs canceláveis, verifique se o IRP de entrada já foi cancelado.

  • Se um IRP de entrada puder ser cancelado antes de ser processado até a conclusão, a rotina StartIo deverá chamar IoSetCancelRoutine com o IRP e o ponto de entrada da rotina Cancelar do driver. A rotina StartIo deve adquirir o bloqueio de rotação de cancelamento para sua chamada para IoSetCancelRoutine. Como alternativa, um driver pode usar IoSetStartIoAttributes para definir o atributo NonCancelable para a rotina StartIo como TRUE. Isso impede que o sistema tente cancelar um IRP que foi passado para StartIo por uma chamada para IoStartPacket.

Como regra geral, um driver que usa E/S em buffer tem uma rotina StartIo mais simples do que uma que usa E/S direta. Os drivers que usam E/S em buffer transferem pequenas quantidades de dados para cada solicitação de transferência, enquanto aqueles que usam E/S direta (seja DMA ou PIO) transferem grandes quantidades de dados para ou de buffers bloqueados que podem abranger limites de página física na memória do sistema.

Drivers de nível superior em camadas acima de drivers de dispositivo físico geralmente configuram seus objetos de dispositivo para corresponder aos de seus respectivos drivers de dispositivo. No entanto, um driver de nível mais alto, particularmente um driver do sistema de arquivos, pode configurar objetos de dispositivo para E/S direta nem em buffer.

Os drivers que configuram seus objetos de dispositivo para E/S em buffer podem contar com o gerenciador de E/S para passar buffers válidos em todos os IRPs que ele envia para o driver. Drivers de nível inferior que configuram objetos de dispositivo para E/S direta podem contar com o driver de nível mais alto em sua cadeia para passar buffers válidos em todos os IRPs enviados por meio de drivers intermediários para o driver de dispositivo de nível inferior subjacente.

Usando E/S em buffer em rotinas StartIo

Se a rotina DispatchRead, DispatchWrite ou DispatchDeviceControl de um driver determinar que uma solicitação é válida e chamar IoStartPacket, o gerente de E/S chamará a rotina StartIo do driver para processar o IRP imediatamente se a fila do dispositivo estiver vazia. Se a fila não estiver vazia, IoStartPacket enfileira o IRP. Eventualmente, uma chamada para IoStartNextPacket da rotina DpcForIsr ou CustomDpc do driver faz com que o gerente de E/S remova o IRP e chame a rotina StartIo do driver.

A rotina StartIo chama IoGetCurrentIrpStackLocation e determina qual operação deve ser executada para atender à solicitação. Ele pré-processa o IRP de qualquer forma necessária antes de programar o dispositivo físico para realizar a solicitação de E/S.

Se o acesso ao dispositivo físico (ou a extensão do dispositivo) precisar ser sincronizado com uma rotina InterruptService , a rotina StartIo deverá chamar uma rotina SynchCritSection para executar a programação de dispositivo necessária. Para obter mais informações, consulte Usando seções críticas.

Um driver de dispositivo físico que usa E/S em buffer transfere dados de ou para um buffer de espaço do sistema, alocado pelo gerenciador de E/S, que o driver encontra em cada IRP em Irp-AssociatedIrp.SystemBuffer>.

Usando e/S direta em rotinas StartIo

Se a rotina DispatchRead, DispatchWrite ou DispatchDeviceControl de um driver determinar que uma solicitação é válida e chamar IoStartPacket, o gerente de E/S chamará a rotina StartIo do driver para processar o IRP imediatamente se a fila do dispositivo estiver vazia. Se a fila não estiver vazia, IoStartPacket enfileira o IRP. Eventualmente, uma chamada para IoStartNextPacket da rotina DpcForIsr ou CustomDpc do driver faz com que o gerente de E/S remova o IRP e chame a rotina StartIo do driver.

A rotina StartIo chama IoGetCurrentIrpStackLocation e determina qual operação deve ser executada para atender à solicitação. Ele pré-processa o IRP de qualquer forma necessária, como dividir uma solicitação de transferência de DMA grande em intervalos de transferência parcial e salvar o estado sobre o Comprimento de uma solicitação de transferência de entrada que deve ser dividida. Em seguida, ele programa o dispositivo físico para realizar a solicitação de E/S.

Se o acesso ao dispositivo físico (ou a extensão do dispositivo) precisar ser sincronizado com o ISR do driver, a rotina StartIo deverá usar uma rotina SynchCritSection fornecida pelo driver para executar a programação necessária. Para obter mais informações, consulte Usando seções críticas.

Qualquer driver que usa E/S direta lê dados ou grava dados de um buffer bloqueado, descrito por uma MDL (lista de descritores de memória), que o driver encontra no IRP em Irp-MdlAddress>. Esse driver geralmente usa E/S em buffer para solicitações de controle de dispositivo. Para obter mais informações, consulte Manipulando solicitações de controle de E/S em Rotinas StartIo.

O tipo MDL é um tipo opaco que os drivers não acessam diretamente. Em vez disso, os drivers que usam o PIO remapeam buffers de espaço do usuário chamando MmGetSystemAddressForMdlSafe com Irp-MdlAddress> como um parâmetro. Os drivers que usam DMA também passam Irp-MdlAddress> para dar suporte a rotinas durante suas operações de transferência para que os endereços de buffer sejam remapeados para intervalos lógicos para seus dispositivos.

A menos que um driver de nível superior estreitamente acoplado divida grandes solicitações de transferência de DMA para o driver de dispositivo subjacente, a rotina StartIo de um driver de dispositivo de nível mais baixo deve dividir cada solicitação de transferência maior do que seu dispositivo pode gerenciar em uma única operação de transferência. Os drivers que usam o AMD do sistema são necessários para dividir solicitações de transferência muito grandes para o controlador de DMA do sistema ou para que seus dispositivos manipulem em uma única operação de transferência.

Se o dispositivo for um dispositivo DMA subordinado, seu driver deverá sincronizar transferências por meio de um controlador DMA do sistema com um objeto adaptador alocado pelo driver, representando o canal DMA e uma rotina AdapterControl fornecida pelo driver. O driver de um dispositivo DMA master barramento também deve usar um objeto de adaptador alocado pelo driver para sincronizar suas transferências e deve fornecer uma rotina AdapterControl se ele usar o suporte de DMA baseado em pacotes do sistema ou uma rotina AdapterListControl se ele usar o suporte de dispersão/coleta do sistema.

Dependendo do design do driver, ele pode sincronizar operações de transferência e controle de dispositivo em um dispositivo físico com um objeto controlador e fornecer uma rotina ControllerControl .

Consulte Objetos adaptáveis e DMA e objetos controller para obter mais informações.

Manipulando solicitações de controle de E/S em rotinas StartIo

Em geral, apenas um subconjunto de solicitações de controle de E/S do dispositivo é passado da rotina DispatchDeviceControl ou DispatchInternalDeviceControl de um driver para processamento adicional pela rotina StartIo do driver. A rotina StartIo do driver só precisa lidar com solicitações de controle de dispositivo válidas que exigem alterações de estado do dispositivo ou retornam informações voláteis sobre o estado atual do dispositivo.

Cada novo driver deve dar suporte ao mesmo conjunto de códigos de controle de E/S públicos que todos os outros drivers para o mesmo tipo de dispositivo. O sistema define códigos de controle de E/S públicos específicos do tipo de dispositivo para solicitações de IRP_MJ_DEVICE_CONTROL como solicitações em buffer.

Consequentemente, os drivers de dispositivo físico fazem transferências de dados de ou para um buffer de espaço do sistema que cada driver encontra no IRP em Irp-AssociatedIrp.SystemBuffer> para solicitações de controle de dispositivo. Até mesmo drivers que configuram seus objetos de dispositivo para E/S direta usam E/S em buffer para atender a solicitações de controle de dispositivo com códigos de controle de E/S públicos.

A definição de cada código de controle de E/S determina se os dados transferidos para essa solicitação são armazenados em buffer. Quaisquer códigos de controle de E/S definidos de forma privada para solicitações de IRP_MJ_INTERNAL_DEVICE_CONTROL específicas do driver entre drivers emparelhados podem definir um código com método armazenado em buffer, método direto ou método nenhum dos dois. Como regra geral, qualquer código de controle de E/S definido de forma privada deve ser definido com o método nem se um driver de nível superior estreitamente acoplado precisar alocar um buffer para essa solicitação.

Programando o dispositivo para operações de E/S

Normalmente, a rotina StartIo em um driver de dispositivo de nível mais baixo deve sincronizar o acesso a qualquer memória ou registro de dispositivo que ele compartilha com o ISR do driver usando KeSynchronizeExecution para chamar uma rotina SynchCritSection fornecida pelo driver. A rotina StartIo do driver usa a rotina SynchCritSection para realmente programar o dispositivo físico para E/S no DIRQL. Para obter mais informações, consulte Usando seções críticas.

Antes de chamar KeSynchronizeExecution, a rotina StartIo deve fazer qualquer pré-processamento necessário para a solicitação. O pré-processamento pode incluir o cálculo de um intervalo inicial de transferência parcial e o salvamento de qualquer informação de estado sobre a solicitação original para outras rotinas de driver.

Se um driver de dispositivo usa DMA, sua rotina StartIo geralmente chama AllocateAdapterChannel com uma rotina AdapterControl fornecida pelo driver. Nessas circunstâncias, a rotina StartIo adia a responsabilidade de programar o dispositivo físico para a rotina AdapterControl . Ele, por sua vez, pode chamar KeSynchronizeExecution para ter um programa de rotina SynchCritSection fornecido pelo driver para o dispositivo para uma transferência de DMA.