Delen via


Minidrivers, Miniport-stuurprogramma's en stuurprogrammaparen

Een minidriver of een minipoortstuurprogramma fungeert als deel van een stuurprogrammapaar. Stuurprogrammaparen zoals (miniport, poort) kunnen de ontwikkeling van stuurprogramma's vereenvoudigen. In een stuurprogrammapaar verwerkt één stuurprogramma algemene taken die gebruikelijk zijn voor een hele verzameling apparaten, terwijl het andere stuurprogramma taken verwerkt die specifiek zijn voor een afzonderlijk apparaat. De stuurprogramma's die apparaatspecifieke taken verwerken, hebben verschillende namen, waaronder minipoortstuurprogramma, miniclassestuurprogramma en minidriver.

Microsoft biedt het algemene stuurprogramma en doorgaans een onafhankelijke hardwareleverancier biedt het specifieke stuurprogramma. Voordat u dit onderwerp leest, moet u de ideeën begrijpen die worden gepresenteerd in Apparaatknooppunten en apparaatstacks en I/O-aanvraagpakketten.

Elk kernelmodusstuurprogramma moet een functie implementeren met de naam DriverEntry, die kort nadat het stuurprogramma is geladen, wordt aangeroepen. De functie DriverEntry vult bepaalde leden van een DRIVER_OBJECT structuur in met aanwijzers naar verschillende andere functies die het stuurprogramma implementeert. De DriverEntry functie vult bijvoorbeeld het Unload lid van de DRIVER_OBJECT-structuur met een aanwijzer naar de Unload functie van de stuurprogramma, zoals weergegeven in het volgende diagram.

Diagram dat de structuur van het bestuurdersobject toont met het lid voor ontladen.

De MajorFunction lid van de DRIVER_OBJECT-structuur is een matrix met aanwijzers voor functies die I/O-aanvraagpakketten verwerken (IRPs), zoals wordt weergegeven in het volgende diagram. Meestal vult het stuurprogramma verschillende leden van de MajorFunction matrix met aanwijzers naar functies (geïmplementeerd door het stuurprogramma) die verschillende soorten IRP's verwerken.

diagram met de structuur van het stuurprogrammaobject met het hoofdfunctielid.

Een IRP kan worden gecategoriseerd op basis van de primaire functiecode, die wordt geïdentificeerd door een constante, zoals IRP_MJ_READ, IRP_MJ_WRITEof IRP_MJ_PNP. De constanten waarmee de primaire functiecode wordt geïdentificeerd, fungeren als indexen in de matrix MajorFunction. Stel dat het stuurprogramma een dispatch-functie implementeert om IRP's met de hoofdfunctiecode IRP_MJ_WRITEte verwerken. In dit geval moet het stuurprogramma het element MajorFunction [IRP_MJ_WRITE] van de matrix invullen met een aanwijzer naar de verzendfunctie.

Meestal vult het stuurprogramma enkele elementen van de MajorFunction matrix in en laat de overige elementen ingesteld op standaardwaarden die door de I/O-manager worden geleverd. In het volgende voorbeeld ziet u hoe u de !drvobj foutopsporingsprogramma-extensie gebruikt om de functie-aanwijzers voor het parallelpoortstuurprogramma te inspecteren.

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

In de uitvoer van het foutopsporingsprogramma ziet u dat parport.sys GsDriverEntry-implementeert, het toegangspunt voor het stuurprogramma. GsDriverEntry-, die automatisch is gegenereerd toen het stuurprogramma werd gebouwd, voert een initialisatie uit en roept vervolgens DriverEntryaan, die is geïmplementeerd door de stuurprogrammaontwikkelaar.

U kunt ook zien dat het parportstuurprogramma (in de functie DriverEntry ) aanwijzers biedt naar dispatchfuncties voor deze belangrijke functiecodes:

  • 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

De resterende elementen van de MajorFunction array bevatten pointers naar de standaardafhandelaarfunctie nt!IopInvalidDeviceRequest.

In de uitvoer van de debugger ziet u dat het parportstuurprogramma functieaanroepers heeft geleverd voor Unload en AddDevice, maar geen functieaanroeper heeft opgegeven voor StartIo. De functie AddDevice is ongebruikelijk omdat de functieaanwijzer niet is opgeslagen in de DRIVER_OBJECT structuur. In plaats daarvan wordt het opgeslagen in het AddDevice- lid van een extensie van de DRIVER_OBJECT-structuur. In het volgende diagram ziet u de functie-aanwijzers die geleverd worden door het parportstuurprogramma in de DriverEntry-functie. De functiepointers die door parport worden geleverd, zijn aangegeven.

diagram van functiepointers in een structuur van stuurprogrammaobjecten.

Het gemakkelijker maken door het gebruik van stuurprogrammaparen

In de loop van de tijd, toen stuurprogrammaontwikkelaars binnen en buiten Microsoft ervaring opdeden met het Windows Driver Model (WDM), realiseerden ze bepaalde inzichten over dispatchfuncties.

  • Dispatchfuncties zijn grotendeels sjabloonmatig. Een groot deel van de code in de verzendfunctie voor IRP_MJ_PNP is bijvoorbeeld hetzelfde voor alle stuurprogramma's. Het is slechts een klein deel van de PnP-code (Plug and Play) die specifiek is voor een afzonderlijk stuurprogramma dat een afzonderlijk stuk hardware beheert.
  • Dispatch-functies zijn ingewikkeld en moeilijk correct te krijgen. Het implementeren van functies zoals threadsynchronisatie, IRP-wachtrijen en IRP-annulering is lastig en vereist een grondig begrip van de werking van het besturingssysteem.

Om het voor ontwikkelaars van stuurprogramma's gemakkelijker te maken, heeft Microsoft verschillende technologiespecifieke stuurprogrammamodellen gemaakt. Op het eerste gezicht lijken de technologiespecifieke modellen heel anders dan elkaar, maar een nadere blik laat zien dat veel van deze modellen zijn gebaseerd op dit paradigma:

  • Het stuurprogramma is gesplitst in twee delen: een die de algemene verwerking afhandelt en een die de verwerking afhandelt die specifiek is voor een bepaald apparaat.
  • Het algemene stuk is geschreven door Microsoft.
  • Het specifieke stuk kan worden geschreven door Microsoft of een onafhankelijke hardwareleverancier.

Stel dat de bedrijven Proseware en Contoso beide een speelgoedrobot maken waarvoor een WDM-stuurprogramma is vereist. Stel ook dat Microsoft een General Robot Driver met de naam GeneralRobot.sysbiedt. Proseware en Contoso kunnen elk kleine stuurprogramma's schrijven die voldoen aan de vereisten van hun specifieke robots. Proseware kan bijvoorbeeld ProsewareRobot.sysschrijven en het paar stuurprogramma's (ProsewareRobot.sys, GeneralRobot.sys) kunnen worden gecombineerd om één WDM-stuurprogramma te vormen. Op dezelfde manier kan het paar stuurprogramma's (ContosoRobot.sys, GeneralRobot.sys) worden gecombineerd om één WDM-stuurprogramma te vormen. In de meest algemene vorm is het idee dat u stuurprogramma's kunt maken met behulp van (specific.sys, general.sys) paren.

Functie-aanwijzers in stuurprogrammaparen

In een paar (specific.sys, general.sys) laadt Windows specific.sys en roept de bijbehorende DriverEntry-functie aan. De DriverEntry--functie van specific.sys ontvangt een aanwijzer naar een DRIVER_OBJECT structuur. Normaal gesproken zou je verwachten dat DriverEntry verschillende elementen van de MajorFunction-tabel vult met pointers naar dispatchfuncties. U zou ook verwachten dat DriverEntry het Unload lid (en mogelijk het StartIo lid) van de DRIVER_OBJECT-structuur en het AddDevice lid van de extensie van de driver-object invult. Echter, in een model voor stuurprogrammaparen doet DriverEntry dit niet noodzakelijkerwijs. In plaats daarvan geeft de DriverEntry--functie van specific.sys de DRIVER_OBJECT-structuur door aan een initialisatiefunctie die door general.sysis geïmplementeerd. In het volgende codevoorbeeld ziet u hoe de initialisatiefunctie kan worden aangeroepen in het paar (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);
}

De initialisatiefunctie in GeneralRobot.sys schrijft functie-aanwijzers naar de juiste leden van de DRIVER_OBJECT-structuur (en de extensie) en de juiste elementen van de MajorFunction matrix. Het idee is dat wanneer de I/O-manager een IRP naar het stuurprogrammapaar verzendt, de IRP eerst naar een verzendfunctie gaat die door GeneralRobot.sysis geïmplementeerd. Als GeneralRobot.sys zelf de IRP kan verwerken, hoeft het specifieke stuurprogramma, ProsewareRobot.sys, niet betrokken te zijn. Als GeneralRobot.sys een aantal, maar niet alle, van de IRP-verwerking kan verwerken, krijgt deze hulp van een van de callback-functies die door ProsewareRobot.syszijn geïmplementeerd. GeneralRobot.sys krijgt aanwijzers naar de ProsewareRobot-callbacks in de GeneralRobotInit-aanroep.

Op een bepaald moment nadat DriverEntry retourneert, wordt er een apparaatstack gemaakt voor het Proseware Robot-apparaatknooppunt. De apparaatstack kan er als volgt uitzien.

diagram van het proseware robotapparaatknooppunt met drie apparaatobjecten in de apparaatstack: afterthought.sys (filter do), prosewarerobot.sys, generalrobot.sys (fdo) en pci.sys (pdo).

Zoals in het voorgaande diagram wordt weergegeven, heeft de apparaatstack voor Proseware Robot drie apparaatobjecten. Het bovenste apparaatobject is een filterapparaatobject (Filter DO) dat is gekoppeld aan het filterstuurprogramma AfterThought.sys. Het middelste apparaatobject is een functioneel apparaatobject (FDO) dat is gekoppeld aan het stuurprogrammapaar (ProsewareRobot.sys, GeneralRobot.sys). Het stuurprogrammapaar fungeert als het functiestuurprogramma voor de apparaatstack. Het onderste apparaatobject is een fysiek apparaatobject (PDO) dat is gekoppeld aan Pci.sys.

U ziet dat het stuurprogrammapaar slechts één niveau in de apparaatstack in beslag neemt en is gekoppeld aan slechts één apparaatobject: de FDO. Wanneer GeneralRobot.sys een IRP verwerkt, kan dit ProsewareRobot.sys aanroepen voor hulp, maar dat is niet hetzelfde als het doorgeven van de aanvraag op de apparaatstack. Het stuurprogrammapaar vormt één WDM-stuurprogramma dat zich op één niveau in de apparaatstack bevindt. Het stuurprogrammapaar voltooit de IRP of geeft deze door naar de apparaatsstapel aan de PDO, die is gekoppeld aan Pci.sys.

voorbeeld van een stuurprogrammapaar

Stel dat u een draadloze netwerkkaart op uw laptopcomputer hebt en door in Apparaatbeheer te kijken, bepaalt u dat netwlv64.sys het stuurprogramma voor de netwerkkaart is. U kunt de extensie !drvobj foutopsporingsprogramma gebruiken om de functieaanwijzers voor netwlv64.syste inspecteren.

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

In de uitvoer van het foutopsporingsprogramma kunt u zien dat netwlv64.sys, GsDriverEntryimplementeert, het toegangspunt voor het stuurprogramma. GsDriverEntry, die automatisch is gegenereerd toen het stuurprogramma werd gebouwd, voert een initialisatie uit en roept vervolgens DriverEntryaan, die is geschreven door de ontwikkelaar van het stuurprogramma.

In dit voorbeeld implementeert netwlv64.sys DriverEntry, maar ndis.sys implementeert AddDevice, Unloaden verschillende dispatcheerfuncties. Netwlv64.sys wordt een NDIS-minipoortstuurprogramma genoemd en ndis.sys wordt de NDIS-bibliotheek genoemd. Samen vormen de twee modules een paar (NDIS miniport, NDIS Library).

In dit diagram ziet u de apparaatstack voor de draadloze netwerkkaart. U ziet dat het stuurprogrammapaar (netwlv64.sys, ndis.sys) slechts één niveau in de apparaatstack in beslag neemt en is gekoppeld aan slechts één apparaatobject: de FDO.

diagram van de stack van het draadloze netwerkkaartapparaat, met netwlv64.sys, ndis.sys als het bestuurderspaar dat aan de fdo is gekoppeld en pci.sys dat aan de pdo is gekoppeld.

Beschikbare stuurprogrammaparen

De verschillende technologiespecifieke stuurprogrammamodellen gebruiken verschillende namen voor de specifieke en algemene onderdelen van een stuurprogrammapaar. In veel gevallen heeft het specifieke gedeelte van het paar het voorvoegsel 'mini'. Hier volgen enkele (specifieke, algemene) paren die beschikbaar zijn:

  • (miniportstuurprogramma voor beeldscherm, poortstuurprogramma voor beeldscherm)
  • (audio-miniportstuurprogramma, audiopoortstuurprogramma)
  • (stuurprogramma voor opslagminiport, stuurprogramma voor opslagpoort)
  • (miniklassestuurprogramma voor batterij, batterijklassestuurprogramma)
  • (HID minidriver, HID-klassestuurprogramma)
  • (changer miniclass driver, changer port driver)
  • (NDIS miniport-stuurprogramma, NDIS-bibliotheek)

Opmerking Zoals u in de lijst kunt zien, gebruiken verschillende modellen de term classdriver voor het algemene gedeelte van een driverpaar. Dit type klassestuurprogramma verschilt van een zelfstandig klassestuurprogramma en verschilt van een klassefilterstuurprogramma.

Concepten voor alle ontwikkelaars van stuurprogramma's

apparaatknooppunten en apparaatstacks

stuurprogrammastacks