Retrieving buffer/packet/payload sizes for USB serial write transfer in userspace Linux C code
Asked Answered
Z

3

17

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...

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.


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 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
...

This tells me first that the drivers (kernel modules) usbserial and ftdi_sio have been hooked/loaded to handle the device; these create a file (device node) called /dev/ttyUSB0 - essentially a serial port from the OS perspective. It also tells me that there is a MaxPacketSize of 64 bytes attributed to the device's endpoints. I can get the MaxPacketSize also by querying 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

Now, let's say I want to write to the device node /dev/ttyUSB0 using the following C program, 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;
}

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 usbmon facility - so in one terminal, we run:

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

... and in another terminal, after compiling and running testusw, we obtain:

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

(Note that the testusw call above will likely reset the Arduino). After testusw, we can go back to the first terminal, and interrupt the cat process with CTRL+C; we are left with a logfile, testusw.2u.mon. We can open this logfile with Virtual USB Analyzer:

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

... and obtain the following visualization:

vusb-analyzer.png

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 testusw above. Also, ignore the data on EP1 IN (that is some junk my Arduino code is sending - which ends with a "Status: -2" error).


Thus, I can observe the following:

  • From the C program, I initiate a write of 100000 bytes
  • As a result, only 4608 bytes are written - effectively acting as a first buffer size
  • usbmon then reports this chunk is sequenced into 18 URB requests of 256 bytes each
  • finally, MaxPacketSize tells me that each URB request is (probably) seqenced into (four) packets of 64 bytes on USB wire

In effect, I have three buffer sizes: 4608, 256 and 64 bytes; similar to what is mentioned in 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

So, my question is: how can these buffer sizes be retrieved from the userspace C code itself - however, only from the device node path /dev/ttyUSB0 as the only input parameter?

I would be OK with running external programs via a system popen command, and parsing the output. For instance, I could obtain MaxPacketSize via lsusb -v -d 0403:6001 | grep MaxPacketSize - 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 /dev/ttyUSB0.

Given that /dev/ttyUSB0 is essentially treated as a serial port, I thought querying via stty would provide something - however, I cannot see anything related to buffer sizes there:

$ 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

I also know I can use udevadm to query for data related to the device node path /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

... but again, I cannot see much related to the encountered buffer sizes.

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?

Many thanks in advance for any answers,
Cheers!

Zoster answered 21/3, 2013 at 9:5 Comment(1)
Why do you need to know the buffer sizes? What's your goal?Broad
L
2

Don't see why you'd want to know this. Linux allows you to use the TIOCGSERIAL ioctl to retrieve a struct serial_struct which has a xmit_fifo_size field. Though I'd be surprised if many USB serial driver bother to write something meaningful there.

Leandro answered 23/4, 2013 at 20:49 Comment(0)
H
1

I've been grappling with similar problems to the question you ask. I don't have an answer for you but have one other bit of information that you might find useful.

On Mac OS X you can use ioctl to find out how many characters are currently in the buffer. The following code will give you the figure

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

I have been using this to try and find when a large file transfer has completed over a serial line (The micro uses Software flow control so it is hard to predict the rate of transfer).

This method works reasonably well however it is not perfect. I'm not sure which buffer the ioctl call is able to access. When the ioctl function call returns a value of 0 bytes in the buffer the file transfer still continues for several seconds longer. The USB chip in my cable claims to only have a 128 byte transmit buffer which should be emptied within 0.3 of a second at my baud rate.

Hilleary answered 21/10, 2013 at 23:5 Comment(0)
L
0

This is an old title but for those who wonder: here related pdf (About of 0453:6001 ic's)

On page (end of)13 and (start) 14 :
TX Size : 256 Bytes
RX Size : 128 Bytes

Have nice(+Healthy) day!

Lasalle answered 7/7, 2020 at 21:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.