Minidrivers, miniportadores y pares de controladores
Un minidriver o un controlador de minipuerto actúa como la mitad de un par de controladores. Los pares de controladores como (minipuerto, puerto) pueden facilitar el desarrollo de controladores. En un par de controladores, un controlador controla las tareas generales que son comunes a toda una colección de dispositivos, mientras que el otro controlador controla las tareas específicas de un dispositivo individual. Los controladores que controlan tareas específicas del dispositivo van por una variedad de nombres, como el controlador de minipuerto, el controlador de miniclase y el minidriver.
Microsoft proporciona el controlador general y, normalmente, un proveedor de hardware independiente proporciona el controlador específico. Antes de leer este tema, debe comprender las ideas presentadas en Nodos de dispositivo y pilas de dispositivos y paquetes de solicitud de E/S.
Cada controlador en modo kernel debe implementar una función denominada DriverEntry, a la que se llama poco después de cargar el controlador. La función DriverEntry rellena determinados miembros de una estructura de DRIVER_OBJECT con punteros a otras funciones que implementa el controlador. Por ejemplo, la función DriverEntry rellena el miembro Unload de la estructura DRIVER_OBJECT con un puntero a la función Unload del controlador, como se muestra en el diagrama siguiente.
El miembro MajorFunction de la estructura DRIVER_OBJECT es una matriz de punteros a funciones que controlan paquetes de solicitud de E/S (IRP), como se muestra en el diagrama siguiente. Normalmente, el controlador rellena varios miembros de la matriz MajorFunction con punteros a funciones (implementadas por el controlador) que controlan varios tipos de IRP.
Un IRP se puede clasificar según su código de función principal, que se identifica mediante una constante, como IRP_MJ_READ, IRP_MJ_WRITE o IRP_MJ_PNP. Las constantes que identifican el código de función principal sirven como índices en la matriz MajorFunction . Por ejemplo, supongamos que el controlador implementa una función de distribución para controlar los IRP que tienen el código de función principal IRP_MJ_WRITE. En este caso, el controlador debe rellenar el elemento MajorFunction[IRP_MJ_WRITE] de la matriz con un puntero a la función de distribución.
Normalmente, el controlador rellena algunos de los elementos de la matriz MajorFunction y deja los elementos restantes establecidos en los valores predeterminados proporcionados por el administrador de E/S. En el ejemplo siguiente se muestra cómo usar la extensión del depurador !drvobj para inspeccionar los punteros de función para el controlador parport.
0: kd> !drvobj parport 2
Driver object (fffffa80048d9e70) is for:
\Driver\Parport
DriverEntry: fffff880065ea070 parport!GsDriverEntry
DriverStartIo: 00000000
DriverUnload: fffff880065e131c parport!PptUnload
AddDevice: fffff880065d2008 parport!P5AddDevice
Dispatch routines:
[00] IRP_MJ_CREATE fffff880065d49d0 parport!PptDispatchCreateOpen
[01] IRP_MJ_CREATE_NAMED_PIPE fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE fffff880065d4a78 parport!PptDispatchClose
[03] IRP_MJ_READ fffff880065d4bac parport!PptDispatchRead
[04] IRP_MJ_WRITE fffff880065d4bac parport!PptDispatchRead
[05] IRP_MJ_QUERY_INFORMATION fffff880065d4c40 parport!PptDispatchQueryInformation
[06] IRP_MJ_SET_INFORMATION fffff880065d4ce4 parport!PptDispatchSetInformation
[07] IRP_MJ_QUERY_EA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[08] IRP_MJ_SET_EA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[09] IRP_MJ_FLUSH_BUFFERS fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0b] IRP_MJ_SET_VOLUME_INFORMATION fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0c] IRP_MJ_DIRECTORY_CONTROL fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0d] IRP_MJ_FILE_SYSTEM_CONTROL fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0e] IRP_MJ_DEVICE_CONTROL fffff880065d4be8 parport!PptDispatchDeviceControl
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL fffff880065d4c24 parport!PptDispatchInternalDeviceControl
[10] IRP_MJ_SHUTDOWN fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[11] IRP_MJ_LOCK_CONTROL fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[12] IRP_MJ_CLEANUP fffff880065d4af4 parport!PptDispatchCleanup
[13] IRP_MJ_CREATE_MAILSLOT fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[15] IRP_MJ_SET_SECURITY fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[16] IRP_MJ_POWER fffff880065d491c parport!PptDispatchPower
[17] IRP_MJ_SYSTEM_CONTROL fffff880065d4d4c parport!PptDispatchSystemControl
[18] IRP_MJ_DEVICE_CHANGE fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[1a] IRP_MJ_SET_QUOTA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[1b] IRP_MJ_PNP fffff880065d4840 parport!PptDispatchPnp
En la salida del depurador, puede ver que parport.sys implementa GsDriverEntry, el punto de entrada del controlador. GsDriverEntry, que se generó automáticamente cuando se creó el controlador, realiza alguna inicialización y, a continuación, llama a DriverEntry, que el desarrollador del controlador implementó.
También puede ver que el controlador parport (en su función DriverEntry ) proporciona punteros para enviar funciones para estos códigos de función principales:
- IRP_MJ_CREATE
- IRP_MJ_CLOSE
- IRP_MJ_READ
- IRP_MJ_WRITE
- IRP_MJ_QUERY_INFORMATION
- IRP_MJ_SET_INFORMATION
- IRP_MJ_DEVICE_CONTROL
- IRP_MJ_INTERNAL_DEVICE_CONTROL
- IRP_MJ_CLEANUP
- IRP_MJ_POWER
- IRP_MJ_SYSTEM_CONTROL
- IRP_MJ_PNP
Los elementos restantes de la matriz MajorFunction contienen punteros a la función de distribución predeterminada nt! IopInvalidDeviceRequest.
En la salida del depurador, puede ver que los punteros de función proporcionados por el controlador parport para Unload y AddDevice, pero no proporcionaron un puntero de función para StartIo. La función AddDevice es inusual porque su puntero de función no se almacena en la estructura DRIVER_OBJECT . En su lugar, se almacena en el miembro AddDevice de una extensión en la estructura DRIVER_OBJECT . En el diagrama siguiente se muestran los punteros de función que el controlador de parport proporciona en su función DriverEntry . Los punteros de función proporcionados por parport están sombreados.
Facilitar el uso de pares de controladores
Durante un período de tiempo, a medida que los desarrolladores de controladores dentro y fuera de Microsoft obtuvieron experiencia con windows Driver Model (WDM), se realizaron un par de cosas sobre las funciones de envío:
- Las funciones de distribución son en gran medida reutilizables. Por ejemplo, gran parte del código de la función de distribución para IRP_MJ_PNP es el mismo para todos los controladores. Es solo una pequeña parte del código de Plug and Play (PnP) que es específico de un controlador individual que controla una pieza de hardware individual.
- Las funciones de envío son complicadas y difíciles de conseguir. La implementación de características como la sincronización de subprocesos, la puesta en cola de IRP y la cancelación de IRP es difícil y requiere un conocimiento profundo de cómo funciona el sistema operativo.
Para facilitar las cosas a los desarrolladores de controladores, Microsoft creó varios modelos de controladores específicos de la tecnología. A primera vista, los modelos específicos de la tecnología parecen bastante diferentes entre sí, pero una mirada más cercana revela que muchos de ellos se basan en este paradigma:
- El controlador se divide en dos partes: una que controla el procesamiento general y otra que controla el procesamiento específico de un dispositivo determinado.
- Microsoft escribe la pieza general.
- Microsoft o un proveedor de hardware independiente pueden escribir la pieza específica.
Supongamos que las empresas Proseware y Contoso hacen un robot de toy que requiere un controlador WDM. Suponga también que Microsoft proporciona un controlador robot general denominado GeneralRobot.sys. Proseware y Contoso pueden escribir controladores pequeños que controlen los requisitos de sus robots específicos. Por ejemplo, Proseware podría escribir ProsewareRobot.sys y el par de controladores (ProsewareRobot.sys, GeneralRobot.sys) se podría combinar para formar un único controlador WDM. Del mismo modo, el par de controladores (ContosoRobot.sys, GeneralRobot.sys) podría combinarse para formar un único controlador WDM. En su forma más general, la idea es que se pueden crear controladores mediante pares (specific.sys, general.sys).
Punteros de función en pares de controladores
En un par (specific.sys, general.sys), Windows carga specific.sys y llama a su función DriverEntry . La función DriverEntry de specific.sys recibe un puntero a una estructura de DRIVER_OBJECT . Normalmente, esperaría que DriverEntry rellene varios elementos de la matriz MajorFunction con punteros para enviar funciones. También esperaría que DriverEntry rellene el miembro Unload (y posiblemente el miembro StartIo ) de la estructura DRIVER_OBJECT y el miembro AddDevice de la extensión del objeto driver. Sin embargo, en un modelo de par de controladores, DriverEntry no necesariamente lo hace. En su lugar, la función DriverEntry de specific.sys pasa la estructura de DRIVER_OBJECT a una función de inicialización implementada por general.sys. En el ejemplo de código siguiente se muestra cómo se puede llamar a la función de inicialización en el par (ProsewareRobot.sys, GeneralRobot.sys).
PVOID g_ProsewareRobottCallbacks[3] = {DeviceControlCallback, PnpCallback, PowerCallback};
// DriverEntry function in ProsewareRobot.sys
NTSTATUS DriverEntry (DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath)
{
// Call the initialization function implemented by GeneralRobot.sys.
return GeneralRobotInit(DriverObject, RegistryPath, g_ProsewareRobottCallbacks);
}
La función de inicialización de GeneralRobot.sys escribe punteros de función a los miembros adecuados de la estructura DRIVER_OBJECT (y su extensión) y los elementos adecuados de la matriz MajorFunction . La idea es que cuando el administrador de E/S envía un IRP al par de controladores, irP va primero a una función de envío implementada por GeneralRobot.sys. Si GeneralRobot.sys puede controlar el IRP por sí solo, el controlador específico, ProsewareRobot.sys, no tiene que estar implicado. Si GeneralRobot.sys puede controlar algunos, pero no todos, del procesamiento de IRP, obtiene ayuda de una de las funciones de devolución de llamada implementadas por ProsewareRobot.sys. GeneralRobot.sys recibe punteros a las devoluciones de llamada de ProsewareRobot en la llamada GeneralRobotInit.
En algún momento después de que Se devuelva DriverEntry , se construye una pila de dispositivos para el nodo de dispositivo Robot proseware. La pila de dispositivos podría tener este aspecto.
Como se muestra en el diagrama anterior, la pila de dispositivos para Proseware Robot tiene tres objetos de dispositivo. El objeto de dispositivo superior es un objeto de dispositivo de filtro (Filter DO) asociado al controlador de filtro AfterThought.sys. El objeto de dispositivo intermedio es un objeto de dispositivo funcional (FDO) asociado al par de controladores (ProsewareRobot.sys, GeneralRobot.sys). El par de controladores actúa como controlador de función para la pila de dispositivos. El objeto de dispositivo inferior es un objeto de dispositivo físico (PDO) asociado a Pci.sys.
Observe que el par de controladores ocupa solo un nivel en la pila de dispositivos y está asociado a un solo objeto de dispositivo: el FDO. Cuando GeneralRobot.sys procesa un IRP, puede llamar a ProsewareRobot.sys para obtener ayuda, pero eso no es lo mismo que pasar la solicitud a la pila del dispositivo. El par de controladores forma un único controlador WDM que se encuentra en un nivel de la pila de dispositivos. El par de controladores completa el IRP o lo pasa a la pila de dispositivos al PDO, que está asociado a Pci.sys.
Ejemplo de un par de controladores
Supongamos que tiene una tarjeta de red inalámbrica en el equipo portátil y, al buscar en Administrador de dispositivos, determina que netwlv64.sys es el controlador de la tarjeta de red. Puede usar la extensión del depurador !drvobj para inspeccionar los punteros de función para netwlv64.sys.
1: kd> !drvobj netwlv64 2
Driver object (fffffa8002e5f420) is for:
\Driver\netwlv64
DriverEntry: fffff8800482f064 netwlv64!GsDriverEntry
DriverStartIo: 00000000
DriverUnload: fffff8800195c5f4 ndis!ndisMUnloadEx
AddDevice: fffff88001940d30 ndis!ndisPnPAddDevice
Dispatch routines:
[00] IRP_MJ_CREATE fffff880018b5530 ndis!ndisCreateIrpHandler
[01] IRP_MJ_CREATE_NAMED_PIPE fffff88001936f00 ndis!ndisDummyIrpHandler
[02] IRP_MJ_CLOSE fffff880018b5870 ndis!ndisCloseIrpHandler
[03] IRP_MJ_READ fffff88001936f00 ndis!ndisDummyIrpHandler
[04] IRP_MJ_WRITE fffff88001936f00 ndis!ndisDummyIrpHandler
[05] IRP_MJ_QUERY_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[06] IRP_MJ_SET_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[07] IRP_MJ_QUERY_EA fffff88001936f00 ndis!ndisDummyIrpHandler
[08] IRP_MJ_SET_EA fffff88001936f00 ndis!ndisDummyIrpHandler
[09] IRP_MJ_FLUSH_BUFFERS fffff88001936f00 ndis!ndisDummyIrpHandler
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[0b] IRP_MJ_SET_VOLUME_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[0c] IRP_MJ_DIRECTORY_CONTROL fffff88001936f00 ndis!ndisDummyIrpHandler
[0d] IRP_MJ_FILE_SYSTEM_CONTROL fffff88001936f00 ndis!ndisDummyIrpHandler
[0e] IRP_MJ_DEVICE_CONTROL fffff8800193696c ndis!ndisDeviceControlIrpHandler
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL fffff880018f9114 ndis!ndisDeviceInternalIrpDispatch
[10] IRP_MJ_SHUTDOWN fffff88001936f00 ndis!ndisDummyIrpHandler
[11] IRP_MJ_LOCK_CONTROL fffff88001936f00 ndis!ndisDummyIrpHandler
[12] IRP_MJ_CLEANUP fffff88001936f00 ndis!ndisDummyIrpHandler
[13] IRP_MJ_CREATE_MAILSLOT fffff88001936f00 ndis!ndisDummyIrpHandler
[14] IRP_MJ_QUERY_SECURITY fffff88001936f00 ndis!ndisDummyIrpHandler
[15] IRP_MJ_SET_SECURITY fffff88001936f00 ndis!ndisDummyIrpHandler
[16] IRP_MJ_POWER fffff880018c35e8 ndis!ndisPowerDispatch
[17] IRP_MJ_SYSTEM_CONTROL fffff880019392c8 ndis!ndisWMIDispatch
[18] IRP_MJ_DEVICE_CHANGE fffff88001936f00 ndis!ndisDummyIrpHandler
[19] IRP_MJ_QUERY_QUOTA fffff88001936f00 ndis!ndisDummyIrpHandler
[1a] IRP_MJ_SET_QUOTA fffff88001936f00 ndis!ndisDummyIrpHandler
[1b] IRP_MJ_PNP fffff8800193e518 ndis!ndisPnPDispatch
En la salida del depurador, puede ver que netwlv64.sys implementa GsDriverEntry, el punto de entrada del controlador. GsDriverEntry, que se generó automáticamente cuando se creó el controlador, realiza alguna inicialización y, a continuación, llama a DriverEntry, que fue escrito por el desarrollador del controlador.
En este ejemplo, netwlv64.sys implementa DriverEntry, pero ndis.sys implementa AddDevice, Unload y varias funciones de distribución. Netwlv64.sys se denomina controlador de miniporte NDIS y ndis.sys se denomina biblioteca NDIS. Juntos, los dos módulos forman un par (miniporte NDIS, biblioteca NDIS).
En este diagrama se muestra la pila de dispositivos para la tarjeta de red inalámbrica. Observe que el par de controladores (netwlv64.sys, ndis.sys) ocupa solo un nivel en la pila de dispositivos y está asociado a un solo objeto de dispositivo: el FDO.
Pares de controladores disponibles
Los diferentes modelos de controladores específicos de la tecnología usan una variedad de nombres para las piezas específicas y generales de un par de controladores. En muchos casos, la parte específica del par tiene el prefijo "mini". Estos son algunos de los pares (específicos y generales) que están disponibles:
- (controlador de minipuerto de pantalla, controlador de puerto de pantalla)
- (controlador de minipuerto de audio, controlador de puerto de audio)
- (controlador de minipuerto de almacenamiento, controlador de puerto de almacenamiento)
- (controlador de miniclase de batería, controlador de clase de batería)
- (Minidriver HID, controlador de clase HID)
- (controlador de miniclase modificador, controlador de puerto del modificador)
- (controlador de minipuerto NDIS, biblioteca NDIS)
Nota Como puede ver en la lista, varios de los modelos usan el término controlador de clase para la parte general de un par de controladores. Este tipo de controlador de clase es diferente de un controlador de clase independiente y diferente de un controlador de filtro de clase.
Temas relacionados
Conceptos para todos los desarrolladores de controladores