Получение размеров буфера/пакета/полезной нагрузки для последовательной передачи данных USB в пользовательском пространстве. Код Linux C - программирование
Подтвердить что ты не робот

Получение размеров буфера/пакета/полезной нагрузки для последовательной передачи данных USB в пользовательском пространстве. Код Linux C

Извиняюсь заранее, я не смогу сразу принять ответ здесь - просто подумал, что хочу записать это, пока у меня есть проблема...

Вкратце: я могу наблюдать три разных размера буфера, когда я запускаю запись в USB-последовательный порт с использованием кода пользователя в C под Linux - и проблема в том, что я хотел бы получить все эти размеры от пользователя -пространственный C-код.


Скажем, у меня есть Arduino Duemillanove с чипом FTDI FT232, запрограммированным на считывание входящих байтов из 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 были подключены/загружены для управления устройством; они создают файл (устройство node), называемый /dev/ttyUSB0 - по существу последовательный порт с точки зрения ОС. Он также говорит мне, что есть MaxPacketSize из 64 байтов, приписываемых конечным точкам устройства. Я могу получить MaxPacketSize также путем запроса через 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

Теперь скажем, что я хочу записать на устройство node /dev/ttyUSB0 с помощью следующей программы на C, 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;
}

Эта программа намеренно записывает большой фрагмент данных за один вызов. Чтобы увидеть, что происходит, сначала разрешите захват запросов USB URB через Linux usbmon объект - поэтому в одном терминале мы запускаем:

$ 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 выше, скорее всего, reset Arduino). После testusw мы можем вернуться к первому терминалу и прервать процесс cat с помощью CTRL + C; мы остаемся с logfile, testusw.2u.mon. Мы можем открыть этот файл журнала с помощью Virtual USB Analyzer:

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

... и получить следующую визуализацию:

vusb-analyzer.png

Обратите внимание, что есть 2 * 9 = 18 запросов URB, показанных для "EP2 OUT", которые выполняют запись, несущую 0x0100 = 256 байт каждый; так что в общей сложности было записано 18 * 256 = 4608 байт - как сообщается "байтами, написанными" на testusw выше. Кроме того, игнорируйте данные на EP1 IN (это какой-то барахл, который отправляет мой код Arduino, который заканчивается ошибкой "Status: -2" ).


Таким образом, я могу заметить следующее:

  • В программе C я начинаю писать 100000 байт
  • В результате записываются только 4608 байты - эффективно действуют как первый размер буфера
  • usbmon затем сообщает, что этот фрагмент секвенирован в 18 запросов URB 256 байтов каждый
  • наконец, MaxPacketSize сообщает мне, что каждый запрос URB (возможно) помещен в (четыре) пакеты из 64 байт на проводе USB

Фактически у меня есть три размера буфера: 4608, 256 и 64 байты; аналогично тому, что упоминается в Serial HOWTO: Основы последовательного порта: 4.7 Путь потока данных; Буферы:

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

Итак, мой вопрос: как эти размеры буфера могут извлекаться из самого кода C пользовательского пространства - однако, только с устройства node path /dev/ttyUSB0 в качестве единственного входного параметра?

Мне было бы хорошо работать с внешними программами с помощью команды popen и анализировать вывод. Например, я могу получить MaxPacketSize через lsusb -v -d 0403:6001 | grep MaxPacketSize - но для этого требуется идентификатор поставщика/продукта, и я не знаю, как его получить, если только часть информации является устройством node path /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 для запроса данных, связанных с устройством 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

... но опять же, я не вижу многого, связанного с размерами найденных буферов.

Чтобы обернуть это, снова задайте вопрос: могу ли я получить найденные размеры буфера, связанные с переносом записи usb-serial из приложения C-пространства пользователя? и если да - как?

Большое спасибо заранее за любые ответы,
Ура!

4b9b3361

Ответ 1

Не понимаю, почему вы хотите это знать. Linux позволяет использовать TIOCGSERIAL ioctl для извлечения struct serial_struct, который имеет поле xmit_fifo_size. Хотя я был бы удивлен, если многие последовательные драйверы USB потрудились написать там что-то значимое.

Ответ 2

Я столкнулся с подобными проблемами с вопросом, который вы задаете. У меня нет ответа для вас, но есть еще одна информация, которую вы можете найти полезной.

В Mac OS X вы можете использовать ioctl, чтобы узнать, сколько символов в данный момент находится в буфере. Следующий код даст вам цифру

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

Я использовал это, чтобы попытаться найти, когда большая передача файлов завершилась по последовательной линии (микро использует управление потоком программного обеспечения, поэтому трудно предсказать скорость передачи).

Этот метод работает достаточно хорошо, однако он не идеален. Я не уверен, к какому буферу может обращаться вызов ioctl. Когда вызов функции ioctl возвращает в буфере значение 0 байт, передача файла продолжается еще несколько секунд. Микросхема USB в моем кабеле утверждает, что имеет только 128-байтовый буфер передачи, который должен быть опустошен в пределах 0,3 секунды при моей скорости передачи.