Восстановление после ошибок 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, проверьте элемент структуры URB hdr.Status после возврата метода.
- Если вы асинхронно отправили 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.
Вызов синхронный и возвращается только после отмены всех ожидающих запросов. WdfUsbTargetPipeAbortSynchronous принимает необязательный параметр Request . Рекомендуется передать дескриптор WDFREQUEST в объект запроса предварительной платформы. Параметр включает платформу использования указанного объекта запроса вместо внутреннего объекта запроса, к которому драйвер не может получить доступ. Это значение параметра гарантирует, что WdfUsbTargetPipeAbortSynchronous не завершается ошибкой из-за нехватки памяти.
Вызовите метод WdfUsbTargetPipeFormatRequestForAbort, чтобы отформатировать объект запроса для запроса abort-pipe, а затем отправить запрос путем вызова метода WdfRequestSend.
Если драйвер отправляет запрос асинхронно, он должен указать указатель на EVT_WDF_REQUEST_COMPLETION_ROUTINE драйвера, который реализует драйвер. Чтобы указать указатель, вызовите метод WdfRequestSetCompletionRoutine .
Драйвер может синхронно отправлять запрос, указав WDF_REQUEST_SEND_OPTION_SYNCHRONOUS в качестве одного из вариантов запроса в WdfRequestSend. Если вы отправляете запрос синхронно, вместо этого вызовите WdfUsbTargetPipeAbortSynchronous .
Шаг 4. Сброс USB-канала
Запустите восстановление ошибки, сбросив канал. Запрос на сброс можно отправить, вызвав один из следующих методов:
Вызовите WdfUsbTargetPipeResetSynchronous, чтобы отправить запрос канала сброса синхронно.
Вызовите метод 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).
Отправьте запрос на перенос цикла, вызвав один из следующих методов:
- Вызовите WdfUsbTargetDeviceCyclePortSynchronous, чтобы отправить запрос на перенос цикла синхронно.
- Вызовите метод WdfUsbTargetDeviceFormatRequestForCyclePort, чтобы отформатировать объект запроса для запроса на порт цикла, а затем отправить запрос путем вызова метода WdfRequestSend. Эти вызовы похожи на вызовы для запроса abort-pipe, как описано на шаге 3.
Драйвер клиента может отправлять запросы на передачу устройству только после завершения запроса на порт цикла. Это связано с тем, что узел устройства удаляется, пока стек USB-драйверов обрабатывает запрос на циклический порт.
Запрос на перенос цикла приводит к повторному перечислению устройства. Стек USB-драйверов сообщает диспетчеру PnP о том, что устройство было отключено. Диспетчер PnP удаляет стек устройств, связанный с драйвером клиента. Стек драйверов сбрасывает устройство, повторно перечисляет его на USB-шине и сообщает диспетчеру PnP о том, что устройство подключено. Затем диспетчер PnP перестраивает стек устройств для USB-устройства.
В результате операции порта цикла любое приложение, которое имеет дескриптор, открытое для устройства, получает уведомление об удалении устройства (если приложение зарегистрировано для такого уведомления). В ответ приложение может сообщить пользователю об отключенном от устройства сообщении. Так как это влияет на взаимодействие с пользователем, драйвер клиента должен выбрать запрос на циклический порт, только если другие механизмы восстановления не разрешают условие ошибки.
Как и операция сброса порта (описанная на шаге 6), для составного устройства операция циклического порта влияет на все устройство, а не отдельные функции устройства.