//базовый адрес отображенного диапазона в виртуальном адресном пространстве
//процессора может быть получен при помощи вызова метода Base(). Физический адрес на
//шине адреса ЦП – при помощи CpuPhysicalAddress().
status = m_MainMem.Initialize(pResListTranslated, pResListRaw, PciConfig.BaseAddressIndexToOrdinal(0));
if (!NT_SUCCESS(status)) {
//Неудача при инициализации области памяти
Invalidate();
return status;
}
//Сюда можно добавить код, выполняющий необходимую инициализацию, специфичную для
//этого устройства
return status;
}
Виртуальная функция OnStopDevice вызывается при остановке устройства. В этом случае система посылает драйверу IRP с старшим кодом IRP_MJ_PNP и кодом подфункции IRP_MN_STOP_DEVICE. Драйвер должен осободить все используемые ресурсы.
NTSTATUS XDSPdrvDevice::OnStopDevice(KIrp I) {
NTSTATUS status = STATUS_SUCCESS;
t << "Entering XDSPdrvDevice::OnStopDevice\n";
//Освободить ресурсы устройства
Invalidate();
// Здесь добавляется код, специфичный для данного устройства.
return status;
}
Виртуальная функция OnRemoveDevice вызывается при извлечении устройства из системы. При этом системная политика PnP сама позаботится об удалении PDO.
NTSTATUS XDSPdrvDevice::OnRemoveDevice(KIrp I) {
t << "Entering XDSPdrvDevice::OnRemoveDevice\n";
// Освободить ресурсы устройства
Invalidate();
// Здесь добавляется код, специфичный для данного устройства.
return STATUS_SUCCESS;
}
Иногда бывает необходимо отменить обработку IRP, уже поставленного в очередь (такие запросы иногда посылает диспетчер В/В). Когда такая ситуация может возникнуть?
Представим такую ситуацию: в приложении пользователя поток послал нашему драйверу запрос на ввод-вывод и завершил свою работу. А IRP-пакет попал в очередь запросов и терпеливо ждет своей очереди на обработку. Конечно же, обработка такого "бесхозного" IRP-пакета должна быть отменена. Для этого драйвером поддерживается целый механизм отмены обработки запросов. В Win2000 DDK подробно описано, почему ЛЮБОЙ драйвер должен поддерживать этот механизм. Это связано, в основном, с проблемами надежности и устойчивости работы системы. Ведь сбой в драйвере — это, практически, сбой в ядре ОС.
В классе KPnPDevice механизм отмены запроса реализован при помощи метода CancelQueuedIrp.
VOID XDSPdrvDevice::CancelQueuedIrp(KIrp I) {
//Получаем очередь IRP-пакетов этого устройства.
KDeviceQueue dq(DeviceQueue());
//Проверить, является ли IRP, который должен быть отменен, тем пакетом, который должен
//быть обработан.
if ( (PIRP)I == CurrentIrp() ) {
//Уничтожить пакет.
CurrentIrp() = NULL;
//При вызове метода CancelQueuedIrp устанавливается глобальная системная
//защелка (SpinLock). Теперь следует ее сбросить.
CancelSpinLock::Release(I.CancelIrql());
t << "IRP canceled " << I << EOL;
I.Information() = 0;
I.Status() = STATUS_CANCELLED;
//Обработать следующий пакет.
PnpNextIrp(I);
}
//Удалить из очереди пакет. Если это удалось, то функция вернет true.
else if (dq.RemoveSpecificEntry(I)) {
// Это удалось. Теперь сбрасываем защелку.
CancelSpinLock::Release(I.CancelIrql());
t << "IRP canceled " << I << EOL;
I.Information() = 0;
I.PnpComplete(this, STATUS_CANCELLED);
} else {
//Неудача. Сбрасываем защелку.
CancelSpinLock::Release(I.CancelIrql());
}
}
Меотод StartIo является виртуальной функцией. Она вызывается системой, когда драйвер готов обрабатывать следующий запрос в очереди. Это чрезвычайно важная функция: она является диспетчером всех запросов на ввод-вывод, поступаемых к нашему драйверу. В функции вызываются обработчики запросов на чтение, запись а также обработчики вызовов IOCTL. К счастью, умный DriverWizard генерирует скелет функции автоматически и вносить изменения в нее в нашем простом случае не требуется. В принципе, в эту функцию можно ввести какие-то дополнительные проверки IRP-пакетов.
VOID XDSPdrvDevice::StartIo(KIrp I) {
t << "Entering StartIo, " << I << EOL;
// Здесь надо проверить, отменен этот запрос или нет. Это производится при помощи вызова
//метода TestAndSetCancelRoutine. Также этот метод устанавливает новую функцию отмены
//пакетов, если это необходимо. Адрес новой функции передается вторым параметром. Если
//он равен NULL,то вызывается старая функция. Если пакет должен быть отменен, функция
//вернет FALSE.
if (!I.TestAndSetCancelRoutine(LinkTo(CancelQueuedIrp), NULL, CurrentIrp())) {
//Пакет отменен.
return;
}
// Начать обработку запроса.
// Выбрать необходимую функцию
switch (I.MajorFunction()) {
case IRP_MJ_READ:
//Чтение
SerialRead(I);
break;
case IRP_MJ_WRITE:
//Запись
SerialWrite(I);
break;
case IRP_MJ_DEVICE_CONTROL:
//IOCTL
switch (I.IoctlCode()) {
default:
//Мы обрабатываем пакет, который не должен быть обработан.
//Поэтому просто выходим.
ASSERT(FALSE);
break;
}
break;
default:
// Драйвер занес в очередь какой-то непонятный пакет,
//он не должен быть обработан.
ASSERT(FALSE);
PnpNextIrp(I);
break;
}
}
Метод Create вызывается, когда пользовательское приложение пытается установить связь с драйвером при помощи вызова API CreateFile(). Обычно этот запрос обрабатывается в нашем объекте устройства и нет смысла пересылать запрос устройству нижнего уровня.
NTSTATUS XDSPdrvDevice::Create(KIrp I) {
NTSTATUS status;
t << "Entering XDSPdrvDevice::Create, " << I << EOL;
//Здесь можно вставить код пользователя, который должен быть вызван при установлении
//приложением связи с устройством.
status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT);
t << "XDSPdrvDevice::Create Status " << (ULONG)status << EOL;