다음을 통해 공유


USB 등시 엔드포인트로 데이터를 전송하는 방법

이 항목에서는 클라이언트 드라이버가 USB 디바이스의 등시 엔드포인트와 데이터를 전송하는 URB(USB 요청 블록)를 빌드하는 방법을 설명합니다.

USB(유니버설 직렬 버스) 디바이스는 등시 엔드포인트를 지원하여 오디오/비디오 스트리밍과 같은 일정한 속도로 시간 종속 데이터를 전송할 수 있습니다. 데이터를 전송하기 위해 클라이언트 드라이버는 등시 엔드포인트에 데이터를 읽거나 쓰는 요청을 발급합니다. 결과적으로 호스트 컨트롤러는 정기적으로 디바이스를 폴링하여 데이터를 보내거나 받는 등시 전송을 시작합니다.

고속 및 최고 속도 디바이스의 경우 폴링은 (IN/OUT) 토큰 패킷을 사용하여 수행됩니다. 엔드포인트가 데이터를 보낼 준비가 되면 디바이스는 데이터를 전송하여 IN 토큰 패킷 중 하나에 응답합니다. 디바이스에 쓰기 위해 호스트 컨트롤러는 OUT 토큰 패킷과 데이터 패킷을 보냅니다. 호스트 컨트롤러 또는 디바이스는 핸드셰이크 패킷을 보내지 않으므로 배달이 보장되지 않습니다. 호스트 컨트롤러는 전송을 다시 시도하지 않으므로 오류가 발생할 경우 데이터가 손실될 수 있습니다.

등시 전송의 경우 호스트 컨트롤러는 버스에서 특정 기간을 예약합니다. 등시 엔드포인트의 예약된 시간을 관리하기 위해 시간은 연속 논리 척으로 나뉘어 버스 간격이라고 합니다. 버스 간격의 단위는 버스 속도에 따라 달라집니다.

전체 속도의 경우 버스 간격은 프레임입니다. 프레임의 길이는 1밀리초입니다.

고속 및 SuperSpeed의 경우 버스 간격은 마이크로프레임입니다. 마이크로프레임의 길이는 125 마이크로초입니다. 8개의 연속 마이크로프레임은 하나의 고속 또는 슈퍼스피드 프레임을 구성합니다.

등시 전송은 패킷 기반입니다. 이 항목의 등시 패킷 이라는 용어는 한 버스 간격으로 전송되는 데이터의 양을 나타냅니다. 엔드포인트의 특성에 따라 각 패킷의 크기가 고정되고 엔드포인트의 특성에 따라 결정됩니다.

클라이언트 드라이버는 요청에 대한 URB를 만들고 URB를 USB 드라이버 스택에 제출하여 등시 전송을 시작합니다. 요청은 USB 드라이버 스택의 하위 드라이버 중 하나에 의해 처리됩니다. URB를 받으면 USB 드라이버 스택이 일련의 유효성 검사를 수행하고 요청에 대한 트랜잭션을 예약합니다. 전체 속도를 위해 각 버스 간격으로 전송할 등시 패킷은 유선의 단일 트랜잭션에 포함됩니다. 특정 고속 디바이스는 버스 간격으로 여러 트랜잭션을 허용합니다. 이 경우 클라이언트 드라이버는 URB(단일 요청)에서 등시 패킷에서 더 많은 데이터를 보내거나 받을 수 있습니다. SuperSpeed 디바이스는 여러 트랜잭션 및 버스트 전송을 지원하므로 버스 간격당 더 많은 바이트를 허용합니다. 버스트 전송에 대한 자세한 내용은 USB 3.0 사양 페이지 9-42를 참조하세요.

시작하기 전에

등시 전송에 대한 요청을 만들기 전에 등시 엔드포인트에 대해 열린 파이프에 대한 정보가 있어야 합니다.

WDM(Windows 드라이버 모델) 루틴을 사용하는 클라이언트 드라이버에는 USBD_INTERFACE_LIST_ENTRY 배열의 USBD_PIPE_INFORMATION 구조 중 하나에 파이프 정보가 있습니다. 클라이언트 드라이버는 디바이스에서 구성 또는 인터페이스를 선택하기 위해 드라이버의 이전 요청에서 해당 배열을 얻었습니다.

WDF(Windows 드라이버 프레임워크) 클라이언트 드라이버는 프레임워크의 대상 파이프 개체에 대한 참조를 가져오고 WdfUsbTargetPipeGetInformation 을 호출하여 WDF_USB_PIPE_INFORMATION 구조에서 파이프 정보를 가져와야 합니다.

파이프 정보에 따라 다음 정보 집합을 확인합니다.

  • 호스트 컨트롤러가 각 패킷의 파이프에 보낼 수 있는 데이터 양입니다.

    클라이언트 드라이버가 요청에서 보낼 수 있는 데이터의 양은 호스트 컨트롤러가 엔드포인트에서 보내거나 받을 수 있는 최대 바이트 수를 초과할 수 없습니다. 최대 바이트 수는 USBD_PIPE_INFORMATIONWDF_USB_PIPE_INFORMATION 구조체의 MaximumPacketSize 멤버로 표시됩니다. USB 드라이버 스택은 select-configuration 또는 select-interface 요청 중에 MaximumPacketSize 값을 설정합니다.

    최고 속도 디바이스의 경우 MaximumPacketSize 는 엔드포인트 설명자의 wMaxPacketSize 필드의 처음 11비트에서 파생되며, 이는 엔드포인트가 트랜잭션에서 보내거나 받을 수 있는 최대 바이트 수를 나타냅니다. 전속 디바이스의 경우 컨트롤러는 버스 간격당 하나의 트랜잭션을 보냅니다.

    고속 등시 전송에서 호스트 컨트롤러는 엔드포인트에서 허용하는 경우 버스 간격으로 추가 트랜잭션을 보낼 수 있습니다. 추가 트랜잭션 수는 디바이스에서 설정하며 wMaxPacketSize의 비트 12..11로 표시됩니다. 이 숫자는 0, 1 또는 2일 수 있습니다. 12..11이 0을 나타내는 경우 마이크로프레임당 추가 트랜잭션은 엔드포인트에서 지원되지 않습니다. 숫자가 1이면 호스트 컨트롤러가 추가 트랜잭션(마이크로프레임당 총 2개의 트랜잭션)을 보낼 수 있습니다. 2는 두 개의 추가 트랜잭션(마이크로프레임당 총 3개의 트랜잭션)을 나타냅니다. USB 드라이버 스택에서 설정한 MaximumPacketSize 값에는 추가 트랜잭션에서 보낼 수 있는 바이트 수가 포함됩니다.

    SuperSpeed 등시 전송의 경우 USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR 특정 값(Usbspec.h 참조)이 중요합니다. USB 드라이버 스택은 이러한 값을 사용하여 버스 간격의 최대 바이트 수를 계산합니다.

    • 엔드포인트 도우미 설명자의 Isochronous.Mult 필드입니다. SuperSpeed 등시 전송에서 추가 트랜잭션(고속 디바이스와 유사)을 버스트 트랜잭션이라고 합니다. Mult 값은 엔드포인트가 지원하는 최대 버스트 트랜잭션 수를 나타냅니다. 서비스 간격에 최대 3개의 버스트 트랜잭션(인덱싱된 0~2개)이 있을 수 있습니다.

    • b 엔드포인트 도우미 설명자의 bMaxBurst 필드입니다. 이 값은 단일 버스트 트랜잭션에 있을 수 있는 wMaxPacketSize 청크의 수를 나타냅니다. 버스트 트랜잭션에는 최대 16개의 청크(인덱싱된 0~15개)가 있을 수 있습니다.

    • wBytesPerInterval 은 호스트가 버스 간격으로 보내거나 받을 수 있는 총 바이트 수를 나타냅니다. 버스 간격당 최대 바이트 수를 (bMaxBurst+1) * (Mult+1) * wMaxPacketSize로 계산할 수 있지만 USB 3.0 사양에서는 wBytesPerInterval 값을 대신 사용하는 것이 좋습니다. wBytesPerInterval 값은 계산된 값보다 작거나 같아야 합니다.

      중요

      클라이언트 드라이버의 경우 앞부분에 설명된 값은 정보 전용입니다. 드라이버는 항상 엔드포인트 설명자의 MaximumPacketSize 값을 사용하여 전송 버퍼의 레이아웃을 결정해야 합니다.

  • 엔드포인트는 얼마나 자주 데이터를 보내거나 받나요?

    Interval 멤버는 엔드포인트가 데이터를 보내거나 받을 수 있는 빈도를 결정하는 데 사용됩니다. 디바이스는 해당 값을 설정하고 클라이언트 드라이버는 변경할 수 없습니다. USB 드라이버 스택은 다른 숫자를 사용하여 데이터 스트림에 등시 패킷을 삽입하는 빈도( 간격 값에서 파생된 폴링 기간)를 결정합니다.

    전속 전송의 경우 간격 및 폴링 기간 값은 항상 1입니다. USB 드라이버 스택은 다른 값을 무시합니다.

    다음 표에서는 고속 및 SuperSpeed 전송에 대한 간격 및 계산된 폴링 기간을 보여 줍니다.

    간격 폴링 기간(2Interval-1)
    1 1; 데이터는 버스 간격마다 전송됩니다.
    2 2; 데이터는 두 번째 버스 간격마다 전송됩니다.
    3 4; 데이터는 네 번째 버스 간격마다 전송됩니다.
    4 8; 데이터는 8번째 버스 간격마다 전송됩니다.
  • 각 버스 속도에 대한 패킷 수에 대한 제한 사항은 무엇인가요?

    URB에서는 전속 디바이스에 대해 최대 255개의 등시 패킷만 보낼 수 있습니다. 고속 및 SuperSpeed 디바이스에 대한 URB의 패킷 1024개. URB에서 보내는 패킷 수는 각 프레임에 있는 패킷 수의 배수여야 합니다.

    폴링 기간 고속/SuperSpeed에 대한 패킷 수
    1 8의 배수
    2 4의 배수
    3 2의 배수
    4 모두

wMaxPacketSize가 1,023인 예제 전속 엔드포인트를 생각해 보세요. 이 예제에서 애플리케이션은 25,575바이트 버퍼를 제공했습니다. 해당 버퍼에 대한 전송에는 25개의 등시 패킷(25575/1023)이 필요합니다.

엔드포인트 설명자에 다음과 같은 특성이 표시된 고속 엔드포인트의 예를 생각해 보세요.

  • wMaxPacketSize 는 1,024입니다.
  • 비트 12..11은 두 개의 추가 트랜잭션을 나타냅니다.
  • 간격 은 1입니다.

클라이언트 드라이버가 구성을 선택한 후 등시 파이프에 대한 MaximumPacketSize 는 3,072바이트(총 트랜잭션 * wMaxPacketSize)를 나타냅니다. 추가 트랜잭션을 사용하면 클라이언트 드라이버가 모든 마이크로프레임에서 3,072바이트, 한 프레임에서 총 24,576바이트를 전송할 수 있습니다. 다음 그림에서는 고속 전송을 위해 하나의 마이크로프레임에서 등시 패킷이 전송되는 빈도를 보여 줍니다.

등시 전송 간격, 폴링 기간 및 패킷 다이어그램

엔드포인트 및 SuperSpeed 엔드포인트 도우미 설명자에 이러한 특성이 표시된 SuperSpeed 엔드포인트의 예를 생각해 보세요.

  • wMaxPacketSize 는 1,024입니다.
  • bMaxBurst 는 15입니다.
  • 간격 은 1입니다.
  • Isochronous.Mult 는 2입니다.
  • wBytesPerInterval 은 45000입니다.

앞의 예제에서는 최대 바이트 수를 wMaxPacketSize * (bMaxBurst +1) * (Mult + 1)로 계산하여 49,152바이트로 계산할 수 있지만 디바이스는 값을 45,000바이트인 wBytesPerInterval 값으로 제한합니다. 이 값은 MaximumPacketSize 45,000에도 반영됩니다. 클라이언트 드라이버는 MaximumPacketSize 값만 사용해야 합니다. 이 예제에서는 요청을 세 개의 버스트 트랜잭션으로 나눌 수 있습니다. 처음 두 개의 버스트 트랜잭션에는 각각 16개의 wMaxPacketSize 청크가 포함됩니다. 마지막 버스트 트랜잭션에는 나머지 바이트를 저장할 12개의 청크가 포함됩니다. 이 이미지는 SuperSpeed 전송을 위해 등시 패킷을 통해 전송되는 폴링 간격 및 바이트를 보여줍니다.

초고속 등시 전송 간격, 폴링 기간 및 패킷의 다이어그램

등시 전송에 대한 요청을 작성하려면 다음을 수행합니다.

  1. 각 등시 패킷의 크기를 가져옵니다.
  2. 프레임당 등시 패킷 수를 결정합니다.
  3. 전체 전송 버퍼를 보유하는 데 필요한 등시 패킷 수를 계산합니다.
  4. URB 구조를 할당하여 전송 세부 정보를 설명합니다.
  5. 패킷 오프셋과 같은 각 등시 패킷의 세부 정보를 지정합니다.

등시 전송 요청 전송에 대한 전체 코드 예제는 USBSAMP입니다.

이 항목의 이 예제에서는 등시 전송의 USBSAMP 구현을 간소화합니다. 샘플은 전송에 필요한 총 수 프레임을 계산합니다. 프레임에서 보낼 수 있는 데이터의 양에 따라 전송 버퍼는 더 작은 청크 크기의 바이트로 나뉩니다.

다음 절차에서는 이전 단계를 자세히 설명하며 클라이언트 드라이버가 고속 등시 엔드포인트에 대한 등시 전송 요청을 빌드하고 보내는 데 사용할 수 있는 계산 및 루틴을 보여 줍니다. 프로시저에 사용된 값은 앞에서 설명한 예제 엔드포인트 특성을 기반으로 합니다.

1단계: 등시 패킷의 크기 가져오기

파이프의 MaximumPacketSize 값을 검사하여 등시 패킷의 크기를 결정합니다.

전속 전송의 경우 등시 패킷의 크기는 한 프레임에서 전송할 수 있는 바이트 수입니다. 고속 및 SuperSpeed 전송의 경우 등시 패킷의 크기는 하나의 마이크로프레임으로 전송할 수 있는 총 바이트 수입니다. 이러한 값은 파이프의 MaximumPacketSize에 표시됩니다.

예제에서 MaximumPacketSize 는 프레임당 1023바이트(최고 속도)입니다. 마이크로프레임당 3072바이트(고속); 마이크로프레임당 45,000바이트(SuperSpeed).

참고

MaximumPacketSize 값은 등시 패킷의 허용되는 최대 크기를 나타냅니다. 클라이언트 드라이버는 각 등시 패킷의 크기를 MaximumPacketSize 값보다 작은 값으로 설정할 수 있습니다.

2단계: 프레임당 등시 패킷 수 결정

전속 전송의 경우 각 프레임에서 하나의 등시 패킷을 전송합니다.

고속 및 SuperSpeed 전송의 경우 이 값은 Interval 값에서 파생되어야 합니다. 예제에서 Interval은 1입니다. 따라서 등시 패킷 수는 프레임당 8개여야 합니다. 다른 간격 값은 필수 구성 요소 섹션의 표를 참조하세요.

3단계: 전체 전송 버퍼를 보유하는 데 필요한 등시 패킷 수 계산

전체 버퍼를 전송하는 데 필요한 등시 패킷 수를 계산합니다. 이 값은 전송 버퍼의 길이를 등시 패킷의 크기로 나누어 계산할 수 있습니다.

이 예제에서는 각 등시 패킷의 크기가 MaximumPacketSize 이고 전송 버퍼 길이가 MaximumPacketSize 값의 배수라고 가정합니다.

예를 들어 전속 전송의 경우 제공된 버퍼 25,575바이트에는 25개의 등시 패킷(25575/1023)이 필요합니다. 고속 전송의 경우 24,576 크기의 버퍼는 전송을 위해 8개의 등시 패킷(24576/3072)으로 나뉩니다. SuperSpeed의 경우 크기가 360,000바이트인 버퍼는 8개의 등시 패킷(360000/45000)에 적합합니다.

클라이언트 드라이버는 다음 요구 사항의 유효성을 검사해야 합니다.

  • 등시 패킷 수는 프레임당 패킷 수의 배수여야 합니다.
  • 전송에 필요한 최대 등시 패킷 수는 전속 디바이스의 경우 255를 초과하면 안 됩니다. 고속 또는 SuperSpeed 디바이스의 경우 1024.

4단계: 전송 세부 정보를 설명하는 URB 구조 할당

  1. 페이징이 아닌 풀에서 URB 구조를 할당합니다.

    클라이언트 드라이버가 WDM 루틴을 사용하는 경우 Windows 8 대한 WDK(Windows 드라이버 키트)가 있는 경우 드라이버는 USBD_IsochUrbAllocate 호출해야 합니다. 클라이언트 드라이버는 루틴을 사용하여 Windows Vista 이상 버전의 Windows 운영 체제를 대상으로 지정할 수 있습니다. Windows 8 대한 WDK가 없거나 클라이언트 드라이버가 이전 버전의 운영 체제용인 경우 ExAllocatePoolWithTag를 호출하여 스택 또는 페이징되지 않은 풀에서 구조를 할당할 수 있습니다.

    WDF 클라이언트 드라이버는 WdfUsbTargetDeviceCreateIsochUrb 메서드를 호출하여 URB 구조에 대한 메모리를 할당할 수 있습니다.

  2. URB 구조체의 UrbIsochronousTransfer 멤버는 등시 전송의 세부 정보를 설명하는 _URB_ISOCH_TRANSFER 구조를 가리킵니다. 다음과 같이 다음 UrbIsochronousTransfer 멤버를 초기화합니다.

    • UrbIsochronousTransfer.Hdr.Length 멤버를 URB의 크기로 설정합니다. URB의 크기를 얻으려면 GET_ISO_URB_SIZE 매크로를 호출하고 패킷 수를 지정합니다.

    • UrbIsochronousTransfer.Hdr.Function 멤버를 로 URB_FUNCTION_ISOCH_TRANSFER설정합니다.

    • UrbIsochronousTransfer.NumberOfPackets 멤버를 등시 패킷 수로 설정합니다.

    • UrbIsochronousTransfer.PipeHandle을 엔드포인트와 연결된 파이프의 불투명 핸들로 설정합니다. 파이프 핸들이 USB(유니버설 직렬 버스) 드라이버 스택에서 사용되는 USBD 파이프 핸들인지 확인합니다.

      USBD 파이프 핸들을 가져오기 위해 WDF 클라이언트 드라이버는 WdfUsbTargetPipeWdmGetPipeHandle 메서드를 호출하고 WDFUSBPIPE 핸들을 프레임워크의 파이프 개체에 지정할 수 있습니다. WDM 클라이언트 드라이버는 USBD_PIPE_INFORMATION 구조체의 PipeHandle 멤버에서 가져온 것과 동일한 핸들을 사용해야 합니다.

    • 전송 방향을 지정합니다. 등시 IN 전송을 위해 UrbIsochronousTransfer.TransferFlags 를 USBD_TRANSFER_DIRECTION_IN 설정합니다(디바이스에서 읽기). 등시 OUT 전송(디바이스에 쓰기)에 대한 USBD_TRANSFER_DIRECTION_OUT.

    • UrbIsochronousTransfer에서 USBD_START_ISO_TRANSFER_ASAP 플래그를 지정합니다. TransferFlags. 플래그는 USB 드라이버 스택에 다음 적절한 프레임으로 전송을 보내도록 지시합니다. 클라이언트 드라이버가 이 파이프에 대해 등시성 URB를 처음 보내는 경우 드라이버 스택은 가능한 한 빨리 URB에서 등시 패킷을 보냅니다. USB 드라이버 스택은 해당 파이프의 후속 URL에 사용할 다음 프레임을 추적합니다. USBD_START_ISO_TRANSFER_ASAP 플래그를 사용하는 후속 등시성 URB 전송이 지연되는 경우 드라이버 스택은 해당 URB의 일부 또는 모든 패킷을 늦은 것으로 간주하고 해당 패킷을 전송하지 않습니다.

      USB 드라이버 스택은 해당 파이프에 대한 이전 URB를 완료한 후 스택이 1024 프레임에 대해 등시 URB를 수신하지 않는 경우 USBD_START_ISO_TRANSFER_ASAP 시작 프레임 추적을 다시 설정합니다. USBD_START_ISO_TRANSFER_ASAP 플래그를 지정하는 대신 시작 프레임을 지정할 수 있습니다. 자세한 내용은 주의 섹션을 참조하세요.

    • 전송 버퍼 및 크기를 지정합니다. UrbIsochronousTransfer.TransferBuffer 또는UrbIsochronousTransfer.TransferBufferMDL의 버퍼를 설명하는 MDL의 버퍼에 대한 포인터를 설정할 수 있습니다.

      전송 버퍼에 대한 MDL 을 검색하기 위해 WDF 클라이언트 드라이버는 전송 방향에 따라 WdfRequestRetrieveOutputWdmMdl 또는 WdfRequestRetrieveInputWdmMdl을 호출할 수 있습니다.

5단계: 전송 시 각 등시 패킷의 세부 정보 지정

USB 드라이버 스택은 각 등시 패킷에 대한 정보를 보유할 수 있을 만큼 크지만 패킷에 포함된 데이터는 포함하지 않는 새 URB 구조를 할당합니다. URB 구조에서 UrbIsochronousTransfer.IsoPacket 멤버는 전송 시 각 등시 패킷의 세부 정보를 설명하는 USBD_ISO_PACKET_DESCRIPTOR 배열입니다. 패킷은 연속적이어야 합니다. 배열의 요소 수는 URB의 UrbIsochronousTransfer.NumberOfPackets 멤버에 지정된 등시 패킷 수여야 합니다.

고속 전송의 경우 배열의 각 요소는 하나의 마이크로프레임에 있는 하나의 등시 패킷과 상관 관계가 있습니다. 전속의 경우 각 요소는 한 프레임에서 전송된 하나의 등시 패킷과 상관 관계가 있습니다.

각 요소에 대해 요청에 대한 전체 전송 버퍼의 시작부터 각 등시 패킷의 바이트 오프셋을 지정합니다. UrbIsochronousTransfer.IsoPacket[i]를 설정하여 해당 값을 지정할 수 있습니다. 오프셋 멤버입니다. USB 드라이버 스택은 지정된 값을 사용하여 보내거나 받을 데이터의 양을 추적합니다.

Full-Speed 전송에 대한 오프셋 설정

예를 들어 이러한 항목은 전송 버퍼에 대한 배열 항목입니다. 클라이언트 드라이버는 최대 1,023바이트까지 하나의 등시 패킷을 전송하는 하나의 프레임을 가지고 있습니다. 25,575바이트의 전송 버퍼는 각각 1,023바이트 길이의 25개의 등시 패킷을 보유할 수 있습니다. 전체 버퍼에는 총 25개의 프레임이 필요합니다.

Frame 1 IsoPacket [0].Offset = 0 (start address)
Frame 2 IsoPacket [1].Offset = 1023
Frame 3 IsoPacket [2].Offset = 2046
Frame 4 IsoPacket [3].Offset = 3069
...
Frame 25 IsoPacket [24].Offset = 24552

Total length transferred is 25,575 bytes.

High-Speed 전송에 대한 오프셋 설정

예를 들어 고속 전송 버퍼에 대한 배열 항목입니다. 이 예제에서는 버퍼가 24,576바이트이고 클라이언트 드라이버에 각각 3,072바이트 길이의 8개의 등시 패킷을 전송하는 하나의 프레임이 있다고 가정합니다.

Microframe 1 IsoPacket [0].Offset = 0 (start address)
Microframe 2 IsoPacket [1].Offset = 3072
Microframe 3 IsoPacket [2].Offset = 6144
Microframe 4 IsoPacket [3].Offset = 9216
Microframe 5 IsoPacket [4].Offset = 12288
Microframe 6 IsoPacket [5].Offset = 15360
Microframe 7 IsoPacket [6].Offset = 18432
Microframe 8 IsoPacket [7].Offset = 21504

Total length transferred is 24,576 bytes.

SuperSpeed 전송에 대한 오프셋 설정

예를 들어 SuperSpeed의 배열 오프셋입니다. 한 프레임에서 최대 45,000바이트를 전송할 수 있습니다. 크기가 360,000인 전송 버퍼는 8개의 마이크로프레임 내에 적합합니다.

Microframe 1 IsoPacket [0].Offset = 0 (start address)
Microframe 2 IsoPacket [1].Offset = 45000
Microframe 3 IsoPacket [2].Offset = 90000
Microframe 4 IsoPacket [3].Offset = 135000
Microframe 5 IsoPacket [4].Offset = 180000
Microframe 6 IsoPacket [5].Offset = 225000
Microframe 7 IsoPacket [6].Offset = 270000
Microframe 8 IsoPacket [7].Offset = 315000

Total length transferred is 360,000 bytes.

UrbIsochronousTransfer.IsoPacket[i]. 길이 멤버는 등시 URB의 각 패킷의 길이를 의미하지 않습니다. IsoPacket[i]. 길이 는 동시 IN 전송을 위해 디바이스에서 수신된 실제 바이트 수를 나타내기 위해 USB 드라이버 스택에 의해 업데이트됩니다. 등시 OUT 전송의 경우 드라이버 스택은 IsoPacket[i]에 설정된 값을 무시합니다. 길이입니다.

전송을 위한 시작 USB 프레임 번호 지정

URB의 UrbIsochronousTransfer.StartFrame 멤버는 전송에 대한 시작 USB 프레임 번호를 지정합니다. 클라이언트 드라이버가 URB를 제출하는 시간과 USB 드라이버 스택이 URB를 처리하는 시간 사이에는 항상 대기 시간이 있습니다. 따라서 클라이언트 드라이버는 항상 드라이버가 URB를 제출할 때 현재 프레임보다 최신인 시작 프레임을 지정해야 합니다. 현재 프레임 번호를 검색하기 위해 클라이언트 드라이버는 URB_FUNCTION_GET_CURRENT_FRAME_NUMBER 요청을 USB 드라이버 스택(_URB_GET_CURRENT_FRAME_NUMBER)으로 보낼 수 있습니다.

등시 전송의 경우 현재 프레임과 StartFrame 값 간의 절대 차이가 USBD_ISO_START_FRAME_RANGE 미만이어야 합니다. StartFrame이 적절한 범위 내에 있지 않으면 USB 드라이버 스택은 URB 헤더의 상태 멤버( _URB_HEADER 참조)를 USBD_STATUS_BAD_START_FRAME 설정하고 전체 URB를 삭제합니다.

URB에 지정된 StartFrame 값은 URB의 첫 번째 등시 패킷이 전송되는 프레임 번호를 나타냅니다. 후속 패킷의 프레임 번호는 엔드포인트의 버스 속도 및 폴링 기간 값에 따라 달라집니다. 예를 들어, 전속 전송의 경우 첫 번째 패킷은 StartFrame에서 전송됩니다. 두 번째 패킷은 StartFrame+1 등에서 전송됩니다. USB 드라이버 스택이 전체 속도를 위해 프레임에서 등시 패킷을 전송하는 방법은 다음과 같습니다.

Frame (StartFrame)   IsoPacket [0]
Frame (StartFrame+1) IsoPacket [1]
Frame (StartFrame+2) IsoPacket [2]
Frame (StartFrame+3) IsoPacket [3]
...

간격 값이 1인 고속 디바이스의 경우 프레임 번호는 8번째 마이크로프레임마다 변경됩니다. USB 드라이버 스택이 고속 프레임에서 등시 패킷을 전송하는 방법은 다음과 같습니다.

Frame (StartFrame) Microframe 1 IsoPacket [0]
...
Frame (StartFrame) Microframe 8 IsoPacket [7]
Frame (StartFrame+1) Microframe 1 IsoPacket [8]
...
Frame (StartFrame+1) Microframe 8 IsoPacket [15]
Frame (StartFrame+2) Microframe 1 IsoPacket [16]
...
Frame (StartFrame+2) Microframe 8 IsoPacket [23]

USB 드라이버 스택이 URB를 처리할 때 드라이버는 프레임 번호가 현재 프레임 번호보다 낮은 URB의 모든 등시 패킷을 삭제합니다. 드라이버 스택은 삭제된 각 패킷에 대한 패킷 설명자의 상태 멤버를 USBD_STATUS_ISO_NA_LATE_USBPORT, USBD_STATUS_ISO_NOT_ACCESSED_BY_HW 또는 USBD_STATUS_ISO_NOT_ACCESSED_LATE 설정합니다. URB의 일부 패킷은 삭제되지만 드라이버 스택은 프레임 번호가 현재 프레임 번호보다 높은 패킷만 전송하려고 시도합니다.

USB 드라이버 스택이 각 등시 패킷을 고속 마이크로프레임에 로드하기 때문에 유효한 StartFrame 멤버에 대한 검사 약간 더 복잡합니다. 그러나 StartFrame의 값은 마이크로프레임이 아닌 1밀리초(전속) 프레임 번호를 나타냅니다. 예를 들어 URB에 기록된 StartFrame 값이 현재 프레임보다 작은 경우 드라이버 스택은 최대 8개의 패킷을 삭제할 수 있습니다. 삭제된 패킷의 정확한 수는 등시 파이프와 연결된 폴링 기간에 따라 달라집니다.

등시 전송 예제

다음 코드 예제에서는 최고 속도, 고속 및 SuperSpeed 전송을 위해 등시 전송을 위한 URB를 만드는 방법을 보여 줍니다.

#define MAX_SUPPORTED_PACKETS_FOR_HIGH_OR_SUPER_SPEED 1024
#define MAX_SUPPORTED_PACKETS_FOR_FULL_SPEED 255

NTSTATUS CreateIsochURB  ( PDEVICE_OBJECT         DeviceObject,
                          PUSBD_PIPE_INFORMATION  PipeInfo,
                          ULONG                   TotalLength,
                          PMDL                    RequestMDL,
                          PURB                    Urb)
{
    PDEVICE_EXTENSION        deviceExtension;
    ULONG                    numberOfPackets;
    ULONG                    numberOfFrames;
    ULONG                    isochPacketSize = 0;
    ULONG                    transferSizePerFrame;
    ULONG                    currentFrameNumber;
    size_t                   urbSize;
    ULONG                    index;
    NTSTATUS                 ntStatus;

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    isochPacketSize = PipeInfo->MaximumPacketSize;

    // For high-speed transfers
    if (deviceExtension->IsDeviceHighSpeed || deviceExtension->IsDeviceSuperSpeed)
    {
        // Ideally you can pre-calculate numberOfPacketsPerFrame for the Pipe and
        // store it in the pipe context.

        switch (PipeInfo->Interval)
        {
        case 1:
            // Transfer period is every microframe (eight times a frame).
            numberOfPacketsPerFrame = 8;
            break;

        case 2:
            // Transfer period is every 2 microframes (four times a frame).
            numberOfPacketsPerFrame = 4;
            break;

        case 3:
            // Transfer period is every 4 microframes (twice in a frame).
            numperOfPacketsPerFrame = 2;
            break;

        case 4:
        default:
            // Transfer period is every 8 microframes (once in a frame).
            numberOfPacketsPerFrame = 1;
            break;
        }

        //Calculate the number of packets.
        numberOfPackets = TotalLength / isochPacketSize;

        if (numberOfPackets > MAX_SUPPORTED_PACKETS_FOR_HIGH_OR_SUPER_SPEED)
        {
            // Number of packets cannot be  greater than 1021.
            ntStatus = STATUS_INVALID_PARAMETER;
            goto Exit;
        }

        if (numberOfPackets % numberOfPacketsPerFrame != 0)
        {

            // Number of packets should be a multiple of numberOfPacketsPerFrame
            ntStatus = STATUS_INVALID_PARAMETER;
            goto Exit;
        }

    }
    else if (deviceExtension->IsDeviceFullSpeed)
    {
        //For full-speed transfers
        // Microsoft USB stack only supports bInterval value of 1 for
        // full-speed isochronous endpoints.

        //Calculate the number of packets.
        numberOfPacketsPerFrame = 1;

        numberOfPackets = TotalLength / isochPacketSize;

        if (numberOfPackets > MAX_SUPPORTED_PACKETS_FOR_FULL_SPEED)
        {
            // Number of packets cannot be greater than 255.
            ntStatus = STATUS_INVALID_PARAMETER;
            goto Exit;
        }
    }

    // Allocate an isochronous URB for the transfer
    ntStatus = USBD_IsochUrbAllocate (deviceExtension->UsbdHandle,
        numberOfPackets,
        &Urb);

    if (!NT_SUCCESS(ntStatus))
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    urbSize = GET_ISO_URB_SIZE(numberOfPackets);

    Urb->UrbIsochronousTransfer.Hdr.Length = (USHORT) urbSize;
    Urb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER;
    Urb->UrbIsochronousTransfer.PipeHandle = PipeInfo->PipeHandle;

    if (USB_ENDPOINT_DIRECTION_IN(PipeInfo->EndpointAddress))
    {
        Urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN;
    }
    else
    {
        Urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_OUT;
    }

    Urb->UrbIsochronousTransfer.TransferBufferLength = TotalLength;
    Urb->UrbIsochronousTransfer.TransferBufferMDL = RequestMDL;
    Urb->UrbIsochronousTransfer.NumberOfPackets = numberOfPackets;
    Urb->UrbIsochronousTransfer.UrbLink = NULL;

    // Set the offsets for every packet for reads/writes

    for (index = 0; index < numberOfPackets; index++)
    {
        Urb->UrbIsochronousTransfer.IsoPacket[index].Offset = index * isochPacketSize;
    }

    // Length is a return value for isochronous IN transfers.
    // Length is ignored by the USB driver stack for isochronous OUT transfers.

    Urb->UrbIsochronousTransfer.IsoPacket[index].Length = 0;
    Urb->UrbIsochronousTransfer.IsoPacket[index].Status = 0;

    // Set the USBD_START_ISO_TRANSFER_ASAP. The USB driver stack will calculate the start frame.
    // StartFrame value set by the client driver is ignored.
    Urb->UrbIsochronousTransfer.TransferFlags |= USBD_START_ISO_TRANSFER_ASAP;

Exit:

    return ntStatus;
}