Como transferir dados para pontos de extremidade isócronos USB

Este tópico descreve como um driver cliente pode criar um URB (Bloco de Solicitação USB) para transferir dados de e para pontos de extremidade isócronos em um dispositivo USB.

Um dispositivo USB (Barramento Serial Universal) pode dar suporte a pontos de extremidade isócronos para transferir dados dependentes de tempo a uma taxa constante, como com streaming de áudio/vídeo. Para transferir dados, o driver do cliente emite uma solicitação para ler ou gravar dados em um ponto de extremidade isócrono. Como resultado, o controlador host inicia uma transferência isócrona que envia ou recebe dados sondando o dispositivo em intervalos regulares.

Para dispositivos de alta velocidade e velocidade total, a sondagem é feita usando pacotes de token (IN/OUT). Quando o ponto de extremidade está pronto para enviar dados, o dispositivo responde a um dos pacotes de token IN enviando dados. Para gravar no dispositivo, o controlador host envia um pacote de token OUT seguido por pacotes de dados. O controlador ou dispositivo host não envia nenhum pacote de handshake e, portanto, não há entrega garantida. Como o controlador host não tenta repetir a transferência, os dados podem ser perdidos se ocorrer um erro.

Para transferências isócronas, o controlador host reserva determinados períodos de tempo no ônibus. Para gerenciar o tempo reservado para pontos de extremidade isócronos, o tempo é dividido em chucks lógicos consecutivos são chamados de intervalos de barramento. A unidade do intervalo de ônibus depende da velocidade do ônibus.

Para velocidade total, um intervalo de barramento é um quadro. O comprimento de um quadro é de 1 milissegundo.

Para alta velocidade e SuperSpeed, o intervalo de ônibus é um microframe. O comprimento de um microframe é de 125 microssegundos. Oito microframes consecutivos constituem um quadro de alta velocidade ou SuperSpeed.

As transferências isocronas são baseadas em pacotes. O termo pacote isócrono neste tópico refere-se à quantidade de dados transferidos em um intervalo de ônibus. As características do ponto de extremidade determinam que o tamanho de cada pacote é fixo e determinado pelas características do ponto de extremidade.

O driver do cliente inicia uma transferência isócrona criando um URB para a solicitação e enviando o URB para a pilha de driver USB. A solicitação é tratada por um dos drivers inferiores na pilha do driver USB. Ao receber o URB, a pilha de driver USB executa um conjunto de validações e agenda transações para a solicitação. Para velocidade total, um pacote isócrono a ser transferido em cada intervalo de barramento está contido em uma única transação na transmissão. Determinados dispositivos de alta velocidade permitem várias transações em um intervalo de ônibus. Nesse caso, o driver do cliente pode enviar ou receber mais dados no pacote isócrono em uma única solicitação (URB). Os dispositivos SuperSpeed dão suporte a várias transações e transferências de intermitência, permitindo ainda mais bytes por intervalo de barramento. Para obter mais informações sobre transferências de intermitência, consulte a página de especificação USB 3.0 9-42.

Antes de começar

Antes de criar uma solicitação para uma transferência isócrona, você deve ter informações sobre o pipe aberto para o ponto de extremidade isócrono.

Um driver cliente que usa rotinas WDM (Modelo de Driver do Windows) tem as informações de pipe em uma das estruturas de USBD_PIPE_INFORMATION de uma matriz de USBD_INTERFACE_LIST_ENTRY . O driver cliente obteve essa matriz na solicitação anterior do driver para selecionar uma configuração ou uma interface no dispositivo.

Um driver de cliente do WDF (Windows Driver Framework) deve obter uma referência ao objeto de pipe de destino da estrutura e chamar WdfUsbTargetPipeGetInformation para obter informações de pipe em uma estrutura de WDF_USB_PIPE_INFORMATION .

Com base nas informações do pipe, determine este conjunto de informações:

  • Quantos dados o controlador de host pode enviar para o pipe em cada pacote.

    A quantidade de dados que o driver cliente pode enviar em uma solicitação não pode exceder o número máximo de bytes que o controlador host pode enviar ou receber de um ponto de extremidade. O número máximo de bytes é indicado pelo membro MaximumPacketSize de estruturas de USBD_PIPE_INFORMATION e WDF_USB_PIPE_INFORMATION . A pilha do driver USB define o valor MaximumPacketSize durante uma solicitação select-configuration ou select-interface.

    Para dispositivos de velocidade total, MaximumPacketSize é derivado dos primeiros 11 bits do campo wMaxPacketSize do descritor de ponto de extremidade, o que indica o número máximo de bytes que o ponto de extremidade pode enviar ou receber em uma transação. Para dispositivos de velocidade total, o controlador envia uma transação por intervalo de barramento.

    Em uma transferência isócrona de alta velocidade, o controlador de host poderá enviar transações adicionais em um intervalo de barramento se o ponto de extremidade permitir. O número de transações adicionais é definido pelo dispositivo e indicado nos bits 12..11 do wMaxPacketSize. Esse número pode ser 0, 1 ou 2. Se 12..11 indicar 0, transações adicionais por microframe não serão suportadas pelo ponto de extremidade. Se o número for 1, o controlador host poderá enviar uma transação adicional (total de duas transações por microframe); 2 indica duas transações adicionais (total de três transações por microframe). O valor MaximumPacketSize definido pela pilha do driver USB inclui o número de bytes que podem ser enviados em transações adicionais.

    Para transferência isócrona SuperSpeed, determinados valores de USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR (consulte Usbspec.h) são importantes. A pilha de driver USB usa esses valores para calcular o número máximo de bytes em um intervalo de barramento.

    • Campo Isochronous.Mult do descritor complementar do ponto de extremidade. Em transferências isócronas SuperSpeed, as transações adicionais (assim como dispositivos de alta velocidade) são conhecidas como transações de intermitência. O valor Mult indica o número máximo de transações de intermitência compatíveis com o ponto de extremidade. Pode haver até três transações de intermitência (indexadas de 0 a 2) em um intervalo de serviço.

    • campo bMaxBurst do descritor complementar do ponto de extremidade. Esse valor indica o número de partes do wMaxPacketSize que podem estar presentes em uma única transação de intermitência. Pode haver até 16 partes (indexadas de 0 a 15) em uma transação de intermitência.

    • wBytesPerInterval indica o número total de bytes que o host pode enviar ou receber em um intervalo de barramento. Embora o número máximo de bytes por intervalo de barramento possa ser calculado como (bMaxBurst+1) * (Mult+1) * wMaxPacketSize, a especificação USB 3.0 recomenda usar o valor wBytesPerInterval . O valor wBytesPerInterval deve ser menor ou igual a esse valor calculado.

      Importante

      Para um driver de cliente, os valores descritos no anterior são apenas para informações. O driver deve sempre usar o valor MaximumPacketSize do descritor de ponto de extremidade para determinar o layout do buffer de transferência.

  • Com que frequência o ponto de extremidade envia ou recebe dados?

    O membro Interval é usado para determinar com que frequência o ponto de extremidade pode enviar ou receber dados. O dispositivo define esse valor e o driver do cliente não pode alterá-lo. A pilha de driver USB usa outro número para determinar a frequência com que insere pacotes isocronos no fluxo de dados: o período de sondagem, derivado do valor Interval .

    Para transmissões de velocidade total, os valores intervalo e período de sondagem são sempre 1; a pilha do driver USB ignora outros valores.

    A tabela a seguir mostra o Intervalo e o período de sondagem calculado para transferências de alta velocidade e SuperSpeed:

    Intervalo Período de sondagem (2Interval-1)
    1 1; Os dados são transferidos a cada intervalo de ônibus.
    2 2; Os dados são transferidos a cada segundo intervalo de ônibus.
    3 4; Os dados são transferidos a cada quarto intervalo de ônibus.
    4 8; Os dados são transferidos a cada oitavo intervalo de ônibus.
  • Quais são as restrições no número de pacotes para cada velocidade de barramento.

    Em um URB, você só pode enviar até 255 pacotes isócronos para um dispositivo de velocidade total; 1024 pacotes em um URB para em dispositivos de alta velocidade e SuperSpeed. O número de pacotes enviados no URB deve ser um múltiplo do número de pacotes em cada quadro.

    Período de sondagem Número de pacotes para alta velocidade/SuperSpeed
    1 Múltiplo de 8
    2 Múltiplo de 4
    3 Múltiplo de 2
    4 Qualquer

Considere um exemplo de ponto de extremidade de velocidade total com wMaxPacketSize é 1.023. Para este exemplo, o aplicativo forneceu buffer de 25.575 bytes. A transferência para esse buffer requer 25 pacotes isócronos (25575/1023).

Considere um exemplo de ponto de extremidade de alta velocidade com as seguintes características indicadas no descritor de ponto de extremidade.

  • wMaxPacketSize é 1.024.
  • Os bits 12..11 indicam duas transações adicionais.
  • O intervalo é 1.

Depois que o driver do cliente seleciona uma configuração, MaximumPacketSize para o pipe isócrono indica 3.072 bytes (total de transações * wMaxPacketSize). As transações adicionais permitem que o driver cliente transfira 3.072 bytes em cada microframe e total de 24.576 bytes em um quadro. A ilustração a seguir mostra a frequência com que um pacote isócrono é transferido em um microframe para transmissões de alta velocidade.

Diagrama de intervalos de transferência isócronos, períodos de sondagem e pacotes.

Considere um exemplo de ponto de extremidade SuperSpeed com estas características indicadas nos descritores complementares de ponto de extremidade SuperSpeed e de ponto de extremidade SuperSpeed:

  • wMaxPacketSize é 1.024.
  • bMaxBurst é 15.
  • O intervalo é 1.
  • Isochronous.Mult é 2.
  • wBytesPerInterval é 45000.

No exemplo anterior, embora o número máximo de bytes possa ser calculado como wMaxPacketSize * (bMaxBurst +1) * (Mult + 1) resultando em 49.152 bytes, o dispositivo limita o valor ao valor wBytesPerInterval de 45.000 bytes. Esse valor também é refletido em MaximumPacketSize 45.000. O driver cliente deve usar apenas o valor MaximumPacketSize . Neste exemplo, a solicitação pode ser dividida em três transações de intermitência. As duas primeiras transações de intermitência contêm 16 partes de wMaxPacketSize. A última transação de intermitência contém 12 partes para conter os bytes restantes. Esta imagem mostra o intervalo de sondagem e os bytes transferidos por meio de um pacote isócrono para transmissão SuperSpeed.

Diagrama de intervalos de transferência isócronos superspeed, períodos de sondagem e pacotes.

Para criar uma solicitação para uma transferência isócrona:

  1. Obtenha o tamanho de cada pacote isócrono.
  2. Determine o número de pacotes isócronos por quadro.
  3. Calcule o número de pacotes isócronos necessários para manter todo o buffer de transferência.
  4. Aloque uma estrutura URB para descrever os detalhes da transferência.
  5. Especifique os detalhes de cada pacote isócrono, como deslocamento de pacote.

Para obter um exemplo de código completo sobre como enviar solicitações de transferência isócronas, USBSAMP.

Este exemplo neste tópico simplifica a implementação de USBSAMP de transferência isócrona. O exemplo calcula o número total de quadros necessários para a transferência. Com base na quantidade de dados que podem ser enviados em um quadro, o buffer de transferência é dividido em bytes menores do tamanho de parte.

O procedimento a seguir elabora as etapas anteriores e mostra cálculos e rotinas que um driver cliente pode usar para criar e enviar uma solicitação de transferência isócrona para um ponto de extremidade isócrono de alta velocidade. Os valores usados no procedimento são baseados nas características de ponto de extremidade de exemplo descritas anteriormente.

Etapa 1: Obter o tamanho de um pacote isócrono

Determine o tamanho de um pacote isócrono inspecionando o valor MaximumPacketSize do pipe.

Para transmissões de velocidade total, o tamanho de um pacote isócrono é o número de bytes que você pode transferir em um quadro. Para transmissões de alta velocidade e SuperSpeed, o tamanho de um pacote isócrono é o número total de bytes que podem ser transferidos em um microframe. Esses valores são indicados no MaximumPacketSize do pipe.

No exemplo, MaximumPacketSize é de 1023 bytes por quadro (velocidade total); 3072 bytes por microframe (alta velocidade); 45.000 bytes por microframe (SuperSpeed).

Observação

O valor MaximumPacketSize indica o tamanho máximo permitido do pacote isócrono. O driver cliente pode definir o tamanho de cada pacote isócrono para qualquer valor menor que o valor MaximumPacketSize .

Etapa 2: Determinar o número de pacotes isócronos por quadro

Para transmissões de velocidade total, você transfere um pacote isócrono em cada quadro.

Para transmissões de alta velocidade e SuperSpeed, esse valor deve ser derivado do valor Interval. No exemplo, Interval é 1. Portanto, o número de pacotes isócronos deve ser oito por quadro. Para outros valores interval, consulte a tabela na seção Pré-requisitos.

Etapa 3: Calcular o número de pacotes isócronos necessários para manter todo o buffer de transferência

Calcule o número de pacotes isócronos necessários para transferir todo o buffer. Esse valor pode ser calculado dividindo o comprimento do buffer de transferência pelo tamanho de um pacote isócrono.

Neste exemplo, supomos que o tamanho de cada pacote isócrono seja MaximumPacketSize e o comprimento do buffer de transferência seja um múltiplo do valor MaximumPacketSize .

Por exemplo, para transferência de velocidade total, um buffer fornecido de 25.575 bytes requer 25 pacotes isócronos (25575/1023). Para transferência de alta velocidade, um buffer de tamanho 24.576 é dividido em oito pacotes isócronos (24576 /3072) para a transferência. Para SuperSpeed, um buffer de tamanho 360.000 bytes cabe em oito pacotes isócronos (360000/45000).

O driver do cliente deve validar estes requisitos:

  • O número de pacotes isócronos deve ser um múltiplo do número de pacotes por quadro.
  • O número máximo de pacotes isócronos necessários para fazer a transferência não deve exceder 255 para o dispositivo de velocidade total; 1024 para um dispositivo de alta velocidade ou SuperSpeed.

Etapa 4: Alocar uma estrutura URB para descrever os detalhes da transferência

  1. Alocar uma estrutura URB em um pool não paginado.

    Se o driver cliente usar rotinas WDM, o driver deverá chamar o USBD_IsochUrbAllocate se você tiver o WDK (Kit de Driver do Windows) para Windows 8. Um driver cliente pode usar a rotina para direcionar o Windows Vista e versões posteriores do sistema operacional Windows. Se você não tiver o WDK para Windows 8 ou se o driver cliente for destinado a uma versão anterior do sistema operacional, você poderá alocar a estrutura na pilha ou no pool não paginado chamando ExAllocatePoolWithTag.

    Um driver cliente WDF pode chamar o método WdfUsbTargetDeviceCreateIsochUrb para alocar memória para a estrutura URB .

  2. O membro UrbIsochronousTransfer da estrutura URB aponta para uma estrutura _URB_ISOCH_TRANSFER que descreve os detalhes de uma transferência isócrona. Inicialize os seguintes membros UrbIsochronousTransfer da seguinte maneira:

    • Defina o membro UrbIsochronousTransfer.Hdr.Length para o tamanho do URB. Para obter o tamanho da URB, chame GET_ISO_URB_SIZE macro e especifique o número de pacotes.

    • Defina o membro UrbIsochronousTransfer.Hdr.Function como URB_FUNCTION_ISOCH_TRANSFER.

    • Defina o membro UrbIsochronousTransfer.NumberOfPackets como o número de pacotes isócronos.

    • Defina urbIsochronousTransfer.PipeHandle como a alça opaca do pipe associado ao ponto de extremidade. Verifique se a alça do pipe é a alça de pipe USBD usada pela pilha de driver do Barramento Serial Universal (USB).

      Para obter o identificador de pipe USBD, um driver cliente do WDF pode chamar o método WdfUsbTargetPipeWdmGetPipeHandle e especificar o identificador WDFUSBPIPE para o objeto pipe da estrutura. Um driver cliente WDM deve usar o mesmo identificador obtido no membro PipeHandle da estrutura USBD_PIPE_INFORMATION .

    • Especifique a direção da transferência. Defina UrbIsochronousTransfer.TransferFlags como USBD_TRANSFER_DIRECTION_IN para uma transferência IN isócrona (leitura do dispositivo); USBD_TRANSFER_DIRECTION_OUT para uma transferência OUT isócrona (gravando no dispositivo).

    • Especifique o sinalizador USBD_START_ISO_TRANSFER_ASAP em UrbIsochronousTransfer. TransferFlags. O sinalizador instrui a pilha de driver USB a enviar a transferência no próximo quadro apropriado. Pela primeira vez que o driver cliente envia um URB isócrono para esse pipe, a pilha de driver envia os pacotes isócronos no URB assim que possível. A pilha de driver USB rastreia o próximo quadro a ser usado para URBs subsequentes nesse pipe. Se houver um atraso no envio de um URB isócrono subsequente que usa o sinalizador USBD_START_ISO_TRANSFER_ASAP, a pilha de driver considerará alguns ou todos os pacotes desse URB como atrasados e não transferirá esses pacotes.

      A pilha de driver USB redefine seu USBD_START_ISO_TRANSFER_ASAP acompanhamento de quadro inicial, se a pilha não receber um URB isócrono para 1024 quadros depois de concluir o URB anterior para esse pipe. Em vez de especificar o sinalizador USBD_START_ISO_TRANSFER_ASAP, você pode especificar o quadro inicial. Para obter mais informações, consulte a seção Comentários.

    • Especifique o buffer de transferência e seu tamanho. Você pode definir um ponteiro para o buffer em UrbIsochronousTransfer.TransferBuffer ou o MDL que descreve o buffer em UrbIsochronousTransfer.TransferBufferMDL.

      Para recuperar o MDL para o buffer de transferência, um driver cliente WDF pode chamar WdfRequestRetrieveOutputWdmMdl ou WdfRequestRetrieveInputWdmMdl, dependendo da direção da transferência.

Etapa 5: Especificar os detalhes de cada pacote isócrono na transferência

A pilha de driver USB aloca a nova estrutura URB que é grande o suficiente para armazenar informações sobre cada pacote isócrono, mas não os dados contidos no pacote. Na estrutura URB , o membro UrbIsochronousTransfer.IsoPacket é uma matriz de USBD_ISO_PACKET_DESCRIPTOR que descreve os detalhes de cada pacote isócrono na transferência. Os pacotes devem ser contíguos. O número de elementos na matriz deve ser o número de pacotes isócronos especificados no membro UrbIsochronousTransfer.NumberOfPackets da URB .

Para uma transferência de alta velocidade, cada elemento na matriz correlaciona-se a um pacote isócrono em um microframe. Para velocidade total, cada elemento se correlaciona a um pacote isócrono transferido em um quadro.

Para cada elemento, especifique o deslocamento de bytes de cada pacote isócrono do início de todo o buffer de transferência para a solicitação. Você pode especificar esse valor definindo urbIsochronousTransfer.IsoPacket[i]. Membro offset . A pilha de driver USB usa o valor especificado para acompanhar a quantidade de dados a serem enviados ou recebidos.

Definindo deslocamento para uma transferência de Full-Speed

Para o exemplo, essas são as entradas de matriz para o buffer de transferência em velocidade total. Em velocidade total, o driver do cliente tem um quadro para transferir um pacote isócrono de até 1.023 bytes. Um buffer de transferência de 25.575 bytes pode conter 25 pacotes isócronos, cada um com 1.023 bytes de comprimento. Um total de 25 quadros são necessários para todo o buffer.

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.

Definindo deslocamento para uma transferência de High-Speed

Para o exemplo, essas são as entradas de matriz para um buffer de transferência em alta velocidade. O exemplo pressupõe que o buffer seja de 24.576 bytes e que o driver cliente tenha um quadro para transferir oito pacotes isócronos, cada um com 3.072 bytes de comprimento.

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.

Configurando deslocamento para uma transferência superspeed

Para o exemplo, esse é o deslocamento de matriz para SuperSpeed. Você pode transferir até 45.000 bytes em um quadro. O buffer de transferência de tamanho 360.000 cabe em oito microframes.

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.

O UrbIsochronousTransfer.IsoPacket[i]. O membro length não implica o comprimento de cada pacote do URB isócrono. IsoPacket[i]. O comprimento é atualizado pela pilha de driver USB para indicar o número real de bytes recebidos do dispositivo para transferências IN isócronas. Para transferências OUT isócronas, a pilha de driver ignora o valor definido em IsoPacket[i]. Comprimento.

Especifique o número de quadro USB inicial para a transferência

O membro UrbIsochronousTransfer.StartFrame do URB especifica o número de quadro USB inicial para a transferência. Sempre há latência entre a hora em que o driver cliente envia um URB e a hora em que a pilha de driver USB processa o URB. Portanto, o driver cliente sempre deve especificar um quadro inicial posterior ao quadro atual quando o driver envia o URB. Para recuperar o número de quadro atual, o driver cliente pode enviar a solicitação URB_FUNCTION_GET_CURRENT_FRAME_NUMBER para a pilha de drivers USB (_URB_GET_CURRENT_FRAME_NUMBER).

Para transferências isócronas, a diferença absoluta entre o quadro atual e o valor de StartFrame deve ser menor que USBD_ISO_START_FRAME_RANGE. Se StartFrame não estiver dentro do intervalo adequado, a pilha de driver USB definirá o membro Status do cabeçalho URB (consulte _URB_HEADER) como USBD_STATUS_BAD_START_FRAME e descartará todo o URB.

O valor StartFrame especificado no URB indica o número do quadro no qual o primeiro pacote isócrono do URB é transferido. O número do quadro para pacotes subsequentes depende da velocidade do barramento e dos valores do período de sondagem do ponto de extremidade. Por exemplo, para uma transmissão de velocidade total, o primeiro pacote é transferido em StartFrame; O segundo pacote é transferido em StartFrame+1 e assim por diante. A maneira como a pilha do driver USB transfere pacotes isócronos, para velocidade total, em quadros é mostrada da seguinte maneira:

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

Para dispositivo de alta velocidade com o valor interval de 1, o número do quadro muda a cada oitavo microframe. A maneira como a pilha do driver USB transfere pacotes isócronos, para alta velocidade, em quadros é mostrada da seguinte maneira:

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]

Quando a pilha de driver USB processa o URB, o driver descarta todos os pacotes isócronos no URB cujos números de quadro são menores que o número de quadro atual. A pilha de driver define o membro Status do descritor de pacote para cada pacote descartado como USBD_STATUS_ISO_NA_LATE_USBPORT, USBD_STATUS_ISO_NOT_ACCESSED_BY_HW ou USBD_STATUS_ISO_NOT_ACCESSED_LATE. Embora alguns pacotes na URB sejam descartados, a pilha de driver tenta transmitir apenas os pacotes cujos números de quadro são maiores que o número de quadro atual.

O marcar para um membro StartFrame válido é um pouco mais complicado em transmissões de alta velocidade porque a pilha do driver USB carrega cada pacote isócrono em um microframe de alta velocidade; no entanto, o valor em StartFrame refere-se ao número de quadro de 1 milissegundo (velocidade total) e não ao microframe. Por exemplo, se o valor StartFrame registrado no URB for um a menos que o quadro atual, a pilha de driver poderá descartar até oito pacotes. O número exato de pacotes descartados depende do período de sondagem associado ao pipe isócrono.

Exemplo de transferência isócrona

O exemplo de código a seguir mostra como criar um URB para uma transferência isócrona para transmissão de velocidade total, alta velocidade e SuperSpeed.

#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;
}