在用户空间 Linux C 代码中获取 USB 串行写传输的缓冲区/数据包/有效负载大小

16

先提前道歉,我无法立即接受答案 - 只是想在遇到问题时记录下来...

简而言之:当我使用用户空间C代码在Linux下初始化对usb串口的写入时,我可以观察到三种不同的缓冲区大小 - 问题是,我希望从用户空间C代码本身检索所有这些大小。


假设我有一个Arduino Duemillanove,并搭载了FTDI FT232芯片 - 编程以从PC的usb/串行连接读取传入的字节并将其丢弃。 当我将此设备插入系统(在Ubunty 11.04 Natty上执行此操作),我可以通过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
...

首先,这告诉我驱动程序(内核模块)usbserialftdi_sio已被挂钩/加载以处理该设备;这些创建了一个文件(设备节点),名为/dev/ttyUSB0 - 从操作系统的角度来看,它本质上是一个串行端口。它还告诉我,该设备的端点有一个MaxPacketSize为64字节。我也可以通过查询lsusb来获取MaxPacketSize

$ 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

现在,假设我想使用以下C程序testusw.c向设备节点/dev/ttyUSB0写入数据:

#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;
}

这个程序故意在一次调用中写入大量数据。为了查看发生了什么,首先让我们通过Linux的usbmon功能捕获USB URB请求 - 因此在一个终端中,我们运行:

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

在另一个终端中,编译并运行testusw后,我们得到:

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

请注意,上面的testusw调用可能会重置Arduino。 在执行testusw之后,我们可以返回第一个终端,并使用CTRL+C中断cat进程; 我们留下了一个日志文件testusw.2u.mon。 我们可以使用Virtual USB Analyzer打开此日志文件:

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

我可以获得以下可视化效果:

vusb-analyzer.png

请注意,“EP2 OUT”显示了2*9 = 18个URB请求,执行写入操作,每个请求携带0x0100 = 256个字节;因此,总共写入了18*256 = 4608个字节 - 如上面的testusw所报告的“已写入字节数”。同时,请忽略EP1 IN上的数据(这是我的Arduino代码发送的一些垃圾数据 - 最终以“状态:-2”错误结束)。


因此,我可以观察到以下内容:

  • 从C程序中,我启动了100000个字节的写入操作
  • 结果只有4608个字节被写入 - 实际上充当第一个缓冲区大小
  • usbmon然后报告此块被分成了18个URB请求,每个请求为256个字节
  • 最后,MaxPacketSize告诉我每个URB请求(可能)被序列化为(四个)64个字节的数据包在USB线上

实际上,我有三种缓冲区大小:460825664个字节;类似于串行HOWTO:串行端口基础知识:4.7数据流路径;缓冲区中提到的内容。

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

因此,我的问题是:如何从用户空间的C代码中检索这些缓冲区大小 - 只能从设备节点路径 / dev / ttyUSB0 作为唯一的输入参数?
如果通过系统 popen 命令运行外部程序并解析输出,我会很满意。例如,我可以通过 lsusb -v -d 0403:6001 | grep MaxPacketSize 获得MaxPacketSize - 但这需要供应商/产品ID,如果唯一信息是设备节点路径 / dev / ttyUSB0 ,则不知道如何获得该信息。
考虑到 / dev / ttyUSB0 本质上被视为串行端口,我认为通过 stty 查询会提供一些内容 - 但我在那里找不到任何与缓冲区大小相关的内容:
$ 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

我也知道可以使用udevadm来查询与设备节点路径/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

...但是,我并没有看到与缓冲区大小有关的内容。

总结一下,问题再次提出:我能否从用户空间的C应用程序中检索与usb-serial写传输相关的遇到的缓冲区大小;如果可以的话 - 如何实现?

非常感谢任何答案,
干杯!


1
你为什么需要知道缓冲区大小?你的目标是什么? - Hasturkun
3个回答

2

不清楚您为什么想知道这个。Linux允许您使用TIOCGSERIAL ioctl来检索一个struct serial_struct,其中包含一个xmit_fifo_size字段。但我会感到惊讶,如果许多USB串行驱动程序会在那里写入有意义的东西。


1
我一直在处理与你所问问题类似的问题。我没有答案,但有另外一些信息可能对你有用。
在Mac OS X上,您可以使用ioctl来查找缓冲区中当前有多少个字符。以下代码将为您提供该数字。
uint ioctlBytestInBuffer;
int returnCode = ioctl(fileDescriptor, TIOCOUTQ, &ioctlBytestInBuffer);

我一直在使用这个方法尝试查找串行线路上传输大文件完成的时间(微控制器使用软件流控,所以很难预测传输速率)。
这种方法运行得相当不错,但并不完美。我不确定ioctl调用能够访问哪个缓冲区。当ioctl函数调用在缓冲区返回0字节值时,文件传输仍会持续几秒钟。我的电缆中的USB芯片声称只有128字节的传输缓冲区,在我的波特率下应在0.3秒内清空。

0
这是一个旧标题,但对于那些想知道的人: 这里相关的pdf(关于0453:6001 ic的内容)
On page (end of)13 and (start) 14 :
TX Size : 256 Bytes
RX Size : 128 Bytes

祝你有一个愉快(+健康)的一天!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接