앞서 USB Client Device 에 대해서 다소 추상적인 개념들을 살펴 보았습니다. 이제는 본격적으로 어떤것들이 어떻게 오가는지에 대해서 알아보겠습니다.



USB Device Descriptor


USB Clinet 가 USB Host 에 연결되면, USB Host(PC)는 해당 Client 에게 Descriptor 를 요청합니다. 통신을 하려면 이게 어떤 놈이지 알아야 되지 않겠습니까? 이 정보가 들어있는 Descriptor 가 필요하지요. Descriptor 의 구조는 아래 그림과 같습니다.




출처:http://howusbworks.blogspot.kr/



 제가 처음 이 그림을 보았을 때, 무슨 의미인지 잘 몰랐습니다. Device 의 Architecture 에 대한 것인지 추상적인 개념인건지 감이 잘 안 오더라고요. 이건 말 그대로 descriptor 의 구조입니다. ^-^;


 개발을 하면서 각종 미디어나 프로토콜의 header 를 많이 보셨을거라고 생각합니다. 맨 처음에는 ID3 ASCII 값으로 시작하고, 뒤에 4 바이트는 데이터 길이고...... 이런 식으로 되어 있지요. 대게 이런 헤더들은 serial 하게 나옵니다. 그런데 지금 살펴볼 Device Descriptor 는 병렬구조입니다. A 다음에 B 가 나오고, 이 뒤에 C 가 나오는것이 아니라, A 다음에 B 가 n 개가 나오고, 각 B 에 대해서 C 가 n 개가 존재하는 형태인 것이죠. 그래서 그림을 그리면 보시는것처럼 다소 복잡하게 표현됩니다. 트리 형태로 말이죠. :)


 하나씩 살펴보기에 앞서, 모든 descriptor 들은 아래와 같은 구조로 되어 있습니다.


 Offset

Field 

Size

Value 

Description 

 0

bLength

1

Number 

해당 Descriptor 의 바이트 길이

 1

bDescription Type

1

Constant

Descriptor 타입 

 2

... 

n

 

... 




Device Descriptor



 가장 먼저 나오는 Descriptor 로서 전반적인 Device 의 설명이 포함됩니다.


 Offset

Field

Size

Value

Description

 0

bLength

1

Number

해당 Descriptor 의 바이트 길이 (18 bytes)

 1

bDescription Type

1

Constant

Device Descriptor (0x01)

 2

bcdUSB

2

BCD

해당 USB 가 지원하는 최대버전을 표시

USB 1.0 은 0x0100, USB 1.1 은 0x0110, USB 2.0 은 0x0200 처럼 표시함

4

bDeviceClass

1

Class

Host(PC)가 해당 USB 를 위한 class driver 를 찾을때 사용함.

Device Class 라는것은 USB Org 에 정의되어 있음.

만약 이어서 나오는 Interface Descriptor 에서 class 를 정의하려면 0x00 을 사용합니다.

만약 vendor 가 직접 명시한 class code 를 쓰려면, 0xff 를 사용합니다.

5

bDeviceSubClass

1

SubClass

USB Org 에서 지정된 subclass code

6

bDeviceProtocol

1

Protocol

USB Org 에서 지정한 protocol code

7

bMaxPacketSize

1

Number

Zero Endpoint 의 최대 패킷 사이즈

8, 16, 32, 64 중 택 1 

8

idVendor

2

ID

USB Org 에서 지정한 제공자(vendor) ID 

10

idProduct

2

ID

제조사에서 지정한 Product ID 

12

bcdDevice

2

BCD

Device 의 릴리즈 번호

14

iManufacturer

1

Index 

제조사의 문자열 descriptor 의 index

15

iProduct

1

Index 

제품의 문자열 descriptor 의 index

16

iSerialNumber

1

Index

시리얼 번호의 문자열 descriptor 의 index 

17

bNumConfigurations

1

Integer

이어서 나오는 Configuration Descriptor 의 갯수



Field 에 보면 직감하시겠지만, b 로 시작하는것은 Binary 를 의미합니다. 그리고 bcd 는 BCD 를, i 는 index 를 의미하지요. 이 index 에 대해서는 밑에서 설명 드리겠습니다. class 니 SubClass 니 하는 것들이 나옵니다. 이것은 무엇일까요?


Class 는 말 그대로 클래스입니다. -_-; 비슷한 성격끼리 묶어놓은 거라고 생각하시면 쉬울것 같네요. http://www.usb.org 에 보면 정의된 class 목록을 볼 수 있습니다. (http://www.usb.org/developers/defined_class)





 앞서 Descriptor 의 구조를 보셔서 아시겠지만, 모두가 특정 포맷에 맞추어져 있습니다. 각 offset 에 있는 데이터가 의미하는 정보가 있지요. 만약 제조사의 이름이나 디바이스의 제품명은 어떻게 넣어야 할까요? 제조사가 "crystalcube" 라면 어디에 어떻게 넣어야 할까요? 일반적으로 어떤 포멧들은 [string byte length] + [ASCII] 형태로 넣곤 합니다. 그런데 이런 형태의 경우 다음 데이터(정보)의 offset 이 고정되지 않습니다. 예를들어 위 Device Descriptor 에서 offset 14 인 'iManufacturer' 를 보죠. 제조사 이름이 "crystalcube" 인 경우 11 byte 이므로, 0x0B, 'c', 'r', 'y', 's', 't', 'a', 'l', 'c', 'u', 'b', 'e' 가 들어갈 것입니다. 총 12 byte 가 되겠네요. 그 다음 나오는 iProduct 는 offsset 이 15 가 아니라, 14 + 12 인 26 이 됩니다. 제조사 이름이 "ms" 라면, iProduct 의 offset 은 14 + 3 인 17 이 되고요. 다시말해 iManufacturer 의 값에 따라 다음 정보의 offset 이 변동됩니다. 이런 단점을 막기 위해서 USB 에서는 index 라는 개념을 사용합니다.


 전체 Descriptor 를 보면 'Device Descriptor' -> ('Configuration Descriptor' -> ('Interface Descriptor' -> 'Endpoint Descriptor'*)*)* -> 'String Descriptor'* 형태로 되어 있습니다. 직렬로 Serialize 했을 때 그렇다는 이야깁니다. * 표시가 붙은 것은 1개 이상 나올 수 있다는 이야기 입니다. index 라는 것이 바로, 마지막에 나오는 'String Descriptor' 의 index 입니다. 이 string descriptor 는 아래와 같은 구조로 되어 있고, 여러개가 나올 수 있습니다.


 Offset

Field

Size

Value

Description

 0

bLength

1

Number

데이터의 길이

 1

bType

1

Constant

타입 (string 은 0x03)

 2

String

n

Unicode

유니코드로서 한 문자당 2 바이트

 

이런 구조의 String Descriptor 가 Array 형태로 여러개 존재합니다. index 라는 것은 각 Descriptor 가 string descriptor array 에서 몇 번째냐는 말이죠. index 는 당연히 0 부터 시작합니다.




.NETMF Code



 이제 해당 descriptor 의 코드를 살펴보겠습니다. 먼저 코드는 아래와 같습니다.



Configuration.DeviceDescriptor deviceDescriptor = new Configuration.DeviceDescriptor(0x04D8, 0x0009, 0x0100);
deviceDescriptor.bcdUSB = 0x0200;
deviceDescriptor.bDeviceClass = 0x00;
deviceDescriptor.bDeviceSubClass = 0x00;
deviceDescriptor.bDeviceProtocol = 0x00;
deviceDescriptor.bMaxPacketSize0 = 0x40;
deviceDescriptor.iManufacturer = 1;
deviceDescriptor.iProduct = 2;
deviceDescriptor.iSerialNumber = 3;



https://msdn.microsoft.com/en-us/library/microsoft.spot.hardware.usbclient.configuration.devicedescriptor(v=vs.102).aspx

위 페이지에 해당 클래스에 대해서 자세히 나와 있습니다.


작성한 Descriptor 를 살펴보면, 생성자에 들어간 0x04D8 은 Vendor ID, 0x0009 는 Product ID, 0x0100 은 USB 버전을 의미합니다. vendor ID 0x04D8 은 'Microchip Technology, Inc.' 입니다. 즉, 그냥 굴러다니는거 주워다가 썼습니다. 만약 자신의 vendor ID 를 가지고 싶으시다면, usb.org 에서 신청하시면 됩니다. 물론 유료입니다. -_-; Product ID 도 역시 그냥 아무거나 넣었습니다. 마지막 0x0100 은 이 디바이스의 version 입니다. 그냥 1.0 이라고 넣어보았습니다.


이후 나오는 프로퍼티들은 표에서 설명한 그대로입니다. iManufacturer, iPoduct, iSerialNumber 는 각각 1, 2, 3 으로 값을 넣었는데, 나중에 String Descriptor 를 해당 index 에 맞게 넣을 예정입니다. :)




나머지 Descriptor 는 다음 포스팅에서 다루도록 하겠습니다.




'USB Client 만들기' 카테고리의 다른 글

.NET Micro Framework 에서 USB Client 만들기 1부  (0) 2015.05.07