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 testusw
kö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:
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 ist256
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