VDD: Virtual Device Drivers http://www-user.tu-chemnitz.de/~heha/bastelecke/Rund%20um%20den%20PC/LPTISA/ http://www-user.tu-chemnitz.de/~heha/bastelecke/Rund%20um%20den%20PC/USB2LPT/API.de.htm http://www.osronline.com/ddkx/other/vdd_24px.htm Instead of modifying your MS-DOS application, rely on the VDM to intercept disallowed hardware accesses and route them to your VDD. VDM-Based Intercepts 1. Modifying an MS-DOS Application or 16-bit Driver 2. Writing a VDD 3. Registering the Driver VDD initialization ########### A skeleton for the VDD initialization function is shown as follows: BOOL VDDInitialize(IN PVOID DllHandle, IN ULONG Reason, IN PCONTEXT Context OPTIONAL) /*++ Routine Description: Arguments: DllHandle - Not Used Reason - Attach or Detach Context - Not Used Return Value: SUCCESS - TRUE FAILURE - FALSE --*/ { switch (Reason) { case DLL_PROCESS_ATTACH: // Allocate VDD's local heap if needed. Check that NT FAX driver // is available by opening that device. break; case DLL_PROCESS_DETACH: // Deallocate VDD's local heap if needed. // Communicate to appropriate Device driver about your departure. break; default: break; } return TRUE; } Öffnen von USB2LPT ########### Eine globale Variable, die das Handle zum USB2LPT-Gerät hält, ist recht praktisch. HANDLE hAccess; for (int n=9; n; n--) { // von hinten probieren TCHAR DevName[12]; wsprintf(DevName,"\\\\.\\LPT%u",sn); hAccess=CreateFile(DevName, GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,0); if (hAccess!=INVALID_HANDLE_VALUE) goto found; } hAccess=0; found: I/O Ports ########### Function, Description VDDInstallIOHook, Hooks the I/O ports the VDD is responsible for. VDDDeinstallIOHook, Unhoooks the I/O ports previously hooked by a VDD. Einzelner OUT-Zugriff Die Funktion sieht wie folgt aus: void outb(BYTE a, BYTE b) { BYTE IoData[2]; DWORD BytesRet; IoData[0]=a; IoData[1]=b; DeviceIoControl(hAccess, CTL_CODE(FILE_DEVICE_UNKNOWN,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS), //0x222010 IoData,sizeof(IoData),NULL,0,&BytesRet,NULL); }Dabei ist a ein Adressbyte, von dem die Basisadresse eines echten Parallelports abgezogen wurde, und es ergibt sich: •0 = Datenport (siehe Beyond Logic) •2 = Steuerport (mit den Bits „Strobe“, „AutoFeed“, aber auch „Direction“ = Richtungswahl für Datenport) •3 = EPP-Adress-Schreib-Zyklus (siehe Beyond Logic) •4 = EPP-Daten-Schreib-Zyklus (a = 5, 6 und 7 machen ebenfalls EPP-Datenzyklen) Weiterhin (etwas abweichend von der Regel „Basisadresse subtrahieren“) gibt es: •8 = ECP-FIFO schreiben (siehe Beyond Logic) •10 = ECP-Konfigurationsregister „ECR“ setzen Man beachte, dass, wie beim echten Parallelport, drei Bits beim Steuerport (+2) invertiert ausgegeben werden! Dieser Umstand lässt sich abschalten, siehe unten. Weiterhin ist eine Ausgabe aufs Statusport (+1) möglich, die jedoch zunächst unwirksam ist (alles Eingänge). Einzelner IN-Zugriff Die Funktion sieht wie folgt aus: BYTE inb(BYTE a) { BYTE IoData[1]; DWORD BytesRet; IoData[0]=a|0x10; // Lese-Bit DeviceIoControl(hAccess, CTL_CODE(FILE_DEVICE_UNKNOWN,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS), IoData,sizeof(IoData),IoData,sizeof(IoData),&BytesRet,NULL); return IoData[0]; }Genauso wie oben ist a ein Adressbyte, von dem die Basisadresse eines echten Parallelports abgezogen wurde. Gelesen werden die Pegel an den Portpins (nicht notwendigerweise dasselbe wie das ausgegebene Byte). Es ergibt sich die folgende Liste: •0 = Datenport •1 = Statusport (mit den Bits „Busy“, „Acknowledge“, „Paper End“ usw.) •2 = Steuerport (tatsächliche Leitungspegel) •3 = EPP-Adress-Lese-Zyklus (siehe Beyond Logic) •4 = EPP-Daten-Lese-Zyklus (a = 5, 6 und 7 machen ebenfalls EPP-Datenzyklen) •8 = ECP-FIFO lesen oder „Konfigurationsregister A“ lesen •9 = „Konfigurationsregister B“ lesen (stets 0) •10 = ECP-Konfigurationsregister „ECR“ (bspw. FIFO-Zustand) lesen Man beachte, dass, wie beim echten Parallelport, ein Bit beim Statusport (+1) und drei Bits beim Steuerport (+2) invertiert eingelesen werden! Dieser Umstand lässt sich abschalten, siehe unten. Es gibt hierzu eine einfache DLL-Implementierung als — gewissermaßen — vorläufige Referenzimplementierung. Jobs mit Wartezyklen, Schleifen und Abbruchbedingungen Die Kombination vieler OUT- und IN-Transfers in einem Paket ermöglicht rasante Geschwindigkeiten; man muss aber gelegentlich etwas warten, und man hat keine Chance mehr, auf (unerwartete) Pegel umgehend zu reagieren. Deshalb gibt es einige Sonder-Adressen, die zum Warten sowie zum Test eines Pins und zum Abbruch bei Nicht-Bedingung führen. •0x20 = warten, danach Wartezeit in 4-µs-Schritten, 0 = 1024 µs •0x21 = warten auf Pegel, danach Beschreibungsbyte: ◦Bit 7:6 = ungenutzt, bitte mit Null füllen ◦Bit 5:4 = Port-Kode, 0 = Datenport, 1 = Statusport, 2 = Steuerport, 3 = verboten ◦Bit 3 = Prüfzustand, 0 = Weiter wenn Pegel LOW (blockiere solange Pegel HIGH), 1 = Weiter wenn Pegel HIGH (blockiere solange Pegel LOW) ◦Bit 2:0 = Bitnummer 0..7 (beim Steuerport lesen 4..7 undefinierte Zustände, bspw. von einer LED) Diese Sonder-Adresse dient insbesondere zur Implementierung von I²C. Die Blockierzeit ist vorläufig unbegrenzt. Diese Sonder-Adressen verhalten sich wie OUT-Adressen (also Bit 4 gelöscht). Die Sonderadresse 0x21 ist noch nicht implementiert. Interrupt Simulation ########### vdd (Win32 Code): SaveCS = getCS( ); SaveIP = getIP( ); SaveAX = getAX( ); setCS (16BitRoutineCS); setIP (16BitRoutineIP); setAX (DO_X_OPERATION); VDDSimulate16( ); setCS (SaveCS); setIP (SaveIP); setAX (SaveAX); . . . . Stub Driver (Initialization): RegisterModule ;Loads VDD push cs pop ax mov bx,offset Simulate16 DispatchCall ;passes the address of worker ;routine to VDD in ax:bx Stub Driver (Run Time) Simulate16: . . . . ;do the operation index passed in ax VDDUnSimulate16 Hooking Callback Events ###########