Lobsters

Reverse Engineering the iPod Classic's Undocumented Mikey Chip

문서 없는 iPod Classic의 Mikey 칩을 역공학해 이어폰 리모컨을 살렸습니다

Rockbox를 구동하는 iPod Classic에서 작동하지 않던 이어폰 리모컨을 되살리기 위해, 작성자는 문서가 없는 Mikey 칩의 레지스터를 직접 조사했습니다. 버튼 이벤트를 해독해 세 버튼을 지원하는 드라이버를 만들었으며, 현재 Rockbox Gerrit에서 검토 중입니다.

AI 요약

Rockbox를 설치한 iPod Classic 6·7세대에서는 이어폰 리모컨의 재생·일시정지와 음량 버튼이 작동하지 않았습니다. 작성자는 헤드폰 잭을 관리하는 Apple의 Mikey 칩을 역공학해 리모컨 입력을 해독하고 Rockbox 드라이버를 작성했습니다. 변경 사항은 Gerrit change 7677로 제출돼 검토 중이며, 아직 정식 빌드에 병합되지는 않았습니다.

문서 없는 Mikey 칩

Mikey는 iPod Classic의 I2C 버스 0번, 주소 0x72에 연결된 컨트롤러입니다. 레지스터는 8개이며, 헤드셋 마이크에 전원을 공급하고 이어폰 리모컨의 버튼을 판독합니다. Rockbox는 녹음에 필요한 마이크 바이어스 전압을 설정하는 데 이 칩을 사용했지만, 리모컨 기능은 구현하지 않았습니다. 소스 코드에는 액세서리 연결 감지와 리모컨 버튼 지원이 TODO로 남아 있었습니다.

작성자가 찾은 선행 자료는 2010년의 iPod Shuffle 3세대 리모컨 역공학 글이었습니다. 원래 블로그는 사라졌지만 위키 미러와 Arduino 구현 코드가 남아 있어 동작 방식을 파악할 수 있었습니다. 가운데 버튼은 마이크 선을 단락하고, 음량 버튼은 서로 다른 저항 부하를 걸어 마이크 선의 전압을 낮춥니다. 버튼 입력 자체는 복잡한 직렬 통신이 아니라 아날로그 신호입니다. 다만 기기를 켤 때 리모컨이 초음파 식별 신호를 보내고 플레이어가 응답합니다. 신호는 전원 공급 8.1ms 뒤에 시작하며, 280kHz와 244kHz의 두 구간으로 구성됩니다. 플레이어의 응답은 4.8ms ACK 펄스입니다. 이 식별 신호는 DRM이 아니며, 작성자는 저항 회로로 음량 버튼을 재현해 이를 확인했습니다.

레지스터를 직접 훑어 찾은 모드

작성자는 Rockbox 디버그 메뉴에 Mikey의 8개 레지스터를 20ms 간격으로 읽고, 값이 바뀔 때마다 타임스탬프와 함께 기록하는 화면을 추가했습니다. 레지스터 0의 값을 0부터 255까지 바꾸며 버튼 입력에 따른 변화를 비교했습니다. 처음에는 기존 재생 버튼 폴링 스레드가 레지스터 0을 20ms마다 7로 되돌리는 사실을 놓쳐, 첫 실험 결과를 전부 폐기해야 했습니다.

깨끗한 데이터로 세 차례 스캔한 결과, 레지스터 0의 특정 구간에서 레지스터 5와 6이 반응했습니다. 값을 좁혀 다시 확인한 끝에 0x2f가 리모컨 판독 모드라는 사실을 찾았습니다. 하위 3비트는 녹음 기능과 같은 마이크 바이어스 값 7을 설정하고, 0x28은 버튼 감지기를 켭니다. 레지스터 5의 0x04와 0x08은 음량 올리기 버튼의 누름과 뗌을, 0x01과 0x02는 음량 내리기 버튼의 누름과 뗌을 나타냅니다. 레지스터 4에서는 가운데 버튼의 상태를 읽습니다. 잭을 뽑으면 칩이 초기화되므로 다시 연결할 때 판독 모드를 재설정해야 합니다.

늦게 도착하는 버튼 해제 이벤트

처음 만든 드라이버는 음량 버튼 상태를 누름·뗌 이벤트로 갱신하고, 가운데 버튼은 레지스터 값의 변화를 읽었습니다. 그러나 실제로 하루 동안 사용하자 가운데 버튼을 한 번 눌렀는데 재생 상태가 두 번 바뀌는 문제가 나타났습니다. 디바운싱으로 해결하려 했지만, 원인은 접점 잡음이 아니었습니다. 버튼 입력이 5초가량 없으면 Mikey의 이벤트 엔진이 잠들고, 다음 가운데 버튼을 누른 뒤 해제 이벤트를 약 1.7~1.8초 늦게 보고했습니다. 버튼을 누른 시점은 즉시 기록하지만 해제와 관련 이벤트는 엔진이 깨어난 뒤 한꺼번에 나타났습니다.

따라서 가운데 버튼의 누름 시간을 신뢰할 수 없었습니다. 드라이버는 누름이 감지될 때마다 60ms짜리 클릭을 발생시키고, 길게 누르기 동작은 지원하지 않도록 바뀌었습니다. Rockbox의 기존 키 매핑은 재생 버튼을 메뉴의 선택 동작으로 처리했지만, 드라이버가 BUTTON_MULTIMEDIA_PLAYPAUSE 코드를 보내도록 수정해 화면에 관계없이 재생·일시정지를 수행하게 했습니다. 음량 버튼도 메뉴와 목록에서 작동하도록 HAVE_VOLUME_IN_LIST 설정을 추가했습니다.

제출 과정과 남은 작업

정식 제출을 준비하며 여러 빌드 설정과 헤드폰 잭 분리 상황을 점검한 결과, 개발 빌드에서 컴파일이 깨지는 문제와 I2C 통신이 실패했을 때 초기화하지 않은 값이 반환되는 문제도 발견했습니다. 후자는 가짜 버튼 입력을 고정할 수 있었습니다. 또 마이크 녹음 중에는 리모컨 폴링 스레드가 모드 레지스터를 덮어쓰지 않도록 명시적인 전환 처리를 추가했습니다.

최종 변경은 8개 파일에 걸쳐 318줄을 추가하는 한 커밋입니다. Rockbox Gerrit change 7677로 제출됐으며, 글 작성 시점에는 검토 중입니다. 작성자는 향후 두 번 클릭으로 다음 곡을 재생하는 동작과, 리모컨이 연결됐을 때만 마이크 선에 전원을 공급해 배터리 소모를 줄이는 작업을 계획으로 남겼습니다.

Lobsters 반응

  • @Lt_Riza_Hawkeye — 기술적인 내용은 흥미롭지만, Claude가 쓴 글이 아니었으면 좋겠습니다.
    • @dv — AI가 글을 생성했을 뿐 아니라, Claude가 자기 기능을 끝냈다고 말하는 것처럼 읽히는 블로그 글은 처음 봅니다. Claude가 일을 다 하고 나서 "좋아, 블로그 글로 무슨 일이 있었는지 요약해"라고 시킨 뒤, 읽어보지도 않고 그대로 올린 느낌입니다. 정말 불쾌합니다.

원문: Terminal Bytes / 번역·요약: Trawling