Передача данных в изохронные конечные точки USB

В этом разделе описывается, как драйвер клиента может создать блок запросов USB (URB) для передачи данных в изохронные конечные точки на USB-устройстве и из нее.

Устройство универсальной последовательной шины (USB) может поддерживать изохронные конечные точки для передачи данных, зависимых от времени, с постоянной скоростью, например с потоковой передачей звука и видео. Для передачи данных драйвер клиента отправляет запрос на чтение или запись данных в изохронную конечную точку. В результате главный контроллер инициирует изохронную передачу, которая отправляет или получает данные путем опроса устройства через регулярные интервалы.

Для высокоскоростных и полноскоростных устройств опрос выполняется с помощью пакетов маркеров (IN/OUT). Когда конечная точка готова к отправке данных, устройство отвечает на один из пакетов токена IN, отправляя данные. Для записи на устройство контроллер узла отправляет пакет токена OUT, за которым следуют пакеты данных. Главный контроллер или устройство не отправляет пакеты подтверждения, и, следовательно, нет гарантированной доставки. Так как главный контроллер не пытается повторить передачу, данные могут быть потеряны при возникновении ошибки.

Для изохронных передач контроллер узла резервирует определенные периоды времени в шине. Для управления зарезервированным временем для изохронных конечных точек время делится на последовательные логические блоки, называемые интервалами шины. Единица интервала шины зависит от скорости шины.

Для полной скорости интервал шины является кадром. Длина кадра составляет 1 миллисекунда.

Для высокой скорости и SuperSpeed интервал шины является микрокадром. Длина микрофрейма составляет 125 микросекунд. Восемь последовательных микрокадров составляют один высокоскоростной или SuperSpeed кадр.

Изохронные передачи основаны на пакетах. Термин изохронный пакет в этом разделе относится к объему данных, передаваемых за один интервал шины. Характеристики конечной точки определяют размер каждого пакета и определяются характеристиками конечной точки.

Драйвер клиента запускает изохронную передачу, создав URB для запроса и отправив URB в стек драйверов USB. Запрос обрабатывается одним из нижних драйверов в стеке драйверов USB. После получения URB стек драйверов USB выполняет набор проверок и планирует транзакции для запроса. Для полной скорости изохронный пакет, передаваемый в каждом интервале шины, содержится в одной транзакции по сети. Некоторые высокоскоростные устройства разрешают несколько транзакций в интервале шины. В этом случае драйвер клиента может отправлять или получать дополнительные данные в изохронном пакете в одном запросе (URB). Устройства SuperSpeed поддерживают несколько транзакций и пиковую передачу, обеспечивая еще больше байтов на каждый интервал шины. Дополнительные сведения о пиковых передачах см. на странице спецификации USB 3.0 9–42.

Перед началом работы

Перед созданием запроса на изохронную передачу необходимо иметь сведения о канале, открытом для изохронной конечной точки.

Клиентский драйвер, использующий подпрограммы модели драйвера Windows (WDM), содержит сведения о канале в одной из USBD_PIPE_INFORMATION структур массива USBD_INTERFACE_LIST_ENTRY . Драйвер клиента получил этот массив в предыдущем запросе драйвера на выбор конфигурации или интерфейса на устройстве.

Драйвер клиента Windows Driver Framework (WDF) должен получить ссылку на объект целевого канала платформы и вызвать WdfUsbTargetPipeGetInformation для получения сведений о канале в структуре WDF_USB_PIPE_INFORMATION .

На основе сведений о канале определите этот набор сведений:

  • Сколько данных может отправить контроллер узла в канал в каждом пакете.

    Объем данных, которые драйвер клиента может отправить в запросе, не может превышать максимальное количество байтов, которое контроллер узла может отправлять или получать от конечной точки. Максимальное число байтов указывается членом MaximumPacketSizeUSBD_PIPE_INFORMATION и WDF_USB_PIPE_INFORMATION структур. Стек драйверов USB задает значение MaximumPacketSize во время запроса select-configuration или select-interface.

    Для полноскоростных устройств MaximumPacketSize является производным от первых 11 бит поля wMaxPacketSize дескриптора конечной точки, который указывает максимальное количество байтов, которое конечная точка может отправлять или получать в транзакции. Для полноскоростных устройств контроллер отправляет одну транзакцию за интервал шины.

    При высокоскоростной изохронной передаче контроллер узла может отправлять дополнительные транзакции в интервале шины, если это позволяет конечная точка. Количество дополнительных транзакций задается устройством и указывается в битах 12..11 wMaxPacketSize. Это число может быть 0, 1 или 2. Если 12..11 указывает на 0, дополнительные транзакции на микрофрейм не поддерживаются конечной точкой. Если число равно 1, контроллер узла может отправить дополнительную транзакцию (всего две транзакции на микрофрейм); 2 обозначает две дополнительные транзакции (всего три транзакции на микрофрейм). Значение MaximumPacketSize , заданное стеком драйверов USB, включает количество байтов, которые могут быть отправлены в дополнительных транзакциях.

    Для изохронной передачи SuperSpeed важны определенные значения USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR (см. usbspec.h). Стек драйверов USB использует эти значения для вычисления максимального количества байтов в интервале шины.

    • Поле Isochronous.Mult дескриптора-компаньона конечной точки. В изохронных передачах SuperSpeed дополнительные транзакции (как и высокоскоростные устройства) называются ускоряющейся транзакцией. Значение Mult указывает максимальное количество транзакций ускорения, поддерживаемых конечной точкой. В интервале службы может быть до трех транзакций ускорения (индексированных от 0 до 2).

    • Поле bMaxBurst дескриптора компаньона конечной точки. Это значение указывает количество блоков wMaxPacketSize , которые могут присутствовать в одной транзакции ускорения. В пиковой транзакции может быть до 16 блоков (индексированных от 0 до 15).

    • wBytesPerInterval указывает общее количество байтов, которые узел может отправлять или получать в интервале шины. Несмотря на то, что максимальное число байтов на интервал шины можно вычислить как (bMaxBurst+1) * (Mult+1) * wMaxPacketSize, спецификация USB 3.0 рекомендует использовать вместо него значение wBytesPerInterval . Значение wBytesPerInterval должно быть меньше или равно вычисляемого значения.

      Важно!

      Для клиентского драйвера значения, описанные выше, предназначены только для сведений. Драйвер всегда должен использовать значение MaximumPacketSize дескриптора конечной точки для определения макета буфера передачи.

  • Как часто конечная точка отправляет или получает данные?

    Элемент Interval используется для определения частоты отправки или получения данных конечной точкой. Устройство задает это значение, и драйвер клиента не может изменить его. Стек драйвера USB использует другое число для определения частоты вставки изохронных пакетов в поток данных: период опроса, производный от значения Interval .

    Для полноскоростных передач значения interval и polling period всегда равны 1; Стек usb-драйверов игнорирует другие значения.

    В следующей таблице показан интервал и вычисляемый период опроса для высокоскоростных передач и передач SuperSpeed.

    Интервал Период опроса (2Interval-1)
    1 1; Данные передаются через каждый интервал шины.
    2 2; Данные передаются каждые секундные интервалы шины.
    3 4; Данные передаются через каждый четвертый интервал шины.
    4 8; Данные передаются каждые восьмой интервал шины.
  • Каковы ограничения на количество пакетов для каждой скорости шины.

    В URB можно отправить до 255 изохронных пакетов для полноскоростного устройства; 1024 пакета в URB для на высокоскоростных устройствах и устройствах SuperSpeed. Количество пакетов, отправляемых в URB, должно быть кратно количеству пакетов в каждом кадре.

    Период опроса Количество пакетов для высокой скорости или SuperSpeed
    1 Кратный из 8
    2 Кратный из 4
    3 Кратный из 2
    4 Любой

Рассмотрим пример полноскоростной конечной точки с wMaxPacketSize 1023. В этом примере приложение предоставило буфер размером 25 575 байт. Для передачи для этого буфера требуется 25 изохронных пакетов (25575/1023).

Рассмотрим пример высокоскоростной конечной точки со следующими характеристиками, указанными в дескрипторове конечной точки.

  • wMaxPacketSize — 1024.
  • Биты 12..11 указывают на две дополнительные транзакции.
  • Интервал равен 1.

После выбора конфигурации драйвером клиента параметр MaximumPacketSize для изохронного канала указывает 3072 байта (общее количество транзакций * wMaxPacketSize). Дополнительные транзакции позволяют драйверу клиента передавать 3072 байта в каждом микрофрейме и в общей сложности 24 576 байт в одном кадре. На следующем рисунке показано, как часто изохронный пакет передается в одном микрофрейме для высокоскоростных передач.

Схема изохронных интервалов передачи, периодов опроса и пакетов.

Рассмотрим пример конечной точки SuperSpeed со следующими характеристиками, указанными в дескрипторов конечной точки и дескрипторах-компаньонах конечных точек SuperSpeed:

  • wMaxPacketSize — 1024.
  • Значение bMaxBurst равно 15.
  • Интервал равен 1.
  • Isochronous.Mult равно 2.
  • WBytesPerInterval имеет значение 45000.

В предыдущем примере, хотя максимальное число байтов можно вычислить как wMaxPacketSize * (bMaxBurst +1) * (Mult + 1), что приводит к 49 152 байтам, устройство ограничивает значение wBytesPerInterval , которое составляет 45 000 байт. Это значение также отображается в 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. В этом примере интервал равен 1. Таким образом, количество изохронных пакетов должно составлять восемь на кадр. Другие значения интервала см. в таблице в разделе Предварительные требования.

Шаг 3. Вычисление количества изохронных пакетов, необходимых для хранения всего буфера передачи

Вычислите количество изохронных пакетов, необходимых для передачи всего буфера. Это значение можно вычислить, разделив длину буфера передачи на размер изохронного пакета.

В этом примере предполагается, что размер каждого изохронного пакета равен MaximumPacketSize , а длина буфера передачи кратна значению MaximumPacketSize .

Например, для полноскоростной передачи предоставленному буферу размером 25 575 байт требуется 25 изохронных пакетов (25575/1023). Для высокоскоростной передачи буфер размером 24 576 делится на восемь изохронных пакетов (24576 /3072) для передачи. Для SuperSpeed буфер размером 360 000 байт помещается в восемь изохронных пакетов (360 000/45000).

Драйвер клиента должен проверить следующие требования:

  • Число изохронных пакетов должно быть кратным количеству пакетов на кадр.
  • Максимальное количество изохронных пакетов, необходимых для передачи, не должно превышать 255 для полноскоростного устройства; 1024 для высокоскоростного устройства или SuperSpeed.

Шаг 4. Выделение структуры URB для описания сведений о передаче

  1. Выделение структуры URB в нестраничном пуле.

    Если драйвер клиента использует подпрограммы WDM, драйвер должен вызвать USBD_IsochUrbAllocate, если у вас есть комплект драйверов Windows (WDK) для Windows 8. Драйвер клиента может использовать подпрограмму для windows Vista и более поздних версий операционной системы Windows. Если у вас нет WDK для Windows 8 или драйвер клиента предназначен для более ранней версии операционной системы, можно выделить структуру в стеке или в невыгружаемом пуле, вызвав ExAllocatePoolWithTag.

    Драйвер клиента WDF может вызывать метод WdfUsbTargetDeviceCreateIsochUrb для выделения памяти для структуры URB .

  2. Элемент UrbIsochronousTransfer структуры URB указывает на _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, используемым стеком драйверов универсальной последовательной шины (USB).

      Чтобы получить дескриптор канала USBD, драйвер клиента WDF может вызвать метод WdfUsbTargetPipeWdmGetPipeHandle и указать дескриптор WDFUSBPIPE для объекта канала платформы. Драйвер клиента WDM должен использовать тот же дескриптор, который был получен в элементе PipeHandleструктуры USBD_PIPE_INFORMATION .

    • Укажите направление передачи. Задайте для UrbIsochronousTransfer.TransferFlags значение USBD_TRANSFER_DIRECTION_IN для изохронной передачи IN (чтение с устройства); USBD_TRANSFER_DIRECTION_OUT для изохронной передачи out (запись на устройство).

    • Укажите флаг USBD_START_ISO_TRANSFER_ASAP в UrbIsochronousTransfer. TransferFlags. Флаг указывает стеку USB-драйвера отправить передачу в следующем соответствующем кадре. Впервые, когда драйвер клиента отправляет изохронную URB для этого канала, стек драйверов отправляет изохронные пакеты в URB, как только это возможно. Стек USB-драйверов отслеживает следующий кадр, который будет использоваться для последующих URB в этом канале. При возникновении задержки при отправке последующего изохронного URB, использующего флаг USBD_START_ISO_TRANSFER_ASAP, стек драйверов считает некоторые или все пакеты этого URB опозданием и не передает эти пакеты.

      Стек usb-драйвера сбрасывает USBD_START_ISO_TRANSFER_ASAP начать отслеживание кадров, если стек не получает изохронный URB для 1024 кадров после завершения предыдущего URB для этого канала. Вместо указания флага USBD_START_ISO_TRANSFER_ASAP можно указать начальный кадр. Дополнительные сведения см. в разделе «Примечания».

    • Укажите буфер передачи и его размер. Можно задать указатель на буфер в UrbIsochronousTransfer.TransferBuffer или MDL , описывающий буфер в UrbIsochronousTransfer.TransferBufferMDL.

      Чтобы получить MDL для буфера передачи, драйвер клиента WDF может вызвать WdfRequestRetrieveOutputWdmMdl или WdfRequestRetrieveInputWdmMdl в зависимости от направления передачи.

Шаг 5. Укажите сведения о каждом изохронном пакете при передаче

Стек usb-драйверов выделяет новую структуру URB , которая достаточно велика для хранения сведений о каждом изохронном пакете, но не данных, содержащихся в пакете. В структуре URB член UrbIsochronousTransfer.IsoPacket представляет собой массив USBD_ISO_PACKET_DESCRIPTOR , описывающий сведения о каждом изохронном пакете при передаче. Пакеты должны быть непрерывными. Число элементов в массиве должно быть числом изохронных пакетов, указанным в элементе UrbIsochronousTransfer.NumberOfPackets URB.

Для высокоскоростной передачи каждый элемент в массиве коррелирует с одним изохронным пакетом в одном микрофрейме. Для полной скорости каждый элемент коррелирует с одним изохронным пакетом, передаваемым в одном кадре.

Для каждого элемента укажите смещение в байтах каждого изохронного пакета с начала всего буфера передачи для запроса. Это значение можно указать, задав UrbIsochronousTransfer.IsoPacket[i]. Член смещения . Стек USB-драйверов использует указанное значение для отслеживания объема данных для отправки или получения.

Настройка смещения для передачи Full-Speed

Например, это записи массива для буфера передачи с полной скоростью. На полной скорости драйвер клиента имеет один кадр для передачи одного изохронного пакета размером до 1023 байт. Буфер передачи размером 25 575 байт может содержать 25 изохронных пакетов длиной 1023 байта. Для всего буфера требуется 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 байт, а драйвер клиента имеет один кадр для передачи восьми изохронных пакетов длиной 3072 байта.

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 помещается в восемь микрофреймов.

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]. Член Length не подразумевает длину каждого пакета изохронного URB. IsoPacket[i]. Длина обновляется стеком usb-драйвера, чтобы указать фактическое количество байтов, полученных с устройства для изохронной передачи in. При изохронных передачах OUT стек драйверов игнорирует значение, заданное в IsoPacket[i]. Длина.

Укажите начальный номер кадра USB для передачи

Элемент UrbIsochronousTransfer.StartFrame URB указывает начальный номер кадра USB для передачи. Задержка между временем отправки драйвера клиента URB и временем, когда стек USB-драйверов обрабатывает URB, всегда существует задержка. Таким образом, драйвер клиента всегда должен указывать начальный кадр, который позже текущего кадра, когда драйвер отправляет URB. Чтобы получить текущий номер кадра, драйвер клиента может отправить запрос URB_FUNCTION_GET_CURRENT_FRAME_NUMBER в стек драйверов USB (_URB_GET_CURRENT_FRAME_NUMBER).

Для изохронной передачи абсолютная разница между текущим кадром и значением начального кадра должна быть меньше USBD_ISO_START_FRAME_RANGE. Если Начальный кадр не находится в правильном диапазоне, стек драйвера USB устанавливает элемент Status заголовка URB (см . _URB_HEADER) USBD_STATUS_BAD_START_FRAME и удаляет весь URB.

Значение StartFrame, указанное в URB, указывает номер кадра, в котором передается первый изохронный пакет URB. Номер кадра для последующих пакетов зависит от скорости шины и значений периода опроса конечной точки. Например, для полной скорости передачи первый пакет передается в StartFrame; второй пакет передается в StartFrame+1 и т. д. Способ передачи изохронных пакетов в кадрах стеком драйвера USB для полной скорости показан следующим образом:

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

Для высокоскоростного устройства со значением Interval 1 номер кадра изменяется каждый восьмой микрофрейм. Способ передачи изохронных пакетов в кадрах стеком драйвера 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, номера кадров которых ниже текущего номера кадра. Стек драйвера задает для элемента Status дескриптора пакета для каждого отброшенного пакета значение USBD_STATUS_ISO_NA_LATE_USBPORT, USBD_STATUS_ISO_NOT_ACCESSED_BY_HW или USBD_STATUS_ISO_NOT_ACCESSED_LATE. Несмотря на то, что некоторые пакеты в URB отклоняются, стек драйверов пытается передать только те пакеты, чьи номера кадров выше текущего номера кадров.

Проверка для допустимого элемента StartFrame немного сложнее в высокоскоростных передачах, так как стек USB-драйверов загружает каждый изохронный пакет в высокоскоростной микрофрейм. Однако значение в StartFrame относится к номеру кадра в 1 миллисекунд (полной скорости), а не микрокадру. Например, если значение StartFrame , записанное в URB, меньше текущего кадра, стек драйверов может отбрасывать до восьми пакетов. Точное количество отброшенных пакетов зависит от периода опроса, связанного с изохронным каналом.

Пример изохронной передачи

В следующем примере кода показано, как создать URB для изохронной передачи для полной, высокой скорости и передачи 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;
}