Как восстановиться после ошибок канала передачи данных USB
Примечание.
Эта статья предназначена для разработчиков драйверов устройств. Если у вас возникли проблемы с USB-устройством, см. раздел «Устранение проблем с USB-C в Windows».
В этой статье содержатся сведения о шагах, которые можно попробовать при сбое передачи данных в 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-код указывает причину сбоя.
- Если вы отправили URB, вызвав метод WdfUsbTargetDeviceSendUrbSynchronously, проверьте элемент Hdr.Status структуры URB после возврата метода.
- Если вы асинхронно отправили URB вызовом метода WdfRequestSend, проверьте статус URB в EVT_WDF_REQUEST_COMPLETION_ROUTINE. Параметр Params указывает на структуру WDF_REQUEST_COMPLETION_PARAMS. Чтобы проверить код состояния USBD, изучите член Usb-UsbdStatus. Сведения о коде см. в USBD_STATUS.
Сбои передачи могут возникать из-за ошибки устройства, например 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, чтобы отформатировать объект для запроса на завершение работы канала, а затем отправьте запрос, вызвав метод WdfRequestSend.
Если драйвер отправляет запрос асинхронно, он должен указать адрес на событие драйвера EVT_WDF_REQUEST_COMPLETION_ROUTINE, которое реализует драйвер. Чтобы указать указатель, вызовите метод WdfRequestSetCompletionRoutine .
Драйвер может синхронно отправлять запрос, указав WDF_REQUEST_SEND_OPTION_SYNCHRONOUS в качестве одной из опций запроса в WdfRequestSend. Если вы отправляете запрос синхронно, вместо этого вызовите WdfUsbTargetPipeAbortSynchronously.
Шаг 4. Сброс USB-канала
Начните устранение ошибки, сбросив канал. Запрос на reset-pipe можно отправить, вызвав один из следующих методов:
Вызовите WdfUsbTargetPipeResetSynchronously, чтобы отправить запрос на сброс канала синхронно.
Вызовите метод WdfUsbTargetPipeFormatRequestForReset, чтобы форматировать объект запроса для сброса канала, а затем отправьте запрос, вызвав метод WdfRequestSend. Эти обращения аналогичны тем, которые используются для запроса abort-pipe, как указано в шаге 3.
Примечание.
Не отправляйте новые запросы на передачу до завершения операции сброса.
Запрос на сброс канала очищает состояние ошибки в устройстве и аппаратное обеспечение хост-контроллера. Чтобы очистить ошибку устройства, стек USB-драйверов отправляет управляющий запрос CLEAR_FEATURE к устройству с помощью селектора функции ENDPOINT_HALT. Получатель запроса — это конечная точка, связанная с каналом. Если условие ошибки произошло на изохронном канале, стек драйверов не принимает никаких действий для очистки устройства, так как в случае ошибок изохронные конечные точки очищаются автоматически.
Чтобы устранить ошибку контроллера узла, стек драйверов очищает состояние ОСТАНОВа канала и сбрасывает тумблер данных канала на 0.
Шаг 5. Сброс USB-порта
Если операция сброса канала не устраняет ошибку и передача данных всё ещё не удаётся, отправьте запрос на сброс порта.
Отменить все передачи на устройство. Для этого перечислите все каналы в текущей конфигурации и отмените ожидающие запросы, запланированные для каждого канала.
Остановите целевой объект ввода-вывода для устройства.
Вызовите метод WdfUsbTargetDeviceGetIoTarget, чтобы получить дескриптор WDFIOTARGET, связанный с целевым объектом устройства фреймворка. Затем вызовите WdfIoTargetStop и укажите объект WDFIOTARGET. В вызове задайте для действия значение WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION).
Отправьте запрос на сброс порта, вызвав метод WdfUsbTargetDeviceResetPortSynchronously.
Операция сброса порта приводит к повторной идентификации устройства на USB-шине. Стек USB-драйверов сохраняет конфигурацию устройства после перечисления. Клиентский драйвер может использовать ранее полученные дескрипторы каналов, так как стек драйверов гарантирует, что существующие дескрипторы каналов остаются допустимыми.
Вы не можете сбросить отдельную функцию составного устройства. Для композитного устройства, когда клиентский драйвер определенной функции отправляет запрос на сброс порта, все устройство сбрасывается. Если USB-устройство поддерживает состояние, запрос на сброс порта может повлиять на драйверы клиентов других функций. Поэтому важно, чтобы клиентский драйвер пытался сбросить канал перед сбросом порта.
Шаг 6. Переподключите USB-порт
Операция работы цикла порта аналогична процессу отключения и повторного подключения устройства, за исключением того, что электрическое отключение устройства не происходит. Устройство отключается и повторно подключается к программному обеспечению. Эта операция приводит к сбросу устройства и его повторному обнаружению. В результате диспетчер PnP перестраивает узел устройства.
Если операция сброса порта не устраняет состояние ошибки и передача данных продолжает завершаться неудачей, отправьте запрос цикл-порта.
Отменить все передачи на устройство. Убедитесь, что вы отменяете ожидающий запрос, запланированный для каждого канала в текущей конфигурации (см. шаг 3).
Остановите целевой объект ввода-вывода для устройства.
Вызовите метод WdfUsbTargetDeviceGetIoTarget, чтобы получить дескриптор WDFIOTARGET, связанный с объектом устройства-цели в рамках платформы. Затем вызовите WdfIoTargetStop и укажите дескриптор WDFIOTARGET. В вызове задайте действие WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION).
Отправьте запрос на перенос цикла, вызвав один из следующих методов:
- Вызовите WdfUsbTargetDeviceCyclePortSynchronously, чтобы отправить запрос на цикл порта синхронно.
- Вызовите метод WdfUsbTargetDeviceFormatRequestForCyclePort для форматирования объекта для запроса порта цикла, а затем отправьте запрос, вызвав метод WdfRequestSend. Эти вызовы аналогичны тем, которые делаются для запроса abort-pipe, как описано в шаге 3.
Драйвер клиента может отправлять запросы на передачу данных устройству только после завершения запроса на порт цикла. Это связано с тем, что узел устройства удаляется, пока стек USB-драйверов обрабатывает запрос на циклический порт.
Запрос на cycle-port приводит к повторному перечислению устройства. Стек USB-драйверов сообщает диспетчеру PnP о том, что устройство было отключено. Диспетчер PnP удаляет стек устройств, связанный с драйвером клиента. Стек драйверов сбрасывает устройство, повторно перечисляет его на USB-шине и сообщает диспетчеру PnP о том, что устройство подключено. Затем диспетчер PnP перестраивает стек устройств для USB-устройства.
В результате работы порта циклов любое приложение, у которого открыт дескриптор устройства, получает уведомление об удалении устройства (если приложение зарегистрировано для получения такого уведомления). В ответ приложение может сообщить пользователю о сообщении об отключении устройства. Так как это влияет на взаимодействие с пользователем, драйвер клиента должен выбрать запрос на циклический порт, только если другие механизмы восстановления не разрешают условие ошибки.
Как и операция сброса порта (описанная на шаге 6), для составного устройства операция циклического порта влияет на все устройство, а не отдельные функции устройства.
Связанные темы
- Фреймворк драйверов в режиме ядра
- USB-передача данных ввода-вывода