Linux
[USB] Device Descriptor
aucd29
2013. 9. 26. 20:54
USB 장치가 USB포트에 끼워지면,
호스트 컨트롤러는 이를 알아채고,
USB 장치에게 묻게 된다.
넌 누구니?
그럼 USB 장치는 자신의 정체를 밝혀야 한다.
만일 장치에서 유효한 대답이 없으면,
USB 장치가 아니라고 판단을 하고
호스트 컨트롤러는 이후 그 장치를 무시하게 된다.
이때 자신의 정체를 밝히기 위해
USB 디바이스가 호스트에 전송하는 데이터를 일컬어
디스크립터(descriptor)라고 부르자고 정해놨다.
누가? USB spec.을 만든 사람들이.
디스크립터는 여러 종류가 있는데,
제일 먼저 디바이스 디스크립터(Device Descriptor)를 디벼보자.
디바이스 디스크립터는 말 그대로
USB 디바이스가 난 이런 디바이스 이다.
라고 호스트에게 알리는 내용이 들어 있다.
디바이스 디스크립터를 자세히 들여다 보면 다음과 같은 항목들이 있다.
(USB 2.0 Spec 9.6.1 참조)
Offset
Field
Size
Value
Description
0
bLength
1
Number
Size of the Descriptor in Bytes (18 bytes)
1
bDescriptorType
1
Constant
Device Descriptor (0x01)
2
bcdUSB
2
BCD
USB Specification Number which device complies too.
4
bDeviceClass
1
Class
Class Code (Assigned by USB Org)
5
bDeviceSubClass
1
SubClass
Subclass Code (Assigned by USB Org)
6
bDeviceProtocol
1
Protocol
Protocol Code (Assigned by USB Org)
7
bMaxPacketSize
1
Number
Maximum Packet Size for Zero Endpoint.
Valid Sizes are 8, 16, 32, 64
8
idVendor
2
ID
Vendor ID (Assigned by USB Org)
10
idProduct
2
ID
Product ID (Assigned by Manufacturer)
12
bcdDevice
2
BCD
Device Release Number
14
iManufacturer
1
Index
Index of Manufacturer String Descriptor
15
iProduct
1
Index
Index of Product String Descriptor
16
iSerialNumber
1
Index
Index of Serial Number String Descriptor
17
bNumConfigurations
1
Integer
Number of Possible Configurations
<출처 : USB in a NutShell >
눈이 뱅뱅(@.@) 돌아 가시는가?
풀어서 설명해 보자면,
첫 번째 항목 ( bLength ) 은 이 디스크립터의 길이를 나타내고
( 디바이스 디스크립터의 크기는 18 Byte로 고정이다. )
두 번째 항목 ( bDescriptorType ) 은 이 디스크립터가 디바이스 디스크립터 임을 나타낸다.
( 0x01은 디바이스 디스크립터를 의미한다. 라고 미리 정해져 있다. )
세번째 항목 ( bcdUSB ) 이 이 디바이스가 USB 1.1 또는 USB 2.0 중 어떤 스펙을 따르는지를 나타내는데,
USB 1.1이 Full Speed (12Mbit/s ), USB 2.0이 High Speed ( 480 Mbit/s )를 지원하므로,
그가이꺼 대~충 설명하자면,
디바이스의 속도를 나타낸다고 봐도 무방하겠다.
이 항목이 0x0100이라면 Full Speed로 동작하는 디바이스 이고,
0x0200이라면 High Speed로 동작하는 디바이스 이다.
(엄밀히 말하자면 조금 틀린 표현이지만, 일단 패쓰)
네 번째 항목 ( bDeviceClass ) 은 이 디바이스가 어떤 클래스에 속해 있는지를 보여준다.
디바이스 클래스가 뭔고 하면
http://www.usb.org/developers/defined_class의 첫 번째 테이블을 보시라.
bDeviceSubClass에 대한 설명은 같은 웹페이지 아래쪽에 있는
“Device / Interface Class Code Details” 라는 제목이 붙은 세개의 테이블로 대신한다.
이제부터가 디바이스 디스크립터의 알짜배기라 할 수 있다.
일곱 번째, bMaxPacketSize 항목
앞서 강의에서 설명한 Default Control Endpoint에서 한번에 전송 가능한 데이터 크기를 나타낸다.
보통은 디바이스에서 Default Control Endpoint ( Endpoint 0 )를 위해 할당된 버퍼의 크기를 그 값으로 준다.
우리가 쓸 PDIUSBD12의 EP0의 버퍼 크기는 16 Byte 이므로, 여기에 16을 써 넣어야 한다.
실제 버퍼는 16 Byte인데, 여기에 32를 써 넣는다면?
디바이스가 자기는 한번에 32 Byte를 받을 수 있다고 했으므로, 호스트는 믿고 한번에 32 Byte를 날린다.
그런데, 버퍼메모리는 16 Byte밖에 없으므로 나머지 16 Byte는 날라가 버릴 것이다. 결과는 통신 오류.
본좌도 안 해 봤다. 시간 많은 행자들 함 해보시라. ㅋㅋ
여덟 번째, idVendor 항목
USB.org에 돈 내고 등록하면, 자기 회사 이름에 번호를 하나 부여해 준다.
http://www.usb.org/developers/vendor/ 를 참조하시라.
물론 기존의 아이디들에 겹치지 않게 아무 아이디나 하나 골라 써도 되지만,
제품 출시를 목적으로 할 경우에는 등록하는 게 좋을 게다.
아홉 번째, idProduct 항목
제품번호이다. 맘에 드는 숫자를 암거나 골라 잡으시라. 개발자 맘이다.
호스트는 idVendor와 idProduct Field를 보고 이 장치에 맞는 드라이버를 로딩하게 된다.
열 번째, bcdDevice는
제품 리비젼 번호쯤으로 생각하면 되겠다.
iManufacturer, iProduct, iSerialNumber는
각각 제조사, 상품명, 시리얼번호를 나타내는 문자열이
String Descriptor의 몇 번째에 위치하는지를 나타내는 인덱스이다.
String Descriptor는 말 그대로 문자열을 저장하는데,
있어도 그만, 없어도 그만인 디스크립터이다.
따라서 String Descriptor가 없다면, 위 세 항목을 다 0으로 세팅해 주면 되고,
있다면 각각에 맞는 인덱스를 주면 된다.
아래에서 예를 들어주겠다.
맨 마지막 bNumConfigurations 항목은
이 디바이스가 가질 수 있는 Configuration의 개수를 나타낸다.
대부분의 디바이스는 하나의 Configuration만 가진다.
이 Configuration에 대해서는 다음 강좌에 자세히 설명하도록 하겠다.
이제까지 Device Descriptor의 각 Field에 대해 알아보았는데,
실제 어떻게 이 디스크립터가 정의되는지 UniFull 펌웨어 예제의 일부를 살펴보자.
먼저 헤더파일에
typedef struct _USB_DEVICE_DESCRIPTOR {
UCHAR bLength;
UCHAR bDescriptorType;
USHORT bcdUSB;
UCHAR bDeviceClass;
UCHAR bDeviceSubClass;
UCHAR bDeviceProtocol;
UCHAR bMaxPacketSize;
USHORT idVendor;
USHORT idProduct;
USHORT bcdDevice;
UCHAR iManufacturer;
UCHAR iProduct;
UCHAR iSerialNumber;
UCHAR bNumConfigurations;
} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
라고 디바이스 디스크립터의 구조체가 정의되어 있고,
실제 우리가 다룰 소스에는
USB_DEVICE_DESCRIPTOR DeviceDescr =
{
sizeof(USB_DEVICE_DESCRIPTOR),
USB_DEVICE_DESCRIPTOR_TYPE,
0x0100,
USB_CLASS_CODE_TEST_CLASS_DEVICE,
0,
0,
EP0_PACKET_SIZE,
0x0905,
0x0010,
0x0000,
1,
2,
0,
1
};
요렇게 USB_DEVICE_DESCRIPTOR 형 DeviceDescr 변수가 초기화 되어 있다.
따라서 호스트가 “야! 디바이스 디스크립터 한 장만 보내 도고.” 하면
DeviceDescr 변수가 저장된 메모리를 찾아내어 디바이스 디스크립터 길이만큼을 읽어 날려주면 된다.
복습의 의미에서 각 항목들을 차례차례 뒤벼보자.
디바이스 디스크립터의 길이 = sizeof(USB_DEVICE_DESCRIPTOR) (18)
디스크립터의 타입 = USB_DEVICE_DESCRIPTOR_TYPE
(헤더파일에는
#define USB_DEVICE_DESCRIPTOR_TYPE 0x01
라고 정의되어 있다.)
만족하는 USB 스펙 = 0x0100
즉 USB spec. 1.1을 준수하는 디바이스이며
Class = USB_CLASS_CODE_TEST_CLASS_DEVICE 이다.
(헤더에
#define USB_CLASS_CODE_TEST_CLASS_DEVICE 0xdc
라고 선언되어 있는데,
이건 http://www.usb.org/developers/defined_class 에도 나와 있듯이
우리 디바이스가 Diagnostic Device Class에 해당한다는걸 나타낸다.
bDeviceClass와 bDeviceSubClass = 0
Diagnostic Device Class는 하나의 Sub Class가 선언되어 있는데,
우리의 경우에는 그 Sub Class에 포함되지 않는다.
신경 꺼라. 이 경우 아무 의미 없다.
bMaxPacketSize = EP0_PACKET_SIZE ( 16 )
D12의 EP0 버퍼 크기는 16 Byte 이다.
idVendor = 0x0905
idProduct = 0x0010
bcdDevice = 0x0000
다 본좌 맘이다.
iManufacturer = 1
iProduct = 2
iSerialNumber = 0
제조사에 해당하는 문자열은 첫 번째 String Descriptor를 참조하고,
제품에 해당하는 문자열은 두 번째 String Descriptor를 참조하고,
시리얼번호에 해당하는 문자열에 관해서는 참조할 String Descriptor 가 없다는 말쌈이시다.
bNumConfigurations = 1
밑에 딸린 Configuration Descriptor가 하나 있다는 의미이다.
다시 말하면
호스트가 “야! 디바이스 디스크립터 한 장만 보내 도고.” 하면
디바이스는
0x12, 0x01, 0x01, 0x00, 0xDC, 0x00, 0x00, … , 0x01, 0x02, 0x00, 0x01 ( 총 18 Byte )
이런 데이터를 호스트로 휘리릭 날린다.
지루하신가?
똑같은 내용 두 번 리바이벌 하는 본좌는 지루하다 못해 몸이 베베 꼬인다.
본좌도 얼렁 코드로 넘어가서 LED가 반짝이는 걸 보고잡다.
그러나 어쩌랴?
행자들이 나중에 스스로 USB 디바이스를 만들기 위해서 알아야만 하는 것들이기에,
본좌. 꽈배기처럼 꼬이는 몸을 풀어가며 키보드를 두들긴다.
졸라 고맙지 아니한가?
호스트 컨트롤러는 이를 알아채고,
USB 장치에게 묻게 된다.
넌 누구니?
그럼 USB 장치는 자신의 정체를 밝혀야 한다.
만일 장치에서 유효한 대답이 없으면,
USB 장치가 아니라고 판단을 하고
호스트 컨트롤러는 이후 그 장치를 무시하게 된다.
이때 자신의 정체를 밝히기 위해
USB 디바이스가 호스트에 전송하는 데이터를 일컬어
디스크립터(descriptor)라고 부르자고 정해놨다.
누가? USB spec.을 만든 사람들이.
디스크립터는 여러 종류가 있는데,
제일 먼저 디바이스 디스크립터(Device Descriptor)를 디벼보자.
디바이스 디스크립터는 말 그대로
USB 디바이스가 난 이런 디바이스 이다.
라고 호스트에게 알리는 내용이 들어 있다.
디바이스 디스크립터를 자세히 들여다 보면 다음과 같은 항목들이 있다.
(USB 2.0 Spec 9.6.1 참조)
Offset
Field
Size
Value
Description
0
bLength
1
Number
Size of the Descriptor in Bytes (18 bytes)
1
bDescriptorType
1
Constant
Device Descriptor (0x01)
2
bcdUSB
2
BCD
USB Specification Number which device complies too.
4
bDeviceClass
1
Class
Class Code (Assigned by USB Org)
5
bDeviceSubClass
1
SubClass
Subclass Code (Assigned by USB Org)
6
bDeviceProtocol
1
Protocol
Protocol Code (Assigned by USB Org)
7
bMaxPacketSize
1
Number
Maximum Packet Size for Zero Endpoint.
Valid Sizes are 8, 16, 32, 64
8
idVendor
2
ID
Vendor ID (Assigned by USB Org)
10
idProduct
2
ID
Product ID (Assigned by Manufacturer)
12
bcdDevice
2
BCD
Device Release Number
14
iManufacturer
1
Index
Index of Manufacturer String Descriptor
15
iProduct
1
Index
Index of Product String Descriptor
16
iSerialNumber
1
Index
Index of Serial Number String Descriptor
17
bNumConfigurations
1
Integer
Number of Possible Configurations
<출처 : USB in a NutShell >
눈이 뱅뱅(@.@) 돌아 가시는가?
풀어서 설명해 보자면,
첫 번째 항목 ( bLength ) 은 이 디스크립터의 길이를 나타내고
( 디바이스 디스크립터의 크기는 18 Byte로 고정이다. )
두 번째 항목 ( bDescriptorType ) 은 이 디스크립터가 디바이스 디스크립터 임을 나타낸다.
( 0x01은 디바이스 디스크립터를 의미한다. 라고 미리 정해져 있다. )
세번째 항목 ( bcdUSB ) 이 이 디바이스가 USB 1.1 또는 USB 2.0 중 어떤 스펙을 따르는지를 나타내는데,
USB 1.1이 Full Speed (12Mbit/s ), USB 2.0이 High Speed ( 480 Mbit/s )를 지원하므로,
그가이꺼 대~충 설명하자면,
디바이스의 속도를 나타낸다고 봐도 무방하겠다.
이 항목이 0x0100이라면 Full Speed로 동작하는 디바이스 이고,
0x0200이라면 High Speed로 동작하는 디바이스 이다.
(엄밀히 말하자면 조금 틀린 표현이지만, 일단 패쓰)
네 번째 항목 ( bDeviceClass ) 은 이 디바이스가 어떤 클래스에 속해 있는지를 보여준다.
디바이스 클래스가 뭔고 하면
http://www.usb.org/developers/defined_class의 첫 번째 테이블을 보시라.
bDeviceSubClass에 대한 설명은 같은 웹페이지 아래쪽에 있는
“Device / Interface Class Code Details” 라는 제목이 붙은 세개의 테이블로 대신한다.
이제부터가 디바이스 디스크립터의 알짜배기라 할 수 있다.
일곱 번째, bMaxPacketSize 항목
앞서 강의에서 설명한 Default Control Endpoint에서 한번에 전송 가능한 데이터 크기를 나타낸다.
보통은 디바이스에서 Default Control Endpoint ( Endpoint 0 )를 위해 할당된 버퍼의 크기를 그 값으로 준다.
우리가 쓸 PDIUSBD12의 EP0의 버퍼 크기는 16 Byte 이므로, 여기에 16을 써 넣어야 한다.
실제 버퍼는 16 Byte인데, 여기에 32를 써 넣는다면?
디바이스가 자기는 한번에 32 Byte를 받을 수 있다고 했으므로, 호스트는 믿고 한번에 32 Byte를 날린다.
그런데, 버퍼메모리는 16 Byte밖에 없으므로 나머지 16 Byte는 날라가 버릴 것이다. 결과는 통신 오류.
본좌도 안 해 봤다. 시간 많은 행자들 함 해보시라. ㅋㅋ
여덟 번째, idVendor 항목
USB.org에 돈 내고 등록하면, 자기 회사 이름에 번호를 하나 부여해 준다.
http://www.usb.org/developers/vendor/ 를 참조하시라.
물론 기존의 아이디들에 겹치지 않게 아무 아이디나 하나 골라 써도 되지만,
제품 출시를 목적으로 할 경우에는 등록하는 게 좋을 게다.
아홉 번째, idProduct 항목
제품번호이다. 맘에 드는 숫자를 암거나 골라 잡으시라. 개발자 맘이다.
호스트는 idVendor와 idProduct Field를 보고 이 장치에 맞는 드라이버를 로딩하게 된다.
열 번째, bcdDevice는
제품 리비젼 번호쯤으로 생각하면 되겠다.
iManufacturer, iProduct, iSerialNumber는
각각 제조사, 상품명, 시리얼번호를 나타내는 문자열이
String Descriptor의 몇 번째에 위치하는지를 나타내는 인덱스이다.
String Descriptor는 말 그대로 문자열을 저장하는데,
있어도 그만, 없어도 그만인 디스크립터이다.
따라서 String Descriptor가 없다면, 위 세 항목을 다 0으로 세팅해 주면 되고,
있다면 각각에 맞는 인덱스를 주면 된다.
아래에서 예를 들어주겠다.
맨 마지막 bNumConfigurations 항목은
이 디바이스가 가질 수 있는 Configuration의 개수를 나타낸다.
대부분의 디바이스는 하나의 Configuration만 가진다.
이 Configuration에 대해서는 다음 강좌에 자세히 설명하도록 하겠다.
이제까지 Device Descriptor의 각 Field에 대해 알아보았는데,
실제 어떻게 이 디스크립터가 정의되는지 UniFull 펌웨어 예제의 일부를 살펴보자.
먼저 헤더파일에
typedef struct _USB_DEVICE_DESCRIPTOR {
UCHAR bLength;
UCHAR bDescriptorType;
USHORT bcdUSB;
UCHAR bDeviceClass;
UCHAR bDeviceSubClass;
UCHAR bDeviceProtocol;
UCHAR bMaxPacketSize;
USHORT idVendor;
USHORT idProduct;
USHORT bcdDevice;
UCHAR iManufacturer;
UCHAR iProduct;
UCHAR iSerialNumber;
UCHAR bNumConfigurations;
} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
라고 디바이스 디스크립터의 구조체가 정의되어 있고,
실제 우리가 다룰 소스에는
USB_DEVICE_DESCRIPTOR DeviceDescr =
{
sizeof(USB_DEVICE_DESCRIPTOR),
USB_DEVICE_DESCRIPTOR_TYPE,
0x0100,
USB_CLASS_CODE_TEST_CLASS_DEVICE,
0,
0,
EP0_PACKET_SIZE,
0x0905,
0x0010,
0x0000,
1,
2,
0,
1
};
요렇게 USB_DEVICE_DESCRIPTOR 형 DeviceDescr 변수가 초기화 되어 있다.
따라서 호스트가 “야! 디바이스 디스크립터 한 장만 보내 도고.” 하면
DeviceDescr 변수가 저장된 메모리를 찾아내어 디바이스 디스크립터 길이만큼을 읽어 날려주면 된다.
복습의 의미에서 각 항목들을 차례차례 뒤벼보자.
디바이스 디스크립터의 길이 = sizeof(USB_DEVICE_DESCRIPTOR) (18)
디스크립터의 타입 = USB_DEVICE_DESCRIPTOR_TYPE
(헤더파일에는
#define USB_DEVICE_DESCRIPTOR_TYPE 0x01
라고 정의되어 있다.)
만족하는 USB 스펙 = 0x0100
즉 USB spec. 1.1을 준수하는 디바이스이며
Class = USB_CLASS_CODE_TEST_CLASS_DEVICE 이다.
(헤더에
#define USB_CLASS_CODE_TEST_CLASS_DEVICE 0xdc
라고 선언되어 있는데,
이건 http://www.usb.org/developers/defined_class 에도 나와 있듯이
우리 디바이스가 Diagnostic Device Class에 해당한다는걸 나타낸다.
bDeviceClass와 bDeviceSubClass = 0
Diagnostic Device Class는 하나의 Sub Class가 선언되어 있는데,
우리의 경우에는 그 Sub Class에 포함되지 않는다.
신경 꺼라. 이 경우 아무 의미 없다.
bMaxPacketSize = EP0_PACKET_SIZE ( 16 )
D12의 EP0 버퍼 크기는 16 Byte 이다.
idVendor = 0x0905
idProduct = 0x0010
bcdDevice = 0x0000
다 본좌 맘이다.
iManufacturer = 1
iProduct = 2
iSerialNumber = 0
제조사에 해당하는 문자열은 첫 번째 String Descriptor를 참조하고,
제품에 해당하는 문자열은 두 번째 String Descriptor를 참조하고,
시리얼번호에 해당하는 문자열에 관해서는 참조할 String Descriptor 가 없다는 말쌈이시다.
bNumConfigurations = 1
밑에 딸린 Configuration Descriptor가 하나 있다는 의미이다.
다시 말하면
호스트가 “야! 디바이스 디스크립터 한 장만 보내 도고.” 하면
디바이스는
0x12, 0x01, 0x01, 0x00, 0xDC, 0x00, 0x00, … , 0x01, 0x02, 0x00, 0x01 ( 총 18 Byte )
이런 데이터를 호스트로 휘리릭 날린다.
지루하신가?
똑같은 내용 두 번 리바이벌 하는 본좌는 지루하다 못해 몸이 베베 꼬인다.
본좌도 얼렁 코드로 넘어가서 LED가 반짝이는 걸 보고잡다.
그러나 어쩌랴?
행자들이 나중에 스스로 USB 디바이스를 만들기 위해서 알아야만 하는 것들이기에,
본좌. 꽈배기처럼 꼬이는 몸을 풀어가며 키보드를 두들긴다.
졸라 고맙지 아니한가?