title: Retrieving buffer/packet/payload sizes for USB serial write transfer in userspace Linux C code
body: <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>