Multiple device driver? (KMDF/WDF) - windows

I've just written a KMDF USB driver. Now I want to connect several (up to at least four) devices to the PC. Where do I start? I've noted that when I connect the second device to the PC it use the very same instance of the driver as for the first connected device. EvtDeviceAdd(...) runs one time per device and since I don't have any handling for several devices things get weird... Right now my EvtDeviceAdd looks like this:
NTSTATUS EvtDeviceAdd(IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit) {
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDF_OBJECT_ATTRIBUTES attributes;
NTSTATUS status;
WDFDEVICE device;
WDF_DEVICE_PNP_CAPABILITIES pnpCaps;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
PDEVICE_CONTEXT pDevContext;
WDFQUEUE queue;
PWSTR driverRegistryPath;
UNREFERENCED_PARAMETER(Driver);
PAGED_CODE();
DbgPrint("New device was added\n");
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
pnpPowerCallbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT);
status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfDeviceCreate failed with Status code %!STATUS!\n", status);
return status;
}
pDevContext = GetDeviceContext(device);
WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps);
pnpCaps.SurpriseRemovalOK = WdfTrue;
WdfDeviceSetPnpCapabilities(device, &pnpCaps);
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchParallel);
ioQueueConfig.EvtIoRead = EvtIoRead;
ioQueueConfig.EvtIoWrite = EvtIoWrite;
ioQueueConfig.EvtIoDeviceControl = EvtIoDeviceControl;
ioQueueConfig.PowerManaged = WdfTrue;
status = WdfIoQueueCreate(device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfIoQueueCreate failed %!STATUS!\n", status);
return status;
}
pDevContext->DeviceIOControlQueue = queue;
status = WdfDeviceCreateDeviceInterface(device, (LPGUID) &GUID_DEVINTERFACE_MYDEVICE, NULL);
if (!NT_SUCCESS(status)) {
DbgPrint("WdfDeviceCreateDeviceInterface failed %!STATUS!\n", status);
return status;
}
}
Where do I start? Is there any good examples?

There is only one instance of the driver in memory for all the connected devices (it's a singleton). The OS calls to the driver are accompanied with the relevant device context and from that point the devices shouldn't interfere with each other operation. The problems begin if non-constant global/static variables are being used. Since the Kernel space is shared, such variables will be actually shared and accessible from all attached devices. For this reason global/static data shouldn't be device-specific and should be guarded since it's shared resource. There are some samples in the WDK demonstrating multi-device drivers.

Related

Win32:- How to set an IMMDevice as default communication device only under the context of WIn32 app

I've a simple win32 forms app and I'm using the following
code to set an IMMDevice as default communication device for the app. But it is getting reflected through out the system. Is there a way I could just set as default communication device only under the context of current app ?
public void SetDefaultAudioDevice(IMMDevice device, AudioDeviceRole role)
{
var config = new PolicyConfig();
int hr;
if (config is IPolicyConfig2 config2)
{
device.GetId(out string result)
hr = config2.SetDefaultEndpoint(result, role);
}
}

Blocking specific USB drives on MacOS

I need to block specific USB drives on MacOS. I found two methods.
Use Disk Arbitration framework and register approval callback.(user mode)
Use MAC policy API in kernel extension and register mpo_mount_check_mount callback.(kernel mode)
In user mode, I can get a kDADiskDescriptionDeviceVendorKey which is the vender id of USB drive. I'm wondering how I can get the same information in mpo_mount_check_mount callback or some other API I don't know. The MAC policy API is undocumented and browsing the XNU source code is extremely challenging for a new MacOS developer.
This is a screenshot from IORegistryExplorer. Could I get this information from a kernel extension using MACF or other APIs? Thanks!
I can use DiskArbitration and IOKit frameworks to get vender id in user mode, but I cannot use the same code in kernel extension.
This is my code.
int GetVenderId(DADiskRef dsk, int *vid)
{
io_service_t ioService;
CFTypeRef vendorid;
ioService = DADiskCopyIOMedia(dsk);
if (0 != ioService)
{
vendorid = IORegistryEntrySearchCFProperty(ioService, kIOServicePlane, CFSTR("idVendor"), NULL, kIORegistryIterateParents | kIORegistryIterateRecursively);
if(0 != vendorid)
{
if (CFNumberGetValue(vendorid, kCFNumberIntType, vid))
{
return 0;
}
CFRelease(vendorid);
}
IOObjectRelease(ioService);
}
return 1;
}

WDF Internal IOCTL Not Returning Output

I’m currently writing a driver that exposes virtual COM ports. In the driver, I send an internal IOCTL from the port FDO down the stack, which is handled from the PDO IO queue. For some reason, the output data is not written to the provided output memory.
I’ve confirmed through windbg that IoCtl_Vcp_GetPortInfo (see below) is being called, and works as intended. The request is completed with STATUS_SUCCESS. At the time that I call WdfRequestComplete, the output buffer has valid data. However, when control returns to GetPortInfo (see below), the provided buffer has not been overwritten. I confirmed this with a hardware breakpoint on access for the receiving buffer. It is not read or written to during the WdfIoTargetSendInteralIoctlSynchronously call.
The code responsible for sending the IOCTL is below:
NTSTATUS GetPortInfo(WDFDEVICE device, _Out_ PVCH_PORT_INFO port_info)
{
NTSTATUS status;
WDFIOTARGET io_target;
WDF_MEMORY_DESCRIPTOR output_descriptor;
PVOID buffer = ExAllocatePoolWithTag(NonPagedPool, sizeof(VCH_PORT_INFO), VCH_POOL_TAG);
//WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&output_descriptor, port_info, sizeof(VCH_PORT_INFO));
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&output_descriptor, buffer, sizeof(VCH_PORT_INFO));
io_target = WdfDeviceGetIoTarget(device);
status = WdfIoTargetSendInternalIoctlSynchronously(io_target, NULL, IOCTL_VCP_INTERNAL_GET_PORT_INFO, NULL, &output_descriptor, NULL, NULL);
DbgBreakPoint();
if (!NT_SUCCESS(status))
return status;
memcpy(port_info, buffer, sizeof(VCH_PORT_INFO));
ExFreePoolWithTag(buffer, VCH_POOL_TAG);
return STATUS_SUCCESS;
}
The code that handles the IOCTL:
NTSTATUS IoCtl_Vcp_GetPortInfo(WDFDEVICE device, WDFREQUEST request)
{
NTSTATUS status;
PVCH_PORT_INFO buffer;
PPORT_PDO_DESCRIPTOR descriptor = PortPdoGetContext(device);
status = WdfRequestRetrieveOutputBuffer(request, sizeof(VCH_PORT_INFO), (PVOID*)&buffer, NULL);
if (!NT_SUCCESS(status))
return status;
buffer->Address = descriptor->Address;
buffer->ForceComIndex = FALSE; // TODO: Implement
buffer->Writeable = descriptor->Writeable;
DbgBreakPoint();
return STATUS_SUCCESS;
}
The IOCTL code definition:
#define DEVICE_TYPE_VIRTUAL_COM_PORT 0xC51
#define IOCTL_VCP_INTERNAL_GET_PORT_INFO CTL_CODE(DEVICE_TYPE_VIRTUAL_COM_PORT, 0x30, METHOD_BUFFERED, FILE_READ_DATA)
Setting the request completion information with the number of output bytes fixes the issue.

MiniFilter driver. Filter attaching problems

I'm developing a miniFilter driver and took the Microsoft's SwapBuffers
miniFilter as example.
An InstaceSetup routin by default is attaching to all volumes. But I don't want
to attach to all of them, only to some choosen...
I tried to set "NULL" instead of "InstanceSetup" in "FLT_REGISTRATION
FilterRegistration" and then to call "FltAttachVolume" in the "DriverEntry"
routin. I've done the following:
PFLT_VOLUME vol;
UNICODE_STRING vname;
....
RtlInitUnicodeString(&vname, L"E:\");
FltGetVolumeFromName(gFilterHandle, &vname, &vol);
...
FltAttachVolume(gFilterHandle, vol, NULL, NULL);
...
When i tried to call FltAttachVolume with the "NULL" 3-d parameter
(PCUNICODE_STRING InstanceName) i received a
"STATUS_FLT_INSTANCE_NAME_COLLISION" error.
If i call FltAttachVolume with a "NOT NULL" 3-d parameter, such as a
"UniqueInstaceName" it returns me "-2145452013".
I'm receiving the same errors, when i,m trying to attach a volume, using a
FilterAttach routine from my User application, like this:
...
driver.driverName = L"swapBuffers";
...
LPCWSTR vname = L"F:\";
...
FilterAttach(driver.driverName, vname, NULL, NULL, NULL);
With "NULL" 3-d parameter (LPCWSTR lpInstanceName):
"ERROR_FLT_INSTANCE_NAME_COLLISION"
With "NOT-NULL": "-2145452013".
In MiniSpy miniFilter there is a User application, and the routine FilterAttach
is used. I tried to call this routine in my application the same way - no
results.
Finally, i changed the swapBuffers inf-file:
there was no DefaultInstance parameter, i set it: "SwapBuffers - Top
Instance".
also i copied this from the MiniSpy inf-file:
[MiniFilter.AddRegistry]
HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance%
HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude%
HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags%
HKR,"Instances\"%Instance2.Name%,"Altitude",0x00000000,%Instance2.Altitude%
HKR,"Instances\"%Instance2.Name%,"Flags",0x00010001,%Instance2.Flags%
HKR,"Instances\"%Instance3.Name%,"Altitude",0x00000000,%Instance3.Altitude%
HKR,"Instances\"%Instance3.Name%,"Flags",0x00010001,%Instance3.Flags%
.............
Instance1.Name = "SwapBuffers - Middle Instance"
Instance1.Altitude = "370000"
Instance1.Flags = 0x1 ; Suppress automatic attachments
Instance2.Name = "SwapBuffers - Bottom Instance"
Instance2.Altitude = "361000"
Instance2.Flags = 0x1 ; Suppress automatic attachments
Instance3.Name = "SwapBuffers - Top Instance"
Instance3.Altitude = "385100"
Instance3.Flags = 0x1 ; Suppress automatic attachments
changing the flags to 0x1 to suppress automatic attachments.
And only installing my SwapBuffers miniFilter through this Inf file, i received
"STATUS_SUCCESS" from FltAttachVolume routine in my driver. But it isn't really
attaching to the disk...
What am i doing wrong?
Thanks.
Instance1.Flags = 0x1
That is fine.
I have somewhat similar code and that works fine.
status = FltRegisterFilter( DriverObject,
&FilterRegistration,
&gFilterHandle );
FLT_ASSERT( NT_SUCCESS( status ) );
if (NT_SUCCESS( status )) {
PSECURITY_DESCRIPTOR sd;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING uniString;
status = FltBuildDefaultSecurityDescriptor(&sd,
FLT_PORT_ALL_ACCESS);
if (!NT_SUCCESS(status)) {
return status;
}
RtlInitUnicodeString(&uniString, PORT_NAME);
InitializeObjectAttributes(&oa,
&uniString,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
sd);
status = FltCreateCommunicationPort(gFilterHandle,
&gServerPort,
&oa,
NULL,
Connect,
Disconnect,
Message,
1);
FltFreeSecurityDescriptor(sd);
BREAK_HERE(); // DbgBreak() macro
//
// Start filtering i/o
//
status = FltStartFiltering(gFilterHandle);
if (!NT_SUCCESS(status)) {
FltUnregisterFilter(gFilterHandle);
}
else {
RtlInitUnicodeString(&uniString, L"\\Device\\HarddiskVolume1");
PFLT_VOLUME vol;
FltGetVolumeFromName(gFilterHandle, &uniString, &vol);
status = FltAttachVolume(gFilterHandle, vol, NULL, NULL);
// status == 0x0 at that point and the mini filter is attached to the Volume
}
}
I normally attach to all volumes but I wanted to try attaching to a single volume and it works fine.

WsKSendTo returns STATUS_INVALID_DEVICE_STATE: what does it mean?

I am calling WsKSendTo on an opened socket (irp returns success in callback). But WskSendTo on that socket returns c0000184, what is referenced to as STATUS_INVALID_DEVICE_STATE. What kind of errors are addressed by this?
Did I miss something in the send routine?
psc->dstaddr.sin_family = AF_INET;
psc->dstaddr.sin_port = 0x6973; // big endian
psc->dstaddr.sin_addr.S_un.S_un_b.s_b1 = 0x02;
psc->dstaddr.sin_addr.S_un.S_un_b.s_b2 =
psc->dstaddr.sin_addr.S_un.S_un_b.s_b3 = 0x17;
psc->dstaddr.sin_addr.S_un.S_un_b.s_b4 = 0x0a;
// create IRP
psc->pirp = IoAllocateIrp(1, FALSE);
if(!psc->pirp){
DbgPrint("ShoutShout: IRP not allocated\n");
FreeShoutContext(psc);
return STATUS_UNSUCCESSFUL;
}
IoSetCompletionRoutine(
psc->pirp,
ShoutShoutComplete,
psc,
TRUE,
TRUE,
TRUE
);
// initiate send
const WSK_PROVIDER_DATAGRAM_DISPATCH *dispatch =
(PWSK_PROVIDER_DATAGRAM_DISPATCH)sockContext->socket->Dispatch;
status = dispatch->WskSendTo(
sockContext->socket, //PWSK_SOCKET
&psc->buf, //__in PWSK_BUF
NULL, // undocumented IN ULONG Flags,
(PSOCKADDR)&psc->dstaddr, // IN PSOCKADDR RemoteAddress OPTIONAL,
0, // IN SIZE_T ControlInfoLength,
0, // IN PCMSGHDR ControlInfo OPTIONAL,
psc->pirp); // IN PIRP Irp );
Afterwards status and pirp->IoStatus->Status contain afore mentioned error code and nothing is sent (or reaching the destination). Any suggestions?
c0000184 is returned if the socket is not fully initialised. For example if the socket is not yet bound to an address. The same problem existst in userland, always bind your socket! (Here using WskBind.)
It is also useful to synchronise the individual steps of the initialisation process: allocation of socket context, socket creation and binding using KEVENT (see KeInitializeEvent for a start).

Resources