I have a KMDF USB driver for my serial device, and an extremely simple completion routine for when I transmit data to that device. I'm trying to limit the number of pending transmit requests to an arbitrary number (in this case, 10, but not very relevant).
So I have a 'port' context that I've successfully used everywhere else in my code, including in other completion routines for other actions (such as sending a request to read a specific register value). I have added a "writes_in_flight" integer to this port context, and I have written some very simple code to increase the number of writes when I send a request and decrease it when the completion routine finishes. Everywhere else in the code I can modify, assign, or print writes_in_flight or other variables in the port context. In another completion routine, I even use "pending_frame_size_reads" to track an annoying implementation I have to do so I can sort received data into the appropriate chunks.
In my completion routine for transmitting data, however, I get a bugcheck for "DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)".
DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high. This is usually
caused by drivers using improper addresses.
If kernel debugger is available get stack backtrace.
Arguments:
Arg1: 000019faed905e44, memory referenced
Arg2: 0000000000000002, IRQL
Arg3: 0000000000000000, value 0 = read operation, 1 = write operation
Arg4: fffff80133d87206, address which referenced memory
Now I know the above isn't terribly useful, but I can also provide the code it has a problem with. This is literally the entire function right now, copied and pasted in its entirety:
void tx_completion(_In_ WDFREQUEST Request, _In_ WDFIOTARGET Target, _In_ PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, _In_ WDFCONTEXT Context)
{
struct synccom_port* port = 0;
NTSTATUS status;
PWDF_USB_REQUEST_COMPLETION_PARAMS usb_completion_params;
//int x = 0; // TEST
//x = 5; // TEST
UNREFERENCED_PARAMETER(Target);
port = (PSYNCCOM_PORT)Context;
if (!port) {
WdfObjectDelete(Request);
return;
}
usb_completion_params = CompletionParams->Parameters.Usb.Completion;
status = CompletionParams->IoStatus.Status;
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, "%s: Read failed: Request status: 0x%x, UsbdStatus: 0x%x\n", __FUNCTION__, status, usb_completion_params->UsbdStatus);
WdfObjectDelete(Request);
return;
}
//DbgPrint(DEVICE_NAME "synccom writes_in_flight = %d\n", port->writes_in_flight); // IRQL
//port->writes_in_flight = 5; // IRQL
//port->writes_in_flight = port->writes_in_flight - 1; // IRQL
//port->writes_in_flight++; // IRQL
//port->writes_in_flight = x; // IRQL
// change from INT to UINT32? // IRQL
if (port->writes_in_flight > 0) port->writes_in_flight--; // IRQL
WdfObjectDelete(Request);
return;
}
And just in case it's relevant:
typedef struct synccom_port {
// .. a lot of variables
BOOLEAN append_status;
BOOLEAN append_timestamp;
BOOLEAN ignore_timeout;
BOOLEAN rx_multiple;
int pending_frame_size_reads;
int tx_modifiers;
int writes_in_flight;
// .. a lot of other variables
} SYNCCOM_PORT, * PSYNCCOM_PORT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SYNCCOM_PORT, GetPortContext)
I know it looks like I'm just throwing code at the wall, and that's because I am. I refused to accept that the port context is pageable or somehow changing an integer can't be done at a certain IRQL. It feels like I'm fundamentally misunderstanding or missing something, and I'm pulling my hair out at this point.
I would greatly appreciate some guidance, if anyone can offer it. Nothing I do works, or makes any real sense.