mikrocontroller.net

Forum: PC-Programmierung Linux USB-Gadget mit FunctionFS: Problem mit Auslesen der Endpoints


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Markus D. (mowlwurf)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich versuche mich in letzter Zeit daran, ein SOM mit einem iMX7 als 
USB-Device an einem Windows 10-Rechner mit den WinUSB-Treibern 
anzumelden.
Betrieben wird das SOM mit einem Yocto-Linux (Angstrom) mit 
Kernelversion 4.9.166. GadgetFS ist im Kernel aktiviert und libcomposite 
wurde geladen.

Bein bisheriger Stand ist, dass ich mit libusbgx das Gadget einrichten, 
das FunctionFS mounten und schlussendlich Endpoint 0 mit den 
USB-Deskriptoren beschreiben kann. Das SOM meldet sich erfolgreich unter 
Windows mit den Endpoints 0 und 1 an und der WinUSB-Treiber wird 
draufgeschaltet.

So weit so schön.

Mein Problem ist nun das Auslesen der Endpoints. Dazu nutze ich jeweils 
den Befehl poll(), nachdem ich mit open() den jeweiligen Endpoint 
geöffnet habe.

Endpoint 0 scheint zu funktionieren, da ich beim An-/Abstecken des 
USB-Kabels die Befehle für "Bind", "Enable", "Disable" usw. empfangen 
kann.

Endpoint 1 zickt da schon mehr rum. Ich komme da sofort aus dem poll() 
zurück, obwohl ich auf dem PC keine Applikation am Laufen hab, die den 
Endpoint 1 beschreibt. Eventuell schickt Windows irgendwelche Daten.
Wenn ich nun versuche, Daten aus dem Endpoint 1 zu lesen, bleibt mein 
Programm im read() hängen und kommt erst mit einer Fehlermeldung zurück, 
wenn ich das USB-Kabel wieder abziehe.

In einem Demo-Bespiel in den Kernel-Quellen habe ich ein Beispiel für 
das FunctionFS gefunden 
(https://github.com/torvalds/linux/blob/master/tools/usb/ffs-test.c).
Dort wird in Zeile 400 mit einem ioctl der Status des Endpoints geprüft. 
Wenn ich das bei meinen Endpoints versuche, kommen die folgenden 
Fehlermeldungen:
    Endpoint 0: Inappropriate ioctl for device
    Endpoint 1: Operation not supported

Irgendwas scheint also mit meinen Endpoints noch nicht zu stimmen. Mir 
gehen aber die Ideen aus, wo ich noch ansetzen könnte.

Meinen Quellcode habe ich angehängt, vielleicht hat einer von Euch eine 
zündende Idee und kann mir einen kleinen Schups in die richtige Richtung 
geben.

Gruß,
Mowlwurf

: Bearbeitet durch User
Autor: DPA (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Ich hab da jetzt nichts ausprobiert, aber beim poll solltest du auf 
jeden fall vor dem read prüfen, ob .revents&POLLIN gesetzt ist. 
Ausserdem solltest du jeweils noch die fälle .revents&POLLERR und 
.revents&POLLNVAL behandeln, die kann es immer geben, egal was in 
.events gesetzt wurde.

Autor: Markus D. (mowlwurf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guter Hinweis, danke.

Ist aber leider nicht hilfreich für mein Problem.
Gerade noch einmal geprüft: wann immer das poll() zurückkommt, ist nur 
POLLIN als Event gesetzt. Das darauffolgende read() auf Endpoint 1 geht 
dann schief ...

Autor: DPA (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Ansonsten sehe ich sonst keinen Fehler bei der Verwendung von poll, der 
das Verhalten erklären könnte.

Ich frage mich aber, ob die epX überhaupt poll unterstützen. Bei ep0 ist 
es da:
https://elixir.bootlin.com/linux/v4.2/source/drivers/usb/gadget/function/f_fs.c#L627

Bei den restlichen epX nicht:
https://elixir.bootlin.com/linux/v4.2/source/drivers/usb/gadget/function/f_fs.c#L1061

Jedoch kenne ich mich nicht mit linux aio (.read_iter) aus, es kann 
sein, dass das dort anders gemacht wird. Beim Code von poll sehe ich da 
zwar kein spezielles Handling. Wenn ich den Code vom Poll syscall
https://elixir.bootlin.com/linux/v4.2/source/fs/select.c#L750
richtig verstehe, dann macht das quasi:
mask = DEFAULT_POLLMASK; // mask = POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM
mask &= pollfd->events | POLLERR | POLLHUP; // pollfd->events=POLLIN; mask=POLLIN;
pollfd->revents = mask; // pollfd->revents=POLLIN

Das würde dann auch erklären, warum es sofort mit POLLIN zurückkehrt. 
Das fühlt sich zwar irgendwie falsch ein, aber naja...

Wenn ich das also alles richtig verstehe, bleibt dir bei allen epX, 
abgesehen von ep0 nichts anderes übrig, als in nem thread blocking read 
zu machen, oder linux aio zu verwenden. Das sollte ja auch keine 
grösseres Problem sein, du hast ja sowieso für jedes epX einen eigenen 
Thread, und man kann das read normalerweise immernoch mit einem Signal 
unerbrechen, oder gar den thread gleich abschiessen.

Autor: Markus D. (mowlwurf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Deine Erklärung klingt plausibel.

In dem Beispiel aus den Kernel-Quellen, das ich bereits oben verlinkt 
habe 
(https://github.com/torvalds/linux/blob/master/tools/usb/ffs-test.c), 
wird auch ohne poll() oder select() einfach ein read() gemacht und der 
jeweilige Thread damit geblockt.

Was mir aber noch nicht klar ist, warum ioctl() auf meine Endpoints 
diese Fehlerausgaben zurückgibt. Muss eventuell erst eine Applikation 
diese Endpoints geöffnet haben?

Autor: DPA (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
ep0 hat eine andere Aufgabe als die restlichen epX. Der Fehler "Endpoint 
0: Inappropriate ioctl for device" ist, weil es den IOCTL für ep0 nicht 
gibt. Im beispiel code wird ep0 auch explizit von dem ioctl 
ausgeschlossen: 
https://github.com/torvalds/linux/blob/master/tools/usb/ffs-test.c#L398

Bei der anderen Fehlermeldung kenne ich mich mit FunctionFS zu wenig 
aus, um da etwas dazu sagen zu können.

Autor: Markus D. (mowlwurf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich werd morgen versuchen, dass die PC-Software sich mit dem Device 
verbindet und Endpoint 1 beschreibt. Mal sehen, wie sich dann das read() 
verhält und was ioctl() zurück gibt.

Auf jeden Fall schon mal vielen Dank für deine Hilfe!

Autor: Gerd E. (robberknight)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mal ne ganz andere Idee:

musst Du unbedingt direkt die USB-Transfers selber behandeln?

Könntest Du nicht eine Standard-Geräteklasse verwenden, für die es 
sowohl auf dem Windows-Host, als auch auf dem Linux-Gadget, bereits 
fertige Treiber gibt?

Ich denke da z.B. an das CDC ACM (serieller Port).

Auf der Linux-Seite kannst Du dann einfach einen normalen tty öffnen und 
darüber die Daten übertragen, unter Windows den entsprechenden Com-Port. 
Das ganze Handling von USB-Endpoints etc. fällt auf beiden Seiten weg.

Autor: Markus D. (mowlwurf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Diese Geräteklasse hab ich mir natürlich schon einmal angeguckt. Da wird 
mir aber die erzielbare Datenrate zu niedrig sein.

Autor: Markus D. (mowlwurf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Für das ioctl der epX habe ich herausgefunden, dass mein SOM/SOC 
entweder keinen FIFO nutzt oder der Status des FIFOs nicht ermittelt 
werden kann:
https://elixir.bootlin.com/linux/v4.2/source/include/linux/usb/gadget.h#L441

Das read() meiner Endpoints epX verhält sich nun allerdings so, dass es 
nur aller 4 Pakete zurückkommt. Dabei ist es unerheblich, ob ich 4-mal 
dasselbe Paket schicke oder nur 1-mal und anschließend 3 Zero-Pakete.

Ein anderes SOM mit einem Snapdragon 410 von Qualcomm zeigt dasselbe 
Verhalten.

Von einem ehemaligen Kollegen weiß ich, dass man dieses Verhalten 
verändern kann. Kann ihn dummerweise nicht mehr fragen ...

Weiß einer von Euch da mehr?

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.