Forum: PC-Programmierung Schreiben eines USB-Treibers mit libusb-win32


von B. O. (veteran)


Lesenswert?

Die Firma Renishaw verkaufte bis 2007 ein Laserinterferometer ML10, das 
mit einem sogenannten DX10-Interface per USB angesteuert wird.

Die mitgelieferte Software passt leider nicht für meinen Anwendungsfall. 
Ich möchte aus der Kommandozeile von Windows nacheinander Messungen 
beauftragen. Als Ergebnis erwarte ich je eine Datei mit einer langen 
Zahlenkolonne (zB 50000 Messwerte für 10 sec Messung bei 5 kHz Takt), 
die ich dann über die Kommandozeile einer weiteren Auswertung übergebe. 
Mit der erhältlichen SDK konnte ich nur statische Messungen (maximaler 
Takt 10 Hz) beauftragen.

Ein selbstgeschriebener Treiber für das DX10-Interface ist das Ziel! Als 
Entwicklungsumgebung nutze ich MinGW, libusb-win32 als 
Windows-Implementation der libusb-0.1 API und libusb0.sys als Windows 
Kerneltreiber. Eine Portierung nach Linux sollte damit kein größeres 
Problem darstellen.
Es wird KEINERLEI Software/DLL vom Hersteller verwendet! Insbesondere 
läuft der dx10server NICHT.

Der Treiber ist inzwischen rudimentär funktionsfähig, und kann statische 
(10 Hz) und dynamische (5 kHz) Messungen durchführen! Allerdings 
verstehe ich den Datenverkehr noch nicht völlig und kann dementsprechend 
keine engen Sicherheitsabfragen einbauen. Darüber hinaus wird die 
Wetterstation noch nicht ausgelesen und bisher nur EIN Laser an EINEM 
Eingang des DX10 unterstützt. Sicherlich ist auch alles etwas 
umständlich programmiert, da es nur durch "trial and error" gewachsen 
ist. - Aber der Anfang ist gemacht!

--> Erste augenscheinlich korrekte Messwerte können in beiden 
Betriebsarten (statisch/dynamisch) ausgegeben werden!

Nun meine Fragen:
- Gibt es überhaupt ein allgemeines Interesse an dem hier beschriebenen 
(sehr speziellen) Treiber?
- Kann ich auf Hilfe beim Weiterentwickeln erhoffen?
- Wenn ja, wo sollte ich meinen Treiber ins Netz stellen? (Hier?)

von Linuxx (Gast)


Lesenswert?

Dieses DX10-Interface scheint etwas sehr firmenspezifisches zu sein. 
Google liefert sonst nixhts brauchbares. DirectX 10 ist es wohl nicht.

von B. O. (veteran)


Angehängte Dateien:

Lesenswert?

Ein großes allgemeines Interesse an dem beschriebenen Treiber scheint 
nicht vorhanden zu sein. Doch bevor ich meine Erkenntnisse nur für mich 
behalte und den Treiber nur selber benutze, können vielleicht auch 
andere (eventuell später) davon profitieren...

Das C-File ist mit MinGW einfach mit make zu übersetzten 
(LDFLAGS=-lusb).

von Peter II (Gast)


Lesenswert?

Ich habe nur mal aus Neugier den Quellcode angeschaut, dabei ist nur die 
Funktion aufgefallen.

Warum verwendest du rdtsc zum bestimmen der Zeit? Das ist meines Wissen 
sehr fehleranfällig wenn mehre Kerne vorhanden sind, dann jede CPU hat 
ihren eigenen Counter.

Selbst wiki warnt davor:
http://en.wikipedia.org/wiki/Time_Stamp_Counter
Under Windows platforms, Microsoft strongly discourages using the TSC 
for high-resolution timing for exactly these reasons, providing instead 
the Windows APIs QueryPerformanceCounter and 
QueryPerformanceFrequency.[2] Under *nix, similar functionality is 
provided by reading the value of CLOCK_MONOTONIC clock using the POSIX 
clock_gettime function.

von physiker (Gast)


Lesenswert?

Wo das Stichwort Linux gefallen ist, es wäre natürlich schön das Ding in 
einen echten Treiber umzuschreiben. Dabei müssten natürlich u.a. der 
Haufen Funktionen die aus process.h bereit gestellt werden, durch andere 
ersetzt werden, da process.h doch sehr Microsoft spezifisch ist. Dann 
könnte man es auch in den Kernel bringen und wenn ein Nutzer dann seinen 
Laser anschliesst, kann Linux da einfach "out of the box" ;)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

physiker schrieb:
> Wo das Stichwort Linux gefallen ist, es wäre natürlich schön das Ding in
> einen echten Treiber umzuschreiben.

Nee, man muss nicht alles, was am USB dranhängt, unbedingt in den
Kernel quetschen.  Der Aufwand, das dort zu pflegen (vor allem dann
plattformübergreifend) ist ungleich größer, als wenn man einen
generischen Treiber und libusb benutzt.  Glaub' mir, es gäbe AVRDUDE
nicht für Linux, Win32, OSX, *BSD, Solaris, wenn man für jedes davon
unterstützte USB-Gerät noch einen eigenen Treiber schreiben müsste.

Da ist die libusb mit einem plattformübergreifend einigermaßen
einheitlichen API einfach ein Segen.  Performancemäßig spielt das
sowieso alles keine Geige.

von NurEinGast (Gast)


Lesenswert?

Interessantes Projekt! Könntest du etwas ausführen, wie du das Protokoll 
reverse engineered hast? Und kannst du gute Dokumentation empfehlen, um 
sich in die libusb einzuarbeiten?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

NurEinGast schrieb:
> um sich in die libusb einzuarbeiten?

So viel ist das nicht.  Wenn du die Grundkonzepte von USB selbst
verstanden hast und ein paar libusb-Beispiele angesehen hast, dann
kannst du die auch selbst benutzen.  Letztlich ist das API nicht viel
mehr als eine Abbildung der USB-Aktionen.

(Bitte keine unsinnigen Mailadressen im Formular eintragen.  Wenn du
keine Mail bekommen willst, weil es dich nicht interessiert, wenn
ein Beitrag gelöscht oder verschoben wird, dann lass das Feld leer.)

von B. O. (veteran)


Lesenswert?

NurEinGast schrieb:
> Könntest du etwas ausführen, wie du das Protokoll reverse engineered hast?

Reverse Engineering ist zeitaufwändig!

Wie MAN es macht, weiß ich nicht. Ich kann nur kurz aufzeigen was ich 
gemacht habe:

Zum Ansehen des USB-Protokolls habe ich usbview.exe verwendet. 
Allerdings musste ich erst einmal verstehen, wo die Bytes stehen, die 
das Protokoll überhaupt ausmachen und woran man erkennt, auf welche Art 
sie geschickt werden. Zudem funkt das Betriebssystem auch noch 
dazwischen...

Ich habe mit einem kleinen USB-Experimentier-Board angefangen. Für das 
Board kannte ich das USB-Protokoll genau und ich habe versucht, in dem 
Datenwust der Ausgabe von usbview.exe meine Daten wiederzufinden.
Nach ich das Verständnis für die Ausgabe von usbview.exe hatte, habe ich 
mir das Zielgerät vorgenommen.

Wenn man Glück hat, ist das Protokoll selbsterklärend, doch meistens... 
Einfacher wird es, wenn man in sehr kleinen Schritten vorgeht:
Ich habe zum Beispiel das DX10-Interface per USB abgestöpselt, den 
Sniffer gestartet und dann nur das Interface nur angestreckt und 10 sec 
gewartet. Die Idee dahinter ist, aus einem reproduzierbaren Zustand 
heraus mehrfach exakt das Gleiche zu machen und sich jedesmal den 
Datenverkehr anzusehen und zu vergleichen. Erstaunlich, wie 
unterschiedlich man doch anscheinend Gleiches darstellen kann!

Irgendwann erkennt man doch Regelmäßigkeiten und bekommt eine Idee eines 
Daten-Protokolls. Hier ist Phantasie und Kreativität notwendig.
Jetzt ist das Abtippen der Byte-Folgen angesagt: Nachstellen der 10 sec 
Startprotokoll, allerdings ohne Verständnis, was im Einzelnen passiert. 
Wenn die erste erwartete Antwort vom Interface kommt, ist der erste 
kleine Schritt getan.

Jetzt gibt es zwei Ansatzmöglichkeiten, die ich beide abwechselnd 
genutzt habe.
Zum einen versuchte ich Regelmäßigkeiten in den Byte-Folgen zu endecken 
(Längen, Prüfsummen, echte Daten, ...). Zum anderen kann man am 
Zielgerät je EINE neue Funktion nutzen und die Veränderung des 
Protokolls herausarbeiten.

Schrittweise habe ich mein Programm angepasst und erweitert und den 
selbst erzeugten Datenverkehr mit dem vorher analysierten verglichen.

Erst beim Arbeiten habe ich die libusb kennengelernt. Gute Doku kenne 
ich nicht. Nur das Testen hat mir geholfen.

von B. O. (veteran)


Lesenswert?

Peter II schrieb:
> Warum verwendest du rdtsc zum bestimmen der Zeit?

Danke für die Info zum "rdtsc".
Ich verwende den Befehl nur als Info in meinem log-File und nicht zum 
Einhalten eines Timings. Daher erscheint mir mein Fehler nicht so 
kritisch. Aber bei Gelegenheit werde ich es ändern.

Bitte aber mehr solcher Anmerkungen! Nur so kann der Code besser werden!

von Thomas (Gast)


Lesenswert?

nun ich hab sowas auch schon gemacht, allerdings bin ich anders an die 
Sache rangegangen. Ich habe den Treiber durch einen guten Disassembler 
geschickt der die API Funktionsaufrufe kennt (IDA Pro)
Dann habe ich den Disassembler Output kommentiert.
Im nächsten Schritt habe ich dann mit dem Theycon Treiber die 
Kommunikation nachgebildet. USB Treiber sind in aller Regel nicht sehr 
groß so dass das Disassembler File übersichtlich bleibt.
Trotzdem ist es ein Haufen Arbeit. Zusätzlich braucht man bei dieser 
Vorgehensweise Knowhow wir unter Win Treiber aufgebaut sind.

Thomas

von B. O. (veteran)


Lesenswert?

> allerdings bin ich anders an die Sache rangegangen.

Sehr gute Idee! Hätte ich auch selber darauf kommen können. Bei 
Gelegenheit werde ich das mal ausprobieren.
Allerdings könnte es rechtlich ein Problem geben - was ich hier aber 
nicht weiter diskutieren will!

Gibt es weitere Anmerkungen zu meinem Progrämmchen?

von TomA (Gast)


Lesenswert?

Hallo B. Obachter,

ich benutze libusb als Treiber und bediene mich seiner Funktionen in 
meiner Software. Als Beispiel hier ein Auszug der Software für einen 
ISP-Programmer für MCS51 und AVR. Die ID's sind nicht wirklich 0, ich 
habe sie nur für diesen Text zu 0 gesetzt. Die Funktionsaufrufe sind den 
Aufrufen der Firmware im eigenentwickelten Programmiergerät angepasst, 
also ähnlich wie bei deinem Gerät. Programmiert ist es, als 
Konsolenprogramm für Win32/64, mit Visual-C++ Express.
Dem Projekt wird die Bibliothek "libusb.lib" hinzugefügt. Wie man dabei 
vorgeht, habe ich mir aus der libusb-Doku/Software und den 
mitgelieferten Beispielen erarbeitet.

Auszug aus meinem C++ Programm:

#include "USB\\lusb0_usb.h"     // für libusb
#include "USB\\requests.h"      // für Funktionsaufrufe

#define MY_VID 0x0000   // Device vendor and product id.
#define MY_PID 0x0000

#define UsbWrite (USB_TYPE_VENDOR | USB_RECIP_OTHER | USB_ENDPOINT_OUT) 
// Device -> Host
#define UsbRead (USB_TYPE_VENDOR | USB_RECIP_OTHER | USB_ENDPOINT_IN) // 
Device <- Host

//--- Aus einer libusb-Beispieldatei kopiert ----------
usb_dev_handle *dev = NULL; /* the device handle */

innerhalb main()

//--- Aus einer libusb-Beispieldatei kopiert ----------
usb_dev_handle *open_dev(void)
{ struct usb_bus *bus;
  struct usb_device *dev;

  for (bus = usb_get_busses(); bus; bus = bus->next)
  { for (dev = bus->devices; dev; dev = dev->next)
    { if (dev->descriptor.idVendor == MY_VID
       & dev->descriptor.idProduct == MY_PID)
      {  return usb_open(dev);
      }
    }
  }
  return NULL;
}

//--- USB initialisieren --------------------------------
  { usb_init(); /* initialize the library */
    usb_find_busses(); /* find all busses */
    usb_find_devices(); /* find all connected devices */
    if(!(dev = open_dev()))
     Beenden(USBInitFehler);
  }

//--- USB Funktionsaufruf -------------------------------
    SendToDriver(UsbWrite,VRQ_ENA_PROGR,0,0,&cVar,1);    // Programmer 
enable

//--- Funktionsdefinition -------------------------------
//int SendToDriver(CMD_READ_BYTE,wWord1,0,&bVar,1); // Byte lesen
int SendToDriver (BYTE bDir,WORD wCommand,WORD wWrd1,WORD wWrd2,char 
*cPtr,BYTE bCount)
{
  return(usb_control_msg(dev,bDir,wCommand,wWrd1,wWrd2,cPtr,bCount, 
5000));
}

*************************************************************
Auszug aus lusb0_usb.h (Bestandteil libusb):

int usb_control_msg(usb_dev_handle *dev, int requesttype, int request,
                        int value, int index, char *bytes, int size,
                        int timeout);

*************************************************************
Auszug aus requests.h (selbstdefiniert):

//---- Benutzerrequests 
--------------------------------------------------
#define VRQ_READWRITE    3  // Benutzer-Statusrequest lesen/schreiben
#define VRQ_WRITE_BUFFER  5  // Bis 8Byte über Puffer schreiben
#define VRQ_READ_BUFFER    6  // Bis 8Byte über Puffer lesen
#define VRQ_SET_SPEED    7  // Geschwindigkeit für SPI-Takt ändern
#define VRQ_DIS_PROGR    8  // Programmiergerät sperren
#define VRQ_ENA_PROGR    9  // Programmiergerät freigeben

Ich hoffe das hilft dir ein wenig weiter.
Gruß. Tom

von B. O. (veteran)


Lesenswert?

> Als Beispiel hier ein Auszug der Software für einen
> ISP-Programmer für MCS51 und AVR.

Mein Code sieht für die Pipe 0 fast genauso aus. Vermutlich hatten wir 
die gleichen Beispiele oder man macht es einfach so.
Interessanter wird das Ganze bei munteren Wechsel der Pipes mit 
bulk-Transfer.

Meine Frage nach Anmerkungen bezieht sich auch weniger auf die 
Low-Level-Kommunikation (die läuft ja auch bei mir anstandslos), sondern
auf die Programmstrukturen darüber.

von B. O. (veteran)


Angehängte Dateien:

Lesenswert?

Lange Zeit ist hier nichts passiert. Ich bin immer noch an Antworten auf 
meine ersten Fragen interessiert.
- Ich stelle zwar fest, dass das Interesse an meinem Treiber hier nicht 
besonders groß ist. Doch habe ich die Hoffnung nicht aufgegeben, dass 
sich irgendwann jemand freut, wenn er das hier findet.
- Hilfe beim Weiterentwickeln ist immer noch gerne willkommen.
- Ich habe auch immer noch nicht die Stelle ins Netz gefunden, an die 
mein Treiber besser hinpasst.

Darüber hinaus gibt es Neuigkeiten von meiner Seite, die zu diesem 
Thread passen:

Zusätzlich zum Laserinterferometer ML10 mit DX10-Interface aus 2007 habe 
ich jetzt das aktuelle (2015) Laserinterferometer XL-80 mit angebunden. 
Als Entwicklungsumgebung nutze ich weiterhin MinGW, libusb-win32 als 
Windows-Implementation der libusb-0.1 API und libusb0.sys als Windows 
Kerneltreiber. Es wird KEINERLEI Software/DLL vom Hersteller verwendet!
Mit meinem Frontend lassen sich wahlweise beide Lasertypen gleichartig 
ansteuern, obwohl sie intern völlig anders angesteuert werden.
Sogar ein Parallelbetrieb mit beiden Lasern gleichzeitig an einem PC ist 
möglich!
Bisher ist beim XL-80 nur der 5kHz Takt freigeschaltet. Die 
Datenstruktur für 50kHz ist aber im Code dokumentiert.

Die Struktur meines ersten hier veröffentlichten Treibers hatte mich 
nicht zufriedengestellt. Er funktionierte zwar, aber der Code war viel 
zu kompliziert, da ich versucht hatte 1:1 den Datenverkehr der 
Herstellersoftware nachzubilden.
Inzwischen habe ich den Datenverkehr drastisch "entschlackt". Damit ist 
der Code sehr viel einfacher, kompakter und übersichtlicher geworden.

Es gibt ein allgemeines C-File (reni_v0.77.c), das die 
Bedienerschnittstelle, die Mess-Schleife und die Werte-Ausgabe enthält. 
Zwei weitere C-Files (dx10_v0.77.c und xl80_v0.77.c) bilden die 
Schnittstellen zu den beiden unterstützten Lasern.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

B. Obachter schrieb:
> Ich stelle zwar fest, dass das Interesse an meinem Treiber hier nicht
> besonders groß ist.

Das ist nicht des Treibers Schuld, sondern liegt an der exotischen damit 
anzusteuernden Hardware. Wer, der diese Hardware nicht hat, kann etwas 
mit Deinem Treiber anfangen?

von B. O. (veteran)


Lesenswert?

Sollte ich deshalb meine Ergebnisse etwa nicht posten?
Wie ich direkt nach dem Zitat gesagt hatte, habe ich die Hoffnung immer 
noch nicht aufgegeben, dass sich irgendwann jemand freut, wenn er das 
hier findet.

Auch ich habe mich über viele Hilfen gefreut, die mir erst ermöglicht 
haben einen USB-Treiber zu schreiben. Da ich mit Treibern aber keine 
Erfahrung habe, würde ich mir einen Austausch dazu wünschen...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

B. Obachter schrieb:
> Sollte ich deshalb meine Ergebnisse etwa nicht posten?

Nein, das ist ein Missverständnis. Es ist gut und begrüßenswert, daß Du 
Deine Ergebnisse hier gepostet hast; ich hatte nur versucht, zu 
erklären, warum die Resonanz so gering ist. Verzeih, wenn das zu sehr 
wie negative Kritik geklungen haben sollte - das ist damit nicht gemeint 
gewesen.

von B. O. (veteran)


Lesenswert?

Alles klar!

Wenn man sich sehr über weiterhelfende Informationen im Netz freut, 
möchte man irgendwann auch wieder etwas zurückgeben, auch wenn es nicht 
der Mainstream ist.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.