Получение размеров буфера/пакета/полезной нагрузки для последовательной передачи данных 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-пространства пользователя? и если да - как?
Большое спасибо заранее за любые ответы,
Ура!
Ответы
Ответ 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 секунды при моей скорости передачи.