Давным-давно, когда деревья были большими и вариации комплектаций одной модели автомобиля можно было перечислить пальцами одной руки, был куплен диагностический адаптер, о котором сегодня пойдет речь. Творение неизвестного китайца получило название Mini-VCI J2534. Откуда он взялся доподлинно неизвестно, но позиционируется как интерфейс для работы с различными Тойотами, а так же как J2534 совместимый адаптер (спойлер - нет). В момент покупки его было достаточно для диагностики и ковыряния в мозгах автомобилей тех лет, но прогресс не стоит на месте и в нынешних реалиях он если так можно выразиться - "не вывозит". О том, можно ли с этим что-то сделать и пойдет речь ниже.
Итак, знакомьтесь - наш пациент снаружи и внутри:
Внутри него живет 16/32 bit ARM7TDMI-S™ CPU, пара CAN-контроллеров, 2 UART'а и еще кучка полезной и не очень периферии.
Суть проблемы
Если закрыть глаза на мелочи в виде почти полного несоответствия стандарту J2534, есть у него проблемы гораздо хуже, а именно невозможность отправлять данные по протоколу ISO-TP длиннее ~48 байт. С последним мириться было нельзя и в голове засела мысль, а что если получится сделать этот мир чуточку лучше.
Если кратко, как происходит передача данных длинной больше 8 байт по CAN-шине (длина сообщения CAN ограничена восемью байтами). Существует такой стандарт ISO15765, он же ISO-TP (Transport Protocol), который покрывает 2 модели OSI (сетевой и транспортный). Передача данных длиной более 7 байт выглядит так:
Источник отправляет First Frame (FF) с данными об общей длине передаваемых данных и первыми 6 байтами payload'а.
Приемник отвечает ему Flow Control фреймом, в котором говорит о минимальном допустимом времени между посылками CF (о них ниже) и количестве CF, после которого источник снова должен дождаться Flow Control фрейм.
-
Источник после приема Flow Control'а продолжает отправку данных фреймами Consecutive Frame (CF) с заданным интервалом о ожиданием следующего Flow Control (если об этом было сказано в пункте 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)
pavel_raskin
24.07.2022 15:20+2хотя на USB 2.0 все работает, но где вы сейчас найдете честный контроллер USB 2.0 в матери/ноутбуке
Нередко бывает достаточно подключить "капризное" устройство через USB-хаб с портами версии 2.0
Kings_Bounty
24.07.2022 18:46И даже достаточно в удлинитель usb v.2.0
pavel_raskin
24.07.2022 23:04А это как влияет? Чем "удлинитель" на четыре провода (т.е. 2.0) лучше собственного разъёма "капризного" устройства на те же четыре контакта? Или подобные проблемы совместимости и со скоростными устройствами бывают?
Kings_Bounty
25.07.2022 00:17USB 3.0 устройство воткнутое в удлинитель 2.0 понижает протокол на 2.0, т.к. обратно совместимо же. Не знаю в чем "механика" этих софтверных проблем.
HardWrMan
25.07.2022 08:22Механика в отсутствии дополнительных сигналов в удлинителе 2.0. Совет бесполезный для изначально не 3.0 устройства. А вот совет с 2.0 или даже 1.0 хабом - работает, т.к. там вступает в работу активный элемент.
alexyr
24.07.2022 20:01Подскажите пожалуйста, как прошить починенную прошивку?
И стоит ли пробовать, если при попытке экспорта Flash Magic выдаёт Operation Failed.
Читаю на 9600 как LPC2114kolyandex Автор
25.07.2022 03:55Мой читался на 38400
alexyr
25.07.2022 09:02спасибо!
38400 это максимальная скорость, на которой он пытается читать.
выше он даже signature не читает. я пробовал все скорости, везде та же ошибка…
подозреваю что шнурок бракованный
alexyr
25.07.2022 10:11Ваш выглядит ближе к тому, что сейчас продаётся: MINI VCI V17.00.020
Мой был куплен в марте этого года: MINI VCI V16.00.017
Не подскажете название/модель вашего и когда был куплен?MINI VCI V17.00.020
kolyandex Автор
25.07.2022 10:26Моему лет 5 точно. Они все должны быть одинаковые по прошивке, какой смысл китайцам что-то новое придумывать. Хотя, в сети гуляют прошивки версии 2 с чем-то, там просто цифры с версией подменены, а по факту прошивка та же.
IvUyr
24.07.2022 21:00определился программой по внутреннему ID как LPC2114, в котором, на минуточку, вообще нет CAN контроллера
С такими приколами я сталкивался с STM32, в одном китайском STLink видел stm32f101, у которого официально нет usb контроллера, но который спокойно общался с компом через него, и есть пара чипов stm32f103c8, у которых по даташиту 64 кб флеш, а по факту - 128.
kolyandex Автор
25.07.2022 05:24+1Если stm'ки из Китая (Али и прочее), то такое неудивительно. Там сплошной перемарк под то, что нужно, даже если есть чипы с большим объемом памяти, китайцы не упустят шанс заработать и перемаркируют их как угодно.
andrey239
спасибо за автомобильную тему :)
на тему шнурков есть интересеная коллекция альтернитивных прошивок для stm32 в https://github.com/moonglow/
а как там J2534 здесь или где-нить ещё? нам его в rusEFI ЭБУ надо или не надо добавлять? :)
kolyandex Автор
В ЭБУ J2534 не нужен :)
Вам для диагностики нужен только ISO-TP ну и своя реализация KWP/UDS если нужны параметры выходящие за границы стандартного OBD.
andrey239
нам калибровка нужна сильно больше, чем диагностика. ISO-TP уже есть, затык с гибко настраиваемым и доступным софтом на стороне PC
kolyandex Автор
Можно сделать прямой доступ к памяти через KWP/UDS, чтобы читать и менять параметры в RAM на лету, штатно на большинстве ЭБУ есть такая штука, но естественно в релизе прикрыта конфигурацией.