Давным-давно, когда деревья были большими и вариации комплектаций одной модели автомобиля можно было перечислить пальцами одной руки, был куплен диагностический адаптер, о котором сегодня пойдет речь. Творение неизвестного китайца получило название Mini-VCI J2534. Откуда он взялся доподлинно неизвестно, но позиционируется как интерфейс для работы с различными Тойотами, а так же как J2534 совместимый адаптер (спойлер - нет). В момент покупки его было достаточно для диагностики и ковыряния в мозгах автомобилей тех лет, но прогресс не стоит на месте и в нынешних реалиях он если так можно выразиться - "не вывозит". О том, можно ли с этим что-то сделать и пойдет речь ниже.

Итак, знакомьтесь - наш пациент снаружи и внутри:

Обратите внимание на маркировку микроконтроллера LPC2119, мы к ней еще вернемся.
Обратите внимание на маркировку микроконтроллера LPC2119, мы к ней еще вернемся.

Внутри него живет 16/32 bit ARM7TDMI-S™ CPU, пара CAN-контроллеров, 2 UART'а и еще кучка полезной и не очень периферии.

Суть проблемы

Если закрыть глаза на мелочи в виде почти полного несоответствия стандарту J2534, есть у него проблемы гораздо хуже, а именно невозможность отправлять данные по протоколу ISO-TP длиннее ~48 байт. С последним мириться было нельзя и в голове засела мысль, а что если получится сделать этот мир чуточку лучше.

Если кратко, как происходит передача данных длинной больше 8 байт по CAN-шине (длина сообщения CAN ограничена восемью байтами). Существует такой стандарт ISO15765, он же ISO-TP (Transport Protocol), который покрывает 2 модели OSI (сетевой и транспортный). Передача данных длиной более 7 байт выглядит так:

  1. Источник отправляет First Frame (FF) с данными об общей длине передаваемых данных и первыми 6 байтами payload'а.

  2. Приемник отвечает ему Flow Control фреймом, в котором говорит о минимальном допустимом времени между посылками CF (о них ниже) и количестве CF, после которого источник снова должен дождаться Flow Control фрейм.

  3. Источник после приема Flow Control'а продолжает отправку данных фреймами Consecutive Frame (CF) с заданным интервалом о ожиданием следующего Flow Control (если об этом было сказано в пункте 2)

    https://en.wikipedia.org/wiki/ISO_15765-2

Что происходит на самом деле и почему ничего не работает нам поможет выяснить обычный анализатор CAN шины (Can Hacker/PEAK CAN и иже с ними). Итак, картина маслом - все смешалось, кони, люди. Приемник сказал жди от меня каждые 8 Consecutive Frame'ов Flow Control и шли мне каждый Consecutive Frame не менее чем через 10 мс, а шнурок мало того, что проигнорировал ожидание FC, так еще и на минимальную задержку между CF не обратил внимания.

Flow Control от приемника - 30 08 0A FFFFFFFFFF, где 08 - количество CF, после которого источник снова должен дождаться Flow Control фрейм, 0A - минимальное допустимое время между посылками CF.

Что мы имеем по факту - задержка около 1мс между CF, вместо желаемой 10мс и отсутствие ожидания Flow Control, что полностью ломает весь процесс передачи.

Ну и ладно, подумаешь, организуем свой ISO-TP с задержками и таймингами, благо шнур позволяет работать с сырыми данными CAN и посмотрим что получилось (гадость какая)

В шнурке используется преобразователь USB-UART FT232, который имеет некоторые проблемы при работе с USB 3.0. И проблемы эти - конские задержки, которые не настраиваются из драйвера, хотя на USB 2.0 все работает, но где вы сейчас найдете честный контроллер USB 2.0 в матери/ноутбуке. В общем, ручное форматирование тоже отпадает, задержки между CF не поддаются критике, работать это тоже не будет.

Остается крайняя мера - залезть внутрь и попробовать исправить кривой софт костылями, насколько это возможно. Не знаю как, но прямо по USB из контроллера можно вычитать и записать флеш память даже без разборки шнурка с помощью программы Flash Magic. После чтения загружаем прошивку в IDA, процессор ARM Little Endian архитектура ARMv4T. Немного помощи руками, создание недостающих регионов и прошивка готова к исследованию.

Функция с реализацией отправки данных по ISO-TP была найдена от обратного (CAN периферия - отправка - обертка - сама функция). Что же по исходникам - вот кусок кода с отправкой данных. То, о чем говорилось выше не предусмотрено вообще никак.

iso_tp_fc_received_ptr = &ctx->iso_tp_fc_received;
while (sended_len < send_len)
{
    if (ff_flag)
    {
        if (cf_counter >= 0xF)
            cf_counter = 0;
        else
            ++cf_counter;
        v21 = 8;
        tx_data.data[0] = cf_counter + 0x20; // Сборка Consecutive frames
        v23 = v21 - 1;
        if (send_len - sended_len < v21 - 1)
            v23 = send_len - sended_len;
        memcpy(&tx_data.data[1], &send_data_[sended_len], v23);
        can_tx_1(ctx, &tx_data);
        sended_len += v23;
    }
    else
    {
        tx_data.data[0] = 0x10;         // Сборка First frame
        tx_data.data[1] = send_len; // Больше 255 байт не предусмотрено, хотя по стандарту должно быть 4 с копейками кб, хотя о чем это я
        memcpy(&tx_data.data[2], send_data_, 6));
        cf_counter = 0;
        set0(iso_tp_fc_received_ptr);
        can_tx_1(ctx, &tx_data);
        if (!wait_fc(ctx, 700)) // Ждем flow control
            return 0;
        ff_flag = 1;
        sended_len += 6;
    }
}

Как видно, Flow Control шнурок ждет всего один раз, а дальше даже не пытается соответствовать ISO-TP. Как только он получит FC, сразу же без задержек начинает слать остатки данных в Consecutive Frame'ах. Ладно, но может он хотя бы обращает внимание на данные из Flow Control? Ха-ха. Нет. Вот функция обработки приема данных по ISO-TP, нас интересует только прием Flow Control.

header = rx_byte_0 & 0xF0;
if (can_rx_ctx->rx_can_data[0] & 0xF0)
{
    switch (header)
    {    
        //Тут были обработчики других заголовков, но они нам не нужны
    case 0x30: //Flow control
        set_1(&iso_tp_ctx->iso_tp_fc_received);
        result = 0;
        break;    
    }
}

Как видим, просто выставляется флажок, что был принят какой-то flow control, а что там в нем нам не важно (мысли китайца).

Что же делать?

Дешево и сердито - засунуть простую задержку между отправкой Consecutive Frame'ов, чтобы приемник успевал отправить свой Flow Control там, где нужно и получил следующий CF уже после. Все что нам нужно, это найти место, в цикле с отправкой, куда можно засунуть переход в функцию с задержкой, благо мест таких полно, а замененные инструкции можно выполнить в новой функции, так что мы ничего не потеряем. Берем IAR, в нем есть поддержка именно такого процессора, чистый проект на ассемблере и пишем элементарный цикл

_my_func
        STMFD   SP!, {R10-R12,LR}
        LDR R10, =39062          ; ~7800 на 1 мс
        B compare
sub:
        SUB     R10, R10, #1
compare:        
        CMP     R10, #0
        BGT sub
        
        MOV     R0, R4          ; та самая замененная инструкция на переход
        LDMFD   SP!, {R10-R12,PC}

Конечный результат выглядит так - слева то, что было, справа то, что стало. Инструкция MOV R0, R4 перенесена.

Прошиваем и наслаждаемся прекрасной работой без сбоев.

Конечно, можно было сделать все по фен шую, и правильную обработку Flow Control фрейма, и честные задержки по желанию приемника, и ожидание остальных Flow Control'ов. Но результат в любом случае достигнут и терять время больше чем один вечер на такое желания нет.

Еще интересный момент - контроллер судя по всему китайский перемарк, т.к. определился программой по внутреннему ID как LPC2114, в котором, на минуточку, вообще нет CAN контроллера, если верить даташиту. Видишь CAN? И я не вижу, а он есть. Вот так вот.

Кому интересны прошивка и база IDA, то вот. Пароль habr.com

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


  1. andrey239
    24.07.2022 09:37
    +2

    спасибо за автомобильную тему :)

    на тему шнурков есть интересеная коллекция альтернитивных прошивок для stm32 в https://github.com/moonglow/

    а как там J2534 здесь или где-нить ещё? нам его в rusEFI ЭБУ надо или не надо добавлять? :)


    1. kolyandex Автор
      24.07.2022 09:41

      В ЭБУ J2534 не нужен :)

      Вам для диагностики нужен только ISO-TP ну и своя реализация KWP/UDS если нужны параметры выходящие за границы стандартного OBD.


      1. andrey239
        24.07.2022 11:08

        нам калибровка нужна сильно больше, чем диагностика. ISO-TP уже есть, затык с гибко настраиваемым и доступным софтом на стороне PC


        1. kolyandex Автор
          24.07.2022 11:10

          Можно сделать прямой доступ к памяти через KWP/UDS, чтобы читать и менять параметры в RAM на лету, штатно на большинстве ЭБУ есть такая штука, но естественно в релизе прикрыта конфигурацией.


  1. pavel_raskin
    24.07.2022 15:20
    +2

    хотя на USB 2.0 все работает, но где вы сейчас найдете честный контроллер USB 2.0 в матери/ноутбуке

    Нередко бывает достаточно подключить "капризное" устройство через USB-хаб с портами версии 2.0


    1. Kings_Bounty
      24.07.2022 18:46

      И даже достаточно в удлинитель usb v.2.0


      1. pavel_raskin
        24.07.2022 23:04

        А это как влияет? Чем "удлинитель" на четыре провода (т.е. 2.0) лучше собственного разъёма "капризного" устройства на те же четыре контакта? Или подобные проблемы совместимости и со скоростными устройствами бывают?


        1. Kings_Bounty
          25.07.2022 00:17

          USB 3.0 устройство воткнутое в удлинитель 2.0 понижает протокол на 2.0, т.к. обратно совместимо же. Не знаю в чем "механика" этих софтверных проблем.


          1. HardWrMan
            25.07.2022 08:22

            Механика в отсутствии дополнительных сигналов в удлинителе 2.0. Совет бесполезный для изначально не 3.0 устройства. А вот совет с 2.0 или даже 1.0 хабом - работает, т.к. там вступает в работу активный элемент.


    1. kolyandex Автор
      24.07.2022 18:47
      +1

      Да, но это лишние телодвижения. А я ленивый.


  1. alexyr
    24.07.2022 20:01

    Подскажите пожалуйста, как прошить починенную прошивку?
    И стоит ли пробовать, если при попытке экспорта Flash Magic выдаёт Operation Failed.
    Читаю на 9600 как LPC2114


    1. alexyr
      24.07.2022 20:21

      Разобрал свой шнурок, и он отличается внутри:

      MINI-VCI


    1. kolyandex Автор
      25.07.2022 03:55

      Мой читался на 38400


      1. alexyr
        25.07.2022 09:02

        спасибо!
        38400 это максимальная скорость, на которой он пытается читать.
        выше он даже signature не читает. я пробовал все скорости, везде та же ошибка…
        подозреваю что шнурок бракованный


      1. alexyr
        25.07.2022 10:11

        Ваш выглядит ближе к тому, что сейчас продаётся: MINI VCI V17.00.020
        Мой был куплен в марте этого года: MINI VCI V16.00.017
        Не подскажете название/модель вашего и когда был куплен?

        MINI VCI V17.00.020


        1. kolyandex Автор
          25.07.2022 10:26

          Моему лет 5 точно. Они все должны быть одинаковые по прошивке, какой смысл китайцам что-то новое придумывать. Хотя, в сети гуляют прошивки версии 2 с чем-то, там просто цифры с версией подменены, а по факту прошивка та же.


  1. IvUyr
    24.07.2022 21:00

    определился программой по внутреннему ID как LPC2114, в котором, на минуточку, вообще нет CAN контроллера

    С такими приколами я сталкивался с STM32, в одном китайском STLink видел stm32f101, у которого официально нет usb контроллера, но который спокойно общался с компом через него, и есть пара чипов stm32f103c8, у которых по даташиту 64 кб флеш, а по факту - 128.


    1. kolyandex Автор
      25.07.2022 05:24
      +1

      Если stm'ки из Китая (Али и прочее), то такое неудивительно. Там сплошной перемарк под то, что нужно, даже если есть чипы с большим объемом памяти, китайцы не упустят шанс заработать и перемаркируют их как угодно.