Abrufen von Puffer-/Paket-/Payload-Größen für die serielle USB-Schreibübertragung im Userspace-Linux-C-Code

Lesezeit: 8 Minuten

Entschuldigung im Voraus, ich kann hier nicht sofort eine Antwort annehmen – dachte nur, ich möchte das aufschreiben, während ich das Problem habe …

Kurz gesagt: Ich kann drei verschiedene Puffergrößen beobachten, wenn ich unter Linux mit User-Space-C-Code einen Schreibvorgang auf einen seriellen USB-Port initiiere – und das Problem ist, dass ich alle diese Größen aus dem User-Space C abrufen möchte selbst codieren.


Nehmen wir an, ich habe einen Arduino Duemillanove mit einem FTDI FT232-Chip, der so programmiert ist, dass er eingehende Bytes von der USB-/seriellen Verbindung vom PC liest und verwirft. Wenn ich dieses Gerät an das System anschließe (unter Ubunty 11.04 Natty), kann ich Folgendes über beobachten tail -f /var/log/syslog:

Mar 21 08:05:05 mypc kernel: [  679.197982] usbserial: USB Serial Driver core
Mar 21 08:05:05 mypc kernel: [  679.223954] USB Serial support registered for FTDI USB Serial Device
Mar 21 08:05:05 mypc kernel: [  679.227354] ftdi_sio 2-2:1.0: FTDI USB Serial Device converter detected
Mar 21 08:05:05 mypc kernel: [  679.227633] usb 2-2: Detected FT232RL
Mar 21 08:05:05 mypc kernel: [  679.227644] usb 2-2: Number of endpoints 2
Mar 21 08:05:05 mypc kernel: [  679.227652] usb 2-2: Endpoint 1 MaxPacketSize 64
Mar 21 08:05:05 mypc kernel: [  679.227660] usb 2-2: Endpoint 2 MaxPacketSize 64
Mar 21 08:05:05 mypc kernel: [  679.227667] usb 2-2: Setting MaxPacketSize 64
...

Das sagt mir zuerst, dass die Treiber (Kernel-Module) usbserial und ftdi_sio eingehakt/geladen wurden, um das Gerät zu handhaben; diese erstellen eine Datei (Geräteknoten) namens /dev/ttyUSB0 – im Wesentlichen eine serielle Schnittstelle aus Sicht des Betriebssystems. Es sagt mir auch, dass es eine gibt MaxPacketSize von 64 Bytes, die den Endpunkten des Geräts zugeordnet sind. Ich kann die MaxPacketSize auch durch Abfragen über erhalten lsusb:

$ lsusb | grep FT
Bus 002 Device 002: ID 0403:6001 Future Technology Devices International, Ltd FT232 USB-Serial (UART) IC
$ lsusb -t | grep -B1 ft
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
    |__ Port 2: Dev 2, If 0, Class=vend., Driver=ftdi_sio, 12M
$ sudo lsusb -v -d 0403:6001 | grep 'bEndpointAddress\|wMaxPacketSize\|idVendor\|idProduct'
  idVendor           0x0403 Future Technology Devices International, Ltd
  idProduct          0x6001 FT232 USB-Serial (UART) IC
        bEndpointAddress     0x81  EP 1 IN
        wMaxPacketSize     0x0040  1x 64 bytes
        bEndpointAddress     0x02  EP 2 OUT
        wMaxPacketSize     0x0040  1x 64 bytes

Nehmen wir nun an, ich möchte in den Geräteknoten schreiben /dev/ttyUSB0 mit dem folgenden C-Programm, testusw.c:

#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */

// testusw.c
// build with: gcc -o testusw -Wall -g testusw.c

int main( int argc, char **argv ) {

  char *serportdevfile;
  int serport_fd;
  char writeData[20000*5]; //100000 bytes data
  unsigned char snippet[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xFE};
  int i;
  int bytesWritten;

  if( argc != 2 ) {
    fprintf(stdout, "Usage:\n");
    fprintf(stdout, "%s port baudrate file/string\n", argv[0]);
    return 1;
  }

  //populate write data
  for (i=0; i<20000; i++) {
    memcpy(&writeData[5*i], &snippet[0], 5);
  }
  // for strlen, fix (after) last byte to 0
  writeData[20000*5] = 0x00;

  // show writeData - truncate to 10 bytes (.10):
  fprintf(stdout, "//%.10s//\n", writeData);

  serportdevfile = argv[1];
  serport_fd = open( serportdevfile, O_RDWR | O_NOCTTY | O_NONBLOCK );
  if ( serport_fd < 0 ) { perror(serportdevfile); return 1; }

  // do a write:
  fprintf(stdout, "Writing %d bytes\n", strlen(writeData));
  bytesWritten = write( serport_fd, writeData, strlen(writeData) );
  fprintf(stdout, " bytes written: %d \n", bytesWritten);

  return 0;
}

Dieses Programm schreibt absichtlich einen großen Datenblock in einem Aufruf. Um zu sehen, was passiert, erfassen wir zunächst USB-URB-Anforderungen über Linux usbmon Anlage – in einem Terminal führen wir also Folgendes aus:

$ sudo cat /sys/kernel/debug/usb/usbmon/2u > testusw.2u.mon

… und in einem anderen Terminal erhalten wir nach dem Kompilieren und Ausführen von testusw:

$ gcc -o testusw -Wall -g testusw.c
$ ./testusw /dev/ttyUSB0
//ª»ÌÝþª»ÌÝþ//
Writing 100000 bytes
 bytes written: 4608
$

(Notiere dass der testusw Der obige Aufruf wird wahrscheinlich den Arduino zurücksetzen). Nach testuswkönnen wir zurück zum ersten Terminal gehen und das unterbrechen cat verarbeiten mit STRG+C; Uns bleibt eine Logdatei, testusw.2u.mon. Diese Logdatei können wir mit öffnen Virtueller USB-Analyzer:

$ ./vusb-analyzer testusw.2u.mon

… und erhalten folgende Visualisierung:

vusb-analyzer.png

Beachten Sie, dass 2*9 = 18 URB-Anforderungen für “EP2 OUT” gezeigt werden, die das Schreiben ausführen, wobei jede 0x0100 = 256 Bytes trägt; insgesamt wurden also 18*256 = 4608 bytes geschrieben – wie von “bytes written” von berichtet testusw Oben. Ignorieren Sie auch die Daten auf EP1 IN (das ist ein Müll, den mein Arduino-Code sendet – was mit einem “Status: -2” -Fehler endet).


Somit kann ich folgendes beobachten:

  • Vom C-Programm initiiere ich einen Schreibvorgang von 100000 Bytes
  • Als Ergebnis nur 4608 Bytes geschrieben werden – effektiv als erste Puffergröße wirkend
  • usbmon dann berichtet, dass dieser Chunk in 18 URB-Anfragen sequenziert ist 256 Byte jeweils
  • Schließlich sagt mir MaxPacketSize, dass jede URB-Anfrage (wahrscheinlich) in (vier) Pakete sequenziert ist 64 Bytes auf USB-Kabel

Tatsächlich habe ich drei Puffergrößen: 4608, 256 und 64 Byte; ähnlich wie in erwähnt Serial HOWTO: Grundlagen der seriellen Schnittstelle: 4.7 Datenflusspfad; Puffer:

application     8k-byte         16-byte        1k-byte        tele-
BROWSER ------- MEMORY -------- FIFO --------- MODEM -------- phone
program         buffer          buffer         buffer         line

Meine Frage lautet also: Wie können diese Puffergrößen aus dem C-Code des Benutzerbereichs selbst abgerufen werden – jedoch nur aus dem Geräteknotenpfad /dev/ttyUSB0 als einziger Eingabeparameter?

Ich wäre damit einverstanden, externe Programme über ein System auszuführen popen Befehl und parsen der Ausgabe. Zum Beispiel könnte ich MaxPacketSize über erhalten lsusb -v -d 0403:6001 | grep MaxPacketSize – aber dafür ist eine Hersteller-/Produkt-ID erforderlich, und ich weiß nicht, wie ich diese erhalten soll, wenn es sich bei der einzigen Information um den Geräteknotenpfad handelt /dev/ttyUSB0.

Angesichts dessen /dev/ttyUSB0 wird im Wesentlichen als serielle Schnittstelle behandelt, dachte ich, Abfragen über stty würde etwas liefern – jedoch kann ich dort nichts im Zusammenhang mit Puffergrößen sehen:

$ stty -a -F /dev/ttyUSB0
speed 115200 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^A; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
-echoctl -echoke

Ich weiß auch, dass ich es verwenden kann udevadm um Daten abzufragen, die sich auf den Geräteknotenpfad beziehen /dev/ttyUSB0:

$ udevadm info --query=all --name=/dev/ttyUSB0
P: /devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0/ttyUSB0/tty/ttyUSB0
N: ttyUSB0
S: serial/by-path/pci-0000:00:1d.0-usb-0:2:1.0-port0
S: serial/by-id/usb-FTDI_FT232R_USB_UART_A9007OH3-if00-port0
E: UDEV_LOG=3
E: DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0/ttyUSB0/tty/ttyUSB0
E: MAJOR=188
E: MINOR=0
E: DEVNAME=/dev/ttyUSB0
E: SUBSYSTEM=tty
E: ID_PORT=0
E: ID_PATH=pci-0000:00:1d.0-usb-0:2:1.0
E: ID_VENDOR=FTDI
E: ID_VENDOR_ENC=FTDI
E: ID_VENDOR_ID=0403
E: ID_MODEL=FT232R_USB_UART
E: ID_MODEL_ENC=FT232R\x20USB\x20UART
E: ID_MODEL_ID=6001
E: ID_REVISION=0600
E: ID_SERIAL=FTDI_FT232R_USB_UART_A9007OH3
E: ID_SERIAL_SHORT=A9007OH3
E: ID_TYPE=generic
E: ID_BUS=usb
E: ID_USB_INTERFACES=:ffffff:
E: ID_USB_INTERFACE_NUM=00
E: ID_USB_DRIVER=ftdi_sio
E: ID_IFACE=00
E: ID_VENDOR_FROM_DATABASE=Future Technology Devices International, Ltd
E: ID_MODEL_FROM_DATABASE=FT232 USB-Serial (UART) IC
E: ID_MM_CANDIDATE=1
E: DEVLINKS=/dev/serial/by-path/pci-0000:00:1d.0-usb-0:2:1.0-port0 /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A9007OH3-if00-port0

# the below has huge output, so pipe it to `less`
$ udevadm info --attribute-walk --name=/dev/ttyUSB0 | less

… aber auch hier kann ich nicht viel im Zusammenhang mit den gefundenen Puffergrößen sehen.

Zum Abschluss noch einmal die Frage: Kann ich die gefundenen Puffergrößen im Zusammenhang mit der USB-Serial-Write-Übertragung von einer User-Space-C-Anwendung abrufen? und wenn ja – wie?

Vielen Dank im Voraus für eventuelle Antworten,
Prost!

  • Warum müssen Sie die Puffergrößen kennen? Was ist dein Ziel?

    – Hasturkun

    21. März 2013 um 10:45 Uhr

Benutzer-Avatar
pw

Verstehe nicht, warum du das wissen willst. Linux ermöglicht Ihnen die Verwendung von TIOCGSERIAL ioctl zum Abrufen von a struct serial_struct das hat ein xmit_fifo_size aufstellen. Obwohl ich überrascht wäre, wenn sich viele USB-Seriell-Treiber die Mühe machen, dort etwas Sinnvolles zu schreiben.

Benutzer-Avatar
Daniel Rosa

Ich habe mich mit ähnlichen Problemen wie der Frage, die Sie stellen, auseinandergesetzt. Ich habe keine Antwort für Sie, aber eine andere Information, die Sie vielleicht nützlich finden könnten.

Unter Mac OS X können Sie mit ioctl herausfinden, wie viele Zeichen sich derzeit im Puffer befinden. Der folgende Code gibt Ihnen die Figur

uint ioctlBytestInBuffer;
int returnCode = ioctl(fileDescriptor, TIOCOUTQ, &ioctlBytestInBuffer);

Ich habe dies verwendet, um herauszufinden, wann eine große Dateiübertragung über eine serielle Leitung abgeschlossen wurde (das Mikro verwendet eine Software-Flusskontrolle, sodass die Übertragungsrate schwer vorherzusagen ist).

Diese Methode funktioniert ziemlich gut, ist aber nicht perfekt. Ich bin mir nicht sicher, auf welchen Puffer der ioctl-Aufruf zugreifen kann. Wenn der ioctl-Funktionsaufruf einen Wert von 0 Bytes im Puffer zurückgibt, wird die Dateiübertragung noch einige Sekunden länger fortgesetzt. Der USB-Chip in meinem Kabel behauptet, nur einen 128-Byte-Sendepuffer zu haben, der bei meiner Baudrate innerhalb von 0,3 Sekunden geleert sein sollte.

Dies ist ein alter Titel, aber für diejenigen, die sich fragen:
hier verwandte pdf (Über 0453:6001 ic’s)

On page (end of)13 and (start) 14 :
TX Size : 256 Bytes
RX Size : 128 Bytes

Schönen (+gesunden) Tag!

1034460cookie-checkAbrufen von Puffer-/Paket-/Payload-Größen für die serielle USB-Schreibübertragung im Userspace-Linux-C-Code

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy