Обработка запроса IRP_MN_SURPRISE_REMOVAL

Диспетчер PnP Windows 2000 и более поздних версий отправляет этот IRP для уведомления драйверов о том, что устройство больше не доступно для операций ввода-вывода и, вероятно, было неожиданно удалено с компьютера ("неожиданное удаление").

Диспетчер PnP отправляет запрос IRP_MN_SURPRISE_REMOVAL по следующим причинам:

  • Если в автобусе есть уведомление о горячем подключении, он уведомляет родительского водителя автобуса устройства о том, что устройство исчезло. Водитель автобуса вызывает IoInvalidateDeviceRelations. В ответ менеджер PnP запрашивает у водителя автобуса дочерние элементы (IRP_MN_QUERY_DEVICE_RELATIONS для BusRelations). Диспетчер PnP определяет, что устройство отсутствует в новом списке дочерних элементов, и инициирует его операции неожиданного удаления для устройства.

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

  • Драйвер функции для устройства определяет, что устройство больше не существует (например, поскольку его запросы многократно истекли). Шина может быть перечислена, но она не имеет уведомления о горячем подключении. В этом случае драйвер функции вызывает IoInvalidateDeviceState. В ответ диспетчер PnP отправляет запрос IRP_MN_QUERY_PNP_DEVICE_STATE в стек устройств. Драйвер функции устанавливает флаг PNP_DEVICE_FAILED в битовой маске PNP_DEVICE_STATE, указывая, что устройство завершилось сбоем.

  • Стек драйверов успешно завершает запрос IRP_MN_STOP_DEVICE , но затем завершается сбоем последующего запроса IRP_MN_START_DEVICE . В таких случаях устройство, вероятно, по-прежнему подключено.

Все драйверы PnP должны обрабатывать этот IRP и устанавливать для параметра Irp-IoStatus.Status> значение STATUS_SUCCESS. Драйвер для устройства PnP должен быть подготовлен к обработке IRP_MN_SURPRISE_REMOVAL в любое время после вызова процедуры AddDevice драйвера. Правильная обработка IRP позволяет драйверам и диспетчеру PnP:

  1. Отключите устройство, если оно все еще подключено.

    Если стек драйверов успешно завершил запрос IRP_MN_STOP_DEVICE , но по какой-либо причине не удалось выполнить последующий запрос IRP_MN_START_DEVICE , устройство должно быть отключено.

  2. Освободите аппаратные ресурсы, назначенные устройству, и сделайте их доступными для другого устройства.

    Как только устройство станет недоступным, его аппаратные ресурсы должны быть освобождены. Затем диспетчер PnP может переназначить ресурсы другому устройству, в том числе тому же устройству, к которому пользователь может подключиться к компьютеру с помощью горячего подключения.

  3. Сведите к минимуму риск потери данных и нарушения работы системы.

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

Диспетчер PnP отправляет IRP_MN_SURPRISE_REMOVAL по адресу IRQL = PASSIVE_LEVEL в контексте системного потока.

Диспетчер PnP отправляет это IRP драйверам, прежде чем уведомлять приложения в пользовательском режиме и другие компоненты режима ядра. После завершения IRP диспетчер PnP отправляет уведомление EventCategoryTargetDeviceChange с GUID_TARGET_DEVICE_REMOVE_COMPLETE компонентам режима ядра, зарегистрированным для такого уведомления на устройстве.

IRP_MN_SURPRISE_REMOVAL IRP обрабатывается сначала верхним драйвером в стеке устройств, а затем каждым следующим более низким драйвером.

В ответ на IRP_MN_SURPRISE_REMOVAL драйвер должен выполнить следующие действия в указанном порядке:

  1. Определите, было ли устройство удалено.

    Драйвер должен всегда пытаться определить, подключено ли устройство. Если это так, драйвер должен попытаться остановить устройство и отключить его.

  2. Освободить аппаратные ресурсы устройства (прерывания, порты ввода-вывода, регистры памяти и каналы DMA).

  3. В родительском водителе автобуса выключите питание слота автобуса, если водитель способен сделать это. Вызовите PoSetPowerState , чтобы уведомить диспетчер питания. Дополнительные сведения см. в разделе Управление питанием.

  4. Запретить новые операции ввода-вывода на устройстве.

    Драйвер должен обрабатывать последующие запросы IRP_MJ_CLEANUP, IRP_MJ_CLOSE, IRP_MJ_POWER и IRP_MJ_PNP , но драйвер должен предотвращать любые новые операции ввода-вывода. Драйвер должен завершиться ошибкой всех последующих irp, которые драйвер обработал бы, если бы устройство присутствовало, кроме закрытия, очистки и PnP IRP.

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

  5. Сбой невыполненных запросов ввода-вывода на устройстве.

  6. Продолжайте передавать все irP, которые драйвер не обрабатывает для устройства.

  7. Отключите интерфейсы устройств с помощью IoSetDeviceInterfaceState.

  8. Очистите все выделения, память, события или другие системные ресурсы, относящиеся к конкретному устройству.

    Драйвер может отложить такую очистку, пока не получит последующий запрос IRP_MN_REMOVE_DEVICE , но если у устаревшего компонента есть открытый дескриптор, который невозможно закрыть, удаление IRP никогда не будет отправлено.

  9. Оставьте объект устройства подключенным к стеку устройств.

    Не отсоединяйте и не удаляйте объект устройства до последующего запроса IRP_MN_REMOVE_DEVICE .

  10. Завершите IRP.

    В драйвере функции или фильтра:

    • Задайте для Irp-IoStatus.Status> значение STATUS_SUCCESS.

    • Настройте следующее расположение стека с помощью IoSkipCurrentIrpStackLocation и передайте IRP следующему более низкому драйверу с помощью IoCallDriver.

    • Распространение состояния из IoCallDriver в качестве состояния возврата из подпрограммы DispatchPnP .

    • Не завершайте IRP.

    В водителе автобуса (который обрабатывает это IRP для дочернего PDO):

    • Задайте для Irp-IoStatus.Status> значение STATUS_SUCCESS.

    • Завершите IRP (IoCompleteRequest) IO_NO_INCREMENT.

    • Возвращается из подпрограммы DispatchPnP .

После успешного выполнения этого IRP и закрытия всех открытых дескрипторов устройства диспетчер PnP отправляет IRP_MN_REMOVE_DEVICE запрос в стек устройств. В ответ на удаление IRP драйверы отсоединяют свои объекты устройств от стека и удаляют их. Если для устаревшего компонента открытый дескриптор для устройства и он оставляет его открытым, несмотря на сбои ввода-вывода, диспетчер PnP никогда не отправляет удаление IRP.

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

В Windows 98/Me диспетчер PnP не отправляет этот IRP. Если пользователь удаляет устройство без предварительного использования соответствующего пользовательского интерфейса, диспетчер PnP отправляет только IRP_MN_REMOVE_DEVICE запрос драйверам устройства. Все драйверы WDM должны обрабатывать как IRP_MN_SURPRISE_REMOVAL , так и IRP_MN_REMOVE_DEVICE. Код для IRP_MN_REMOVE_DEVICE должен проверка, получил ли драйвер предварительное IRP с неожиданным удалением, и обрабатывать оба случая.

Использование GUID_REENUMERATE_SELF_INTERFACE_STANDARD

Интерфейс GUID_REENUMERATE_SELF_INTERFACE_STANDARD позволяет драйверу запрашивать повторное включение устройства.

Чтобы использовать этот интерфейс, отправьте IRP_MN_QUERY_INTERFACE IRP драйверу шины с параметром InterfaceType = GUID_REENUMERATE_SELF_INTERFACE_STANDARD. Драйвер автобуса предоставляет указатель на структуру REENUMERATE_SELF_INTERFACE_STANDARD, которая содержит указатели на отдельные подпрограммы интерфейса. Подпрограмма ReenumerateSelf запрашивает, чтобы водитель автобуса повторно задал дочернее устройство.

О PNP_DEVICE_STATE

Тип PNP_DEVICE_STATE — это битовая маска, описывающая состояние PnP устройства. Драйвер возвращает значение этого типа в ответ на запрос IRP_MN_QUERY_PNP_DEVICE_STATE .

typedef ULONG PNP_DEVICE_STATE, *PPNP_DEVICE_STATE;

Биты флага в PNP_DEVICE_STATE значении определяются следующим образом.

Бит флага Описание
PNP_DEVICE_DISABLED

Устройство физически присутствует, но отключено на оборудовании.

PNP_DEVICE_DONT_DISPLAY_IN_UI

Не отображайте устройство в пользовательском интерфейсе. Задайте для устройства, которое физически присутствует, но непригодным для использования в текущей конфигурации, например игровой порт на ноутбуке, который нельзя использовать при отстыковке ноутбука. (Также см. флаг NoDisplayInUI в структуре DEVICE_CAPABILITIES .)

PNP_DEVICE_FAILED

Устройство присутствует, но не работает должным образом.

Если этот флаг и PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED установлены, устройство должно быть остановлено, прежде чем диспетчер PnP назначит новые аппаратные ресурсы (безостановочная перебалансировка для устройства не поддерживается).

PNP_DEVICE_NOT_DISABLEABLE

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

Драйвер задает этот бит для устройства, необходимого для правильной работы системы. Например, если драйвер получает уведомление о том, что устройство находится в пути подкачки (IRP_MN_DEVICE_USAGE_NOTIFICATION для DeviceUsageTypePaging), драйвер вызывает IoInvalidateDeviceState и задает этот флаг в результирующем запросе IRP_MN_QUERY_PNP_DEVICE_STATE .

Если этот бит задан для устройства, диспетчер PnP распространяет этот параметр на родительское устройство устройства, его родительское устройство и т. д.

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

PNP_DEVICE_REMOVED

Устройство было физически удалено.

PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED

Требования к ресурсам для устройства изменились.

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

PNP_DEVICE_DISCONNECTED

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

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

Единственная цель этого флага — сообщить клиентам, подключено ли устройство. Установка флага не влияет на загрузку драйвера.

Диспетчер PnP запрашивает PNP_DEVICE_STATE устройства сразу после запуска устройства, отправляя запрос IRP_MN_QUERY_PNP_DEVICE_STATE в стек устройств. В ответ на это IRP драйверы устройства устанавливают соответствующие флаги в PNP_DEVICE_STATE.

Если какие-либо характеристики состояния изменяются после первоначального запроса, драйвер уведомляет диспетчер PnP, вызвав IoInvalidateDeviceState. В ответ на вызов IoInvalidateDeviceState диспетчер PnP снова запрашивает PNP_DEVICE_STATE устройства.

Если устройство помечено PNP_DEVICE_NOT_DISABLEABLE, отладчик отображает DNUF_NOT_DISABLEABLE пользовательский флаг для devnode. Отладчик также отображает значение DisableableDepends , которое подсчитывает количество причин, по которым устройство не может быть отключено. Это значение представляет собой сумму X+Y, где X — единица, если устройство не может быть отключено, а Y — количество дочерних устройств устройства, которые нельзя отключить.