Выбрать главу

 {

  t << "Error constructing device XDSPdrvDevice" << (ULONG) m_Unit << " status " << (ULONG) status << EOL;

  delete pDevice;

 } else {

  m_Unit++; //Устройство создано удачно

 }

 //Вернуть статус устройства.

 return status;

}

Все. Работа объекта драйвера на этом окончена. Как мы можем видеть, объект драйвера практически не выполняет каких-либо функций управления аппаратурой, но он жизненно необходим для правильной инициализации драйвера. В нашем случае НЕ ТРЕБУЕТСЯ вносить какие-либо изменения в текст, сформированный DriverWizard.

Основным классом драйвера является класс устройства. Класс устройства XDSPdrvDevice является подклассом класса KpnpDevice. Конструктор получает два параметра: указатель на PDO и номер драйвера в системе.

XDSPdrvDevice::XDSPdrvDevice(PDEVICE_OBJECT Pdo, ULONG Unit) : KPnpDevice(Pdo, NULL) {

 t << "Entering XDSPdrvDevice::XDSPdrvDevice (constructor)\n";

 //Здесь проверяется код ошибки, которую вернул конструктор суперкласса. В случае

 //успешного создания объекта базового класса значение переменной m_ConstructorStatus

 //будет NT_SUCCESS.

 if ( ! NT_SUCCESS(m_ConstructorStatus) ) {

  //Ошибка в создании объекта устройства

  return;

 }

 //Запомнить номер драйвера

 m_Unit = Unit;

 //Инициализация устройства нижнего уровня. В роли устройства нижнего уровня в нашем

 //драйвере выступает PDO. Но в случае стека драйверов в качестве устройства нижнего

 //уровня может выступать объект устройства другого драйвера.

 m_Lower.Initialize(this, Pdo);

 // Установить объект нижнего уровня для нашего драйвера.

 SetLowerDevice(&m_Lower);

 // Установить стандартную политику PnP для данного устройства.

 SetPnpPolicy();

}

Порядок вызова методов m_Lower.Initialize(this, Pdo), SetLowerDevice(&m_Lower) и SetPnpPolicy() является жизненно важным. Его нарушение может вызвать серьезные сбои в работе драйвера. Не стоит редактировать текст конструктора, сгенерированный DriverWizard.

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

XDSPdrvDevice::~XDSPdrvDevice() {

 t << "Entering XDSPdrvDevice::~XDSPdrvDevice() (destructor)\n";

}

Метод DefaultPnp — виртуальная функция, которая должна быть переопределена любым объектом устройства. Эта обработчик по умолчанию для IRP-пакета, у которого старший код функции (major function code) равен IRP_MJ_PNP. Драйвер обрабатывает некоторые из таких пакетов, у которых младший код функции равен IRP_MN_STOP_DEVICE, IRP_MN_START_DEVICE и т.п. (см. ниже) также при помощи виртуальных функций. Но те пакеты, которые не обрабатываются объектом устройства, передаются этой функции. Она ничего с ними не делает, а просто передает их устройству нижнего уровня (если такое есть, конечно). Не стоит изменять текст этой функции.

NTSTATUS XDSPdrvDevice::DefaultPnp(KIrp I) {

 t << "Entering XDSPdrvDevice::DefaultPnp with IRP minor function=" << PNPMinorFunctionName(I.MinorFunction()) << EOL;

 I.ForceReuseOfCurrentStackLocationInCalldown();

 return m_Lower.PnpCall(this, I);

}

Метод SystemControl выполняет похожую функцию для IRP-пакетов, у которых старший код функции IRP_MJ_SYSTEM_CONTROL. Он также является виртуальной функцией и не выполняет никаких полезных действий, а просто передает IRP-пакет устройству нижнего уровня. Что-то менять в тексте этого метода надо только в том случае, если наше устройство является WMI-провайдером.       

NTSTATUS XDSPdrvDevice::SystemControl(KIrp I) {

 t << "Entering XDSPdrvDevice::SystemControl\n";

 I.ForceReuseOfCurrentStackLocationInCalldown();

 return m_Lower.PnpCall(this, I);

}

Метод Invalidate вызывается, когда устройство тем или иным образом завершает свою работу: из функций OnStopDevice, OnRemoveDevice а также при всевозможных ошибках. Метод Invalidate объекта устройства также вызывается из деструктора. Его можно вызывать несколько раз — не произойдет ничего страшного; но в методах Invalidate нет никакой защиты от реентерабельности. Т.е. если при работе метода Invalidate возникает какая– либо ошибка и из-за этого Invalidate должен будет вызваться снова, то ни DriverWorks, ни ОС Windows не станут этому мешать. Разработчик должен сам предусмотреть такую возможность и принять меры, чтобы подобная ситуация не привела к нехорошим последствиям.

В методе Invalidate объекта устройства вызываются методы Invalidate всех ресурсов, которые использует драйвер: областей памяти, регистров, контроллеров DMA и т.п. При этом выполняется процедура, обратная процедуре инициализации: освобождаются все ресурсы, используемые объектом, закрываются все его хэндлы, но сам объект не уничтожается и может быть проинициализирован снова. В нашем простом случае нет смысла что-либо корректировать в тексте этого метода — DriverWizard все сделал за нас. Еще бы, ведь наше устройство имеет только один ресурс — диапазон адресов памяти. Но при проектировании более сложных драйверов следует обращать внимание на данный метод. Если разработчик добавляет какие-либо системные ресурсы вручную, то он должен включить соответствующий код в метод Invalidate.

VOID XDSPdrvDevice::Invalidate() {

 //Вызвать метод Invalidate для диапазона адресов памяти.

 m_MainMem.Invalidate();

}

Далее следует виртуальная функция OnStartDevice. Она вызывается при приходе IRP– пакета со старшим кодом IRP_MJ_PNP и кодом подфункции IRP_MN_START_DEVICE. Обычно это происходит при старте драйвера после выполнения всех необходимых проверок и инициализаций. В этой функции драйвер инициализирует физическое устройство и приводит его в рабочее состояние. Также здесь драйвер получает список ресурсов, которые имеются в устройстве. На основе этого списка ресурсов выполняется их инициалиция. Хотя мы не вносим изменений в данную функцию, но нельзя не отметить ее огромную важность. Именно в данной функции выполняется инициализация устройства и получение списка его ресурсов. По другому мы их получить никак не можем, т.к. имеем дело с PnP устройством, для которого система распределяет ресурсы самостоятельно.

NTSTATUS XDSPdrvDevice::OnStartDevice(KIrp I) {

 t << "Entering XDSPdrvDevice::OnStartDevice\n";

 NTSTATUS status = STATUS_SUCCESS; I.Information() = 0;

 //Здесь драйвер получает список ресурсов устройства

 PCM_RESOURCE_LIST pResListRaw = I.AllocatedResources();

 PCM_RESOURCE_LIST pResListTranslated = I.TranslatedResources();

 // Наше устройство является PCI – карточкой и в своем конфигурационном поле содержит

 //базовые адреса диапазонов памяти и портов ввода-вывода. Получаем эти данные

 KPciConfiguration PciConfig(m_Lower.TopOfStack());

 // Инициализируем каждый диапазон отображения адресов. Теперь, после инициализации,