Обработка запроса 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:
Отключите устройство, если оно все еще подключено.
Если стек драйверов успешно завершил запрос IRP_MN_STOP_DEVICE , но по какой-либо причине не удалось выполнить последующий запрос IRP_MN_START_DEVICE , устройство должно быть отключено.
Освободите аппаратные ресурсы, назначенные устройству, и сделайте их доступными для другого устройства.
Как только устройство станет недоступным, его аппаратные ресурсы должны быть освобождены. Затем диспетчер PnP может переназначить ресурсы другому устройству, в том числе тому же устройству, к которому пользователь может подключиться к компьютеру с помощью горячего подключения.
Сведите к минимуму риск потери данных и нарушения работы системы.
Устройства, поддерживающие горячее подключение, и их драйверы должны быть разработаны для обработки неожиданного удаления. Пользователи ожидают, что смогут в любое время удалять устройства, поддерживающие горячее подключение.
Диспетчер 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 драйвер должен выполнить следующие действия в указанном порядке:
Определите, было ли устройство удалено.
Драйвер должен всегда пытаться определить, подключено ли устройство. Если это так, драйвер должен попытаться остановить устройство и отключить его.
Освободить аппаратные ресурсы устройства (прерывания, порты ввода-вывода, регистры памяти и каналы DMA).
В родительском водителе автобуса выключите питание слота автобуса, если водитель способен сделать это. Вызовите PoSetPowerState , чтобы уведомить диспетчер питания. Дополнительные сведения см. в разделе Управление питанием.
Запретить новые операции ввода-вывода на устройстве.
Драйвер должен обрабатывать последующие запросы IRP_MJ_CLEANUP, IRP_MJ_CLOSE, IRP_MJ_POWER и IRP_MJ_PNP , но драйвер должен предотвращать любые новые операции ввода-вывода. Драйвер должен завершиться ошибкой всех последующих irp, которые драйвер обработал бы, если бы устройство присутствовало, кроме закрытия, очистки и PnP IRP.
Драйвер может задать в расширении устройства немного, чтобы указать, что устройство было неожиданно удалено. Подпрограммы диспетчеризации драйвера должны проверка этого бита.
Сбой невыполненных запросов ввода-вывода на устройстве.
Продолжайте передавать все irP, которые драйвер не обрабатывает для устройства.
Отключите интерфейсы устройств с помощью IoSetDeviceInterfaceState.
Очистите все выделения, память, события или другие системные ресурсы, относящиеся к конкретному устройству.
Драйвер может отложить такую очистку, пока не получит последующий запрос IRP_MN_REMOVE_DEVICE , но если у устаревшего компонента есть открытый дескриптор, который невозможно закрыть, удаление IRP никогда не будет отправлено.
Оставьте объект устройства подключенным к стеку устройств.
Не отсоединяйте и не удаляйте объект устройства до последующего запроса IRP_MN_REMOVE_DEVICE .
Завершите 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 — количество дочерних устройств устройства, которые нельзя отключить.