Поделиться через


Восстановление после ошибок USB-канала

Примечание

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

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

Драйвер USB-клиента взаимодействует со своим устройством, отправляя передачи управления в конечную точку по умолчанию; передача данных в массовые, прерывания и изохронные конечные точки устройства. Иногда эти передачи могут завершаться сбоем из-за различных причин, таких как состояние остановки в конечной точке. Если передача завершается сбоем, связанный канал не сможет обрабатывать запросы до тех пор, пока условие ошибки не будет очищено.

Для передачи управления стек драйверов USB автоматически очищает условия ошибок. Для передачи данных клиент должен предпринять соответствующие действия, чтобы восстановить состояние ошибки. При сбое передачи данных стек USB-драйвера сообщает об ошибке драйверу клиента с помощью кодов состояния USBD. В зависимости от кода состояния драйвер может предоставить механизм восстановления ошибок.

В этой статье приводятся рекомендации по восстановлению ошибок с помощью этих операций.

  • Сброс USB-канала
  • Сброс USB-порта, к которому подключено устройство
  • Переключите USB-порт для повторного перечисления стека устройств для драйвера клиента.

Чтобы очистить условие ошибки, начните с операции сброса канала и выполняйте более сложные операции, такие как сброс порта и порта цикла, только если это необходимо.

Сведения о координации различных механизмов восстановления:

Драйвер клиента должен координировать различные операции восстановления и гарантировать, что в данный момент времени используется только один метод. Например, рассмотрим устройство с двумя конечными точками: массовой и прерыванием. После отправки нескольких запросов на передачу данных на устройство драйвер замечает, что запросы на массовом канале завершаются ошибкой. Для восстановления после этих ошибок драйвер сбрасывает массовый канал. Однако эта операция не устраняет ошибки передачи, и массовая передача продолжает завершаться сбоем. Поэтому драйвер отправляет запрос на сброс USB-порта. Между тем передача данных начинает завершаться сбоем в канале прерывания, а затем выполняется запрос на сброс устройства. Для восстановления после сбоев передачи прерываний драйвер отправляет запрос канала сброса в канал прерывания. Если эти две операции не скоординированы, драйвер может одновременно запустить две операции сброса устройства из-за сбоев на обоих каналах. Эти одновременные операции могут быть проблематичными.

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

Это важно знать

В этой статье используется платформа драйвера в режиме ядра (KMDF).

Предварительные требования

  • Драйвер клиента должен создать объект целевого устройства USB платформы.

    Если вы используете шаблоны USB, которые предоставляются с Microsoft Visual Studio Professional 2012, код шаблона выполняет эти задачи. Код шаблона получает дескриптор целевого объекта устройства и сохраняет его в контексте устройства.

    Драйвер клиента KMDF должен получить дескриптор WDFUSBDEVICE, вызвав метод WdfUsbTargetDeviceCreateWithParameters . Дополнительные сведения см. в разделе "Исходный код устройства" статьи Общие сведения о структуре кода драйвера USB-клиента (KMDF).

  • Драйвер клиента должен иметь дескриптор объекта целевого канала платформы. Дополнительные сведения см. в разделе Перечисление USB-каналов.

Шаг 1. Определение причины ошибки

Драйвер клиента инициирует передачу данных с помощью блока запросов USB (URB). После завершения запроса стек usb-драйвера возвращает код состояния USBD, который указывает, была ли передача выполнена успешно или она завершилась сбоем. При сбое код USBD указывает причину сбоя.

Сбои передачи могут возникать из-за ошибки устройства, например USBD_STATUS_STALL_PID или USBD_STATUS_BABBLE_DETECTED. Они также могут возникнуть из-за ошибки, сообщаемой контроллером узла, например USBD_STATUS_XACT_ERROR.

Шаг 2. Определение подключения устройства к порту

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

Шаг 3. Отмена всех ожидающих передач в канал

Перед отправкой запросов, которые сбрасывают канал или порт, отмените все ожидающие запросы на передачу в канал, который стек USB-драйверов еще не завершил. Отменить запросы можно одним из следующих способов:

  • Остановите целевой объект ввода-вывода, вызвав метод WdfIoTargetStop .

    Чтобы остановить целевой объект ввода-вывода, сначала получите дескриптор WDFIOTARGET, связанный с объектом канала платформы, вызвав метод WdfUsbTargetPipeGetIoTarget . С помощью дескриптора вызовите WdfIoTargetStop. В вызове задайте для действия WdfIoTargetCancelSentIo (см . WDF_IO_TARGET_SENT_IO_ACTION)**, чтобы указать платформе отменить все запросы, которые не были выполнены стеком драйверов USB. Для завершенных запросов драйвер клиента должен дождаться вызова обратного вызова завершения платформой.

  • Отправка запроса abort-pipe. Вы можете отправить запрос, вызвав один из следующих методов:

    • Вызовите метод WdfUsbTargetPipeAbortSynchronously .

      Вызов является синхронным и возвращается только после отмены всех ожидающих запросов. WdfUsbTargetPipeAbortSynchronously принимает необязательный параметр Request . Рекомендуется передать дескриптор WDFREQUEST в объект запроса предварительно размещенной платформы. Параметр позволяет платформе использовать указанный объект запроса вместо внутреннего объекта запроса, к которому драйвер не может получить доступ. Это значение параметра гарантирует, что WdfUsbTargetPipeAbortSynchronously не завершится сбоем из-за нехватки памяти.

    • Вызовите метод WdfUsbTargetPipeFormatRequestForAbort , чтобы отформатировать объект запроса для запроса abort-pipe, а затем отправьте запрос, вызвав метод WdfRequestSend .

      Если драйвер отправляет запрос асинхронно, он должен указать указатель на EVT_WDF_REQUEST_COMPLETION_ROUTINE драйвера, который реализует драйвер. Чтобы указать указатель, вызовите метод WdfRequestSetCompletionRoutine .

      Драйвер может отправить запрос синхронно, указав WDF_REQUEST_SEND_OPTION_SYNCHRONOUS в качестве одного из параметров запроса в WdfRequestSend. Если вы отправляете запрос синхронно, вместо этого вызовите WdfUsbTargetPipeAbortSynchronously .

Шаг 4. Сброс USB-канала

Запустите восстановление ошибки, сбросив канал. Вы можете отправить запрос на сброс канала, вызвав один из следующих методов:

  • Вызовите WdfUsbTargetPipeResetSynchronously , чтобы отправить запрос канала сброса синхронно.

  • Вызовите метод WdfUsbTargetPipeFormatRequestForReset , чтобы отформатировать объект запроса для запроса по каналу сброса, а затем отправьте запрос, вызвав метод WdfRequestSend . Эти вызовы аналогичны вызовам для запроса abort-pipe, как описано на шаге 3.

Примечание

Не отправляйте новые запросы на передачу до завершения операции сброса канала.

Запрос на сброс канала очищает состояние ошибки на устройстве и оборудовании хост-контроллера. Чтобы удалить ошибку устройства, стек драйвера USB отправляет на устройство запрос CLEAR_FEATURE управления с помощью селектора функций ENDPOINT_HALT. Получателем запроса является конечная точка, связанная с каналом. Если ошибка возникла в изохронном канале, стек драйверов не выполняет никаких действий по очистке устройства, так как в случае ошибок изохронные конечные точки очищаются автоматически.

Чтобы удалить ошибку контроллера узла, стек драйвера очищает состояние HALT канала и сбрасывает переключатель данных канала в значение 0.

Шаг 5. Сброс USB-порта

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

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

  2. Остановите целевой объект ввода-вывода для устройства.

    Вызовите метод WdfUsbTargetDeviceGetIoTarget , чтобы получить дескриптор WDFIOTARGET, связанный с объектом целевого устройства платформы. Затем вызовите WdfIoTargetStop и укажите дескриптор WDFIOTARGET. В вызове задайте для действия WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION).

  3. Отправьте запрос на сброс порта, вызвав метод WdfUsbTargetDeviceResetPortSynchronously .

Операция сброса порта приводит к повторному перечислению устройства в USB-шине. Стек драйверов USB сохраняет конфигурацию устройства после перечисления . Драйвер клиента может использовать ранее полученные дескрипторы канала, так как стек драйверов гарантирует, что существующие дескрипторы канала остаются действительными.

Вы не можете сбросить отдельную функцию составного устройства. Для составного устройства, когда драйвер клиента определенной функции отправляет запрос на сброс порта, сбрасывается все устройство. Если USB-устройство сохраняет состояние, запрос на сброс порта может повлиять на клиентские драйверы других функций. Поэтому важно, чтобы драйвер клиента попытается сбросить канал перед сбросом порта.

Шаг 6. Переключите USB-порт

Операция циклического порта похожа на устройство, которое отключено и подключено к порту, за исключением того, что устройство не отключено по электросети. Устройство отключается и повторно подключается к программному обеспечению. Эта операция приводит к сбросу устройства и перечислению. В результате диспетчер PnP перестраивает узел устройства.

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

  1. Отмена всех передач на устройство. Обязательно отмените ожидающий запрос, запланированный для каждого канала в текущей конфигурации (см. шаг 3).

  2. Остановите целевой объект ввода-вывода для устройства.

    Вызовите метод WdfUsbTargetDeviceGetIoTarget , чтобы получить дескриптор WDFIOTARGET, связанный с объектом целевого устройства платформы. Затем вызовите WdfIoTargetStop и укажите дескриптор WDFIOTARGET. В вызове задайте для действия WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION).

  3. Отправьте запрос на перенос цикла, вызвав один из следующих методов:

    • Вызовите WdfUsbTargetDeviceCyclePortSynchronously , чтобы отправить запрос на перенос цикла синхронно.
    • Вызовите метод WdfUsbTargetDeviceFormatRequestForCyclePort , чтобы отформатировать объект запроса для запроса на порт цикла, а затем отправьте запрос, вызвав метод WdfRequestSend . Эти вызовы аналогичны вызовам для запроса abort-pipe, как описано на шаге 3.

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

Запрос на перенос цикла приводит к повторному перечислению устройства. Стек драйверов USB сообщает диспетчеру PnP о том, что устройство отключено. Диспетчер PnP удаляет стек устройств, связанный с драйвером клиента. Стек драйверов сбрасывает устройство, повторно перечисляет его в USB-шине и сообщает диспетчеру PnP о том, что устройство подключено. Затем диспетчер PnP перестраивает стек устройств для USB-устройства.

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

Аналогично операции сброса порта (описанной в шаге 6), для составного устройства операция циклического порта влияет на все устройство, а не на отдельные функции устройства.