Recuperación de tamaños de búfer/paquete/carga útil para la transferencia de escritura serial USB en el espacio de usuario Código C de Linux
buffer packet (2)
Disculpas de antemano. No podré aceptar una respuesta aquí inmediatamente. Solo pensé que me gustaría anotar esto, mientras tengo el problema ...
En resumen: puedo observar tres tamaños de búfer diferentes, cuando inicio una escritura en un puerto USB de serie utilizando el código C de espacio de usuario en Linux, y el problema es que me gustaría recuperar todos estos tamaños del espacio C de usuario. código en sí mismo.
Digamos, tengo un Arduino Duemillanove, con un chip FTDI FT232 programado para leer los bytes entrantes de la conexión USB / serie de la PC, y descartarlos. Cuando conecto este dispositivo en el sistema (hice esto en Ubunty 11.04 Natty), puedo observar lo siguiente a través de 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
...
Esto me dice primero que los controladores (módulos del kernel) usbserial
y ftdi_sio
se han enganchado / cargado para manejar el dispositivo; estos crean un archivo (nodo de dispositivo) llamado /dev/ttyUSB0
, esencialmente un puerto serie desde la perspectiva del sistema operativo. También me dice que hay un MaxPacketSize
de 64 bytes atribuido a los puntos finales del dispositivo. También puedo obtener el MaxPacketSize consultando a través de 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
Ahora, digamos que quiero escribir en el nodo del dispositivo /dev/ttyUSB0
utilizando el siguiente programa 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;
}
Este programa escribe deliberadamente una gran parte de los datos en una llamada. Para ver lo que está sucediendo, primero usbmon
las solicitudes URB de USB a través de la instalación usbmon
de Linux, así que en un terminal ejecutamos:
$ sudo cat /sys/kernel/debug/usb/usbmon/2u > testusw.2u.mon
... y en otra terminal, después de compilar y ejecutar testusw, obtenemos:
$ gcc -o testusw -Wall -g testusw.c
$ ./testusw /dev/ttyUSB0
//ª»ÌÝþª»ÌÝþ//
Writing 100000 bytes
bytes written: 4608
$
(Tenga en cuenta que la llamada de testusw
anterior probablemente reiniciará el Arduino). Después de testusw
, podemos volver al primer terminal e interrumpir el proceso cat
con CTRL + C ; Nos quedamos con un archivo de registro, testusw.2u.mon
. Podemos abrir este archivo de registro con Virtual USB Analyzer :
$ ./vusb-analyzer testusw.2u.mon
... y obtener la siguiente visualización:
Tenga en cuenta que se muestran 2 * 9 = 18 solicitudes de URB para "EP2 OUT" que realizan la escritura, con 0x0100 = 256 bytes cada una; por lo tanto, en total, se escribieron 18 * 256 = 4608 bytes, como se indica en "bytes escritos" por testusw
arriba. Además, ignore los datos en EP1 IN (es decir, algo de correo no deseado que está enviando el código de Arduino, que termina con un error "Estado: -2").
Así, puedo observar lo siguiente:
- Desde el programa C, inicio una escritura de 100000 bytes.
- Como resultado, solo se escriben
4608
bytes, actuando efectivamente como un primer tamaño de búfer -
usbmon
luego informa que este fragmento está secuenciado en 18 solicitudes URB de256
bytes cada una - Finalmente, MaxPacketSize me dice que cada solicitud de URB está (probablemente) secuenciada en (cuatro) paquetes de
64
bytes en el cable USB
En efecto, tengo tres tamaños de búfer: 4608
, 256
y 64
bytes; similar a lo que se menciona en el CÓMO serie: Conceptos básicos sobre el puerto serie: 4.7 Ruta de flujo de datos; Buffers :
application 8k-byte 16-byte 1k-byte tele-
BROWSER ------- MEMORY -------- FIFO --------- MODEM -------- phone
program buffer buffer buffer line
Entonces, mi pregunta es: ¿cómo se pueden recuperar estos tamaños de búfer del propio código C del espacio de usuario? Sin embargo, solo de la ruta del nodo del dispositivo /dev/ttyUSB0
como el único parámetro de entrada.
Estaría bien con ejecutar programas externos a través de un comando popen
sistema y analizar la salida. Por ejemplo, podría obtener MaxPacketSize a través de lsusb -v -d 0403:6001 | grep MaxPacketSize
lsusb -v -d 0403:6001 | grep MaxPacketSize
- pero eso requiere una identificación del proveedor / producto, y no sé cómo obtener eso, si solo la información es la ruta del nodo del dispositivo /dev/ttyUSB0
.
Dado que /dev/ttyUSB0
se trata esencialmente como un puerto serie, pensé que la consulta a través de stty
proporcionaría algo; sin embargo, no puedo ver nada relacionado con el tamaño del búfer allí:
$ 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
También sé que puedo usar udevadm
para consultar datos relacionados con la ruta del nodo del 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
... pero nuevamente, no puedo ver mucho relacionado con los tamaños de búfer encontrados.
Para resumir esto, nuevamente la pregunta: ¿puedo recuperar los tamaños de búfer encontrados relacionados con la transferencia de escritura usb-serial desde una aplicación C de espacio de usuario; Y si es así, ¿cómo?
Muchas gracias de antemano por cualquier respuesta,
¡Aclamaciones!
He estado lidiando con problemas similares a la pregunta que haces. No tengo una respuesta para usted, pero tengo otra información que puede serle útil.
En Mac OS X puede usar ioctl para averiguar cuántos caracteres hay actualmente en el búfer. El siguiente código te dará la figura.
uint ioctlBytestInBuffer;
int returnCode = ioctl(fileDescriptor, TIOCOUTQ, &ioctlBytestInBuffer);
He estado usando esto para intentar encontrar cuando se ha completado una transferencia de archivos grandes a través de una línea serie (el micro usa el control de flujo del software, por lo que es difícil predecir la tasa de transferencia).
Este método funciona razonablemente bien, pero no es perfecto. No estoy seguro de a qué búfer puede acceder la llamada ioctl. Cuando la llamada a la función ioctl devuelve un valor de 0 bytes en el búfer, la transferencia de archivos continúa por varios segundos más. El chip USB en mi cable dice que solo tiene un búfer de transmisión de 128 bytes que debe vaciarse a 0.3 de segundo a mi velocidad de transmisión.
No veo por qué querría saber esto. Linux le permite usar el ioctl TIOCGSERIAL
para recuperar una struct serial_struct
que tiene un campo xmit_fifo_size
. Aunque me sorprendería si muchos controladores serie USB se molestan en escribir algo significativo allí.