기기들이 서로간에 통신하는 많은 방법들이 존재하고 있지만 근거리에서 유선을 사용하려면 USB 방식이 가장 대중적이고 편리한 방법이다. 안드로이드(Android) 에서도 USB를 지원하며 다양한 기기들과 통신하는 방법을 제공하고 있다.
그중 AOA (Android Open Accessory)에 관해 알아보자.
주의
사내에서 진행된 기술 세미나의 발표 자료를 컬럼 형태로 수정해서 공유합니다.
모든 내용에 관한 저작권은 발표자와 (주)데브구루에 있습니다.
내용 중 잘못된 부분이나 새로운 정보가 있다면 언제라도 아래 메일로 연락
부탁드립니다.
발표 : 김진환 (jhkim@devguru.co.kr)
정리 : 송지호 (songjiho@devguru.co.kr)
USB의 버스 특성 상 USB버스 상에는 하나의 Host와 여러 주변 장치(Peripheral)들이 존재하나 논리적으로는 하나의 Host에 하나의 Device가 연결된 모습이 된다. 이러한 이유로 안드로이드가 다른 USB 장치와 연결될 때
1) 연결되는 다른 USB 장치가 Host라면 Android 기기는 Device가 되어야 하고
2) 반대로 안드로이드 기기가 Host로 동작하고 연결되는 장치가 Device가 되는 경우가 있을 수 있다.
2) 반대로 안드로이드 기기가 Host로 동작하고 연결되는 장치가 Device가 되는 경우가 있을 수 있다.
안드로이드 기기를 PC와 연결할 때가 1)번의 모습이고, 안드로이드 기기에
USB마우스나 저장장치(Mass Storage)를 연결하는 경우가 2)번이 된다.
안드로이드 기기는 1)혼자 동작하는(Host) 2)모바일 장치(Device)라는 특성 상 위의 두 가지 경우가 모두 빈번하게 일어날 수 있는 상황이라 이 두 경우에 대해서 안드로이드에서 어떠한 준비를 하였고 이를 어떻게 이용할 수 있는지 살펴보자.
1.1.1) 가장 고전적인 방법은 하나의 기기에 Host와 Device용 포트를 모두 준비하는 것이다.
구조적으로는 USB Host Controller도 있고 Device로 동작하기 위한 별도의 USB 칩도 내장되어서 각각이 물리적으로 연결 포트를 구분하여 사용되는 것이다. PC와 연결할 때는 USB B커넥터에 연결하고, 기기에 USB저장치나 마우스를 연결하고 싶다면 A커넥터에 주변 기기들을 연결하는 모습이다. 초기에 USB 프린터에서 이런 모습들이 가끔 있었다. PC와 연결해서 프린팅을 할 때와 USB 메모리 스틱에 담겨 있는 사진을 바로 출력할 때는 프린터의 역할이 다르기 때문에 Host와 Device 기능을 모두 포함해야만 두가지 상황을 모두 대응 할 수 있었다.
1.1.2) 물리적으로 두개의 다른 포트를 가지는 단점 극복을 위해서 USB 포럼에서는 하나의 USB포트를 통해서 Host와 Device 중 어떠한 모드로 동작할지 구분하는 방법을 만들었는데 이를 OTG (On The Go)라 하고 이 OTG를 이용해서 안드로이드를 Host나 Device로 동작 시키는 방법이 있다. 이 경우 USB 스펙상 OTG 케이블의 방향에 의해 호스트와 디바이스가 결정되므로 사용자가 매번 케이블의 방향을 확인해야 하고 양쪽이 모두 미니5핀을 지원해야 하는 단점이 있다.참고 1) 특히 이미 사용되고 있던 케이블을 사용할 수 없고 새로운 규격인 미니5핀을 사용해야 한다는 제한 조건 때문에 기존의 A커넥터를 사용하는(즉 USB 호스트 컨트롤러를 가지고 있는) PC와의 연결은 불가능 하게 된다. 하지만 하나의 포트로 Host와 Device모두를 지원할 수 있는 장점 때문에 안드로이드에서는 디바이스와의 연결은 OTG를 사용하고 있다.
OTG와 관련해서 듀얼-롤 디바이스(Dual-role Device)를 검색해 보자. 조금 상세한 내용을 쉽게 찾아볼 수 있을 것이다. 듀얼-롤 디바이스들 상호간에는 USB케이블의 방향에 종속되어서 자신의 역할을 결정하는 방법 외에도 HNP(Host Negotiation Protocol)라는 협상 프로토콜을 이용해서 Host를 결정하는 방법도 제시되어 있다. 하드웨어적으로도 가능하고 소프트웨어적으로도 가능하다고 하는데 필자는 이러한 구조의 디바이스를 개발하거나 분석해 본 경험이 없다.
4.1. Accessory Mode에서 동작할 앱이 있는 경우 manufacturer name, model name, version에 맞는 앱이 실행된다.
4.2. Accessory Mode에서 동작할 앱이 없는 경우 액세서리(Host, PC)에서 보낸 Identity의 Description과 URL이 표시된다.
4.3. 하나의 액세서리에 여러 개의 앱이 등록된 경우는 등록된 앱들을 보여주고 사용자가 선택하게 한다. 이후에는 최초에 선택된 앱으로 자동 실행되고 선택된 앱을 삭제하고 다시 설치해야 선택 팝업 창이 다시 발생한다. 현재 까지의 테스트 결과로는 이후 액세서리 모드로 변경에 실패하고 안드로이드 기기를 재 부팅 해야 정상 동작 하는 경우가 있었다. 아직 까지는 안드로이드의 문제인지 필자가 테스트한 어플리케이션의 문제인지 명확하지는 않다.
Bus Mastering 구조인
USB에서 안드로이드 기기는 하드웨어와
OS의 발전사적인 요소와 사용 상의 필요로 인해서
Host와 Device의 두
가지 기능을 그때그때 수행해야 하는 디바이스로 성장했고 안드로이드에서
USB스팩안에서 벤더에게 허락된 자유도를 최대한
활용해서 두 가지 기능을 모두 구현할 수 있게 만들어 놓은 구조가
AOA인 것이다.
많은
USB 문서들은 Host를
중심으로 기술하는데 AOA에서는 안드로이드를
중심에 놓고 기술하는 것 같아 문서를 보는데 약간의 불편함이 있었다.
호스트에서 전송하는
Vendor Specific Command를
USB 스펙과 유사한 폼으로 정리해 보았다.
6.1 USB Vendor Request Packet Format for AOA
안드로이드 기기는 1)혼자 동작하는(Host) 2)모바일 장치(Device)라는 특성 상 위의 두 가지 경우가 모두 빈번하게 일어날 수 있는 상황이라 이 두 경우에 대해서 안드로이드에서 어떠한 준비를 하였고 이를 어떻게 이용할 수 있는지 살펴보자.
1.1 하나기 기기에
USB Host와 Device 기능을 모두 넣는 방법
1.1.1) 가장 고전적인 방법은 하나의 기기에 Host와 Device용 포트를 모두 준비하는 것이다.
구조적으로는 USB Host Controller도 있고 Device로 동작하기 위한 별도의 USB 칩도 내장되어서 각각이 물리적으로 연결 포트를 구분하여 사용되는 것이다. PC와 연결할 때는 USB B커넥터에 연결하고, 기기에 USB저장치나 마우스를 연결하고 싶다면 A커넥터에 주변 기기들을 연결하는 모습이다. 초기에 USB 프린터에서 이런 모습들이 가끔 있었다. PC와 연결해서 프린팅을 할 때와 USB 메모리 스틱에 담겨 있는 사진을 바로 출력할 때는 프린터의 역할이 다르기 때문에 Host와 Device 기능을 모두 포함해야만 두가지 상황을 모두 대응 할 수 있었다.
1.1.2) 물리적으로 두개의 다른 포트를 가지는 단점 극복을 위해서 USB 포럼에서는 하나의 USB포트를 통해서 Host와 Device 중 어떠한 모드로 동작할지 구분하는 방법을 만들었는데 이를 OTG (On The Go)라 하고 이 OTG를 이용해서 안드로이드를 Host나 Device로 동작 시키는 방법이 있다. 이 경우 USB 스펙상 OTG 케이블의 방향에 의해 호스트와 디바이스가 결정되므로 사용자가 매번 케이블의 방향을 확인해야 하고 양쪽이 모두 미니5핀을 지원해야 하는 단점이 있다.참고 1) 특히 이미 사용되고 있던 케이블을 사용할 수 없고 새로운 규격인 미니5핀을 사용해야 한다는 제한 조건 때문에 기존의 A커넥터를 사용하는(즉 USB 호스트 컨트롤러를 가지고 있는) PC와의 연결은 불가능 하게 된다. 하지만 하나의 포트로 Host와 Device모두를 지원할 수 있는 장점 때문에 안드로이드에서는 디바이스와의 연결은 OTG를 사용하고 있다.
참고 1)
OTG란 On THE GO 의 약어로 바로 실행한다는 의미를 가지고 있다. USB
장치를 직접 인식하여 바로 동작 하도록 지원해 준다는 의미이다. 현재는 USB
3.1도 지원하고 있다. 기존 B타입 커넥터를 위해서 USB3.1 OTG변황 젠더들도
많이 판매되고 있다. OTG와 관련해서 듀얼-롤 디바이스(Dual-role Device)를 검색해 보자. 조금 상세한 내용을 쉽게 찾아볼 수 있을 것이다. 듀얼-롤 디바이스들 상호간에는 USB케이블의 방향에 종속되어서 자신의 역할을 결정하는 방법 외에도 HNP(Host Negotiation Protocol)라는 협상 프로토콜을 이용해서 Host를 결정하는 방법도 제시되어 있다. 하드웨어적으로도 가능하고 소프트웨어적으로도 가능하다고 하는데 필자는 이러한 구조의 디바이스를 개발하거나 분석해 본 경험이 없다.
2. AOA 소개
이러한 호스트와의 연결 문제의 해결을 위해서 구글에서 제안 된 방법이
안드로이드 오픈 액세서리 모드이다.
AOA는 이미 시중에 널리 퍼진 호스트 컨트롤러와의 통신을 위해서 기존의 하드웨어 적인 요소를 변경할 수 없다는 전제 하에 만들어졌다. 즉 소프트웨어의 동작으로 기능을 변경하는 소프트웨어 적인 프로토콜이란 의미이며 하드웨어적으로 변경이 없으므로 PC가 호스트이고 안드로이드가 디바이스로 동작한다는 의미이기도 하다.
유사한 문제에 대해서 USB 스펙에서는 인터페이스를 여러 개 만들고 필요한 인터페이스를 선택하여 사용하는 방법, 인터페이스를 상황에 따라서 변경하는 Alternate Interface를 이용하는 방법, 그리고 Configuration을 여러 개 만들고 상황에 따라서 특정 Configuration을 활성화 시켜서 사용하는 방법 등을 제시하고 있다.
AOA는 이들 중 Configuration을 변경하는 방법과 유사한 방법이다. 차이점이라면 Configuration을 변경하는 방법은 USB 스펙에 있는 표준 커멘드(Standard Command)인 SET_CONFIGURTATION을 사용하고 AOA에는 이와 유사한 벤더커멘드(Vendor Command)를 제시하고 이를 통해서 모드를 변경한다는 것이다.
AOA를 이용하는 절차는 아래 그림과 같이 1)정상적인 연결 2)AOA 프로토콜을 지원하는 장치인지 확인 3)지원 버전 확인 4)식별자 전송 5)액세서리 모드 시작 명령 6)안드로이드에서 모드 변경 7)재 연결 8)Bulk Endpoint를 통한 통신으로 진행되게 된다.
AOA의 재미있는 특징 중에 Manifest 를 통해서 호스트가 통신하기 원하는
앱을 직접 선택할 수 있는 기능이 있다. 안드로이드 앱이 인스톨 될 때
Manifest를 등록하고, 상기 그림2의 4)번에서 호스트가 식별자를 전달할 때
이 식별자와 매칭 되는 Manifest를 등록한 앱이 자동으로 실행되고 USB를
통해서 호스트와 통신하게 된다.
3. 코드 작성을 위한 정보들
사실 AOA는 특별한 기술사양이라기 보다는 하나의 디바이스를 상황에 맞춰서 다양한 디바이스로 사용하기 위해 많이 사용되고 있던 방법을 구글에서 자신들의 안드로이드 OS에 탑재한 것이다.
여기에서 ‘다양한 디바이스’ 라는 말에 초점을 맞춰 USB 디바이스별로 꼭 필요한 정보인 VID와 PID 그리로 Interface를 생각해 보자.
안드로이드 기기가 어떠한 모드이든지 간에 VID는 Google의 VID 0x18D1을 사용하고 AOA 버전1.0에서 PID는 0x2D00 와 0x2D01를 사용한다. 0x2D00에서는 Bulk IN과 Bulk OUT 전송을 위한 Endpoint를 가지고 있는 인터페이스만 들어 있고 0x2D01 일때는 ADB 지원을 위한 인터페이스가 하나 더 추가되어 있다.
ADB가 포함된 경우는 AOA 모드를 개발할 때 디버깅을 하라는 세심한 배려일 듯 하다.참고2)
AOA는 이미 시중에 널리 퍼진 호스트 컨트롤러와의 통신을 위해서 기존의 하드웨어 적인 요소를 변경할 수 없다는 전제 하에 만들어졌다. 즉 소프트웨어의 동작으로 기능을 변경하는 소프트웨어 적인 프로토콜이란 의미이며 하드웨어적으로 변경이 없으므로 PC가 호스트이고 안드로이드가 디바이스로 동작한다는 의미이기도 하다.
유사한 문제에 대해서 USB 스펙에서는 인터페이스를 여러 개 만들고 필요한 인터페이스를 선택하여 사용하는 방법, 인터페이스를 상황에 따라서 변경하는 Alternate Interface를 이용하는 방법, 그리고 Configuration을 여러 개 만들고 상황에 따라서 특정 Configuration을 활성화 시켜서 사용하는 방법 등을 제시하고 있다.
AOA는 이들 중 Configuration을 변경하는 방법과 유사한 방법이다. 차이점이라면 Configuration을 변경하는 방법은 USB 스펙에 있는 표준 커멘드(Standard Command)인 SET_CONFIGURTATION을 사용하고 AOA에는 이와 유사한 벤더커멘드(Vendor Command)를 제시하고 이를 통해서 모드를 변경한다는 것이다.
AOA를 이용하는 절차는 아래 그림과 같이 1)정상적인 연결 2)AOA 프로토콜을 지원하는 장치인지 확인 3)지원 버전 확인 4)식별자 전송 5)액세서리 모드 시작 명령 6)안드로이드에서 모드 변경 7)재 연결 8)Bulk Endpoint를 통한 통신으로 진행되게 된다.
사용자 입장에서는 호스트(PC)와 연결된 이후 별도로 앱을 찾아서
실행시켜야 하는 번거로움이 줄어들게 된다. 이는 개발자에게도
사용자로부터 들어오는 ‘프로그램이 동작하지 않는다’ 거나 ‘PC와 연결한
이후 어떻게 진행해요 하는가?’ 와 같은 넌센스콜을 줄일 수 있는 이점이
있다.
그림3 AOA를 위한 안드로이드 어플리케이션 측 설정 사항
사실 AOA는 특별한 기술사양이라기 보다는 하나의 디바이스를 상황에 맞춰서 다양한 디바이스로 사용하기 위해 많이 사용되고 있던 방법을 구글에서 자신들의 안드로이드 OS에 탑재한 것이다.
여기에서 ‘다양한 디바이스’ 라는 말에 초점을 맞춰 USB 디바이스별로 꼭 필요한 정보인 VID와 PID 그리로 Interface를 생각해 보자.
안드로이드 기기가 어떠한 모드이든지 간에 VID는 Google의 VID 0x18D1을 사용하고 AOA 버전1.0에서 PID는 0x2D00 와 0x2D01를 사용한다. 0x2D00에서는 Bulk IN과 Bulk OUT 전송을 위한 Endpoint를 가지고 있는 인터페이스만 들어 있고 0x2D01 일때는 ADB 지원을 위한 인터페이스가 하나 더 추가되어 있다.
ADB가 포함된 경우는 AOA 모드를 개발할 때 디버깅을 하라는 세심한 배려일 듯 하다.참고2)
참고 2)
https://source.android.com/devices/accessories/aoa2
를 참조하면 AOA v20에서 신규로 지원하는 인터페이스들을 볼 수
있다.
PC에서 AOA 모드의 안드로이드 기기와 Bulk IN/OUT 통신을 하기 위해서는 단순한 통로 역할을 하는 간단한 디바이스 드라이버를
만들거나 MS의 WinUSB를 이용하면 된다. 리눅스라면 libusb를 이용하면 편리하겠다. WinUSB나 libusb는 디바이스 드라이버를 작성해본 분이라면 쉽게 사용 가능하겠고 그렇지 않은
개발자들도 정보를 잘 찾아보면 드라이버 작성법을 배워야 하는 부담 없이
개발이 가능할 것으로 보인다.
안드로이드의 실제 코드는 아래 사이트를 통해 찾아보면 좋겠다.
안드로이드 USB 통신 절차 예제
4. AOA를 이용하는 방법에 관해서 몇 가지
정보들
일반적인 동작 시나리오에서 안드로이드 앱은 Manifest 파일을 통해서 USB
액세서리와 통신할 앱으로 등록하고, 등록된 앱은 액세서리에서 모드 변경 요청이
들어올 때 자동으로 실행되고 USB엑세서리와 통신할 수 있는 권한을 획득한다. 이
경우 다음과 같은 세가지 상황을 테스트해 보았다. 4.1. Accessory Mode에서 동작할 앱이 있는 경우 manufacturer name, model name, version에 맞는 앱이 실행된다.
4.2. Accessory Mode에서 동작할 앱이 없는 경우 액세서리(Host, PC)에서 보낸 Identity의 Description과 URL이 표시된다.
4.3. 하나의 액세서리에 여러 개의 앱이 등록된 경우는 등록된 앱들을 보여주고 사용자가 선택하게 한다. 이후에는 최초에 선택된 앱으로 자동 실행되고 선택된 앱을 삭제하고 다시 설치해야 선택 팝업 창이 다시 발생한다. 현재 까지의 테스트 결과로는 이후 액세서리 모드로 변경에 실패하고 안드로이드 기기를 재 부팅 해야 정상 동작 하는 경우가 있었다. 아직 까지는 안드로이드의 문제인지 필자가 테스트한 어플리케이션의 문제인지 명확하지는 않다.
Manifest 에 대해서는 아래 사이트를 참조하면 상세한 내용을 확인 할 수 있다.
AOA에서 모드 변경이란
USB 버스 리셋 후 새로운 디바이스 디스크립터를 올려주는 것이다. 그렇다면 원래의 디바이스 디스크립터로 돌려주는 방법은 어떻게 될까?? 현재 프로토콜상에서는 그러한 기능이 들어 있지 않다. 따라서 원래 상태로 돌아오려면 물리적으로 재 연결하거나 소프트웨어적으로
버스 리셋을 발생 시키거나 해야 한다. 또한
AOA 모드에서는 MTP나
CDC등의 다른 기능들을 함께 사용할 수 없다는
제한점이 있다.
참고 자료
*
AOA 프로토콜
6. 편집자 주)
bmRequest Type | bRequest | wValue | wIndex | wLength | Data or Description |
---|---|---|---|---|---|
0xC0 | 0x33 (51) | 0 | 0 | 2 | Protocol version Number (16 bits in little Endian format) If zero is returned, the device is not AOA compliant |
0x40 | 0x34 (52) | 0 | StringID | Size of Data | Zero terminated UTF8 String * StringID means 0 : manufacturer name 1 : model name 2 : description 3 : version 4 : URI 5 : serial number |
0x40 | 0x35 (53) | 0 | 0 | 0 | None * Start the device in accessory mode |
※ 표지에 쓰인 이미지는 아래를 참조함
댓글 없음:
댓글 쓰기