簡體   English   中英

在用戶空間 Linux C 代碼中檢索用於 USB 串行寫入傳輸的緩沖區/數據包/有效負載大小

[英]Retrieving buffer/packet/payload sizes for USB serial write transfer in userspace Linux C code

提前道歉,我將無法立即在這里接受答案-只是想記下來,而我遇到了問題...

簡而言之:當我在 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
...

這首先告訴我驅動程序(內核模塊) usbserialftdi_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

...並獲得以下可視化:

vusb-analyzer.png

請注意,對於執行寫入的“EP2 OUT”顯示了 2*9 = 18 個 URB 請求,每個請求攜帶 0x0100 = 256 個字節; 所以總共寫入了 18*256 = 4608 個字節 - 正如上面testusw的“寫入的字節數”所報告的testusw 另外,忽略 EP1 IN 上的數據(這是我的 Arduino 代碼正在發送的一些垃圾 - 以“狀態:-2”錯誤結尾)。


因此,我可以觀察到以下幾點:

  • 從 C 程序中,我開始寫入 100000 字節
  • 結果,只寫入了4608個字節 - 有效地充當第一個緩沖區大小
  • usbmon然后報告這個塊被排序為 18 個256字節的 URB 請求
  • 最后,MaxPacketSize 告訴我每個 URB 請求(可能)在 USB 線上被排序為(四個) 64字節的數據包

實際上,我有三個緩沖區大小: 460825664字節; 類似於Serial HOWTO: Serial Port Basics: 4.7 Data Flow Path 中提到的內容 緩沖器

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 lsusb -v -d 0403:6001 | grep 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 串行寫入傳輸相關的緩沖區大小嗎? 如果是這樣 - 如何?

非常感謝您的任何答案,
干杯!

不明白你為什么想知道這個。 Linux 允許您使用TIOCGSERIAL ioctl 來檢索具有xmit_fifo_size字段的struct serial_struct 雖然如果許多 USB 串行驅動程序費心在那里寫一些有意義的東西,我會感到驚訝。

我一直在努力解決與您提出的問題類似的問題。 我沒有給你答案,但還有一點你可能會覺得有用的信息。

在 Mac OS X 上,您可以使用 ioctl 來找出當前緩沖區中的字符數。 下面的代碼會給你圖

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

我一直在使用它來嘗試查找通過串行線路完成大文件傳輸的時間(微型使用軟件流控制,因此很難預測傳輸速率)。

這種方法相當有效,但並不完美。 我不確定 ioctl 調用能夠訪問哪個緩沖區。 當 ioctl 函數調用在緩沖區中返回 0 字節的值時,文件傳輸仍會繼續多幾秒鍾。 我的電纜中的 USB 芯片聲稱只有一個 128 字節的傳輸緩沖區,以我的波特率應該在 0.3 秒內清空。

這是一個舊標題,但對於那些想知道的人: 這里有相關的 pdf (關於 0453:6001 ic)

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

祝您有美好(+健康)的一天!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM