检索用户空间 Linux C 代码中 USB 串行写入传输的缓冲区/数据包/有效负载大小
Posted
技术标签:
【中文标题】检索用户空间 Linux C 代码中 USB 串行写入传输的缓冲区/数据包/有效负载大小【英文标题】:Retrieving buffer/packet/payload sizes for USB serial write transfer in userspace Linux C code 【发布时间】:2013-03-10 16:58:12 【问题描述】:提前道歉我不能立即在这里接受答案 - 只是想我想记下这个,而我有问题......
简而言之:当我在 Linux 下使用用户空间 C 代码启动对 USB 串行端口的写入时,我可以观察到三种不同的缓冲区大小 - 问题是,我想从用户那里检索所有这些大小-空格 C 代码本身。
假设我有一个带有 FTDI FT232 芯片的 Arduino Duemillanove - 编程为从 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
...
这首先告诉我驱动程序(内核模块)usbserial
和 ftdi_sio
已被挂钩/加载以处理设备;这些创建了一个名为/dev/ttyUSB0
的文件(设备节点)——从操作系统的角度来看,本质上是一个串行端口。它还告诉我有一个 64 字节的 MaxPacketSize
归因于设备的端点。我也可以通过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
...并获得如下可视化:
请注意,执行写入的“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 请求(可能)在 USB 线上被排序为(四个)64
字节的数据包
实际上,我有三个缓冲区大小:4608
、256
和 64
字节;类似于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
所以,我的问题是:如何从用户空间 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 串行写入传输相关的缓冲区大小吗?如果是这样 - 如何?
非常感谢您的任何回答, 干杯!
【问题讨论】:
为什么需要知道缓冲区大小?你的目标是什么? 【参考方案1】:不明白你为什么想知道这个。 Linux 允许您使用TIOCGSERIAL
ioctl 检索具有xmit_fifo_size
字段的struct serial_struct
。虽然如果许多 USB 串行驱动程序费心在那里写一些有意义的东西,我会感到惊讶。
【讨论】:
【参考方案2】:我一直在努力解决与您提出的问题类似的问题。我没有给你的答案,但有一点你可能会觉得有用的信息。
在 Mac OS X 上,您可以使用 ioctl 找出缓冲区中当前有多少字符。下面的代码会给你图
uint ioctlBytestInBuffer;
int returnCode = ioctl(fileDescriptor, TIOCOUTQ, &ioctlBytestInBuffer);
我一直在使用它来尝试查找大型文件传输何时通过串行线路完成(微型使用软件流控制,因此很难预测传输速率)。
此方法效果不错,但并不完美。我不确定 ioctl 调用能够访问哪个缓冲区。当 ioctl 函数调用在缓冲区中返回 0 字节的值时,文件传输仍会继续多几秒钟。我电缆中的 USB 芯片声称只有一个 128 字节的传输缓冲区,以我的波特率应该在 0.3 秒内清空。
【讨论】:
【参考方案3】:这是一个古老的标题,但对于那些想知道的人来说: here related pdf(大约 0453:6001 ic's)
On page (end of)13 and (start) 14 :
TX Size : 256 Bytes
RX Size : 128 Bytes
祝你有美好(+健康)的一天!
【讨论】:
以上是关于检索用户空间 Linux C 代码中 USB 串行写入传输的缓冲区/数据包/有效负载大小的主要内容,如果未能解决你的问题,请参考以下文章