BI-MPP Cvičení 11 - Ovladače (Windows), Miroslav Skrbek (C)2010,2011 1 z 6 11. Implementace ovladače ve Windows Náplň cvičení V tomto cvičení se naučíte napsat ovladač zařízení pro operační systém Windows. Úkol Ovladač USB zařízení pro Linux přepište do podoby ovladače pro Windows včetně ovládací aplikace na straně PC. Obsah 11. Implementace ovladače ve Windows Náplň cvičení Úkol Návod Hlavičkové soubory Vsupní bod ovladače Funkce EvtDriverDeviceAdd Překlad ovladače Přidání nového zařízení Registrace souborového rozhraní ovladače Zpětná volání pro souborové operace Návod Hlavičkové soubory Ovladač začněte vložením požadovaných hlavičkových souborů do zdrojového souboru #include <ntddk.h> #include <wdf.h> #include <initguid.h> #include <stdlib.h> #include <ntstrsafe.h> Vsupní bod ovladače
BI-MPP Cvičení 11 - Ovladače (Windows), Miroslav Skrbek (C)2010,2011 2 z 6 Dále vytvořte vstupní bod ovladače NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPat { WDF_DRIVER_CONFIG config; WDF_OBJECT_ATTRIBUTES attributes; DbgPrint("MPP: DriverEntry - begin\n"); // Inicializace argumentů nezbytných pro vytvoření // instance ovladače. WDF_OBJECT_ATTRIBUTES_INIT(&attributes); WDF_DRIVER_CONFIG_INIT( &config, EvtDriverDeviceAdd ); // // Vytvoření instance ovladače // status = WdfDriverCreate(DriverObject, RegistryPath, &attributes, &config, WDF_NO_HANDLE); DbgPrint("MPP: DriverEntry - end (status: %d)\n", status); Funkce EvtDriverDeviceAdd Napište funkci EvtDriverDeviceAdd, která bude volána pokaždé, když je nové zařízení připojeno do systému (obdoba funkce probe v Linuxu). NTSTATUS EvtDriverDeviceAdd (IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit) { DbgPrint("MPP: EvtDriverDeviceAdd - begin\n"); // tělo funkce v tomto místě doplníme později return STATUS_SUCCESS; Překlad ovladače Pro překlad ovladače vytvořte soubor makefile s následujícím obsahem!include $(NTMAKEENV)\makefile.def a soubor SOURCES TARGETNAME=mpp TARGETTYPE=DRIVER KMDF_VERSION_MAJOR=1 MSC_WARNING_LEVEL=/W4 /WX INF_NAME=mpp NTTARGETFILE0=$(OBJ_PATH)\$(O)\$(INF_NAME).inf
BI-MPP Cvičení 11 - Ovladače (Windows), Miroslav Skrbek (C)2010,2011 3 z 6 MISCFILES=$(NTTARGETFILE0) C_DEFINES= SOURCES=mpp.c Dále nakopírujte vzorový soubor mpp.idx spolu s makefile a SOURCES ke zdrojovému souboru. V hlavním menu Start vyberte Všechny programy Windows Driver Kits WDK 7600.16385.1 Build Environments Windows XP a otevřte tak terminálové okno se všemi potřebnými nastaveními pro překlad. V tomto okně příkazem cd přejděte do adresáře se zdrojovými soubory. Napište příkaz build a vyčkejte dokončení překladu. Ovladač nainstalujte z nabídky Ovladací panely Přidat hardware nebo použijte program devcon. Využijte inf souboru, který se vytvořil během překladu. Otevřete Ovladací panely System Správce zařízení a ovladač vyhledejte. Dále spusťte program DbgView, kterým zobrazíte ladící zprávy jádra. Přidání nového zařízení NTSTATUS EvtDriverDeviceAdd (IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit) { WDFDEVICE device; PCWSTR systemdevicenamestr = L"\\Device\\mpp0"; UNICODE_STRING systemdevicename; DbgPrint("MPP: EvtDriverDeviceAdd - begin\n"); // Vytvoř systémové jméno pro zařázení DbgPrint("System device name: %ws\n", systemdevicenamestr); RtlInitUnicodeString(&systemDeviceName, systemdevicenamestr); // Přiřaď zařízení systémové jméno status = WdfDeviceInitAssignName(DeviceInit,&systemDeviceName); if (!NT_SUCCESS(status)) { // Vytvoř instanci zařízení status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device); return STATUS_SUCCESS; Registrace souborového rozhraní ovladače NTSTATUS EvtDriverDeviceAdd (IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit) { WDF_IO_QUEUE_CONFIG ioqueueconfig; WDFQUEUE hqueue; WDFDEVICE device; PCWSTR systemdevicenamestr = L"\\Device\\mpp0";
BI-MPP Cvičení 11 - Ovladače (Windows), Miroslav Skrbek (C)2010,2011 4 z 6 PCWSTR devicefilenamestr = L"\\DosDevices\\mpp"; UNICODE_STRING systemdevicename; UNICODE_STRING devicefilename; DbgPrint("System device name: %ws\n", systemdevicenamestr); RtlInitUnicodeString(&systemDeviceName, systemdevicenamestr); status = WdfDeviceInitAssignName(DeviceInit,&systemDeviceName); if (!NT_SUCCESS(status)) { // Nastav způsob předávání dat mezi uživatelským paměťovým // prostorem a paměťovým prostorem jádra při // voláních souborových operací z aplikace. // Nastavuje na WdfDeviceIoBuffered, což znamená, že data se z uživatelského // prostoru překopírují do operačním systémem vyhrazeného // bufferu v prostoru jádra. Jednoduchý, ale pomalejší způsob. WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered); // Vytvoření instance nového zařízení status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device); // Registrace souborového rozhraní, kterým se bude zařízení prezentovat aplikacím // Zavedli jsme nový typ DEVINTERFACE_FIT. Interface je reprezentován // námi vytvořeným GUID GUID GUID_DEVINTERFACE_FIT = { 0x5781faab, 0x0434, 0x48bb, { 0x89, 0x5c, 0xba, 0 status = WdfDeviceCreateDeviceInterface(device, &GUID_DEVINTERFACE_FIT, NULL); // Z důvodu kompatibility a snadnějšímu přístupu k ovladači ze strany aplikace vy // DOSově kompatibilní jméno souboru pro ovladač DbgPrint("MPP: Device filename: %ws\n", devicefilenamestr); RtlInitUnicodeString(&deviceFilename, devicefilenamestr); status = WdfDeviceCreateSymbolicLink(device, &devicefilename); if (!NT_SUCCESS(status)) { // Inicializuj V/V frontu, přes kterou budeme dostávat události // od aplikace. WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential); // Definuj zpětná volání pro souborové operace ioqueueconfig.evtiodefault = EvtIoDefault; ioqueueconfig.evtioread = EvtIoRead; ioqueueconfig.evtiowrite = EvtIoWrite;
BI-MPP Cvičení 11 - Ovladače (Windows), Miroslav Skrbek (C)2010,2011 5 z 6 ioqueueconfig.evtiodevicecontrol = EvtIoControl; // Registruj V/V frontu status = WdfIoQueueCreate(device, &ioqueueconfig, WDF_NO_OBJECT_ATTRIBUTES, &hqueue); Zpětná volání pro souborové operace EvtIoDeafault // Implicitni callback pro V/V události void EvtIoDefault(IN WDFQUEUE Queue, IN WDFREQUEST Request) { DbgPrint("IO Default"); EvtIoControl void EvtIoControl(IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t OutputBufferLength, IN size_t InputBufferLength, IN ULONG IoControlCode) { int r; int* ibuf; PVOID obuf; size_t isize; size_t osize; r = WdfRequestRetrieveInputBuffer(Request, InputBufferLength, &ibuf, &isize); r = WdfRequestRetrieveOutputBuffer(Request, OutputBufferLength, &obuf, &osize); DbgPrint("EvtIoDeviceControl[ibuflen: %d, obuflen: %d, ioc: %d]\n", InputBufferLength, OutputBufferLength, IoControlCode);
BI-MPP Cvičení 11 - Ovladače (Windows), Miroslav Skrbek (C)2010,2011 6 z 6 switch (IoControlCode) { case 100: // zde doplňte funkcionalitu pro I/O Control Code 100 WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, l); break; case 101: // zde doplňte funkcionalitu pro I/O Control Code 100 WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, l); break; default: EvtIoRead // Callback pro operaci čtení (read) void EvtIoRead(IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t Length) { int r; PVOID buf; size_t size; DbgPrint("EvtIoRead"); r = WdfRequestRetrieveOutputBuffer(Request, Length, &buf, &size); return // zde doplňte požadovanou funkcionalitu WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, size); EvtIoWrite // Callback pro operaci zapisu (write) void EvtIoWrite(IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t Length) { int r; PVOID buf; size_t size; DbgPrint("EvtIoWrite"); r = WdfRequestRetrieveInputBuffer(Request, Length, &buf, &size); // zde doplňte požadovanou funkcionalitu WdfRequestComplete(Request, STATUS_SUCCESS);