Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Потеря данных при чтении через cdc_acm  (Прочитано 19735 раз)
0 Пользователей и 1 Гость смотрят эту тему.
WWX
Участник

ru
Offline Offline

« : 05-02-2021 13:07 » 

Доброго времени суток.

Есть некое устройство, работающее через USB (на high speed).
При подключении к хосту определяется как "/dev/ttyACMx".
Насколько я понимаю, работает через драйвер "cdc_acm".

Чтение из этого устройства организовано примерно так:
- Вызов "read" с соответствующими параметрами.
- В зависимости от того, сколько байт прочитано за предыдущий вызов "read", перевызываю "read" с соответствующими изменениями параметров.

(За один вызов "read" всегда читается 4095 байт или меньше. (Во всяком случае ни разу не видел, чтобы было 4096 или больше.)

При чтении больших объёмов (до 512 МБ) из этого "виртуального COM-порта" периодически пропадают данные.
Замечено, что перед пропажей "read" возвращает 16 (или более) раз подряд число 4095.
Если перед вызовом "read" поставить задержку, то "read" всегда возвращает 4095 и после примерно 20-го вызова кусок данных теряется (т. е. при наличии задержки воспроизводится почти сразу).

Пропавший кусок всегда кратен 512 Б (что логично, т. к. соответствует размеру bulk endpoint'а на high speed).
Смотрел USB шину аппаратным USB-анализатором. Никаких ошибок на шине нет. Все данные, даже те, которые не добрались до пользовательского приложения, на шине присутствуют и подтверждены (ACK).

Вопрос в том, почему теряются данные?

Могу предположить, что в случае если "read" не успевает выкачивать данные, то происходит переполнение какого-то внутреннего буфера.
Возможно имеет смысл обновить драйвер "cdc_acm", только не очень понятно какой пакет обновлять.

ОС: Centos 7

Вывод "uname -r":
3.10.0-1160.6.1.el7.x86_64
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 05-02-2021 19:14 » 

Что буфера нерезиновые, это понятно, от того и потери.
Умеет ли устройство управлять потоком? Попробуй включить hardware flow control на tty. Устройства класса ttyACM должны быть подобны модемам.
Кстати, там же буфером можно управлять, может получится большими порциями забирать.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
WWX
Участник

ru
Offline Offline

« Ответ #2 : 08-02-2021 10:21 » 

Спасибо за ответ.

Цитата
Что буфера нерезиновые, это понятно, от того и потери.
Т. е. потеря данных от полного заполнения буфера считается нормальным? Мне казалось, что в этом случае драйвер должен подождать, пока пользовательская программа заберёт данные и до этого момента не слать плате USB токены IN.

Цитата
Умеет ли устройство управлять потоком? Попробуй включить hardware flow control на tty. Устройства класса ttyACM должны быть подобны модемам.
Какого-то специального "hardware flow control" на устройстве нет, кроме предусмотренного стандартом USB. Физически плата соединена USB шнуром (физического COM-порта нет).
Это USB устройство, реализующее протокол CDC. Грубо говоря, там есть две BULK endpoints: In и Out.
Если хост хочет что-то прочитать, он шлёт токен IN и в ответ получает 512 байт (если устройству есть что передать).

Вопрос состоит в том, почему он (хост) шлёт токен IN тогда, когда его буфер переполнен; а потом ещё и подтверждает приём данных (шлёт токен ACK).

Цитата
Кстати, там же буфером можно управлять, может получится большими порциями забирать.
А как задать размер буфера?
(Понятно, что это не решение, но хотя бы в порядке эксперимента.)
И можно ли как-то программно узнать размер буфера?
(Тогда можно будет просто запрашивать данные меньшими порциями.)
Записан
darkelf
Молодой специалист

ua
Offline Offline

« Ответ #3 : 08-02-2021 10:58 » 

может есть что в логах ядра?
Есть возможность устанавливать общий размер буферов для всей подсистемы USB.
Ещё, как я понимаю, есть альтернативный вариант общения с драйвером, но использовать не приходилось, так-что по конкретике подсказать не смогу.
Ещё вариант - попробовать работать через libusb.
Записан
WWX
Участник

ru
Offline Offline

« Ответ #4 : 08-02-2021 12:50 » 

В логах (dmesg; /var/log/messages) никаких ошибок нет.
Пробовал в "/sys/module/usbcore/parameters/usbfs_memory_mb" увеличивать размер буфера (с 16 на 1000) - поведение не изменилось.

С остальным надо разбираться. В любом случае спасибо за ссылки — наводки.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #5 : 09-02-2021 02:47 » 

User space работает же не с USB, а с tty-устройством, соотв. и поведение у него должно быть от tty.
Должны ли данные пропадать? Наверно зависит от управления потоком, как согласована концепция управления последовательным устройством со стороны tty и со стороны USB-устройства. В tty есть вариант без управления потоком, с аппаратным управлением (RTS/CTS или его соотв. эмуляцией) и управляющими кодами xon/xoff. И так, что должен делать последовательный порт без управления потоком, к которому подключено устройство, шлющая массу данных, а потребитель ее не забирает? Логично, что складировать больше размера буфера не имеет смысла, значит только терять.

Попробуй ioctl на дескрипторе открытого tty. В заголовках Линукса есть TIOCM_RTS.

Еще вот пример включения на коленке утилитой stty: https://tldp.org/HOWTO/Serial-HOWTO-9.html
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
WWX
Участник

ru
Offline Offline

« Ответ #6 : 09-02-2021 11:47 » 

Цитата
Должны ли данные пропадать? Наверно зависит от управления потоком, как согласована концепция управления последовательным устройством со стороны tty и со стороны USB-устройства. В tty есть вариант без управления потоком, с аппаратным управлением (RTS/CTS или его соотв. эмуляцией) и управляющими кодами xon/xoff. И так, что должен делать последовательный порт без управления потоком, к которому подключено устройство, шлющая массу данных, а потребитель ее не забирает? Логично, что складировать больше размера буфера не имеет смысла, значит только терять.
Ну, в общем-то логично, это объясняет потерю данных.
Но тогда получается, что хост должен запрашивать данные (слать токены IN) бесконечно (во всяком случае до тех пор, пока устройство отвечает на них данными).

А получается примерно так (цифры раз от раза могут немного варьироваться):
Без ошибок принимается 84'992 Б (166 пакетов по 512 Б).
Затем теряется 3'584 Б (7 пакетов).
Затем опять идут правильные данные (1004 Б - то, что успевает считать "read" до его возврата (не кратно 512 Б)).
Все "read" возвращают странное число 4095. Всего вызовов "read" было - 21.
После этого "read" не вызываются, но всего по USB шине передано 412'672 Б (806 пакетов), это больше чем 21*4095, но много меньше, чем плата готова переслать (512 МБ).

Цитата
Еще вот пример включения на коленке утилитой stty
Пробовал таким способом (ioctl тоже попробовал) - видно, что аппаратный контроль потока вроде бы включился (если я правильно понимаю куда смотреть):
Код:
stty -a -F /dev/ttyACM0
speed 115200 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; 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 -cmspar 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
, но данные по-прежнему теряются (как описано выше), поведение не изменилось.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 09-02-2021 20:00 » 

Нагуглил только один похожий вопрос о потерях на ttyACM, но там не дали какого-то толкового ответа.

Вот исходник линуксового драйвера, если он чем-то поможет: https://github.com/torvalds/linux/blob/master/drivers/usb/class/cdc-acm.c
Там есть много отладочных сообщений.
Как включить debug лог: https://stackoverflow.com/questions/50504516/enable-linux-kernel-driver-dev-dbg-debug-messages
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
WWX
Участник

ru
Offline Offline

« Ответ #8 : 15-02-2021 08:15 » new

Похоже, что там какие-то проблемы в синхронизации, когда буфер полностью заполняется. Но проявляется это только в том случае, если устройство готово слать много данных (> 64 кБ) на большой скорости (на запросы IN практически всегда отвечать ACK, а не NAK (в режиме High Speed)). Возможно такое встречается не так уж и часто, этим можно объяснить редкое упоминание ошибки.
(Если меня кто-то поправит - буду только рад.)

Конкретно в моём случае проблема решается запрашиванием всего объёма доступной памяти (512 МБ) не целиком, а по кускам (32 кБ); устройство это позволяет.
(Во всяком случае несколько ночей в таком режиме проработало без ошибок.)

В любом случае - спасибо за ответы.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines