USB 컨트롤 전송을 보내는 방법
이 문서에서는 컨트롤 전송의 구조와 클라이언트 드라이버가 디바이스에 컨트롤 요청을 보내는 방법을 설명합니다.
기본 엔드포인트 정보
모든 USB 디바이스는 기본 엔드포인트라는 하나 이상의 엔드포인트를 지원해야 합니다. 기본 엔드포인트를 대상으로 하는 모든 전송을 컨트롤 전송이라고 합니다. 제어 전송의 목적은 호스트가 디바이스 정보를 가져오거나, 디바이스를 구성하거나, 디바이스에 고유한 제어 작업을 수행할 수 있도록 하는 것입니다.
먼저 기본 엔드포인트의 이러한 특성을 연구해 보겠습니다.
- 기본 엔드포인트의 주소는 0입니다.
- 기본 엔드포인트는 양방향입니다. 즉, 호스트는 엔드포인트로 데이터를 보내고 한 번의 전송 내에서 데이터를 수신할 수 있습니다.
- 기본 엔드포인트는 디바이스 수준에서 사용할 수 있으며 디바이스의 인터페이스에 정의되어 있지 않습니다.
- 호스트와 디바이스 간에 연결이 설정되는 즉시 기본 엔드포인트가 활성화됩니다. 구성을 선택하기 전에도 활성화됩니다.
- 기본 엔드포인트의 최대 패킷 크기는 디바이스의 버스 속도에 따라 달라집니다. 저속, 8바이트; 전체 및 고속, 64바이트; SuperSpeed, 512바이트
컨트롤 전송 레이아웃
제어 전송은 우선 순위가 높은 전송이므로 호스트가 버스에서 특정 양의 대역폭을 예약합니다. 저속 및 전속 디바이스의 경우 대역폭의 10%입니다. 높음 및 슈퍼스피드 전송 디바이스의 경우 20%입니다. 이제 컨트롤 전송의 레이아웃을 살펴보겠습니다.
제어 전송은 설정 트랜잭션, 데이터 트랜잭션 및 상태 트랜잭션의 세 가지 트랜잭션으로 나뉩니다. 각 트랜잭션에는 토큰 패킷, 데이터 패킷 및 핸드셰이크 패킷의 세 가지 유형의 패킷이 포함됩니다.
특정 필드는 모든 패킷에 공통적으로 적용됩니다. 이러한 필드는 다음과 같습니다.
- 패킷의 시작을 나타내는 동기화 필드입니다.
- 패킷 유형, 트랜잭션 방향을 나타내는 PID(패킷 식별자)이며 핸드셰이크 패킷의 경우 트랜잭션의 성공 또는 실패를 나타냅니다.
- EOP 필드는 패킷의 끝을 나타냅니다.
다른 필드는 패킷 유형에 따라 달라집니다.
토큰 패킷
모든 설정 트랜잭션은 토큰 패킷으로 시작됩니다. 패킷의 구조는 다음과 같습니다. 호스트는 항상 토큰 패킷을 보냅니다.
PID 값은 토큰 패킷의 형식을 나타냅니다. 가능한 값은 다음과 같습니다.
- 설치: 컨트롤 전송에서 설정 트랜잭션의 시작을 나타냅니다.
- IN: 호스트가 디바이스에서 데이터를 요청하고 있음을 나타냅니다(읽기 사례).
- OUT: 호스트가 디바이스에 데이터를 보내고 있음을 나타냅니다(쓰기 사례).
- SOF: 프레임의 시작을 나타냅니다. 이 유형의 토큰 패킷에는 11비트 프레임 번호가 포함됩니다. 호스트는 SOF 패킷을 보냅니다. 이 패킷이 전송되는 빈도는 버스 속도에 따라 달라집니다. 전체 속도를 위해 호스트는 1millisecond마다 패킷을 보냅니다. 고속 버스에서 125 마이크로초마다.
데이터 패킷
토큰 패킷 바로 다음에 페이로드가 포함된 데이터 패킷이 있습니다. 각 데이터 패킷에 포함될 수 있는 바이트 수는 기본 엔드포인트의 최대 패킷 크기에 따라 달라집니다. 데이터 패킷은 전송 방향에 따라 호스트 또는 디바이스에서 보낼 수 있습니다.
핸드셰이크 패킷
데이터 패킷 바로 뒤에 핸드셰이크 패킷이 있습니다. 패킷의 PID는 호스트 또는 디바이스에서 패킷을 받았는지 여부를 나타냅니다. 핸드셰이크 패킷은 전송 방향에 따라 호스트 또는 디바이스에서 보낼 수 있습니다.
Beagle, Ellisys, LeCroy USB 프로토콜 분석기와 같은 USB 분석기를 사용하여 트랜잭션 및 패킷의 구조를 확인할 수 있습니다. 분석기 디바이스는 유선을 통해 USB 디바이스로 데이터를 보내거나 받는 방법을 보여 있습니다. 이 예제에서는 LeCroy USB 분석기에서 캡처한 일부 추적을 살펴보겠습니다. 이 예제는 정보 전용입니다. 이는 Microsoft의 보증이 아닙니다.
트랜잭션 설정
호스트는 항상 컨트롤 전송을 시작합니다. 이렇게 하려면 설치 트랜잭션을 보냅니다. 이 트랜잭션에는 설정 토큰이라는 토큰 패킷과 8 바이트 데이터 패킷이 포함됩니다. 이 스크린샷은 설정 트랜잭션 예제를 보여줍니다.
이전 추적에서 호스트는 설정 토큰 패킷 #434를 전송하여 제어 전송을 시작합니다(H로 표시 ). PID는 설치 토큰을 나타내는 SETUP을 지정합니다. PID 뒤에는 디바이스 주소와 엔드포인트의 주소가 잇습니다. 제어 전송의 경우 해당 엔드포인트 주소는 항상 0입니다.
다음으로 호스트는 데이터 패킷 #435를 보냅니다. PID는 DATA0이며 이 값은 패킷 시퀀싱에 사용됩니다(설명). PID 뒤에는 이 요청에 대한 기본 정보가 포함된 8바이트가 잇습니다. 이러한 8바이트는 요청 유형과 디바이스가 응답을 작성할 버퍼의 크기를 나타냅니다.
모든 바이트는 역순으로 수신됩니다. 섹션 9.3에 설명된 대로 다음과 같은 필드와 값이 표시됩니다.
필드 | 크기 | 값 | 설명 |
---|---|---|---|
bmRequestType (9.3.1 bmRequestType 참조) | 1 | 0x80 | 데이터 전송 방향은 디바이스에서 호스트로(D7은 1) 요청이 표준 요청입니다(D6... D5는 0)입니다. 요청 수신자가 디바이스입니다(D4는 0). |
bRequest (섹션 참조 9.3.2 및 표 9-4) | 1 | 0x06 | 요청 유형이 GET_DESCRIPTOR. |
wValue (표 9-5 참조) | 2 | 0x0100 | 요청 값은 설명자 유형이 DEVICE임을 나타냅니다. |
wIndex (섹션 9.3.4 참조) | 2 | 0x0000 | 호스트에서 디바이스로의 방향입니다(D7은 1). 엔드포인트 번호는 0입니다. |
wLength (섹션 9.3.5 참조) | 2 | 0x0012 | 요청은 18바이트를 검색하는 것입니다. |
따라서 이 컨트롤(읽기) 전송에서 호스트가 디바이스 설명자를 검색하는 요청을 보내고 해당 설명자를 보유할 전송 길이로 18바이트를 지정한다고 결론을 내릴 수 있습니다. 디바이스에서 이러한 18바이트를 보내는 방법은 기본 엔드포인트가 한 트랜잭션에서 보낼 수 있는 데이터의 양에 따라 달라집니다. 해당 정보는 데이터 트랜잭션의 디바이스에서 반환된 디바이스 설명자에 포함됩니다.
이에 대한 응답으로 디바이스는 핸드셰이크 패킷(D로 표시된 #436)을 보냅니다. PID 값은 ACK(ACK 패킷)입니다. 이는 디바이스가 트랜잭션을 승인했음을 나타냅니다.
데이터 트랜잭션
이제 요청에 대한 응답으로 디바이스가 반환하는 내용을 살펴보겠습니다. 실제 데이터는 데이터 트랜잭션에서 전송됩니다.
다음은 데이터 트랜잭션에 대한 추적입니다.
ACK 패킷을 받으면 호스트가 데이터 트랜잭션을 시작합니다. 트랜잭션을 시작하기 위해 IN(IN 토큰이라고 함)으로 방향을 가진 토큰 패킷(#450)을 보냅니다.
이에 대한 응답으로 디바이스는 IN 토큰을 따르는 데이터 패킷(#451)을 보냅니다. 이 데이터 패킷에는 실제 디바이스 설명자가 포함됩니다. 첫 번째 바이트는 디바이스 설명자의 길이를 나타내며 18바이트(0x12)입니다. 이 데이터 패킷의 마지막 바이트는 기본 엔드포인트에서 지원하는 최대 패킷 크기를 나타냅니다. 이 경우 디바이스는 기본 엔드포인트를 통해 한 번에 8바이트를 보낼 수 있습니다.
참고 항목
기본 엔드포인트의 최대 패킷 크기는 디바이스의 속도에 따라 달라집니다. 고속 디바이스의 기본 엔드포인트는 64바이트입니다. 저속 디바이스는 8바이트입니다.
호스트는 디바이스에 ACK 패킷(#452)을 전송하여 데이터 트랜잭션을 승인합니다.
반환되는 데이터의 양을 계산해 보겠습니다. 설정 트랜잭션에서 데이터 패킷(#435)의 wLength 필드에서 호스트는 18바이트를 요청했습니다. 데이터 트랜잭션에서 디바이스 설명자의 처음 8바이트만 디바이스에서 수신된 것을 볼 수 있습니다. 그렇다면 호스트는 나머지 10바이트 안에 저장된 정보를 어떻게 받나요? 디바이스는 8바이트 후 마지막 2바이트의 두 트랜잭션에서 이 작업을 수행합니다.
이제 호스트가 기본 엔드포인트의 최대 패킷 크기를 알고 있으므로 호스트는 새 데이터 트랜잭션을 시작하고 패킷 크기에 따라 다음 부분을 요청합니다.
다음 데이터 트랜잭션은 다음과 같습니다.
호스트는 IN 토큰(#463)을 보내고 디바이스에서 다음 8바이트를 요청하여 이전 데이터 트랜잭션을 시작합니다. 디바이스는 디바이스 설명자의 다음 8바이트가 포함된 데이터 패킷(#464)으로 응답합니다.
8바이트를 받으면 호스트는 ACK 패킷(#465)을 디바이스에 보냅니다.
다음으로 호스트는 다음과 같이 다른 데이터 트랜잭션에서 마지막 2바이트를 요청합니다.
따라서 디바이스에서 호스트로 18바이트를 전송하기 위해 호스트는 전송되고 시작된 세 개의 데이터 트랜잭션(8+8+2)의 바이트 수를 추적합니다.
참고 항목
데이터 트랜잭션 19, 23, 26에서 데이터 패킷의 PID를 확인합니다. PID는 DATA0과 DATA1을 번갈아 사용합니다. 이 시퀀스를 데이터 토글이라고 합니다. 여러 데이터 트랜잭션이 있는 경우 데이터 토글을 사용하여 패킷 시퀀스를 확인합니다. 이 메서드는 데이터 패킷이 중복되거나 손실되지 않도록 합니다.
통합 데이터 패킷을 디바이스 설명자의 구조에 매핑하여(표 9-8 참조) 다음 필드와 값을 확인합니다.
필드 | 크기 | 값 | 설명 |
---|---|---|---|
bLength | 1 | 0x12 | 디바이스 설명자의 길이(18바이트)입니다. |
bDescriptorType | 1 | 0x01 | 설명자 유형이 디바이스입니다. |
bcdUSB | 2 | 0x0100 | 사양 버전 번호는 1.00입니다. |
bDeviceClass | 1 | 0x00 | 디바이스 클래스는 0입니다. 구성의 각 인터페이스에는 클래스 정보가 있습니다. |
bDeviceSubClass | 1 | 0x00 | 디바이스 클래스가 0이므로 서브클래스는 0입니다. |
bProtocol | 1 | 0x00 | 프로토콜은 0입니다. 이 디바이스는 클래스별 프로토콜을 사용하지 않습니다. |
bMaxPacketSize0 | 1 | 0x08 | 엔드포인트의 최대 패킷 크기는 8바이트입니다. |
idVendor | 2 | 0x0562 | Telex Communications. |
idProduct | 2 | 0x0002 | USB 마이크. |
bcdDevice | 2 | 0x0100 | 디바이스 릴리스 번호를 나타냅니다. |
iManufacturer | 1 | 0x01 | 제조업체 문자열입니다. |
iProduct | 1 | 0x02 | 제품 문자열입니다. |
iSerialNumber | 1 | 0x03 | 일련 번호. |
bNumConfigurations | 1 | 0x01 | 구성 수입니다. |
이러한 값을 검사하여 디바이스에 대한 몇 가지 예비 정보가 있습니다. 디바이스는 저속 USB 마이크입니다. 기본 엔드포인트의 최대 패킷 크기는 8바이트입니다. 디바이스는 하나의 구성을 지원합니다.
상태 트랜잭션
마지막으로 호스트는 마지막 트랜잭션인 상태 트랜잭션을 시작하여 제어 전송을 완료합니다.
호스트는 OUT 토큰 패킷(#481)을 사용하여 트랜잭션을 시작합니다. 이 패킷의 목적은 디바이스가 요청된 모든 데이터를 전송했는지 확인하는 것입니다. 이 상태 트랜잭션에 전송된 데이터 패킷이 없습니다. 디바이스가 ACK 패킷으로 응답합니다. 오류가 발생한 경우 PID는 NAK 또는 STALL일 수 있습니다.
드라이버 모델
필수 조건
클라이언트 드라이버가 파이프를 열거하기 전에 다음 요구 사항이 충족되는지 확인합니다.
클라이언트 드라이버가 프레임워크 USB 대상 디바이스 개체를 만들었어야 합니다.
Microsoft Visual Studio Professional 2012와 함께 제공되는 USB 템플릿을 사용하는 경우 템플릿 코드는 이러한 작업을 수행합니다. 템플릿 코드는 대상 디바이스 개체에 대한 핸들을 가져오고 디바이스 컨텍스트에 저장합니다.
KMDF 클라이언트 드라이버
KMDF 클라이언트 드라이버는 WdfUsbTargetDeviceCreateWithParameters 메서드를 호출하여 WDFUSBDEVICE 핸들을 가져와야 합니다. 자세한 내용은 KMDF(USB 클라이언트 드라이버 코드 구조) 이해의 "디바이스 소스 코드"를 참조하세요.
UMDF 클라이언트 드라이버
UMDF 클라이언트 드라이버는 프레임워크 대상 디바이스 개체를 쿼리하여 IWDFUsbTargetDevice 포인터를 가져와야 합니다. 자세한 내용은 UMDF(USB 클라이언트 드라이버 코드 구조) 이해에서 "IPnpCallbackHardware 구현 및 USB 관련 작업"을 참조하세요.
컨트롤 전송의 가장 중요한 측면은 설치 토큰의 형식을 적절하게 지정하는 것입니다. 요청을 보내기 전에 다음 정보 집합을 수집합니다.
- 요청의 방향: 호스트할 디바이스 또는 디바이스에 호스트합니다.
- 요청 받는 사람: 디바이스, 인터페이스, 엔드포인트 또는 기타.
- 요청 범주: 표준, 클래스 또는 공급업체.
- GET_DESCRIPTPOR 요청과 같은 요청 유형입니다. 자세한 내용은 USB 사양의 섹션 9.5를 참조하세요.
- wValue 및 wIndex 값입니다. 이러한 값은 요청 유형에 따라 달라집니다.
공식 USB 사양에서 모든 정보를 가져올 수 있습니다.
UMDF 드라이버를 작성하는 경우 OSR USB Fx2 학습 키트용 UMDF 샘플 드라이버에서 헤더 파일 Usb_hw.h를 가져옵니다. 이 헤더 파일에는 컨트롤 전송을 위한 설정 패킷의 서식을 지정하는 데 유용한 매크로와 구조가 포함되어 있습니다.
모든 UMDF 드라이버는 디바이스에서 데이터를 보내고 받기 위해 커널 모드 드라이버와 통신해야 합니다. USB UMDF 드라이버의 경우 커널 모드 드라이버는 항상 Microsoft에서 제공하는 드라이버 WinUSB (Winusb.sys)입니다.
UMDF 드라이버가 USB 드라이버 스택에 대한 요청을 할 때마다 Windows I/O 관리자가 WinUSB에 요청을 보냅니다. 요청을 받은 후 WinUSB는 요청을 처리하거나 USB 드라이버 스택에 전달합니다.
제어 전송 요청을 보내기 위한 Microsoft 정의 메서드
호스트의 USB 클라이언트 드라이버는 디바이스에 대한 정보를 얻거나, 디바이스를 구성하거나, 공급업체 제어 명령을 보내기 위해 대부분의 제어 요청을 시작합니다. 이러한 모든 요청은 다음으로 분류할 수 있습니다.
표준 요청은 USB 사양에 정의됩니다. 표준 요청을 보내는 목적은 디바이스, 해당 구성, 인터페이스 및 엔드포인트에 대한 정보를 가져오는 것입니다. 각 요청의 받는 사람은 요청 유형에 따라 달라집니다. 받는 사람은 디바이스, 인터페이스 또는 엔드포인트일 수 있습니다.
참고 항목
모든 컨트롤 전송의 대상은 항상 기본 엔드포인트입니다. 받는 사람은 호스트가 관심 있는 정보(설명자, 상태 등)를 가진 디바이스의 엔터티입니다.
요청은 구성 요청, 기능 요청 및 상태 요청으로 더 분류할 수 있습니다.
- GET_DESCRIPTOR 요청 과 같이 호스트가 구성할 수 있도록 디바이스에서 정보를 가져오기 위해 구성 요청이 전송됩니다. 이러한 요청은 디바이스에서 특정 구성 또는 대체 설정을 설정하기 위해 호스트에서 보내는 쓰기 요청일 수도 있습니다.
- 디바이스, 인터페이스 또는 엔드포인트에서 지원하는 특정 부울 디바이스 설정을 사용하거나 사용하지 않도록 설정하기 위해 클라이언트 드라이버에서 기능 요청을 보냅니다.
- 상태 요청을 사용하면 호스트가 디바이스, 엔드포인트 또는 인터페이스의 USB 정의 상태 비트를 얻거나 설정할 수 있습니다.
자세한 내용은 USB 사양 버전 2.0의 섹션 9.4를 참조하세요. 표준 요청 형식은 헤더 파일인 Usbspec.h로 정의됩니다.
클래스 요청은 특정 디바이스 클래스 사양에 의해 정의됩니다.
공급업체 요청은 공급업체에서 제공하며 디바이스에서 지원하는 요청에 따라 달라집니다.
Microsoft에서 제공하는 USB 스택은 이전 추적에 표시된 대로 디바이스와의 모든 프로토콜 통신을 처리합니다. 드라이버는 클라이언트 드라이버가 여러 가지 방법으로 제어 전송을 보낼 수 있도록 하는 DDI(디바이스 드라이버 인터페이스)를 노출합니다. 클라이언트 드라이버가 WDF(Windows 드라이버 파운데이션) 드라이버인 경우 루틴을 직접 호출하여 일반적인 유형의 제어 요청을 보낼 수 있습니다. WDF는 KMDF와 UMDF 모두에 대해 기본적으로 제어 전송을 지원합니다.
특정 유형의 제어 요청은 WDF를 통해 노출되지 않습니다. 이러한 요청의 경우 클라이언트 드라이버는 WDF 하이브리드 모델을 사용할 수 있습니다. 이 모델을 사용하면 클라이언트 드라이버가 WDM URB 스타일 요청을 빌드하고 서식을 지정한 다음 WDF 프레임워크 개체를 사용하여 해당 요청을 보낼 수 있습니다. 하이브리드 모델은 커널 모드 드라이버에만 적용됩니다.
UMDF 드라이버의 경우:
usb_hw.h에 정의된 도우미 매크로 및 구조를 사용합니다. 이 헤더는 OSR USB Fx2 학습 키트용 UMDF 샘플 드라이버에 포함되어 있습니다.
이 표를 사용하여 USB 드라이버 스택에 제어 요청을 보내는 가장 좋은 방법을 결정합니다. 이 테이블을 볼 수 없는 경우 이 문서의 표를 참조하세요.
컨트롤 요청을 보내려는 경우... | KMDF 드라이버의 경우... | UMDF 드라이버의 경우... | WDM 드라이버의 경우 URB 구조(도우미 루틴)를 빌드합니다. |
---|---|---|---|
CLEAR_FEATURE: 디바이스, 해당 구성, 인터페이스 및 엔드포인트에서 특정 기능 설정을 사용하지 않도록 설정합니다. USB 사양의 섹션 9.4.1을 참조하세요. |
|
|
_URB_CONTROL_FEATURE_REQUEST (UsbBuildFeatureRequest) URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT URB_FUNCTION_CLEAR_FEATURE_TO_OTHER |
GET_CONFIGURATION: 현재 USB 구성을 가져옵니다. USB 사양의 섹션 9.4.2를 참조하세요. | KMDF는 기본적으로 첫 번째 구성을 선택합니다. 디바이스 정의 구성 번호를 검색하려면 다음을 수행합니다.
|
UMDF는 기본적으로 첫 번째 구성을 선택합니다. 디바이스 정의 구성 번호를 검색하려면 다음을 수행합니다.
|
_URB_CONTROL_GET_CONFIGURATION_REQUEST URB_FUNCTION_GET_CONFIGURATION |
GET_DESCRIPTOR: 디바이스, 구성, 인터페이스 및 엔드포인트 설명자를 가져옵니다. USB 사양의 섹션 9.4.3을 참조하세요. 자세한 내용은 USB 설명자를 참조 하세요. |
다음 메서드를 호출합니다.
|
다음 메서드를 호출합니다.
|
_URB_CONTROL_DESCRIPTOR_REQUEST (UsbBuildGetDescriptorRequest) URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE |
GET_INTERFACE: 인터페이스에 대한 현재 대체 설정을 가져옵니다. USB 사양의 섹션 9.4.4를 참조하세요. |
|
|
_URB_CONTROL_GET_INTERFACE_REQUEST URB_FUNCTION_GET_INTERFACE |
GET_STATUS: 디바이스, 엔드포인트 또는 인터페이스에서 상태 비트를 가져옵니다. 섹션 9.4.5를 참조하세요. 은 USB 사양에 있습니다. |
|
|
_URB_CONTROL_GET_STATUS_REQUEST (UsbBuildGetStatusRequest) URB_FUNCTION_GET_STATUS_FROM_DEVICE URB_FUNCTION_GET_STATUS_FROM_INTERFACE URB_FUNCTION_GET_STATUS_FROM_ENDPOINT URB_FUNCTION_GET_STATUS_FROM_OTHER. |
SET_ADDRESS: USB 사양의 섹션 9.4.6을 참조하세요. | 이 요청은 USB 드라이버 스택에서 처리됩니다. 클라이언트 드라이버가 이 작업을 수행할 수 없습니다. | 이 요청은 USB 드라이버 스택에서 처리됩니다. 클라이언트 드라이버가 이 작업을 수행할 수 없습니다. | 이 요청은 USB 드라이버 스택에서 처리됩니다. 클라이언트 드라이버가 이 작업을 수행할 수 없습니다. |
SET_CONFIGURATION: 구성을 설정합니다. USB 사양의 섹션 9.4.7을 참조하세요. 자세한 내용은 USB 디바이스에 대한 구성을 선택하는 방법을 참조 하세요. |
기본적으로 KMDF는 각 인터페이스에서 기본 구성 및 첫 번째 대체 설정을 선택합니다. 클라이언트 드라이버는 WdfUsbTargetDeviceSelectConfigType 메서드를 호출하고 WdfUsbTargetDeviceSelectConfigTypeUrb을 요청 옵션으로 지정하여 기본 구성을 변경할 수 있습니다. 그런 다음, 이 요청에 대한 URB의 서식을 지정하고 USB 드라이버 스택에 제출해야 합니다. | 기본적으로 UMDF는 각 인터페이스에서 기본 구성 및 첫 번째 대체 설정을 선택합니다. 클라이언트 드라이버는 구성을 변경할 수 없습니다. | _URB_SELECT_CONFIGURATION (USBD_SelectConfigUrbAllocateAndBuild) URB_FUNCTION_SELECT_CONFIGURATION |
SET_DESCRIPTOR: 기존 디바이스, 구성 또는 문자열 설명자를 업데이트합니다. USB 사양의 섹션 9.4.8을 참조하세요. 이 요청은 일반적으로 사용되지 않습니다. 그러나 USB 드라이버 스택은 클라이언트 드라이버에서 이러한 요청을 수락합니다. |
|
|
_URB_CONTROL_DESCRIPTOR_REQUEST URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE |
SET_FEATURE: 디바이스, 해당 구성, 인터페이스 및 엔드포인트에서 특정 기능 설정을 사용하도록 설정합니다. USB 사양의 섹션 9.4.9를 참조하세요. |
|
|
_URB_CONTROL_FEATURE_REQUEST (UsbBuildFeatureRequest) URB_FUNCTION_SET_FEATURE_TO_DEVICE URB_FUNCTION_SET_FEATURE_TO_INTERFACE URB_FUNCTION_SET_FEATURE_TO_ENDPOINT URB_FUNCTION_SET_FEATURE_TO_OTHER |
SET_INTERFACE: 인터페이스의 대체 설정을 변경합니다. USB 사양의 섹션 9.4.9를 참조하세요. 자세한 내용은 USB 인터페이스에서 대체 설정을 선택하는 방법을 참조 하세요. |
WdfUsbTargetDeviceSelectConfig
|
|
_URB_SELECT_INTERFACE (USBD_SelectInterfaceUrbAllocateAndBuild) URB_FUNCTION_SELECT_INTERFACE |
SYNC_FRAME: 엔드포인트의 동기화 프레임 번호를 설정하고 가져옵니다. USB 사양의 섹션 9.4.10을 참조하세요. | 이 요청은 USB 드라이버 스택에서 처리됩니다. 클라이언트 드라이버가 이 작업을 수행할 수 없습니다. | 이 요청은 USB 드라이버 스택에서 처리됩니다. 클라이언트 드라이버가 이 작업을 수행할 수 없습니다. | 이 요청은 USB 드라이버 스택에서 처리됩니다. 클라이언트 드라이버가 이 작업을 수행할 수 없습니다. |
디바이스 클래스별 요청 및 공급업체 명령의 경우 |
|
|
_URB_CONTROL_VENDOR_OR_CLASS_REQUEST (UsbBuildVendorRequest) URB_FUNCTION_VENDOR_DEVICE URB_FUNCTION_VENDOR_INTERFACE URB_FUNCTION_VENDOR_ENDPOINT URB_FUNCTION_VENDOR_OTHER URB_FUNCTION_CLASS_DEVICE URB_FUNCTION_CLASS_INTERFACE URB_FUNCTION_CLASS_ENDPOINT URB_FUNCTION_CLASS_OTHER |
공급업체 명령에 대한 제어 전송을 보내는 방법 - KMDF
이 절차에서는 클라이언트 드라이버가 컨트롤 전송을 보낼 수 있는 방법을 보여줍니다. 이 예제에서 클라이언트 드라이버는 디바이스에서 펌웨어 버전을 검색하는 공급업체 명령을 보냅니다.
공급업체 명령에 대한 상수 선언 하드웨어 사양을 검토하고 사용하려는 공급업체 명령을 결정합니다.
WDF_MEMORY_DESCRIPTOR 구조를 선언하고 WDF_MEMORY_DESCRIPTOR_INIT_BUFFER 매크로를 호출하여 초기화합니다. 이 구조는 USB 드라이버가 요청을 완료한 후 디바이스에서 응답을 받습니다.
요청을 동기적으로 보내는지 비동기적으로 보내는지에 따라 보내기 옵션을 지정합니다.
WdfUsbTargetDeviceSendControlTransferSynchronously를 호출 하여 요청을 동기적으로 보내는 경우 시간 제한 값을 지정합니다. 시간 제한이 없으면 스레드를 무기한 차단할 수 있으므로 이 값이 중요합니다.
이를 위해 WDF_REQUEST_SEND_OPTIONS 구조를 선언하고 WDF_REQUEST_SEND_OPTIONS_INIT 매크로를 호출하여 초기화합니다. 옵션을 WDF_REQUEST_SEND_OPTION_TIMEOUT 지정합니다.
다음으로, WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT 매크로를 호출하여 시간 제한 값을 설정합니다.
요청을 비동기적으로 보내는 경우 완료 루틴을 구현합니다. 완료 루틴에서 할당된 모든 리소스를 해제합니다.
설정 토큰을 포함하도록 WDF_USB_CONTROL_SETUP_PACKET 구조체를 선언하고 구조체의 형식을 지정합니다. 이렇게 하려면 WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR 매크로를 호출하여 설치 패킷의 형식을 지정합니다. 호출에서 요청의 방향, 받는 사람, 보낸 요청 옵션(3단계에서 초기화됨) 및 공급업체 명령에 대한 상수를 지정합니다.
WdfUsbTargetDeviceSendControlTransferSynchronously 또는 WdfUsbTargetDeviceFormatRequestForControlTransfer를 호출하여 요청을 보냅니다.
프레임워크에서 반환된 NTSTATUS 값을 확인하고 수신된 값을 검사합니다.
이 코드 예제에서는 펌웨어 버전을 검색하기 위해 USB 디바이스에 제어 전송 요청을 보냅니다. 요청이 동기적으로 전송되고 클라이언트 드라이버는 5초(100나노초 단위)의 상대 시간 제한 값을 지정합니다. 드라이버는 수신된 응답을 드라이버 정의 디바이스 컨텍스트에 저장합니다.
enum {
USBFX2_GET_FIRMWARE_VERSION = 0x1,
....
} USBFX2_VENDOR_COMMANDS;
#define WDF_TIMEOUT_TO_SEC ((LONGLONG) 1 * 10 * 1000 * 1000) // defined in wdfcore.h
const __declspec(selectany) LONGLONG
DEFAULT_CONTROL_TRANSFER_TIMEOUT = 5 * -1 * WDF_TIMEOUT_TO_SEC;
typedef struct _DEVICE_CONTEXT
{
...
union {
USHORT VersionAsUshort;
struct {
BYTE Minor;
BYTE Major;
} Version;
} Firmware; // Firmware version.
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;
__drv_requiresIRQL(PASSIVE_LEVEL)
VOID GetFirmwareVersion(
__in PDEVICE_CONTEXT DeviceContext
)
{
NTSTATUS status;
WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket;
WDF_REQUEST_SEND_OPTIONS sendOptions;
USHORT firmwareVersion;
WDF_MEMORY_DESCRIPTOR memoryDescriptor;
PAGED_CODE();
firmwareVersion = 0;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, (PVOID) &firmwareVersion, sizeof(firmwareVersion));
WDF_REQUEST_SEND_OPTIONS_INIT(
&sendOptions,
WDF_REQUEST_SEND_OPTION_TIMEOUT
);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(
&sendOptions,
DEFAULT_CONTROL_TRANSFER_TIMEOUT
);
WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket,
BmRequestDeviceToHost, // Direction of the request
BmRequestToDevice, // Recipient
USBFX2_GET_FIRMWARE_VERSION, // Vendor command
0, // Value
0); // Index
status = WdfUsbTargetDeviceSendControlTransferSynchronously(
DeviceContext->UsbDevice,
WDF_NO_HANDLE, // Optional WDFREQUEST
&sendOptions,
&controlSetupPacket,
&memoryDescriptor, // MemoryDescriptor
NULL); // BytesTransferred
if (!NT_SUCCESS(status))
{
KdPrint(("Device %d: Failed to get device firmware version 0x%x\n", DeviceContext->DeviceNumber, status));
TraceEvents(DeviceContext->DebugLog,
TRACE_LEVEL_ERROR,
DBG_RUN,
"Device %d: Failed to get device firmware version 0x%x\n",
DeviceContext->DeviceNumber,
status);
}
else
{
DeviceContext->Firmware.VersionAsUshort = firmwareVersion;
TraceEvents(DeviceContext->DebugLog,
TRACE_LEVEL_INFORMATION,
DBG_RUN,
"Device %d: Get device firmware version : 0x%x\n",
DeviceContext->DeviceNumber,
firmwareVersion);
}
return;
}
GET_STATUS 대한 컨트롤 전송을 보내는 방법 - UMDF
이 절차에서는 클라이언트 드라이버가 GET_STATUS 명령에 대한 컨트롤 전송을 보낼 수 있는 방법을 보여줍니다. 요청의 수신자는 디바이스이며 요청은 비트 D1-D0의 정보를 가져옵니다. 자세한 내용은 USB 사양의 그림 9-4를 참조하세요.
OSR USB Fx2 학습 키트용 UMDF 샘플 드라이버에서 사용할 수 있는 헤더 파일 Usb_hw.h를 포함합니다.
WINUSB_CONTROL_SETUP_PACKET 구조체를 선언합니다.
WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS 도우미 매크로를 호출하여 설치 패킷을 초기화합니다.
BmRequestToDevice를 받는 사람으로 지정합니다.
인덱스 값에 0을 지정합니다.
도우미 메서드 SendControlTransferSynchronously 호출하여 요청을 동기적으로 보냅니다.
도우미 메서드는 IWDFUsbTargetDevice::FormatRequestForControlTransfer 메서드를 호출하여 초기화된 설치 패킷을 프레임워크 요청 개체 및 전송 버퍼와 연결하여 요청을 빌드합니다. 그런 다음 도우미 메서드는 IWDFIoRequest::Send 메서드를 호출하여 요청을 보냅니 다. 메서드가 반환된 후 반환된 값을 검사합니다.
상태가 자체 전원 원격 절전 모드 해제를 나타내는지 확인하려면 WINUSB_DEVICE_TRAITS 열거형에 정의된 다음 값을 사용합니다.
이 코드 예제에서는 디바이스의 상태를 가져오기 위해 제어 전송 요청을 보냅니다. 이 예제에서는 SendControlTransferSynchronously라는 도우미 메서드를 호출하여 요청을 동기적으로 보냅니다.
HRESULT
CDevice::GetDeviceStatus ()
{
HRESULT hr = S_OK;
USHORT deviceStatus;
ULONG bytesTransferred;
TraceEvents(TRACE_LEVEL_INFORMATION,
DRIVER_ALL_INFO,
"%!FUNC!: entry");
// Setup the control packet.
WINUSB_CONTROL_SETUP_PACKET setupPacket;
WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS(
&setupPacket,
BmRequestToDevice,
0);
hr = SendControlTransferSynchronously(
&(setupPacket.WinUsb),
& deviceStatus,
sizeof(USHORT),
&bytesReturned
);
if (SUCCEEDED(hr))
{
if (deviceStatus & USB_GETSTATUS_SELF_POWERED)
{
m_Self_Powered = true;
}
if (deviceStatus & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED)
{
m_remote_wake-enabled = true;
}
}
return hr;
}
다음 코드 예제에서는 SendControlTransferSynchronously라는 도우미 메서드의 구현을 보여 줍니다. 이 메서드는 요청을 동기적으로 보냅니다.
HRESULT
CDevice::SendControlTransferSynchronously(
_In_ PWINUSB_SETUP_PACKET SetupPacket,
_Inout_ PBYTE Buffer,
_In_ ULONG BufferLength,
_Out_ PULONG LengthTransferred
)
{
HRESULT hr = S_OK;
IWDFIoRequest *pWdfRequest = NULL;
IWDFDriver * FxDriver = NULL;
IWDFMemory * FxMemory = NULL;
IWDFRequestCompletionParams * FxComplParams = NULL;
IWDFUsbRequestCompletionParams * FxUsbComplParams = NULL;
*LengthTransferred = 0;
hr = m_FxDevice->CreateRequest( NULL, //pCallbackInterface
NULL, //pParentObject
&pWdfRequest);
if (SUCCEEDED(hr))
{
m_FxDevice->GetDriver(&FxDriver);
hr = FxDriver->CreatePreallocatedWdfMemory( Buffer,
BufferLength,
NULL, //pCallbackInterface
pWdfRequest, //pParetObject
&FxMemory );
}
if (SUCCEEDED(hr))
{
hr = m_pIUsbTargetDevice->FormatRequestForControlTransfer( pWdfRequest,
SetupPacket,
FxMemory,
NULL); //TransferOffset
}
if (SUCCEEDED(hr))
{
hr = pWdfRequest->Send( m_pIUsbTargetDevice,
WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
0); //Timeout
}
if (SUCCEEDED(hr))
{
pWdfRequest->GetCompletionParams(&FxComplParams);
hr = FxComplParams->GetCompletionStatus();
}
if (SUCCEEDED(hr))
{
HRESULT hrQI = FxComplParams->QueryInterface(IID_PPV_ARGS(&FxUsbComplParams));
WUDF_TEST_DRIVER_ASSERT(SUCCEEDED(hrQI));
WUDF_TEST_DRIVER_ASSERT( WdfUsbRequestTypeDeviceControlTransfer ==
FxUsbComplParams->GetCompletedUsbRequestType() );
FxUsbComplParams->GetDeviceControlTransferParameters( NULL,
LengthTransferred,
NULL,
NULL );
}
SAFE_RELEASE(FxUsbComplParams);
SAFE_RELEASE(FxComplParams);
SAFE_RELEASE(FxMemory);
pWdfRequest->DeleteWdfObject();
SAFE_RELEASE(pWdfRequest);
SAFE_RELEASE(FxDriver);
return hr;
}
Winusb.sys 디바이스의 함수 드라이버로 사용하는 경우 애플리케이션에서 제어 전송을 보낼 수 있습니다. WinUSB에서 설정 패킷의 형식을 지정하려면 이 문서의 표에 설명된 UMDF 도우미 매크로 및 구조를 사용합니다. 요청을 보내려면 WinUsb_ControlTransfer 함수를 호출합니다.