For drivers, UMDF or KMDF, the Zw functions aren’t really the way to go [https://community.osr.com/discussion/97540/zwreadfile-zwwritefile-within-ioctl] and [https://community.osr.com/discussion/221820/zwreadfile-issue] … they go through a different control path, the WdfIoXXX functions are your only option. You might be basing your work on the MS nonPnP driver sample … if so then don’t do that, it’s time has long, long, long since passed …
I just so happen to be working on a project that involved accessing a serial port from driverland, here’s what you’ll need … [note that this is an amended snippet, be sure you google each of the API’s before you drop it into production code]
— snip —
[partial function body]
WDF_OBJECT_ATTRIBUTES attribs;
WDF_OBJECT_ATTRIBUTES_INIT(&attribs);
attribs.ParentObject = m_device; // WDFDEVICE of the driver
status = WdfIoTargetCreate(m_device, &attribs, &m_ioTarget); // we will need to save the ioTarget for later ...
if (NT_SUCCESS(status))
{
constexpr WCHAR rootPath[] = L"\\DosDevices\\"; // this is going to be different under UMDF
constexpr size_t rootPathSize = sizeof(rootPath) / sizeof(rootPath[0]);
size_t size = info->NameLength + rootPathSize;
WCHAR* portNameBuffer = new (NonPagedPoolNx , 'gnaB') WCHAR[size+1];
UNICODE_STRING portName = { 0 };
IO_STATUS_BLOCK ioStatusBlock = { 0 };
WDF_IO_TARGET_OPEN_PARAMS params;
wcscpy(portNameBuffer, rootPath);
wcsncat(portNameBuffer, info->Name, info->NameLength);
portNameBuffer[size] = L'\0';
RtlInitUnicodeString(&portName, portNameBuffer);
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
¶ms,
&portName,
MAXIMUM_ALLOWED); // this should be pared down to specific rights
params.EvtIoTargetQueryRemove = EvtIoTargetQueryRemove; // these can be NULL, will take the OS defaults then
params.EvtIoTargetRemoveCanceled = EvtWdfIoTargetRemoveCanceled;
params.EvtIoTargetRemoveComplete = EvtWdfIoTargetRemoveComplete;
status = WdfIoTargetOpen(m_ioTarget, ¶ms);
if (!NT_SUCCESS(status))
{
DbgPrintEx(DPFLTR_IHVAUDIO_ID, DPFLTR_ERROR_LEVEL, "%s: Failed WdfIoTargetOpen 0x%x\n",
__FUNCTION__, status);
}
else
{
found = true;
WDFREQUEST request;
status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, m_ioTarget, &request);
if (NT_SUCCESS(status))
{
// Set the baud rate.
SERIAL_BAUD_RATE baudRate = { 115200 };
WDF_MEMORY_DESCRIPTOR memDesc;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, &baudRate, sizeof(baudRate));
WDF_REQUEST_SEND_OPTIONS sendOptions;
WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, 0);
ULONG_PTR bytesReturned = 0;
status = WdfIoTargetSendIoctlSynchronously(m_ioTarget, request, IOCTL_SERIAL_SET_BAUD_RATE, &memDesc, nullptr, &sendOptions, &bytesReturned);
if (!NT_SUCCESS(status))
{
DbgPrintEx(DPFLTR_IHVAUDIO_ID, DPFLTR_ERROR_LEVEL, "%s: serial set baud rate failed 0x%x\n",
__FUNCTION__, status);
}
}
}
delete[] portNameBuffer;
portNameBuffer = nullptr;
}
}
— snip —
… to read from that target …
— snip —
NTSTATUS IoTarget::Read(ULONG BytesToRead, PVOID ReadBuffer, ULONG* BytesRead, ULONG Timeout)
{
WDFREQUEST request;
NTSTATUS status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, m_ioTarget, &request);
if (NT_SUCCESS(status))
{
WDF_MEMORY_DESCRIPTOR memoryDesc;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDesc, ReadBuffer, BytesToRead);
WDF_REQUEST_SEND_OPTIONS sendOptions;
WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, 0);
if (Timeout != 0)
{
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&sendOptions, WDF_REL_TIMEOUT_IN_MS(Timeout));
}
ULONG_PTR bytesRead = 0;
status = WdfIoTargetSendReadSynchronously(m_ioTarget, request, &memoryDesc, 0, &sendOptions, &bytesRead);
if (NT_SUCCESS(status) && BytesRead != nullptr)
{
*BytesRead = static_cast<ULONG>(bytesRead);
} else {
TRACE2 ("Failed to read %i bytes %s\n", BytesToRead, Bus_GetNTStatusString(status)); // custom debugging functions
*BytesRead = 0;
}
WdfObjectDelete(request);
}
return status;
}
— snip —
… and to write to that object …
— snip —
NTSTATUS IoTarget::Write(ULONG BytesToWrite, const PVOID WriteBuffer, ULONG* BytesWritten, ULONG Timeout)
{
WDFREQUEST request;
NTSTATUS status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, m_ioTarget, &request);
if (NT_SUCCESS(status))
{
WDF_MEMORY_DESCRIPTOR memoryDesc;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDesc, WriteBuffer, BytesToWrite);
WDF_REQUEST_SEND_OPTIONS sendOptions;
WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, 0);
if (Timeout != 0)
{
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&sendOptions, WDF_REL_TIMEOUT_IN_MS(Timeout));
}
ULONG_PTR bytesWritten = 0;
status = WdfIoTargetSendWriteSynchronously(m_ioTarget, request, &memoryDesc, 0, &sendOptions, &bytesWritten);
if (NT_SUCCESS(status) && BytesWritten != nullptr)
{
*BytesWritten = static_cast<ULONG>(bytesWritten);
} else {
TRACE2 ("Failed to write %i bytes %s\n", BytesToWrite, Bus_GetNTStatusString(status)); // custom debugging functions
*BytesWritten = 0;
}
WdfObjectDelete(request);
}
return status;
}
— snip —
Basically you want to do is create an IoTarget, then open something that will use that target (here a serial port), then tell the OS how to communicate with that something (synchronous or asynchronous) …
There is much GoogleFu is in your future, very much like with a chainsaw you will need to understand what the tool does before you can really safely use it/ them …