Rutinas StartIo en controladores de Lowest-Level
La llamada del administrador de E/S a la rutina de envío de un controlador es la primera fase en satisfacer una solicitud de E/S del dispositivo. La rutina StartIo es la segunda fase. Es probable que todos los controladores de dispositivo con una rutina StartIo llamen a IoStartPacket desde sus rutinas DispatchRead y DispatchWrite , y normalmente para un subconjunto de los códigos de control de E/S que admite en su rutina DispatchDeviceControl . La rutina IoStartPacket agrega el IRP a la cola de dispositivos proporcionada por el sistema del dispositivo o, si la cola está vacía, llama inmediatamente a la rutina StartIo del controlador para procesar el IRP.
Puede suponer que cuando se llama a la rutina StartIo de un controlador, el dispositivo de destino no está ocupado. Esto se debe a que el administrador de E/S llama a StartIo en dos circunstancias; cualquiera de las rutinas de distribución del controlador acaba de llamar a IoStartPacket y la cola del dispositivo estaba vacía, o la rutina DpcForIsr del controlador está completando otra solicitud y acaba de llamar a IoStartNextPacket para poner en cola el siguiente IRP.
Antes de llamar a la rutina StartIo en un controlador de dispositivo de nivel superior, esa rutina de distribución del controlador debe haber sondeado y bloqueado el búfer de usuario, si es necesario, para configurar direcciones de búfer asignadas válidas en la irP en cola en su rutina StartIo . Si un controlador de dispositivo de nivel superior configura sus objetos de dispositivo para la E/S directa (o para la E/S directa ni almacenada en búfer), el controlador no puede aplazar el bloqueo de un búfer de usuario a su rutina StartIo ; se llama a cada rutina StartIo en un contexto de subproceso arbitrario en IRQL = DISPATCH_LEVEL.
Nota
Cualquier memoria de búfer a la que tenga acceso la rutina StartIo de un controlador debe estar bloqueada o asignada desde la memoria de espacio del sistema residente y debe ser accesible en un contexto de subproceso arbitrario.
En general, cualquier rutina StartIo del controlador de dispositivo de nivel inferior es responsable de llamar a IoGetCurrentIrpStackLocation con el IRP de entrada y, a continuación, hacer cualquier procesamiento específico de la solicitud es necesario para iniciar la operación de E/S en su dispositivo. El procesamiento específico de la solicitud puede incluir lo siguiente:
Configurar o actualizar cualquier información de estado sobre la solicitud actual que mantiene el controlador. La información de estado se puede almacenar en la extensión del dispositivo del objeto de dispositivo de destino o en otro lugar del grupo no paginado asignado por el controlador.
Por ejemplo, si un controlador de dispositivo mantiene un valor booleano InterruptExpected para la operación de transferencia actual, su rutina StartIo podría establecer esta variable en TRUE. Si el controlador mantiene un contador de tiempo de espera para la operación actual, su rutina StartIo podría configurar este valor o la rutina StartIo podría poner en cola la rutina CustomTimerDpc del controlador.
Si la rutina StartIo comparte el acceso a la información de estado o a los recursos de hardware con otras rutinas de controlador, la información de estado o el recurso deben protegerse mediante un bloqueo de giro. (Consulte Bloqueos de número).
Si la rutina StartIo comparte acceso a la información de estado o a los recursos con la rutina InterruptService del controlador, StartIo debe usar KeSynchronizeExecution para llamar a una rutina SynchCritSection que tenga acceso a la información de estado o de recursos. (Consulte Uso de secciones críticas).
Asignar un número de secuencia al IRP en caso de que el controlador deba registrar un error de E/S del dispositivo mientras procesa el IRP.
Consulte Errores de registro para obtener más información.
Si es necesario, traduzca los parámetros en la ubicación de la pila de E/S del controlador en valores específicos del dispositivo.
Por ejemplo, un controlador de disco podría necesitar calcular el desplazamiento del sector inicial o de bytes a la dirección del disco físico para una operación de transferencia y si la longitud solicitada de la transferencia cruzará un límite de sector determinado o superará la capacidad de transferencia de su dispositivo físico.
Si el controlador controla un dispositivo multimedia extraíble, compruebe si hay cambios en los medios antes de programar el dispositivo para E/S y notificar a su sistema de archivos sobrestituido si el medio ha cambiado.
Para obtener más información, vea Compatibilidad con medios extraíbles.
Si el dispositivo usa DMA, comprobar si la longitud solicitada (número de bytes que se va a transferir, que se encuentra en la ubicación de la pila de E/S del controlador) debe dividirse en operaciones de transferencia parcial, como se explica en Técnicas de entrada y salida, suponiendo que un controlador de nivel superior estrechamente acoplado no realice transferencias grandes presplit para el controlador del dispositivo.
La rutina StartIo de este tipo de controlador de dispositivo también puede ser responsable de llamar a KeFlushIoBuffers y, si el controlador usa DMA basado en paquetes, para llamar a AllocateAdapterChannel con la rutina AdapterControl del controlador.
Consulte Objetos de adaptador y DMA y Mantenimiento de la coherencia de caché para obtener más información.
Si el dispositivo usa PIO, asigne la dirección virtual base del búfer, descrita en irP en Irp-MdlAddress>, a una dirección de espacio del sistema con MmGetSystemAddressForMdlSafe.
En el caso de las solicitudes de lectura, la rutina StartIo del controlador del dispositivo puede ser responsable de llamar a KeFlushIoBuffers antes de que comiencen las operaciones de PIO. Consulte Mantener la coherencia de caché para obtener más información.
Si un controlador que no es WDM usa un objeto de controlador, llamando a IoAllocateController para registrar su rutina ControllerControl .
Si el controlador controla irP cancelables, compruebe si el IRP de entrada ya se ha cancelado.
Si se puede cancelar un IRP de entrada antes de que se procese hasta su finalización, la rutina StartIo debe llamar a IoSetCancelRoutine con el IRP y el punto de entrada de la rutina Cancel del controlador. La rutina StartIo debe adquirir el bloqueo de número de cancelación para su llamada a IoSetCancelRoutine. Como alternativa, un controlador puede usar IoSetStartIoAttributes para establecer el atributo NonCancelable para la rutina StartIo en TRUE. Esto impide que el sistema intente cancelar un IRP que se haya pasado a StartIo mediante una llamada a IoStartPacket.
Como regla general, un controlador que usa E/S almacenada en búfer tiene una rutina StartIo más sencilla que una que usa E/S directa. Los controladores que usan la E/S almacenada en búfer transfieren pequeñas cantidades de datos para cada solicitud de transferencia, mientras que los que usan E/S directa (ya sea DMA o PIO) transfieren grandes cantidades de datos a o desde búferes bloqueados que pueden abarcar límites de página físicos en la memoria del sistema.
Los controladores de nivel superior superados por encima de los controladores de dispositivos físicos suelen configurar sus objetos de dispositivo para que coincidan con los de sus respectivos controladores de dispositivo. Sin embargo, un controlador de nivel superior, especialmente un controlador del sistema de archivos, puede configurar objetos de dispositivo para E/S directa ni almacenada en búfer.
Los controladores que configuran sus objetos de dispositivo para la E/S almacenada en búfer pueden confiar en el administrador de E/S para pasar búferes válidos en todos los IRP que envía al controlador. Los controladores de nivel inferior que configuran objetos de dispositivo para E/S directa pueden confiar en el controlador de nivel superior de su cadena para pasar búferes válidos en todos los IRP enviados a través de los controladores intermedios al controlador de dispositivo de nivel inferior subyacente.
Uso de E/S almacenado en búfer en rutinas startIo
Si la rutina DispatchRead, DispatchWrite o DispatchDeviceControl de un controlador determina que una solicitud es válida y llama a IoStartPacket, el administrador de E/S llama a la rutina StartIo del controlador para procesar el IRP inmediatamente si la cola del dispositivo está vacía. Si la cola no está vacía, IoStartPacket pone en cola el IRP. Finalmente, una llamada a IoStartNextPacket desde la rutina DpcForIsr o CustomDpc del controlador hace que el administrador de E/S quite la cola del IRP y llame a la rutina StartIo del controlador.
La rutina StartIo llama a IoGetCurrentIrpStackLocation y determina qué operación se debe realizar para satisfacer la solicitud. Preprocesa el IRP de cualquier manera necesario antes de programar el dispositivo físico para llevar a cabo la solicitud de E/S.
Si el acceso al dispositivo físico (o la extensión del dispositivo) debe sincronizarse con una rutina InterruptService , la rutina StartIo debe llamar a una rutina SynchCritSection para realizar la programación de dispositivos necesaria. Para obtener más información, consulte Uso de secciones críticas.
Un controlador de dispositivo físico que usa E/S almacenado en búfer transfiere datos a o desde un búfer de espacio del sistema, asignado por el administrador de E/S, que el controlador encuentra en cada IRP en Irp-AssociatedIrp.SystemBuffer>.
Uso de E/S directa en rutinas StartIo
Si la rutina DispatchRead, DispatchWrite o DispatchDeviceControl de un controlador determina que una solicitud es válida y llama a IoStartPacket, el administrador de E/S llama a la rutina StartIo del controlador para procesar el IRP inmediatamente si la cola del dispositivo está vacía. Si la cola no está vacía, IoStartPacket pone en cola el IRP. Finalmente, una llamada a IoStartNextPacket desde la rutina DpcForIsr o CustomDpc del controlador hace que el administrador de E/S quite la cola del IRP y llame a la rutina StartIo del controlador.
La rutina StartIo llama a IoGetCurrentIrpStackLocation y determina qué operación se debe realizar para satisfacer la solicitud. Preprocesa el IRP de cualquier manera necesario, como dividir una solicitud de transferencia DMA grande en intervalos de transferencia parciales y guardar el estado sobre la longitud de una solicitud de transferencia entrante que se debe dividir. A continuación, programa el dispositivo físico para llevar a cabo la solicitud de E/S.
Si el acceso al dispositivo físico (o la extensión del dispositivo) debe sincronizarse con el ISR del controlador, la rutina StartIo debe usar una rutina SynchCritSection proporcionada por el controlador para realizar la programación necesaria. Para obtener más información, consulte Uso de secciones críticas.
Cualquier controlador que use E/S directa lee datos en o escribe datos de un búfer bloqueado, descrito por una lista de descriptores de memoria (MDL), que el controlador encuentra en irP en Irp-MdlAddress>. Este controlador suele usar la E/S almacenada en búfer para las solicitudes de control de dispositivos. Para obtener más información, consulte Control de solicitudes de control de E/S en Rutinas startIo.
El tipo MDL es un tipo opaco al que los controladores no acceden directamente. En su lugar, los controladores que usan pio reasignan búferes de espacio de usuario mediante una llamada a MmGetSystemAddressForMdlSafe con Irp-MdlAddress> como parámetro. Los controladores que usan DMA también pasan Irp-MdlAddress> para admitir rutinas durante sus operaciones de transferencia para que las direcciones del búfer se reasignan a los intervalos lógicos de sus dispositivos.
A menos que un controlador de nivel superior estrechamente acoplado divida las solicitudes de transferencia de DMA grandes para el controlador de dispositivo subyacente, una rutina StartIo del controlador de dispositivo de nivel inferior debe dividir cada solicitud de transferencia mayor que su dispositivo puede administrar en una sola operación de transferencia. Los controladores que usan DMA del sistema son necesarios para dividir las solicitudes de transferencia que son demasiado grandes para el controlador DMA del sistema o para que sus dispositivos controlen en una sola operación de transferencia.
Si el dispositivo es un dispositivo DMA subordinado, su controlador debe sincronizar las transferencias a través de un controlador DMA del sistema con un objeto de adaptador asignado por el controlador, que representa el canal DMA y una rutina AdapterControl proporcionada por el controlador. El controlador de un dispositivo DMA maestro de bus también debe usar un objeto de adaptador asignado por el controlador para sincronizar sus transferencias y debe proporcionar una rutina AdapterControl si usa la compatibilidad con DMA basada en paquetes del sistema o una rutina AdapterListControl si usa la compatibilidad de dispersión/recopilación del sistema.
Dependiendo del diseño del controlador, puede sincronizar las operaciones de transferencia y control de dispositivos en un dispositivo físico con un objeto de controlador y proporcionar una rutina ControllerControl .
Para obtener más información, vea Objetos de adaptador y DMA y Objetos de controlador .
Control de solicitudes de control de E/S en rutinas StartIo
En general, solo se pasa un subconjunto de solicitudes de control de E/S del dispositivo desde la rutina DispatchDeviceControl o DispatchInternalDeviceControl del controlador para su posterior procesamiento por la rutina StartIo del controlador. La rutina StartIo del controlador solo tiene que controlar las solicitudes de control de dispositivos válidas que requieren cambios de estado del dispositivo o devolver información volátil sobre el estado actual del dispositivo.
Cada controlador nuevo debe admitir el mismo conjunto de códigos de control de E/S públicos que todos los demás controladores para el mismo tipo de dispositivo. El sistema define códigos de control de E/S públicos específicos del tipo de dispositivo para IRP_MJ_DEVICE_CONTROL solicitudes como solicitudes almacenadas en búfer.
Por lo tanto, los controladores de dispositivos físicos realizan transferencias de datos hacia o desde un búfer de espacio del sistema que cada controlador encuentra en el IRP en Irp-AssociatedIrp.SystemBuffer> para las solicitudes de control de dispositivos. Incluso los controladores que configuran sus objetos de dispositivo para la E/S directa usan E/S almacenadas en búfer para satisfacer las solicitudes de control de dispositivos con códigos de control de E/S públicos.
La definición de cada código de control de E/S determina si los datos transferidos para esa solicitud se almacena en búfer. Los códigos de control de E/S definidos de forma privada para solicitudes de IRP_MJ_INTERNAL_DEVICE_CONTROL específicas del controlador entre controladores emparejados pueden definir un código con método almacenado en búfer, método directo o método ninguno. Como regla general, cualquier código de control de E/S definido de forma privada debe definirse con el método ni si un controlador de nivel superior estrechamente acoplado debe asignar un búfer para esa solicitud.
Programación del dispositivo para operaciones de E/S
Normalmente, la rutina StartIo de un controlador de dispositivo de nivel inferior debe sincronizar el acceso a cualquier memoria o dispositivo que registre con el ISR del controlador mediante KeSynchronizeExecution para llamar a una rutina SynchCritSection proporcionada por el controlador. La rutina StartIo del controlador usa la rutina SynchCritSection para programar realmente el dispositivo físico para E/S en DIRQL. Para obtener más información, consulte Uso de secciones críticas.
Antes de llamar a KeSynchronizeExecution, la rutina StartIo debe realizar cualquier preprocesamiento necesario para la solicitud. El preprocesamiento puede incluir el cálculo de un intervalo de transferencia parcial inicial y guardar cualquier información de estado sobre la solicitud original para otras rutinas de controlador.
Si un controlador de dispositivo usa DMA, su rutina StartIo normalmente llama a AllocateAdapterChannel con una rutina AdapterControl proporcionada por el controlador. En estas circunstancias, la rutina StartIo pospone la responsabilidad de programar el dispositivo físico en la rutina AdapterControl . A su vez, puede llamar a KeSynchronizeExecution para que un programa de rutina SynchCritSection proporcionado por el controlador sea el dispositivo para una transferencia DMA.