Compartir a través de


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.

diagrama que muestra la estructura del objeto de controlador con el miembro unload.

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.

diagrama que muestra la estructura driver-object con el miembro majorfunction.

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.

diagrama de punteros de función en una estructura de objetos de controlador.

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.

diagrama del nodo de dispositivo robot proseware, que muestra tres objetos de dispositivo en la pila de dispositivos: afterthought.sys (filtro do), prosewarerobot.sys, generalrobot.sys (fdo) y pci.sys (pdo).

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.

diagrama de la pila de dispositivos de tarjeta de red inalámbrica, que muestra netwlv64.sys, ndis.sys como el par de controladores asociado con el fdo y pci.sys asociado con el pdo .

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.

Conceptos para todos los desarrolladores de controladores

Nodos de dispositivo y pilas de dispositivos

Pilas de controladores