Dear members,
This is my first time on the forum, and I am seeking some assistance. I am working on my final bachelor's degree exam, but I've hit a wall right at the end. My mentor has confirmed that the project is fine, yet it’s not functioning as expected.
As you can see, I am working on creating a device driver (KMDF without WDF) that interacts with an application (similar to Termit). The task is to control the fan speed thats connect on an Arduino S3 controller via an application. Sending commands over USB works fine, but I run into issues during the reading process.
The Arduino is programmed to return a string (the content isn’t important) if I send a speed greater than 1000 RPM, but I am unable to read any data. I would like to highlight that I have placed the reading function in a thread to ensure uninterrupted sending from the application without any congestion. When I connect to the port using Putty, it returns text in its terminal, so I believe the issue lies with my approach.
Your assistance would mean a lot to me.
CODE:
NTSTATUS ConfigureSerialPort(PDEVICE_OBJECT SerialDeviceObject, PFILE_OBJECT SerialFileObject, PSERIAL_CONFIG SerialConfig) {
NTSTATUS status;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PIRP irp;
SERIAL_BAUD_RATE baudRate;
SERIAL_LINE_CONTROL lineControl;
baudRate.BaudRate = SerialConfig->BaudRate;
baudRate.BaudRate = SerialConfig->BaudRate;
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_SERIAL_SET_BAUD_RATE, SerialDeviceObject, &baudRate, sizeof(baudRate), NULL, 0, FALSE, &event, &ioStatus);
if (irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(SerialDeviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
if (!NT_SUCCESS(status)) {
KdPrint(("IOCTL_SERIAL_SET_BAUD_RATE: neuspjesno postavljen seriski port, status: %x\n", status));
return status;
}
lineControl.WordLength = SerialConfig->WordLength;
lineControl.StopBits = SerialConfig->StopBits;
lineControl.Parity = SerialConfig->Parity;
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_SERIAL_SET_LINE_CONTROL, SerialDeviceObject, &lineControl, sizeof(lineControl), NULL, 0, FALSE, &event, &ioStatus);
if (irp == NULL) {
KdPrint(("IOCTL_SERIAL_SET_LINE_CONTROL: neuspjesno postavljen seriski port"));
return STATUS_INSUFFICIENT_RESOURCES;
}
status = IoCallDriver(SerialDeviceObject, irp);
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
if (!NT_SUCCESS(status)) {
KdPrint(("ConfigureSerialPort: Cekanje na event nije uspjelo, status: %x\n", status));
IoCancelIrp(irp);
}
else {
status = ioStatus.Status;
}
}
return status;
}
NTSTATUS AsyncReadCompletionRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context) {
UNREFERENCED_PARAMETER(DeviceObject);
PKEVENT event = (PKEVENT)Context;
KeSetEvent(event, IO_NO_INCREMENT, FALSE);
KdPrint(("AsyncReadCompletionRoutine: IRP zavrsen sa statusom: %x\n", Irp->IoStatus.Status));
if (Irp->IoStatus.Status == STATUS_CANCELLED) {
KdPrint(("AsyncReadCompletionRoutine: IRP otkazan\n"));
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS ReceiveThreadFunction(PVOID Context) {
UNREFERENCED_PARAMETER(Context);
NTSTATUS status;
UCHAR* buffer = NULL;
IO_STATUS_BLOCK ioStatus;
LARGE_INTEGER timeout;
PIRP irp;
KEVENT event;
KdPrint(("ReceiveThreadFunction zapoceo\n"));
while (TRUE) {
timeout.QuadPart = -RECEIVE_TIMEOUT * 10 * 1000;
if (KeReadStateEvent(g_StopThreadEvent) != 0) {
KdPrint(("ReceiveThreadFunction: Zaustavni dogadaj signaliziran, izlazak iz petlje\n"));
break;
}
if (g_SerialPortDeviceObject == NULL || g_SerialPortFileObject == NULL) {
LARGE_INTEGER delayInterval;
delayInterval.QuadPart = -2 * 1000 * 1000 * 10;
KeDelayExecutionThread(KernelMode, FALSE, &delayInterval);
continue;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
buffer = (UCHAR*)ExAllocatePoolWithTag(NonPagedPool, BUFFER_SIZE, 'tag1');
if (buffer == NULL) {
KdPrint(("ReceiveThreadFunction: Neuspjela alokacija memorije za meduspremnik\n"));
continue;
}
KdPrint(("ReceiveThreadFunction: Kreiranje IRP za citanje podataka\n"));
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ, g_SerialPortDeviceObject, buffer, BUFFER_SIZE, NULL, &ioStatus);
if (irp == NULL) {
KdPrint(("ReceiveThreadFunction: Neuspjela alokacija IRP-a\n"));
ExFreePoolWithTag(buffer, 'tag1');
continue;
}
IoGetNextIrpStackLocation(irp)->FileObject = g_SerialPortFileObject;
IoSetCompletionRoutine(irp, AsyncReadCompletionRoutine, &event, TRUE, TRUE, TRUE);
status = IoCallDriver(g_SerialPortDeviceObject, irp);
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout);
if (status == STATUS_TIMEOUT) {
if (IoCancelIrp(irp)) {
KdPrint(("ReceiveThreadFunction: Vrijeme cekanja isteklo\n"));
ExFreePoolWithTag(buffer, 'tag1');
continue;
}
}
status = ioStatus.Status;
}
if (NT_SUCCESS(status) && ioStatus.Information > 0) {
KdPrint(("ReceiveThreadFunction: Primljeni string: "));
for (ULONG i = 0; i < ioStatus.Information; i++) {
if (buffer[i] >= 32 && buffer[i] <= 126) {
KdPrint(("%c", buffer[i]));
}
}
KdPrint(("\n"));
}
else if (ioStatus.Information == 0) {
KdPrint(("ReceiveThreadFunction: Primljeni prazan buffer\n"));
}
else {
KdPrint(("ReceiveThreadFunction: Primanje neuspjesno ili nema primljenih podataka, status: %x, Informacija: %I64u\n", status, ioStatus.Information));
}
ExFreePoolWithTag(buffer, 'tag1');
}
KdPrint(("ReceiveThreadFunction zavrsava\n"));
PsTerminateSystemThread(STATUS_SUCCESS);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(In PDRIVER_OBJECT DriverObject, In PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);
NTSTATUS status;
HANDLE threadHandle;
status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
if (!NT_SUCCESS(status)) {
KdPrint(("Neuspjelo stvaranje uredjaja\n"));
return status;
}
status = IoCreateSymbolicLink(&SymLinkName, &DeviceName);
if (!NT_SUCCESS(status)) {
IoDeleteDevice(DeviceObject);
return status;
}
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
DriverObject->MajorFunction[i] = DispatchPassTrue;
}
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDevCTL;
DriverObject->DriverUnload = Unload;
g_StopThreadEvent = (PKEVENT)ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), 'Stop');
if (g_StopThreadEvent == NULL) {
KdPrint(("Neuspjela alokacija memorije za stop thread event\n"));
IoDeleteSymbolicLink(&SymLinkName);
IoDeleteDevice(DeviceObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
KeInitializeEvent(g_StopThreadEvent, NotificationEvent, FALSE);
KdPrint(("Stop thread event created\n"));
status = PsCreateSystemThread(&threadHandle, (ACCESS_MASK)0, NULL, NULL, NULL, ReceiveThreadFunction, NULL);
if (!NT_SUCCESS(status)) {
KdPrint(("Neuspjelo stvaranje receive threada: %x\n", status));
ExFreePoolWithTag(g_StopThreadEvent, 'Stop');
IoDeleteSymbolicLink(&SymLinkName);
IoDeleteDevice(DeviceObject);
return status;
}
status = ObReferenceObjectByHandle(threadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID*)&g_ReceiveThreadHandle, NULL);
if (!NT_SUCCESS(status)) {
KdPrint(("Neuspjelo referenciranje receive threada: %x\n", status));
ZwClose(threadHandle);
ExFreePoolWithTag(g_StopThreadEvent, 'Stop');
IoDeleteSymbolicLink(&SymLinkName);
IoDeleteDevice(DeviceObject);
return status;
}
ZwClose(threadHandle);
KdPrint(("Driver loaded\n"));
return STATUS_SUCCESS;
}