USB-Logo Die API zum USB2LPT (Anwendungsprogrammierer-Schnittstelle) 25pol. SubD-Buchse

Es gibt auch eine API für Programmierer, die sehr einfach gehalten ist. Das Ziel von USB2LPT ist es eigentlich, ohne Änderung von Software auf ein Parallelport zuzugreifen. Damit ist eine API eigentlich kontraproduktiv.

Weil aber das Abfangen und Umleiten von Portzugriffen wegen der Paketorientierung von USB über eine bestimmte Geschwindigkeit nicht hinauskommt, erscheint eine API für neue, spezialisierte Programme dennoch sinnvoll.

Die Strategie ist einfach und erfordert keine zusätzlichen DLLs: Öffnen Sie mit CreateFile das Gerät "\\.\LPT1" (oder "LPT2" wenn es das zweite Gerät ist usw.), und schicken/holen die zu transferierenden Daten über möglichst wenige Aufrufe von DeviceIoControl.

Parallelport-kompatibler Portzugriff

Diese Zugriffe erlauben die Verwendung des USB2LPT wie ein echtes Parallelport (inklusive ECP und EPP, wer's braucht), ohne irgendwelche Schweinereien wie Debugregister-Trap, und ohne Fummeleien mit READ_PORT_UCHAR/WRITE_PORT_UCHAR. Sie benötigen auch keine InpOut32.DLL (oder ähnliches), und, zur Freude aller Administratoren, es wird kein Sicherheitsloch geöffnet. Denn InpOut32.DLL ist wie eine Einladung, den Rechner mit wilden Portzugriffen totzulegen (notfalls lässt sich damit sogar eine Festplatte formatieren).

Ö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:
Da sich auch normale Parallelschnittstellen mit diesem CreateFile()-Aufruf öffnen lassen, sollte ein USB2LPT-Test folgen.
// Den 8051 XRAM oder ATmega Flash-Speicher ab Adresse 6 auslesen, wo das Firmware-Datum steht.
// Klappt das nicht, ist's kein USB2LPT.
#include "usb2lpt.h"
 WORD addr = 6;
 WORD date = 0;		// FAT-Datumsstempel
 DWORD BytesRet;
 if (DeviceIoControl(hAccess,IOCTL_VLPT_XramRead/*0x22228E*/,&adr,sizeof(adr),&date,sizeof(date),&BytesRet,NULL)) {
  // dies ist tatsächlich ein USB2LPT, und man das Datum wie folgt zur Anzeige bringen:
   FILETIME ft;
   DosDateTimeToFileTime(date,0,&ft);
   SYSTEMTIME st;
   FileTimeToSystemTime(&ft,&st);
   TCHAR s[20];		// In diesen Zeichenpuffer kommt das landestypisch formatierte Datum
   GetDateFormat(LOCALE_USER_DEFAULT,0,&st,NULL,s,20);
   // ...
 }else{
  // dies ist ein Standard-Parallelport oder irgendetwas anderes
 }
Der Hintergedanke, warum USB2LPT sich genauso wie ein echtes Parallelport öffnen lässt, ist, dass ein „Upper Filter Driver“ für normale Parallelports vorgesehen ist, der den gleichen Zugriffsmechanismus erlaubt und die Verwendung von InpOut32.DLL erspart.

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: Weiterhin (etwas abweichend von der Regel „Basisadresse subtrahieren“) gibt es: 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: 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 [Auge] als — gewissermaßen — vorläufige Referenzimplementierung.

Kombinierte Zugriffe

Die Einzelzugriffe bringen geschwindigkeitsmäßig keinen Vorteil gegenüber der Verwendung von Portzugriffen und Trapping. Im Gegenteil, OUT-Zugriffe werden hierbei nicht automatisch zusammengefasst, und so kann es noch langsamer werden.

Sieht man sich die Programmstücke genauer an, sieht man, dass alles über Ein- und Ausgabepuffer eines IOCTL-Kodes (0x222010) abgewickelt wird. Das ist kombinierfähig bis zu beliebigen Puffergrößen. Eine sinnvolle Obergrenze sind 64 Bytes, so ist es auch im Treiber realisiert.

Die Kombination von Zugriffen erfolgt durch das Hintereinanderschreiben von OUT-Adressen und OUT-Daten sowie IN-Adressen im Eingabepuffer. IN-Adressen sind das gleiche wie OUT-Adressen mit gesetztem Bit 4. Und: Für jede IN-Adresse benötigt man im Ausgabepuffer 1 Byte Platz. Das ist alles! Also eine Art Mikrocode für die USB2LPT-Firmware.

Beispielsweise:

// Lesen aller 17 Portpins in einem Rutsch
void GetPinStates(BYTE states[3]) {
 static const BYTE SendBytes[3]={0x10,0x11,0x12};
 DWORD BytesRet;
 DeviceIoControl(hAccess,
   CTL_CODE(FILE_DEVICE_UNKNOWN,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS),
   SendBytes,3,states,3,&BytesRet,NULL);
}

Erweiterter Portzugriff

Das USB2LPT-Gerät stellt eigentlich ein Multi-IO-Gerät mit 17 Anschlüssen dar. (Die Revisionen 4 und 7 haben sogar 20 Anschlüsse.) Mit dem erweiterten Portzugriff kann jedes einzelne Pin in seiner Datenrichtung geschaltet werden.

Die entsprechende Hintertür sind vor allem weitere Adressen:

Gesetzte Bits sind Ausgänge. Die drei „unteren“ Bits des Statusports stellen standardmäßig Masseanschlüsse an den SubD-Pins 21, 22 und 23 her (siehe Schaltplan).

Die noch verbleibende Adresse 11 ist nicht belegt.

Die Feature-Register-Bits:

  1. Offene-Senke-Simulation für Datenport (+0)
  2. Offene-Senke-Simulation für Steuerport (+2) in Betriebsart SPP
  3. Offene-Senke-Simulation für Steuerport (+2) in allen anderen Betriebsarten
  4. ungenutzt
  5. Serialized: USB-Gerät gibt sich mit Seriennummer aus. Daher eineindeutige Zuordnung auch bei wechselnden USB-Topologien.
  6. DarkBlue: dunklere blaue LED mittels Pulsweitenmodulation (nur High-Speed-Versionen, Tastverhältnis 12,5 %)
  7. DirectIO (keine Invertierungen; Zugang zu Bits 0..2 des Statusports)
Die Offene-Senke-Simulation (svw. „open collector“) ermöglicht kollisionsfreie Wired-And-Verknüpfungen, wie sie für manche anzuschließende Hardware notwendig ist.

Nur High-Speed und Full-Speed: Durch Offene-Senke-Simulation wird der 5-V-Spannungspegel für HIGH-Ausgänge erreicht. Ansonsten werden nur maximal 3,3 V ausgegeben, was für manche Hardware nicht ausreichen könnte.

Nur Low-Speed: Da hier nur schwache interne Pullup-Widerstände vorhanden sind (Richtwert 40 kΩ), ist hierbei die Offene-Senke-Simulation mit Vorsicht zu verwenden (in der Regel mit externem Pull-Up-Widerstand).
Der HIGH-Ausgangspegel liegt bei 5 V. Falls 3,3 V gewünscht sind, muss dass USB2LPT-Gerät modifiziert werden, durch Einsetzen einer Betriebsspannung reduzierenden Doppeldiode BAV199 bei D2 (Brücke SJ3) durchkratzen, und Ersatz von R1 durch 1,5 kΩ. Siehe Schaltplan.

Wartezyklen und Bitoperationen

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 Opcodes zum Warten sowie zur Bitmanipulation. Diese Sonder-Adressen verhalten sich wie OUT-Adressen (also Bit 4 gelöscht).

Die Sonderadresse 0x24 ist noch nicht implementiert.

Nur High-Speed:

Geplant: GPIF-beschleunigte High-Speed-Transfers

High-Speed-Transfers sind solche, die das GPIF (General Purpose Interface) benutzen. Datenraten in voller USB-Geschwindigkeit sind selbst mit einem 8-bit-Bus kein Problem. 48 MByte/s sind möglich. Damit dauert das Konfigurieren großer RAM-basierter FPGAs (bspw. Spartan-3) nur noch Sekundenbruchteile, und Videostreaming wird möglich.

Die gewünschte Waveform wird durch Beschreiben passender RAM-Bereiche geladen.

Durch Umschalten in den High-Speed-Transfer-Modus geht das normale Verhalten als Parallelport verloren! Während des High-Speed-Transfers blinkt die blaue LED.

Die genauere Spezifikation wird noch festgelegt.

Nicht geplant: Peripherienutzung (Timer u.ä.)

Solches Ansinnen geht weit über den Zweck eines USB2LPT hinaus und ist nicht kompatibel zwischen Low-Speed (ATmega8) und High-Speed (Ez-USB FX2: CY7C68013A) lösbar. Wer so etwas unbedingt braucht, hat die Möglichkeit, die Firmware zu erweitern; siehe im nächsten Abschnitt.

Lesen und Schreiben des RAM, EEPROM oder Flash-Speichers

Ist bereits vollständig implementiert; siehe „brenner.c“. Dokumentation folgt.

Der Zugriff ist derselbe wie bei Cypress' ezusb.sys und geladenem vend_ax.hex. Dabei macht man sich eine un(ter)dokumentierte Eigenart von Windows' DeviceIoControl() zu Nutze: Ist im Gerätesteuerkode METHOD_IN_DIRECT gesetzt, fungiert lpOutBuffer als weiterer Eingabepuffer. Dieser enthält schließlich die Nutzdaten für die Control-Transfer-Datenphase. lpInBuffer zeigt nur auf den Wert von wValue (wenn nInBufferSize==2, wIndex ist dann Null) oder auf die Werte von wValue und wIndex (wenn nInBufferSize==4).

Es sind belegt:

Symbolische KonstanteWert der KonstanteZugehöriges bRequestSpeicher beim 8051-ControllerSpeicher beim ATmega-Controller
IOCTL_VLPT_AnchorDownload0x002222810xA0XRAM (limitiert)nicht implementiert
IOCTL_VLPT_RamRead0x002222860xA1iRAM, dRAM: Register, E/A
dRAM ist ab 0x100 erreichbar
mittels selbstmodifizierendem Kode
RAM: Register, E/A
IOCTL_VLPT_RamWrite0x00222285
IOCTL_VLPT_EepromRead0x0022228A0xA2
wie vend_ax.iic
Boot-I²C-EEPROM 24C64
Beliebige I²C-Peripherie mit wIndex!=0
interner EEPROM
IOCTL_VLPT_EepromWrite0x00222289
IOCTL_VLPT_XramRead0x0022228E0xA3
wie vend_ax.iic
XRAM (komplett) == ROM
Der 8051 ist von-Neumannisiert
Flash-Speicher
IOCTL_VLPT_XramWrite0x0022228Dnicht implementiert (Lock-Bit gesetzt)
bmRequestType ist 0x40 (OUT, Vendor, Device) bzw. 0xC0 (IN, Vendor, Device).
Künftige Firmware unterstützt auch 0x41 (OUT, Vendor, Interface:wIndex=0) und 0xC1 (IN, Vendor, Interface:wIndex=0).

EEPROM-Inhalt

USB2LPT-Version1.0, 1.11.2, 1.3, 1.4, 1.71.51.61.8Anmerkung
ControllerAN2131CY7C68013AATmega48ATmega8AT90USB162
Größe8 KByte (externer 24C64 via I²C)256 Byte512 Byte
FirmwareUSB2LPT.A51USB2LPT2.A51usb2lpt5.cusb2lpt6.cusb2lpt8.cpp
AdresseInhalt
00xB20xC20x120xFF (frei)0x08Firmware-Start
0xBE (beliebig)0xCE (beliebig)USB-Deskriptoren
(um Flash-Speicher
zu sparen)
0x42USB-Bootloader bleibt aktiv
1..restliche Firmware (*.IIC)frei (0xFF)Kein Speicherabzug, sondern segmentiertes Bootloader-Image
(zu erzeugen mittels hex2bix)
0xFFF0.. (-16)Ab hier garantierter Datenbereichzurzeit mit 0xFF gelöscht
0xFFF0..0xFFF5 (-16)angedacht für TUserCfg„roamende“ Konfigurationsdaten
0xFFF9 (-7)ECR-Startwertab 2012-03-06
0xFFFA (-6)frei (0xFF)OSCCAL-Startwertfreinur 12,8-MHz-Version (ohne Quarz)
0xFFFB (-5)Feature-Startwertwird von der Firmware automatisch geschrieben
0xFFFC..0xFFFF (-4)Seriennummer (DWORD)0 oder 0xFFFFFFFF = keine Seriennummer
0xFFFF (-1)Seriennummer (BYTE)-Nur ganz frühe Geräte; praktisch ausgestorbene Variante

Da die EEPROMs allesamt mit unvollständiger Adressdekodierung arbeiten, gelangt man mit den hohen Adressen automatisch ans EEPROM-Ende, unabhängig von der jeweiligen EEPROM-Größe.


Die USB-Schnittstelle

Dieser Abschnitt ist für die Implementierung von Treibern relevant. Also insbesondere für Nicht-Windows-Systeme. Oder auch zum Nachbau von USB-Geräten mit anderen Mikrocontrollern. Grundlegend ist die Übermittlung von „Mikrocode“, die Übertragung von Adressen und Datenbytes, genauso wie oben beschrieben. Im einfachsten Fall zwei Bytes für einen OUT-Befehl sowie ein Byte für einen IN-Befehl, gefolgt vom Abfragen des IN-Datenbytes.

Für neuere Firmware-Implementierungen (ab 2009) gilt: Das OUT-Datenbyte zu einem OUT-Adressbyte darf sich in einem nachfolgenden Transfer befinden. Ansonsten müssen sich OUT-Adresse und zugehöriges OUT-Datenbyte stets in einem USB-Transfer-Block befinden.

Für Full-Speed- und High-Speed-USB2LPT gilt: Es existiert Doppel-Pufferung für USB-OUT und USB-IN-Bulk-Transfers. Daher darf man bis zu 3 USB-OUT-Blöcke senden, bevor man die Ergebnisse (in ebenso vielen Blöcken) abholen muss.
In allen anderen Fällen gilt: Der USB-IN-Transfer-Block muss sich dem USB-OUT-Transfer-Block anschließen (keine Schachtelung erlaubt).

USB2LPT bietet (bis zu) 4 Möglichkeiten zu deren Übertragung. Ein gemischter Zugriff (bspw. Senden des Mikrocodes nach Methode 1 und Lesen der IN-Datenbytes nach Methode 2) ist nicht vorgesehen und dessen Funktion implementationsabhängig.

Hersteller-spezifische (also haftmann#software-erdachte) Bulk-Pipes (seit 2004)

Diese Schnittstelle ist auf minimalem Overhead ausgelegt. Sie ist die ursprüngliche Schnittstelle.

Unter der USB-Adresse mit VID = 0x16C0, PID = 0x06B3 .. 0x06B6 und dem Interface 0 bietet USB2LPT zwei Bulk-Pipes zum direkten Mikrocode-Transfer an.

  1. Bulk-Out für (OUT-Adresse+OUT-Datenbyte) und/oder (IN-Adresse)
  2. Bulk-In für (IN-Datenbyte)
repetierend bis zur verfügbaren FIFO-Puffergröße.

Bei Low-Speed (seit 2007) gibt es hierbei zwei Alternate Settings:

  1. Zwei Bulk-Pipes (laut USB-Standard verboten, funktioniert jedoch unter Win98,Me,2k,XP)
  2. Zwei Interrupt-Pipes mit bInterval = 10 (bewirkt um Faktor 8 höhere Latenzzeit — der Windows-Treiber schaltet unter Windows Vista und Windows 7 automatisch auf diese Alternate Setting.)
Die FIFO-Größe ist durch die jeweilige Geschwindigkeit begrenzt: Bei Verwendung größerer Datenmengen sollte man zum Schreiben und gleichzeitigen Lesen der Daten zwei nebenläufige Prozesse (Threads) verwenden oder „zu Fuß“ diese Stückelung vornehmen. Der Windows-Treiber usb2lpt.sys macht es mittels quasi-paralleler Abarbeitung, er setzt zwei IRPs „nach unten“ ab und wartet auf beider Komplettierung. Die darunter liegenden Treiberschichten zerstückeln den Bulk-Transfer selbständig in die von den Deskriptoren diktierten Häppchen.
Das ist der Grund, warum der Treiber usb2lpt.sys DeviceIoControl() benutzt (zwei Puffer gleichzeitig) und nicht ReadFile()/WriteFile() (jeweils nur 1 Puffer)! Naja, man hätte auch ReadFile() verballhornen können und den Puffer bidirektional benutzen (das geht!! Wird von HID auch so benutzt!), aber mich schreckt das eher zurück. Zusätzlich unterstützt usb2lpt.sys die Mikrocodeübertragung per WriteFile() und das Abholen der Ergebnisdaten per ReadFile(), um Programmierumgebungen nutzen zu können, die kein DeviceIoControl() unterstützen, etwa Skriptsprachen.

Windows stückelt zu lange Transferpuffer selbständig in FIFO-Häppchen! Die maximale Blockgröße unter Windows ist typisch 4 KByte.

Bei Umschaltung auf GPIF-Transfers (nur High-speed-fähige USB2LPT Rev. 2,3,4,7) erfolgt über diese beiden Bulk-Pipes der High-Speed-Datenstrom mit bis zu 48 MByte/s (Burst, praktisch maximal 20 MByte/s). Bei Full-Speed sind's maximal 0,8 MByte/s, ohne GPIF, aber mit schneller 8-Bit-FIFO, ähnlich FT245. Eine genauere Spezifikation werde ich erst bei Bedarf erarbeiten und implementieren; insbesondere wie dieser Modus aktiviert und wieder deaktiviert wird.

Bei Fehlsteuerung antwortet die BULK- bzw. INTERRUPT-Pipe mit STALL:

Die STALL-Bedingung verschwindet erst durch den den SETUP-Transfer CLEAR_FEATURE(EP,STALL). IMHO mach das Windows automatisch. Oder??

Das Konzept mit den beiden BULK-Pipes ist einer gewöhnlichen seriellen oder TCP-Verbindung entlehnt und lässt sich daher mühelos auf eine serielle Schnittstelle oder einer Ethernet-Verbindung umsetzen.

Q: Wieviel Bytes Mikrocode kann ich hinschicken, bevor ich Ergebnisbytes abholen muss?
A: Das ist zurzeit nicht definiert. Es kommt auf die Anzahl der Ergebnisbytes an, die irgendwo im Mikrocontroller gepuffert werden müssen. Da der kleinste Controller 512 Bytes RAM aufweist, wird das Limit hiermit (130821) auf 256 Bytes festgelegt. Auf der sicheren Seite liegt man zurzeit bei 7 Bytes, der kleinsten FIFO-Größe im HID-Modus. Erfolgt das Senden und Empfangen gleichzeitig (via DeviceIoControl()) ist die Datenmenge unbegrenzt: Windows und das USB-Protokoll kümmert sich um entsprechende Pufferungen.

Hersteller-spezifische (also haftmann#software-erdachte) Control-Befehle (seit 2009)

Um das Problem mit der Latenzzeit bei Low-Speed-Interrupt-Pipes zu umgehen, gibt es — besonders vorteilhaft für kurze Mikrocode-Abschnitte mit bis zu 4 Bytes — eine Steuerungsmöglichkeit über die Control-Pipe, also Endpoint 0, mit minimiertem Overhead.

Unter der USB-Adresse mit VID = 0x16C0, PID = 0x06B3 .. 0x06B6 und dem Interface 0 kann man USB2LPT folgende vendor-spezifische Befehle schicken:

* Enthält der Mikrocode keine IN-Befehle, muss wLength = 0 sein, und bmRequestType sollte 0x40 (Datenrichtung: OUT) sein.

Für minimale Latenz werden für kurze Mikrocodes die vier „freien“ Bytes des Setup-Transfers mit Nutzdaten gefüllt. Das ist der Trick.

Mikrocode darf auch im Block unvollständig übertragen werden, dann wird dieser mit dem nächsten Transfer fortgesetzt. Beispiel:

Bei Fehlsteuerung antwortet der SETUP-Transfer mit STALL:

Die STALL-Bedingung verschwindet USB-konform automatisch mit dem nächsten SETUP-Transfer.

HID-Interface mit Input- und Output-Report (nicht bei Low-Speed; ab 2011)

Das HID-Interface mit VID = 0x16C0, PID = 0x06B3 .. 0x06B6 bietet 64 Byte große Input- und Output-Reports an. Verschachtelung ist nicht erlaubt, es gibt keine Pufferung. Das heißt, der Input-Report muss abgeholt werden (sofern es IN-Datenbytes gibt), bevor der nächste Output-Report abgeschickt wird. Unter Windows puffert das System automatisch (durch Polling).

Beide Reports sind gleichartig aufgebaut:

Die Übertragung erfolgt über zwei eigenständige Interrupt-Pipes, Endpoint 1, mit bInterval = 1 ms.

Der Report-Deskriptor des Low-Speed-USB2LPT bietet keine Input- und Output-Reports, danach lässt sich eine Fallunterscheidung machen.

Seit September 2011 ist der HID-Report-Deskriptor so gestaltet, dass sich dieser mit den HidP-Funktionen vernünftig enumerieren lässt. Im wesentlichen wurden verschiedene Dummy-Usages zugeordnet.

HID-Interface mit Feature-Reports (seit 2010)

Das HID-Interface mit VID = 0x16C0, PID = 0x06B3 .. 0x06B6 bietet 2-8 Byte große Feature-Reports an. Hier wurde auf Minimierung der USB-Bandbreite und günstige Latenz optimiert, speziell für den Low-Speed-Einsatz.

Der Feature-Report überträgt per Feature-Out den Mikrocode und per Feature-In die IN-Datenbytes. Er ist wie folgt aufgebaut:

Die Übertragung erfolgt über die Control-Pipe und Endpoint 0.

Diese Übertragungsart soll auch bei Full-Speed und High-Speed verfügbar sein, ist jedoch dort ungetestet.

Seit September 2011 ist der HID-Report-Deskriptor so gestaltet, dass sich dieser mit den HidP-Funktionen vernünftig enumerieren lässt. Im wesentlichen wurden verschiedene Dummy-Usages zugeordnet. Dies kann man mit dem Programm hidparse auflisten.