Note that there are some explanatory texts on larger screens.

plurals
  1. PORetrieving buffer/packet/payload sizes for USB serial write transfer in userspace Linux C code
    text
    copied!<p>Apologies in advance I won't be able to accept an answer here immediately - just thought I'd like to jot this down, while I have the problem...</p> <p>In brief: I can observe three different buffer sizes, when I initiate a write to an usb-serial port using user-space C code under Linux - and the problem is, I would like to retrieve all these sizes from the user-space C code itself.</p> <hr> <p>Let's say, I have an Arduino Duemillanove, with an FTDI FT232 chip - programmed to read incoming bytes from usb/serial connection from PC, and discard them. When I plug in this device in the system (did this on Ubunty 11.04 Natty), I can observe the following via <code>tail -f /var/log/syslog</code>:</p> <pre><code>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 ... </code></pre> <p>This tells me first that the drivers (kernel modules) <code>usbserial</code> and <code>ftdi_sio</code> have been hooked/loaded to handle the device; these create a file (device node) called <code>/dev/ttyUSB0</code> - essentially a serial port from the OS perspective. It also tells me that there is a <code>MaxPacketSize</code> of 64 bytes attributed to the device's endpoints. I can get the MaxPacketSize also by querying via <code>lsusb</code>:</p> <pre><code>$ 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 </code></pre> <p>Now, let's say I want to write to the device node <code>/dev/ttyUSB0</code> using the following C program, <strong><code>testusw.c</code></strong>:</p> <pre><code>#include &lt;stdio.h&gt; /* Standard input/output definitions */ #include &lt;string.h&gt; /* String function definitions */ #include &lt;unistd.h&gt; /* UNIX standard function definitions */ #include &lt;fcntl.h&gt; /* File control definitions */ #include &lt;errno.h&gt; /* Error number definitions */ #include &lt;termios.h&gt; /* 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&lt;20000; i++) { memcpy(&amp;writeData[5*i], &amp;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 &lt; 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; } </code></pre> <p>This program deliberately writes a big chunk of data in one call. To see what is happening, first let's capture USB URB requests via Linux's <code>usbmon</code> facility - so in one terminal, we run:</p> <pre><code>$ sudo cat /sys/kernel/debug/usb/usbmon/2u &gt; testusw.2u.mon </code></pre> <p>... and in another terminal, after compiling and running testusw, we obtain:</p> <pre><code>$ gcc -o testusw -Wall -g testusw.c $ ./testusw /dev/ttyUSB0 //ª»ÌÝþª»ÌÝþ// Writing 100000 bytes bytes written: 4608 $ </code></pre> <p>(Note that the <code>testusw</code> call above will likely reset the Arduino). After <code>testusw</code>, we can go back to the first terminal, and interrupt the <code>cat</code> process with <kbd>CTRL</kbd>+<kbd>C</kbd>; we are left with a logfile, <code>testusw.2u.mon</code>. We can open this logfile with <a href="http://vusb-analyzer.sourceforge.net/tutorial.html" rel="nofollow noreferrer">Virtual USB Analyzer</a>:</p> <pre><code>$ ./vusb-analyzer testusw.2u.mon </code></pre> <p>... and obtain the following visualization:</p> <p><a href="https://i.stack.imgur.com/qDFsR.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/qDFsR.png" alt="vusb-analyzer.png"></a></p> <p>Note that there are 2*9 = 18 URB requests shown for "EP2 OUT" that perform the writing, carrying 0x0100 = 256 bytes each; so in total, 18*256 = 4608 bytes were written - as reported by "bytes written" by <code>testusw</code> above. Also, ignore the data on EP1 IN (that is some junk my Arduino code is sending - which ends with a "Status: -2" error).</p> <hr> <p>Thus, I can observe the following:</p> <ul> <li>From the C program, I initiate a write of 100000 bytes</li> <li>As a result, only <strong><code>4608</code></strong> bytes are written - effectively acting as a first buffer size</li> <li><code>usbmon</code> then reports this chunk is sequenced into 18 URB requests of <strong><code>256</code></strong> bytes each</li> <li>finally, MaxPacketSize tells me that each URB request is (probably) seqenced into (four) packets of <strong><code>64</code></strong> bytes on USB wire</li> </ul> <p>In effect, I have three buffer sizes: <code>4608</code>, <code>256</code> and <code>64</code> bytes; similar to what is mentioned in <a href="http://www.tldp.org/HOWTO/Serial-HOWTO-4.html#ss4.7" rel="nofollow noreferrer">Serial HOWTO: Serial Port Basics: 4.7 Data Flow Path; Buffers</a>:</p> <pre><code>application 8k-byte 16-byte 1k-byte tele- BROWSER ------- MEMORY -------- FIFO --------- MODEM -------- phone program buffer buffer buffer line </code></pre> <p>So, my question is: how can these buffer sizes be retrieved from the userspace C code itself - however, only from the device node path <code>/dev/ttyUSB0</code> as the only input parameter?</p> <p>I would be OK with running external programs via a system <code>popen</code> command, and parsing the output. For instance, I could obtain MaxPacketSize via <code>lsusb -v -d 0403:6001 | grep MaxPacketSize</code> - but that requires vendor/product ID, and I don't know how to obtain that, if only piece of information is the device node path <code>/dev/ttyUSB0</code>.</p> <p>Given that <code>/dev/ttyUSB0</code> is essentially treated as a serial port, I thought querying via <code>stty</code> would provide something - however, I cannot see anything related to buffer sizes there:</p> <pre><code>$ stty -a -F /dev/ttyUSB0 speed 115200 baud; rows 0; columns 0; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^A; eol = &lt;undef&gt;; eol2 = &lt;undef&gt;; swtch = &lt;undef&gt;; 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 </code></pre> <p>I also know I can use <code>udevadm</code> to query for data related to the device node path <code>/dev/ttyUSB0</code>:</p> <pre><code>$ 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 </code></pre> <p>... but again, I cannot see much related to the encountered buffer sizes.</p> <p>To wrap this up, the question again: can I retrieve the encountered buffer sizes related to the usb-serial write transfer from a user-space C application; and if so - how?</p> <p>Many thanks in advance for any answers,<br> Cheers!</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload