2013-03-21 15 views
16

Scuse in anticipo Non sarò in grado di accettare una risposta qui immediatamente - ho pensato che mi piacerebbe annotarlo, mentre ho il problema ...Recupero dimensioni buffer/pacchetto/carico utile per trasferimento in scrittura seriale USB in spazio utente Linux C code

In breve: posso osservare tre diverse dimensioni del buffer, quando avvio una scrittura su una porta USB-seriale utilizzando il codice C dello spazio utente sotto Linux - e il problema è che vorrei recuperare tutte queste dimensioni dal codice C dello spazio utente stesso.


Diciamo, ho un Arduino Duemillanove, con un chip FTDI FT232 - programmato per leggere byte in arrivo dalla connessione USB/seriale dal PC, e gettarli. Quando collego il dispositivo nel sistema (fatto questo su Ubunty 11.04 Natty), posso osservare quanto segue via 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 
... 

Questo mi dice prima che i driver (moduli del kernel) usbserial e ftdi_sio sono stati agganciati/caricato per gestire il dispositivo; questi creano un file (nodo del dispositivo) chiamato /dev/ttyUSB0 - essenzialmente una porta seriale dal punto di vista del sistema operativo. Mi dice anche che c'è uno MaxPacketSize di 64 byte attribuito agli endpoint del dispositivo. Posso ottenere il MaxPacketSize anche interrogando via 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 

Ora, diciamo che voglio scrivere al nodo del dispositivo /dev/ttyUSB0 utilizzando il seguente programma C, 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; 
} 

Questo programma scrive deliberatamente una grande porzione di dati in una sola chiamata. Per vedere che cosa sta accadendo, prima cerchiamo di catturare le richieste URB USB tramite impianto di Linux usbmon - così in un unico terminale, corriamo:

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

... e in un altro terminale, dopo la compilazione e l'esecuzione testusw, otteniamo:

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

(Si noti che la chiamata sopra riportata probabilmente ripristinerà Arduino). Dopo il testusw, possiamo tornare al primo terminale e interrompere il processo cat con CTRL + C; ci rimane un file di registro, testusw.2u.mon. Possiamo aprire questo file di log con Virtual USB Analyzer:

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

... e avere la seguente visualizzazione:

vusb-analyzer.png

noti che ci sono 2 richieste * 9 = 18 URB mostrati per "EP2 OUT" che esegue la scrittura, trasportando 0x0100 = 256 byte ciascuno; quindi in totale sono stati scritti 18 * 256 = 4608 byte, come riportato dai "byte scritti" sopra il testusw. Inoltre, ignora i dati su EP1 IN (che è un po 'di junk che il mio codice Arduino sta inviando - che termina con un errore "Status: -2").


Così, posso osservare quanto segue:

  • Dal programma C, avvio la procedura di scrittura di 100000 byte
  • Di conseguenza, solo 4608 byte sono scritti - in pratica si comportano come prima dimensione del buffer
  • usbmon quindi segnala che questo blocco viene sequenziato in 18 richieste URB di 256 byte ciascuna
  • infine, MaxPacketSize mi dice che ogni richiesta URB è (probabilmente) seqenced in (quattro) pacchetti di 64 byte sul filo USB

In effetti, ho tre dimensioni del buffer: 4608, 256 e 64 byte; simile a quanto riportato nella Serial HOWTO: Serial Port Basics: 4.7 Data Flow Path; Buffers:

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

Quindi, la mia domanda è: come può queste dimensioni di buffer essere recuperate dal codice userspace C stesso - tuttavia, solo dal percorso del nodo dispositivo /dev/ttyUSB0 come unico parametro di input?

Sarei OK con l'esecuzione di programmi esterni tramite un comando di sistema popen e analizzando l'output. Ad esempio, potrei ottenere MaxPacketSize tramite lsusb -v -d 0403:6001 | grep MaxPacketSize - ma ciò richiede ID fornitore/prodotto e non so come ottenerlo, se solo una parte dell'informazione è il percorso del nodo dispositivo /dev/ttyUSB0.

Dato che /dev/ttyUSB0 è essenzialmente trattata come una porta seriale, ho pensato che l'interrogazione tramite stty fornirebbe qualcosa - tuttavia, non riesco a vedere tutto ciò che riguarda tampone formati lì:

$ 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 

so anche che posso usare udevadm alla query per i dati relativi al percorso del nodo dispositivo /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 

... ma ancora, non riesco a vedere molto in rapporto alle dimensioni del buffer incontrati.

Per riaggiustare, la domanda nuovamente: è possibile recuperare le dimensioni del buffer rilevato relative al trasferimento di scrittura seriale USB da un'applicazione C dello spazio utente; e se sì - come?

Molte grazie in anticipo per qualsiasi risposta,
Cheers!

+1

Perché è necessario conoscere le dimensioni del buffer? Qual è il tuo obiettivo? – Hasturkun

risposta

2

Non capisco perché vorresti saperlo. Linux consente di utilizzare l'ioctl TIOCGSERIAL per recuperare un struct serial_struct che ha un campo xmit_fifo_size. Anche se sarei sorpreso se molti driver seriali USB si preoccupassero di scrivere qualcosa di significativo lì.

1

Sono stato alle prese con problemi simili alla domanda che chiedi. Non ho una risposta per te, ma ho un altro bit di informazione che potresti trovare utile.

Su Mac OS X è possibile utilizzare ioctl per scoprire quanti caratteri sono attualmente nel buffer.Il seguente codice vi darà la cifra

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

Ho utilizzato questo per cercare di trovare quando un grande trasferimento di file ha completato oltre una linea seriale (Il micro utilizza il controllo di flusso software, quindi è difficile prevedere il tasso di di trasferimento).

Questo metodo funziona abbastanza bene ma non è perfetto. Non sono sicuro di quale buffer è in grado di accedere alla chiamata ioctl. Quando la chiamata alla funzione ioctl restituisce un valore di 0 byte nel buffer, il trasferimento del file continua ancora per diversi secondi. Il chip USB nel mio cavo afferma di avere solo un buffer di trasmissione da 128 byte che dovrebbe essere svuotato entro 0,3 di secondo al mio baud rate.