Пишу драйвер батареи: для APC smart на ком порту.
Драйвер легаси, но с пнп кодом: через devcon добавляю устройство, а дальше обычный пнп.
В AddDevice атачусь к тому что приходит, дальше открываю компорт через IoGetDeviceObjectPointer — но в стёк к нему не добавляюсь.
Порт открывается, IRP_MJ_DEVICE_CONTROL с IOCTL_SERIAL_* проходят вроде нормально — без ошибок.
А на IRP_MJ_WRITE ухожу в BSOD.
Пожалуйста помогите, уже неделю не могу осилить!
RtlInitUnicodeString(&ObjectName, L"\\DosDevices\\COM1");
Status = IoGetDeviceObjectPointer(&ObjectName, STANDARD_RIGHTS_ALL, &FileObject, &ComPdo);
...
pDeviceData->WriteBuffSize = OEM_SERIAL_BUFFSIZE;
pDeviceData->WriteBuff = ExAllocatePoolWithTag(NonPagedPool, pDeviceData->WriteBuffSize, DRIVER_TAG);
...
RtlCopyMemory(pDeviceData->WriteBuff, "Y", 1);//(*WriteBuff) = 'Y';
Status = SmBatt_SerialPortWrite(pDeviceData->ComPdo, pDeviceData->ComFileObject, pDeviceData->WriteBuff, 1);
...
NTSTATUS
IOSyncRequestCompletionRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
{
UNREFERENCED_PARAMETER(Irp);
UNREFERENCED_PARAMETER(DeviceObject);
// If the lower driver didn't return STATUS_PENDING, we don't need to
// set the event because we won't be waiting on it.
// This optimization avoids grabbing the dispatcher lock and improves perf.if (Irp->PendingReturned == TRUE)
KeSetEvent((PKEVENT)Context, 0, FALSE);
// We don't want IO to get our IRP and free it.return(STATUS_MORE_PROCESSING_REQUIRED);
}
NTSTATUS
IOSyncRequest(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS Status;
KEVENT Event;
DebugPrint("IOSyncRequest...");
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
Irp->IoStatus.Information = 0;
IoSetCompletionRoutine(Irp, IOSyncRequestCompletionRoutine, (PVOID)&Event, TRUE, TRUE, TRUE);
// Call the device to do the read and wait for it to finish.
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING) { // Wait for the IRP
Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
if (Status == STATUS_SUCCESS)
Status = Irp->IoStatus.Status;
}
KeClearEvent(&Event);
DebugPrint("IOSyncRequest - DONE!!!");
return(Status);
}
NTSTATUS
SendDevIoControlReq(ULONG IoControlCode, BOOLEAN Internal, PDEVICE_OBJECT DeviceObject, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, ULONG_PTR *pnBytesReturned)
{
NTSTATUS Status;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
PAGED_CODE();
DebugPrint("SendDevIoControlReq...");
Irp = IoAllocateIrp((DeviceObject->StackSize + 1), FALSE);
if (Irp == NULL) {
DebugPrint("SendDevIoControlReq: Failed to allocate IRP");
return(STATUS_INSUFFICIENT_RESOURCES);
}
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MajorFunction = ((TRUE == Internal) ? IRP_MJ_INTERNAL_DEVICE_CONTROL : IRP_MJ_DEVICE_CONTROL);
IrpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
IrpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
IrpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
Irp->UserBuffer = OutputBuffer;
Irp->AssociatedIrp.SystemBuffer = InputBuffer;
// Call the device to do the read and wait for it to finish.
Status = IOSyncRequest(DeviceObject, Irp);
IoFreeIrp(Irp);
DebugPrint("SendDevIoControlReq - DONE!!!");
if (pnBytesReturned)
*pnBytesReturned = Irp->IoStatus.Information;
return(Status);
}
NTSTATUS
SmBatt_SerialPortWrite(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, PVOID Buffer, ULONG NumberOfBytesToWrite)
{
NTSTATUS Status;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
PAGED_CODE();
DebugPrint("SmBatt_SerialPortWrite...");
Irp = IoBuildAsynchronousFsdRequest(IRP_MJ_WRITE, DeviceObject, Buffer OPTIONAL, NumberOfBytesToWrite, NULL, NULL);
//Irp = IoAllocateIrp((DeviceObject->StackSize + 1), FALSE);if (Irp == NULL) {
DebugPrint("SerialPortWrite: Failed to allocate IRP");
return(STATUS_INSUFFICIENT_RESOURCES);
}
/*IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MajorFunction = IRP_MJ_WRITE;
IrpSp->Parameters.Write.Length = NumberOfBytesToWrite;
IrpSp->FileObject = FileObject;
Irp->AssociatedIrp.SystemBuffer = Buffer;*/
// Call the device to do the read and wait for it to finish.
Status = IOSyncRequest(DeviceObject, Irp);
IoFreeIrp(Irp);
DebugPrint("SmBatt_SerialPortWrite - DONE!!!");
return(Status);
}
Re: BSOD при записи/чтении
От:
Аноним
Дата:
05.12.11 07:54
Оценка:
I_>Пожалуйста помогите, уже неделю не могу осилить!
I_>
I_> // Call the device to do the read and wait for it to finish.
I_> Status = IOSyncRequest(DeviceObject, Irp);
I_> IoFreeIrp(Irp);
I_>
Нельзя так делать. Нужно установить Completion Routine и в ней освобождать IRP.
I_>>Пожалуйста помогите, уже неделю не могу осилить!
I_>>
I_>> // Call the device to do the read and wait for it to finish.
I_>> Status = IOSyncRequest(DeviceObject, Irp);
I_>> IoFreeIrp(Irp);
I_>>
А>Нельзя так делать. Нужно установить Completion Routine и в ней освобождать IRP.
Можете более подробно и аргументированно? (что бы я понял свою ошибку)
В моём случае IoFreeIrp вызывается после отработки Completion Routine, потому что вызывающий поток ждёт на KEVENT сигнала, а сигнал посылается из Completion Routine.
Re[3]: BSOD при записи/чтении
От:
Аноним
Дата:
05.12.11 09:28
Оценка:
I_>В моём случае IoFreeIrp вызывается после отработки Completion Routine, потому что вызывающий поток ждёт на KEVENT сигнала, а сигнал посылается из Completion Routine.
А, это я не доглядел )
Re: BSOD при записи/чтении
От:
Аноним
Дата:
05.12.11 10:19
Оценка:
Здравствуйте, Ivan_83, Вы писали:
I_>Здравствуйте.
I_>Порт открывается, IRP_MJ_DEVICE_CONTROL с IOCTL_SERIAL_* проходят вроде нормально — без ошибок. I_>А на IRP_MJ_WRITE ухожу в BSOD.
I_>Пожалуйста помогите, уже неделю не могу осилить!
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Ivan_83, Вы писали:
I_>>Здравствуйте.
I_>>Порт открывается, IRP_MJ_DEVICE_CONTROL с IOCTL_SERIAL_* проходят вроде нормально — без ошибок. I_>>А на IRP_MJ_WRITE ухожу в BSOD.
I_>>Пожалуйста помогите, уже неделю не могу осилить!
А>!analyze -v что показывает?
К какой программе/утилите относится эта команда?
(это мой первый драйвер под винду — тонкостей и сленга я ещё не знаю)
Re[3]: BSOD при записи/чтении
От:
Аноним
Дата:
05.12.11 11:05
Оценка:
Здравствуйте, Ivan_83, Вы писали:
А>>!analyze -v что показывает?
I_>К какой программе/утилите относится эта команда? I_>(это мой первый драйвер под винду — тонкостей и сленга я ещё не знаю)
При падении системы в папке x:\Windows\Minidump создается .dmp файл с датой падения в имени файла. Этот файл надо открыть в WinDbg (CTRL + D) и выполнить команду !analyze -v При этом желательно настроить символы: CTRL + S, там указать: SRV*f:\localsymbols*http://msdl.microsoft.com/download/symbols , где f:\localsymbols -- путь к директории, куда WinDbg скачает символы. Символы надо настроить перед использованием !analyze -v
Здравствуйте, Аноним, Вы писали:
А>При падении системы в папке x:\Windows\Minidump создается .dmp файл с датой падения в имени файла. Этот файл надо открыть в WinDbg (CTRL + D) и выполнить команду !analyze -v При этом желательно настроить символы: CTRL + S, там указать: SRV*f:\localsymbols*http://msdl.microsoft.com/download/symbols , где f:\localsymbols -- путь к директории, куда WinDbg скачает символы. Символы надо настроить перед использованием !analyze -v
Дополню немного...
Думаю, не лишним будет установить формирование дампа памяти ядра ("Загрузка и восстановление"->"Запись отладочной информации"->"Дамп памяти ядра") и в WinDBG указать еще путь до символов разрабатываемого драйвера + указать путь к исходникам для получения наиболее полной картины происходящего.
Здравствуйте, x64, Вы писали:
P>>Дополню немного...
x64>Ну коли так, то и сюда не лишним будет заглянуть.
P>>...указать путь к исходникам для получения наиболее полной картины происходящего.
x64>А это необязательно, WinDbg сам найдёт, если дамп смотреть на той же машине, где всё это и собиралось.
Пришлось всё таки руками указать все пути, у меня фрибилд, видимо поэтому.
Также сделал полный дамп, он оказался более информативным.
Вот результаты:
ATTEMPTED_SWITCH_FROM_DPC (b8)
A wait operation, attach process, or yield was attempted from a DPC routine.
This is an illegal operation and the stack track will lead to the offending
code and original DPC routine.
Arguments:
Arg1: fffffa80053dc230, Original thread which is the cause of the failure
Arg2: fffffa80043f5060, New thread
Arg3: 0000000000000000, Stack address of the original thread
Arg4: 0000000000000000
Debugging Details:
------------------
FAULTING_THREAD: fffffa80053dc230
DEFAULT_BUCKET_ID: VISTA_DRIVER_FAULT
BUGCHECK_STR: 0xB8
PROCESS_NAME: svchost.exe
CURRENT_IRQL: 2
LAST_CONTROL_TRANSFER: from fffff80001ae0f32 to fffff80001ade4da
532:
533: // Call the device to do the read and wait for it to finish.
534: Status = IoCallDriver(DeviceObject, Irp);
535: if (Status == STATUS_PENDING) { // Wait for the IRP
> 536: Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
537: if (Status == STATUS_SUCCESS)
538: Status = Irp->IoStatus.Status;
539: }
540: KeClearEvent(&Event);
541: DebugPrint("IOSyncRequest - DONE!!!");
Надо было сразу написать, что SmBatt_SerialPortWrite() вызываешь из DPC.
DPC выполняются на DISPATCH_LEVEL, а там ждать нельзя (например, вызовом KeWaitForSingleObject).
Другими словами, этот вызов у тебя должен быть асинхронным (а результат будет в функции завершения).
Здравствуйте, x64, Вы писали:
I_>>Вот результаты:
x64>Надо было сразу написать, что SmBatt_SerialPortWrite() вызываешь из DPC. x64>DPC выполняются на DISPATCH_LEVEL, а там ждать нельзя (например, вызовом KeWaitForSingleObject). x64>Другими словами, этот вызов у тебя должен быть асинхронным (а результат будет в функции завершения).
Закоментил старт таймера и оно записало в порт, бесперебойник пикнул (наконец то! — команда диагностики до него дошла)
Я подозревал что какая то мелочь всё портит, но думал что там что то с директ_ио/буфферед_ио.
Здравствуйте, x64, Вы писали:
I_>>СПАСИБО ВСЕМ!
x64>Да на кой ж мне "пасибы"-то ваши — оценочки давай, и побольше!
done
Ещё вопрос: как потом дистрибутить дрова, чтобы у юзера без лишних вопросов вставали, те без включения тестмоде на х64?
Обязательно покупать сертификат?
Сколько стоит? Где? У мс нужно что то регать/спрашивать/тестить?
(DPInst я видел, для инсталяции, врочем inf + devcon + cmd файл тоже меня устраивают)
Здравствуйте, x64, Вы писали:
I_>>Вот результаты:
x64>Надо было сразу написать, что SmBatt_SerialPortWrite() вызываешь из DPC. x64>DPC выполняются на DISPATCH_LEVEL, а там ждать нельзя (например, вызовом KeWaitForSingleObject). x64>Другими словами, этот вызов у тебя должен быть асинхронным (а результат будет в функции завершения).
лолшто?
Callers of KeWaitForSingleObject must be running at IRQL <= DISPATCH_LEVEL.