Это моя маленькая статья с описанием (почти) всего, что я знаю об интерфейсе Apple Lightning и связанных с ним технологиях: Tristar, Hydra, HiFive, SDQ, IDBUS и др. Но сначала маленькое предупреждение…
Читайте эту статью на свой страх и риск! Информация основана на большом количестве внутренних материалов AppleInternal (утечка данных, схем, исходных кодов), которые я прочёл по диагонали. И, конечно, на моих собственных исследованиях. Должен предупредить, что я никогда раньше не проводил подобных исследований. Таким образом, эта статья может использовать неправильные или просто странные термины и оказаться частично или полностью неправильной!
Прежде чем углубиться, давайте кратко разберёмся в терминах:
Что такое Lightning?
Lightning — это цифровой интерфейс, используемый в большинстве устройств Apple iOS с конца 2012 года. Он заменил старый 30-контактный разъём.
На картинке выше гнездо разъёма, а на картинке ниже его распиновка:
Пожалуйста, обратите внимание, что в разъёме контакты с обеих сторон коннектора не соединены в одном и том же порядке. Таким образом, хост-устройство должно определить ориентацию кабеля, прежде чем что-то делать.
Хотя это не всегда так. У многих аксессуаров Lightning, которые мне попадались, в разъёмах зеркальная распиновка.
Что такое Tristar и Hydra?
Tristar — это интегральная схема, встроенная в каждое устройство с гнездом разъёма Lightning. По сути, это MUX:
Кроме всего прочего, его основная цель состоит в том, чтобы соединяться со штекерным разъёмом Lightning, как только он подключён — определять ориентацию, Accessory ID и надлежащим образом маршрутизировать внутренние интерфейсы, такие как USB, UART и SWD.
Hydra — это новый вариант Tristar, используемый начиная с iPhone 8/X. Видимо, наиболее существенным изменением является поддержка беспроводной зарядки, но это ещё предстоит проверить:
Мне известны пять основных вариантов Tristar/Hydra:
- TI THS7383 — Tristar первого поколения в iPad mini 1 и iPad 4
- NXP CBTL1608A1 — Tristar первого поколения в iPhone 5 и iPod touch 5
- NXP CBTL1609A1 — таинственный Tristar первого поколения в iPod nano 7 — источник
- NXP CBTL1610Ax — TriStar второго поколения, используется начиная с iPhone 5C/5S и, по-видимому, во всём остальном, что не поддерживает беспроводную зарядку. Существует несколько поколений (x — номер поколения)
- NXP CBTL1612Ax — Hydra используется с iPhone 8/X и, видимо, во всём остальном, что поддерживает беспроводную зарядку. Существует несколько поколений (x — номер поколения)
С этого момента я буду использовать только термин TriStar, но имейте в виду, что он также означает Hydra, поскольку они очень похожи в большинстве аспектов, которые будут рассмотрены в этом тексте.
Что такое HiFive?
HiFive — это дочерний интерфейс Lightning, то есть штекерный разъём. Он также содержит логический элемент — этот чип известен как SN2025/BQ2025.
Что такое SDQ и IDBUS?
Эти два термина часто считают своего рода синонимами. Для удобства я буду использовать только термин IDBUS, так как он кажется мне более правильным (и именно так технология называется в спецификации THS7383).
Итак, IDBUS — это цифровой протокол, используемый для коммуникации между Tristar и HiFive. Очень похож на протокол Onewire.
Теперь можем начать
Давайте прослушаем коммуникации Tristar и HiFive. Возьмите логический анализатор, переходную плату Lightning с соединением для гнезда и штекерного разъёма, какой-нибудь аксессуар (обычный кабель Lightning-to-USB отлично подойдёт) и, конечно, какое-нибудь устройство с портом Lightning.
Сначала подключите каналы логического анализатора к обеим линиям ID переходной платы (контакты 4 и 8) и подключите плату к устройству, но пока не подключайте аксессуар:
Сразу после этого начните выборку (подойдёт любая частота от 2 МГц и выше). Вы увидите что-то вроде этого:
Как видете, Tristar опрашивает каждую линию ID по очереди — одну за другой. Но поскольку мы не подключили никакого аксессуара, опрос явно провалился. В какой-то момент устройство устанет от этого бесконечного потока отказов и остановит его. А пока давайте разберёмся, что именно происходит во время опроса:
Сначала мы видим длинный интервал (около 1,1 миллисекунды), когда просто уровень высокий, но больше ничего не происходит:
Видимо, это время используется для зарядки внутреннего конденсатора HiFive — энергия от него будет затем использоваться для питания внутренних логических чипов.
Гораздо интереснее то, что происходит потом:
Очевидно, это поток каких-то данных. Но как его интерпретировать? Как расшифровать? Давайте виртуально разделим его на минимальные значимые части — то, что я называю словами:
По сути слово — это сочетание падения-подъёма-падения:
- Содержательный этап — интервал, который определяет значение слова
- Этап восстановления — интервал, который, видимо, требуется для обработки содержательной стадии на стороне получателя и/или для подготовки следующего слова на стадии отправки
Вот таблица известных слов с их интервалами для обоих этапов, которые мы обсуждали выше (все единицы измерения в микросекундах):
Содержание | Восстановление | ||||
---|---|---|---|---|---|
Слово | Min | Typ | Max | Min | Typ |
BREAK | 12 | 14 | 16 | 2.5 | 4.5 |
WAKE | 22 | 24 | 27 | 1100? | |
ZERO | 6 | 7 | 8 | 3 | |
ONE | 1 | 1.7 | 2.5 | 8.5 | |
ZERO и STOP* | 6 | 7 | 8 | 16 | |
ONE и STOP* | 1 | 1.7 | 2.5 | 21 |
Используя приведённую выше таблицу теперь мы можем построить простой декодер протокола:
Как видите, сначала хост посылает BREAK — когда Tristar хочет отправить новый запрос, хост всегда начинает с этого слова. Затем наступает этап передачи данных. Пожалуйста, обратите внимание, что у последнего (8-го) бита в байте более длительный этап восстановления. Когда этап передачи данных заканчивается, хост отправляет ещё один BREAK. Затем дочернее устройство должно отправить ответ (после задержки не менее 2,5 микросекунд — см. таблицу). Tristar будет ждать ответа около 2,2 мс. Если ответ не выдан в этот промежуток времени, Tristar попытается опросить другую линию ID.
Теперь давайте рассмотрим этап данных на примере выше —
0x74 0x00 0x02 0x1f
:0x74
— тип запроса/ответа. Всегда чётный для запроса и нечётный для ответа (тип запроса +1)
0x00 0x02
— фактические данные. Может быть пустым
0x1f
— это CRC8 как байта типа запроса, так и всех данных (полином — 0x31, начальное значение — 0xff)
Давайте подключим к нашей установке какой-нибудь аксессуар и посмотрим, что произойдёт. Я буду использовать оригинальный кабель Lightning-to-USB от Apple:
И вот что появляется на IDBUS после запроса 0x74:
HiFive ответил! И если вы прокрутите дальше, то увидите много других пар запрос/ответ:
Некоторые запросы не нуждаются в ответе:
Интерпретация запросов и ответов IDBUS
Самый важный запрос IDBUS — это 0x74, он используется для двух целей: чтобы приказать HiFive включить полное напряжение и силу тока (в случае, если оно поддерживается аксессуаром), спросить его о конфигурации контактов, которые поддерживаются кабелем, и некоторых других метаданных.
О том, как кодируются данные ответа 0x75, известно не так уж много. Но некоторые биты доступны в старой спецификации Tristar:
Первый байт данных ответа 0x75
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
ACCx | Dx | DATA[43:40] |
Конфигурация ACCx, когда ID найден на ID0
ACCx[1:0] | ACC1 | ACC2 | HOST_RESET |
---|---|---|---|
00 | Hi-Z (IDBUS) | Hi-Z | Hi-Z |
01 | UART1_RX | UART1_TX | Hi-Z |
10 | JTAG_DIO | JTAG_CLK | Hi-Z |
11 | Hi-Z | Hi-Z | HIGH |
Конфигурация ACCx, когда ID найден на ID1
ACCx[1:0] | ACC1 | ACC2 | HOST_RESET |
---|---|---|---|
00 | Hi-Z | Hi-Z (IDBUS) | Hi-Z |
01 | UART1_RX | UART1_TX | Hi-Z |
10 | JTAG_DIO | JTAG_CLK | Hi-Z |
11 | Hi-Z | Hi-Z | HIGH |
Конфигурация Dx, когда ID найден на ID0
Dx[1:0] | DP1 | DN1 | DP2 | DN2 |
---|---|---|---|---|
00 | Hi-Z | Hi-Z | Hi-Z | Hi-Z |
01 | USB0_DP | USB0_DN | Hi-Z | Hi-Z |
10 | USB0_DP | USB0_DN | UART1_TX | UART1_RX |
11 | Hi-Z | Hi-Z | Hi-Z | Hi-Z |
Конфигурация Dx, когда ID найден на ID1
Dx[1:0] | DP1 | DN1 | DP2 | DN2 |
---|---|---|---|---|
00 | Hi-Z | Hi-Z | Hi-Z | Hi-Z |
01 | Hi-Z | Hi-Z | USB0_DP | USB0_DN |
10 | USB0_DP | USB0_DN | UART1_TX | UART1_RX |
11 | Hi-Z | Hi-Z | Hi-Z | Hi-Z |
Используя эти таблицы, давайте расшифруем ID нашего кабеля (
10 0C 00 00 00 00
) с учётом того, что линия ID найдена на контакте ID0:Первый байт ответа 0x75 кабеля
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
ACCx | Dx | DATA[43:40] | |||||
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
Таким образом, ACCx — это 00, Это означает, что пин ID0 просто привязан к IDBUS, а Dx = 01 означает, что пины DP1/DN1 настроены как USB0_DP/USB0_DN. Именно то, что мы ожидали от стандартного USB-кабеля.
А теперь давайте перехватим что-нибудь поинтереснее:
Аксессуар | ID (HOSTID = 1) |
---|---|
DCSD | 20 00 00 00 00 00 |
KongSWD (без работающего Astris) | 20 02 00 00 00 00 |
KongSWD (с работающим Astris) | A0 00 00 00 00 00 |
KanziSWD (без работающего Astris) | 20 0E 00 00 00 00 |
KanziSWD (с работающим Astris) | A0 0C 00 00 00 00 |
Haywire (HDMI) | 0B F0 00 00 00 00 |
Зарядка UART | 20 00 10 00 00 00 |
Lightning на 3,5 мм/EarPods с Lightning | 04 F1 00 00 00 00 |
Вот полный (?) список запросов IDBUS от @spbdimka:
Совет №1: вы можете легко получить свойства аксессуара, включая его идентификатор, используя accctl:
Это внутренняя утилита Apple, поставляемая с сборками NonUI/InternalUI. Но вы можете легко запустить её на любом устройстве после джейлбрейка.
Совет №2: вы можете легко получить конфигурацию контактов кабеля с помощью diags:
tristar -p
Обратите внимание, что эта команда доступна только на iOS 7+.
Совет №3: вы можете легко отслеживать запросы/ответы 0x74/0x75, генерируемые SWD-пробами, установив
debug
env var, равное 3:astrisctl setenv debug 3
Затем на виртуальном COM от кабеля вы увидите что-то вроде этого:
HOSTID
В одной из таблиц выше можно увидеть упоминание некоего HOSTID. Это 16-битное значение, передаваемое в запросе 0x74. Похоже, что оно также влияет на ответ HiFive. По крайней мере, если установить для него недопустимое значение (да, это возможно с diags), HiFive перестаёт с ним работать:
Впрочем, в прошивке KongSWD/KanziSWD есть переменная окружения disableIdCheck, которую вы можете настроить так, чтобы игнорировать недопустимый HOSTID.
Важное примечание: У Kong и Kanzi нет HiFive в качестве выделенного непрограммируемого чипа. Эти аксессуары эмулируют его с помощью микроконтроллера и/или блока FPGA, что позволяет его легко обновлять/перепрограммировать.
WAKE
В таблице Accessory ID выше можно заметить, что Kong и Kanzi посылают разные ответы в зависимости от того, запускается или нет Astris, это программное обеспечение AppleInternal, предназначенное для отладки с помощью SWD-проб (или зондов). Если вы расшифруете эти ответы с помощью приведённых выше таблиц, то обнаружите, что когда Astris не запускается, зонд будет действовать точно так же, как DCSD — USB на линиях D1 и debug UART на линиях D2. Но когда отладочное программное обеспечение работает, линии ACCID переключаются на SWD.
Но что, если мы хотим запустить Astris после того, как зонд уже подключён к устройству? Что будет делать кабель? Как он будет переключаться между линиями ACC на SWD? Вот тут-то WAKE и вступает в игру! HiFive (или устройство, которое его эмулирует) может инициировать WAKE — и процесс перечисления IDBUS начнётся снова: Tristar отправит запрос 0x74, Kong/Kanzi ответит новым идентификатором, Tristar подтвердит его и направит линии ACC на внутренние линии SWD (SoC должен это поддерживать на физическом уровне, конечно).
Рукопожатия питания
Последнее, что я собираюсь рассмотреть — рукопожатия питания (power handshakes). Это алгоритм, основанный на запросах/ответах IDBUS, которые драйверы ядра Tristar используют перед тем, как разрешить зарядку от аксессуара.
Когда кабель Lightning просто где-то лежит, подключённый к зарядному устройству/компьютеру, но не подключённый к устройству, HiFive ограничивает ток на PWR действительно небольшим значением (около 10-15 мА по моим измерениям). Чтобы включить полный ток, запрос 0x74 должен быть выдан Tristar и обработан HiFive. Для SecureROM/iBoot этого достаточно, но при загрузке ядра необходимо сделать дополнительные шаги:
- TriStar выдаёт два запроса 0x70
- Как только второй запрос обработан HiFive и отправлен ответ, он вообще отключает ток примерно на 20 миллисекунд
- По истечении этого времени Tristar выдаёт ещё один запрос 0x70, но с содержанием 0x80 в данных. HiFive обрабатывает его и отвечает
- На этом этапе драйвер ядра, ответственный за Tristar, должен разрешить зарядку
Важное замечание: это та часть, которую я знаю меньше всего. И это одна из тех частей, которые я в основном сам отреверсил. Таким образом, будьте осторожны с этой информацией
Несколько слов об ESN и интерфейсе Tristar I2C
Ещё одна особенность Tristar, о которой я хотел бы рассказать, — ESN. Это маленький блоб, который Tristar хранит в своём EEPROM (на CBTL1610A2 и более поздних версиях). Его можно получить по IDBUS с помощью кабеля Serial Number Reader (или Kanzi, они в основном одинаковые, за исключением разных USB-PID и немного отличающихся корпусов)
Проще говоря, отправив этот блоб на ttrs.apple.com, вы можете получить серийный номер устройства. Этот механизм используется сотрудниками Apple Store/Apple Premium Reseller для извлечения SN с мёртвых устройств (если Tristar ещё жив):
Что происходит на IDBUS при получении ESN, задокументировал @spbdimka:
Подготовка
Процедура «прошивки» ESN на Tristar называется подготовка (provisioning). Она происходит с диагностикой на стороне устройства, через EzLink на принимающей стороне в три этапа.
Вы можете проверить состояние с помощью diags:
tristar --prov_stat
… а также получить ESN:
tristar --esn
Кстати, у diags вообще богатый набор команд Tristar (доступен, начиная с iOS 7):
Tristar I2C
Tristar доступен на шине I2C (адрес 0x34 для записи, 0x35 для чтения). Именно так diag и драйверы ядра с ним взаимодействуют.
О реестрах публично известно не так уж много. Много информации о самой карте регистра можно получить из утёкшего исходного кода iBoot (только для THS7383 — кажется, обратно совместимого с CBTL1608 — и CBTL1610), но не так много о том, что нужно туда записать, чтобы добиться каких-то интересных результатов.
Ещё одним источником знаний является модуль Tristar из diags (легко извлекаемый через SWD во время его работы). Например, мне удалось отреверсить алгоритмы чтения состояния подготовки и ESN. Затем я реализовал это как дополнение к моей нагрузке для iBoot под названием Lina:
Я также попытался изменить алгоритм записи ESN, но потерпел неудачу — механизм слишком сложный для меня. Однако фрагменты кода от Lina доступны здесь.
Электрические характеристики Tristar
Сам Tristar питается от источника 1.8V. Линии для IDBUS устойчивы к 3.0V, согласно моему осциллографу:
Таким образом, без схемы сдвига уровня лучше не пытаться взаимодействовать с IDBUS с 5V-устройствами, как некоторые модели Arduino.