Введение

В процессе разработки прошивок для маршрутизаторов на линуксах, время от времени попадаются "интересные" баги. Такие от которых через три дня хочется рвать волосы в самых неожиданных местах. Об одной из таких проблем и пойдёт речь дальше.

Завязка

Большинство наших маршрутизаторов имеют на борту LTE-модем. Некоторые — сразу два. Тут был как раз такой случай. Новые маршрутизаторы у нас появляются довольно часто, так что технология отработана: раз, проверили что всё включается, два, адаптировали device tree, три, отдаём тестировщикам для поиска всякого интересного.

Развитие

И они, конечно, нашли: не работают два модема одновременно. Как так? У меня же работали! Оказывается, если два quectel, тогда не работают. В каком бы порядке не включали, первый стартует нормально, а второй в EDL-режиме. Причём, поскольку EDL-режим стартует намного быстрее (меньше секунды, против 10 сек), то выглядит так, что второй стартует первым. Проблема повторяется с разными моделями от этого производителя. Как-будто, второй модем подключается к usb, осматривается, видит первый и говорит: "На этой шине уже есть модем quectel. Не положено иметь два модема quectel на одной шине." Выглядит примерно так:

[   40.828935] usb 3-1: new high-speed USB device number 2 using xhci-hcd
[   41.084049] qcserial 3-1:1.0: Qualcomm USB modem converter detected
[   41.084719] usb 3-1: Qualcomm USB modem converter now attached to ttyUSB0
[   42.388984] usb 1-1: new high-speed USB device number 2 using xhci-hcd
[   43.462712] option 1-1:1.0: GSM modem (1-port) converter detected
[   43.463214] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB1
[   43.464313] option 1-1:1.1: GSM modem (1-port) converter detected
[   43.464875] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB2
[   43.466322] option 1-1:1.2: GSM modem (1-port) converter detected
[   43.466983] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB3
[   43.468368] option 1-1:1.3: GSM modem (1-port) converter detected
[   43.469121] usb 1-1: GSM modem (1-port) converter now attached to ttyUSB4
[   43.590363] qmi_wwan 1-1:1.4: cdc-wdm0: USB WDM device
[   43.592423] qmi_wwan 1-1:1.4 wwan0: register 'qmi_wwan' at usb-xhci-hcd.2.auto-1, WWAN/QMI device, f6:0c:68:0e:11:4d
EDL-режим

специальный режим модемов от компании qualcomm для раскирпичивания сложных случаев. Расшифровывается как Emergency DownLoad. Присутствует во всех андроид-телефонах на чипах от qualcom, но попасть туда штатными средствами обычно нельзя.

...второй модем подключается к usb, осматривается, видит первый...

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

Пробуем другую модель маршрутизатора. Параллельно, коллега запускает пару на PC. Если на PC тоже не заработает, то вопрос уже к quectel. На другом маршрутизаторе проблемы нет. Видимо, дело не в самих модемах. Либо не хватает энергии, либо дело в разводке usb или портов. На проблемном маршрутизаторе модемы подключены прямо к корневым хабам, а на другом — между корневым хабом и каждым из модемов есть ещё по одному хабу. Пока коллега проверяет два модема подключенные непосредственно к одному хабу на PC, я прозвоню электричество.

Модемы вставлены в разъёмы mpci-e. Замечательный разъём. Всем хорош, только питание на контактах №№ 2, 24 и 52. А все чётные контакты находятся на нижней стороне разъёма и спрятаны под модемом, когда он подключен. Надо искать где-то в другом месте. Должны быть какие-то резисторы или конденсаторы через которые проходит эта линия питания. Обращаемся к схеме разводки платы. Тем временем коллега сообщил, что на PC проблема не проявляется. Питание так же оказалось в норме (3,3 В на обоих разъёмах). Видимо проблема в софте.

Попробуем отключить всю программную обработку в пространстве пользователя. Ибо, всё больше и больше складывается впечатление, что это аппаратная проблема. Отключение программной обработки не решило проблему. Посмотрим включение питания и если не поможет, будем "нырять" в ядро. Стандартно, модемы включаются сразу один за другим. Добавим задержку в 10 секунд. Тоже не помогает. В логах ядра есть какое-то сообщение типа alloc_contig_range [x, y] RFNs busy. Сообщение как-будто указывает на проблемы с выделением последовательного участка памяти, что может быть источником проблемы. Может быть не хватает памяти на дескрипторы. Причём перед вторым модемом его намного больше. Может быть проблема как-то связана с выделяемой памятью

В sysfs оказалось, что количество буферов URB отличается (в /sys/bus/usb/devices/<device addr>/urbnum). Или это не количество буферов, а номер текущего? Гугл говорит: номер текущего. Ну что ж, посмотрим, что там с памятью.

alloc_contig_range вызывается (через цепочку) из dma_alloc_coherent. Найдём все вызовы dma_alloc в подсистеме usb (driver/usb) и поставим там отладочную печать. Нашёлся usb_alloc_dev в drivers/usb/core/usb.c. Все сообщения "alloc_contig_range" приходят именно оттуда. Там вызывается xhci_alloc_dev. Из которого вызывается xhci_alloc_virt_device. Внутри, правда, код выглядит вполне невинно, так что начинает складываться впечатление, что информационные сообщения из alloc_contig_range — это ложный след. Тем не менее, надо отработать эту версию до конца.

Интересно. Внутри на пол-дороги вызывается xhci_ring_alloc, который срабатывает дважды для сбойного случая и 14 раз для хорошего случая. Но тут он вызывается откуда-то из другого места. Другое место оказалось usb_hcd_alloc_bandwidth. Который проверяет достаточно ли пропускной способности шины для нового устройства. Но при этом ему передаются структуры текущих usb настроек и конфигураций. То есть похоже, он проверяет что можно включить запрашиваемые настройки. На это же намекает комментарий "check whether a new bandwidth setting exceeds the bus bandwidth". С другой стороны, странно, что функция для проверки чего-то выделяет память.

Оказалось, что для обоих модемов функция возвращает 0, что интерпретируется, как успех. Но выделяется разное количество памяти. Надо проверить, что там в этой структуре usb_host_config. А там "representation of a device's configuration", то есть описание настройки устройства (а не хоста, как некоторые могли бы подумать). По простому говоря, все дескрипторы собранные с устройства собраны в эту структуру.

USB дескрипторы

Каждое USB устройство может работать в одной из нескольких конфигураций (чаще всего одна). Конфигурация содержит список интерфейсов. Соответсвенно, включая ту или иную конфигурацию, включается её список интерфейсов. Каждый интерфейс состоит из точек доступа (endpoint). Любая USB-транзакция идёт от или к какой-то точке на устройстве. Всё это добро описано в дескрипторах. Первое что делает хост, когда подключается устройство — скачивает дескрипторы.

В общем, вроде тут бага нет. Посмотрим, что хост получает от устройства на старте с помощью tcpdump. Добавим в ядро драйвер usbmon и пересоберём libpcap с поддержкой usb. Прослушаем, что происходит на шине во время подключения модемов. Включили, собрали, insmod usbmon.ko;tcpdump -D показывает заветные usbmon0, usbmon1 и так далее. Снимаем трассировку при добавлении первого и второго модемов и сравниваем в wireshark. Последовательность оказывается довольно простой: приходит прерывание, инициируется сброс порта, дальше хост читает дескрипторы. Проблемный модем (или корневой хаб) проявляет себя довольно рано: при начальном сбросе порта приходится делать его дважды и уже первые дескрипторы показывают, что этого было достаточно, чтобы устройство инициализировалось неправильно. После сброса порта (SET_FEATURE PORT_RESET) там читается статус и драйвер контроллера видимо не доволен статусом который он прочёл. Поэтому он делает ещё один сброс. Несколько повторных тестов показали, что двойной сброс связан с несовершенством тестовой методики: запихивание модема в порт рукой не всегда проходит гладко. А в остальном процедуры инициализации идентичны. В общем, и здесь тупик.

Катарсис

Значит, если разница уже в самых первых дескрипторах, то модем уже при старте знает, что он не должен запустится нормально. Получить он это знание может либо по радио, либо по проводу. Радио — скорее всего неправильный вариант. Попробуем провода. Беглое гугление показывает, что для того чтобы перевести чип qualcomm в EDL режим надо заземлить какую-нибудь ножку. Возможно при старте одного модема, паразитно заземляется какая-то ножка на втором или что-то типа того? Втыкаем в первый разъём модем и проверяем все ножки на втором разъёме при выключенном, потом при включенном модеме и вуаля! Во втором случае, ножки 3 и 5 показывают уровень 1,8 В. На схеме платы ножки обозначены как coex_1 и coex_2. Смотрим в доки модема: reserved. Что за ...?! Оказалось, старая версия. В новой написано COEX_UART_RX и COEX_UART_TX. И пометка "It is prohibited to be pulled up high before startup". То есть когда первый модем стартует, он подтягивает эти пины (как и положено UARTу), а второй видя такое непотребство впадает в панику и запускается в EDL-режиме.

Заключение

Выпаиваем резисторы и выпиваем шампанское. Эпопея длинной в 2 дня закончилась

Комментарии (13)


  1. Lirein
    17.01.2023 05:48
    +24

    Хабр всё таки торт. Приятно читать такие статьи, как бальзам на душу. С теплом вспоминается время когда мы в Сургутнефтегазе разрабатывали и отлаживали универсальный кустовой контроллер на QNX.


    1. romanetz_omsk
      17.01.2023 12:48
      +1

      Может, запилите об этом статью? Необычно применение QNX да и кастомной железки, обычно на кустах попроще всë


      1. Lirein
        17.01.2023 14:32

        Это было давненько, лет 11 назад. Какая конкретно микросхема ADE использовалась, например, я не помню. Вообще на публикацию подобных историй нужно бы спросить разрешение у начальника отдела АСТУП в ПУ СургутАСУнефть, и нужно время чтобы это описать, включая иллюстрации, без них статья - не статья.
        В двух словах там было два контроллера - свой и заказной. Свой мы делали по своему же ТЗ, умел в RMS плюс писал пусковые токи. А вот заказной умудрились прееврать ТЗ по полной, в итоге там был обмен с УСО через Modbus-RTU, со всеми вытекающими, ни о каком RMS не было и речи. Именно с этого проекта у меня прочно осела в лексиконе фраза "Нет ничего не возможного - делайте!". А прозвучала она в ответ на моё возражение что мы не можем записать графики пусковых токов опрашивая УСО через Modbus по RS232.


    1. event1 Автор
      17.01.2023 12:54
      +5

      Хабр всё таки торт

      Хабр лишь на столько торт, на сколько мы сами его испечём.

      Спасибо на добром слове


  1. lelik363
    17.01.2023 08:14

    По итогу работы какие-либо организационные выводы были сделаны?


    1. SergeyMax
      17.01.2023 10:19
      +18

      Да, было принято решение делать хорошо. Делать плохо запретили приказом гендиректора.


      1. kulhaker478
        17.01.2023 12:24
        +1


    1. event1 Автор
      17.01.2023 12:58

      С одной стороны надо бы, а с другой — бегать и перепроверять все модемы (мы штук 20 поддерживаем, но основных пять) заранее, не практично. По-этому, нет, не сделаны. Но теперь мы знаем про такую особенность и в следующий раз перепрыгнем через эти грабли


  1. MinimumLaw
    17.01.2023 11:49

    Выпаиваем резисторы и выпиваем шампанское. Эпопея длинной в 2 дня закончилась

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

    Как всегда - это 90% сложности, но всего 10% времени. При чем самого интересного времени в процессе разработки и проектирования. Дальше скучная рутина.


    1. event1 Автор
      17.01.2023 12:55

      Это не покупное, а разработанное на заказ. Просто поменяли BOM для следующей партии.


      1. MinimumLaw
        17.01.2023 13:05
        -2

        Тогда, безусловно, проще. Но тогда косяк в ТЗ или при приемке.


  1. adron_s
    17.01.2023 18:35

    Знакомая проблема. Я на эти же грабли месяц назад наступил с PC-Engines APU4 - после очередного обновления биоза. В итоге все решилось доработкой linux драйвера gpio и установкой нужных состояний для его(gpio) выходов - они были выведены на слоты mPCI express, куда модемы собственно и вставлялись.


  1. adron_s
    17.01.2023 19:32
    +2

    Кстати вот описание этой проблемы и способы ее решения: https://forums.quectel.com/t/ep06-e-over-usb-identified-as-05c6-9008/3851/12
    Мне именно эта ссылка и помогла во всем разобраться.