NTSTATUS KbFiltr_CreateRawPdo( WDFDEVICE Device, ULONG InstanceNo ) /*++ Routine Description: This routine creates and initialize a PDO. Arguments: Return Value: NT Status code. --*/ { NTSTATUS status; PWDFDEVICE_INIT pDeviceInit = NULL; PRPDO_DEVICE_DATA pdoData = NULL; WDFDEVICE hChild = NULL; WDF_OBJECT_ATTRIBUTES pdoAttributes; WDF_DEVICE_PNP_CAPABILITIES pnpCaps; WDF_IO_QUEUE_CONFIG ioQueueConfig; WDFQUEUE queue; WDF_DEVICE_STATE deviceState; PDEVICE_EXTENSION devExt; DECLARE_CONST_UNICODE_STRING(deviceId,KBFILTR_DEVICE_ID ); DECLARE_CONST_UNICODE_STRING(hardwareId,KBFILTR_DEVICE_ID ); DECLARE_CONST_UNICODE_STRING(deviceLocation,L"Keyboard Filter\0" ); DECLARE_UNICODE_STRING_SIZE(buffer, MAX_ID_LEN); DebugPrint(("Entered KbFiltr_CreateRawPdo\n")); // // Allocate a WDFDEVICE_INIT structure and set the properties // so that we can create a device object for the child. // pDeviceInit = WdfPdoInitAllocate(Device); if (pDeviceInit == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } // // Mark the device RAW so that the child device can be started // and accessed without requiring a function driver. Since we are // creating a RAW PDO, we must provide a class guid. // status = WdfPdoInitAssignRawDevice(pDeviceInit, &GUID_DEVCLASS_KEYBOARD); if (!NT_SUCCESS(status)) { goto Cleanup; } // // Since keyboard is secure device, we must protect ourselves from random // users sending ioctls and creating trouble. // status = WdfDeviceInitAssignSDDLString(pDeviceInit, &SDDL_DEVOBJ_SYS_ALL_ADM_ALL); if (!NT_SUCCESS(status)) { goto Cleanup; } // // Assign DeviceID - This will be reported to IRP_MN_QUERY_ID/BusQueryDeviceID // status = WdfPdoInitAssignDeviceID(pDeviceInit, &deviceId); if (!NT_SUCCESS(status)) { goto Cleanup; } // // For RAW PDO, there is no need to provide BusQueryHardwareIDs // and BusQueryCompatibleIDs IDs unless we are running on // Windows 2000. // if (!RtlIsNtDdiVersionAvailable(NTDDI_WINXP)) { // // On Win2K, we must provide a HWID for the device to get enumerated. // Since we are providing a HWID, we will have to provide a NULL inf // to avoid the "found new device" popup and get the device installed // silently. // status = WdfPdoInitAddHardwareID(pDeviceInit, &hardwareId); if (!NT_SUCCESS(status)) { goto Cleanup; } } // // We could be enumerating more than one children if the filter attaches // to multiple instances of keyboard, so we must provide a // BusQueryInstanceID. If we don't, system will throw CA bugcheck. // status = RtlUnicodeStringPrintf(&buffer, L"%02d", InstanceNo); if (!NT_SUCCESS(status)) { goto Cleanup; } status = WdfPdoInitAssignInstanceID(pDeviceInit, &buffer); if (!NT_SUCCESS(status)) { goto Cleanup; } // // Provide a description about the device. This text is usually read from // the device. In the case of USB device, this text comes from the string // descriptor. This text is displayed momentarily by the PnP manager while // it's looking for a matching INF. If it finds one, it uses the Device // Description from the INF file to display in the device manager. // Since our device is raw device and we don't provide any hardware ID // to match with an INF, this text will be displayed in the device manager. // status = RtlUnicodeStringPrintf(&buffer,L"Keyboard_Filter_%02d", InstanceNo ); if (!NT_SUCCESS(status)) { goto Cleanup; } // // You can call WdfPdoInitAddDeviceText multiple times, adding device // text for multiple locales. When the system displays the text, it // chooses the text that matches the current locale, if available. // Otherwise it will use the string for the default locale. // The driver can specify the driver's default locale by calling // WdfPdoInitSetDefaultLocale. // status = WdfPdoInitAddDeviceText(pDeviceInit, &buffer, &deviceLocation, 0x409 ); if (!NT_SUCCESS(status)) { goto Cleanup; } WdfPdoInitSetDefaultLocale(pDeviceInit, 0x409); // // Initialize the attributes to specify the size of PDO device extension. // All the state information private to the PDO will be tracked here. // WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, RPDO_DEVICE_DATA); // // Set up our queue to allow forwarding of requests to the parent // This is done so that the cached Keyboard Attributes can be retrieved // WdfPdoInitAllowForwardingRequestToParent(pDeviceInit); status = WdfDeviceCreate(&pDeviceInit, &pdoAttributes, &hChild); if (!NT_SUCCESS(status)) { goto Cleanup; } // // Get the device context. // pdoData = PdoGetData(hChild); pdoData->InstanceNo = InstanceNo; // // Get the parent queue we will be forwarding to // devExt = FilterGetData(Device); pdoData->ParentQueue = devExt->rawPdoQueue; // // Configure the default queue associated with the control device object // to be Serial so that request passed to EvtIoDeviceControl are serialized. // A default queue gets all the requests that are not // configure-fowarded using WdfDeviceConfigureRequestDispatching. // WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential); ioQueueConfig.EvtIoDeviceControl = KbFilter_EvtIoDeviceControlForRawPdo; status = WdfIoQueueCreate(hChild, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue // pointer to default queue ); if (!NT_SUCCESS(status)) { DebugPrint( ("WdfIoQueueCreate failed 0x%x\n", status)); goto Cleanup; } // // Set some properties for the child device. // WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); pnpCaps.Removable = WdfTrue; pnpCaps.SurpriseRemovalOK = WdfTrue; pnpCaps.NoDisplayInUI = WdfTrue; pnpCaps.Address = InstanceNo; pnpCaps.UINumber = InstanceNo; WdfDeviceSetPnpCapabilities(hChild, &pnpCaps); // // TODO: In addition to setting NoDisplayInUI in DeviceCaps, we // have to do the following to hide the device. Following call // tells the framework to report the device state in // IRP_MN_QUERY_DEVICE_STATE request. // WDF_DEVICE_STATE_INIT(&deviceState); deviceState.DontDisplayInUI = WdfTrue; WdfDeviceSetDeviceState(hChild, &deviceState); // // Tell the Framework that this device will need an interface so that // application can find our device and talk to it. // status = WdfDeviceCreateDeviceInterface( hChild, &GUID_DEVINTERFACE_KBFILTER, NULL ); if (!NT_SUCCESS (status)) { DebugPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status)); goto Cleanup; } // // Add this device to the FDO's collection of children. // After the child device is added to the static collection successfully, // driver must call WdfPdoMarkMissing to get the device deleted. It // shouldn't delete the child device directly by calling WdfObjectDelete. // status = WdfFdoAddStaticChild(Device, hChild); if (!NT_SUCCESS(status)) { goto Cleanup; } // // pDeviceInit will be freed by WDF. // return STATUS_SUCCESS; Cleanup: DebugPrint(("KbFiltr_CreatePdo failed %x\n", status)); // // Call WdfDeviceInitFree if you encounter an error while initializing // a new framework device object. If you call WdfDeviceInitFree, // do not call WdfDeviceCreate. // if (pDeviceInit != NULL) { WdfDeviceInitFree(pDeviceInit); } if(hChild) { WdfObjectDelete(hChild); } return status; }
NTSTATUS vJoy_CreateRawPdo( WDFDEVICE Device, ULONG InstanceNo ) /*++ Routine Description: This routine creates and initialize a PDO. Arguments: Return Value: NT Status code. --*/ { NTSTATUS status; PWDFDEVICE_INIT pDeviceInit = NULL; PRPDO_DEVICE_DATA pdoData = NULL; WDFDEVICE hChild = NULL; WDF_OBJECT_ATTRIBUTES pdoAttributes; WDF_DEVICE_PNP_CAPABILITIES pnpCaps; WDF_IO_QUEUE_CONFIG ioQueueConfig; WDFQUEUE queue; WDF_DEVICE_STATE deviceState; PDEVICE_EXTENSION devExt; DECLARE_CONST_UNICODE_STRING(deviceId,VJOY_RAW_DEVICE_ID ); //DECLARE_CONST_UNICODE_STRING(hardwareId,VJOY_HARDWARE_ID ); DECLARE_CONST_UNICODE_STRING(deviceLocation,L"vJoy Raw Device\0" ); DECLARE_UNICODE_STRING_SIZE(buffer, MAX_ID_LEN); PDEVICE_OBJECT ChildDeviceObject; PDEVICE_OBJECT ParentDeviceObject; DECLARE_CONST_UNICODE_STRING( SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_R_RES_R, L"D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GR;;;WD)(A;;GR;;;RC)" ); DECLARE_CONST_UNICODE_STRING( SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_R, L"D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GRGWGX;;;WD)(A;;GR;;;RC)" ); int iInterface; WCHAR RefStr[20]; UNICODE_STRING RefStr2; WDF_FILEOBJECT_CONFIG FileObjInit; WDF_OBJECT_ATTRIBUTES FileObjAttributes; WDF_OBJECT_ATTRIBUTES LockAttributes; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Entered vJoy_CreateRawPdo\n"); // // Allocate a WDFDEVICE_INIT structure and set the properties // so that we can create a device object for the child. // pDeviceInit = WdfPdoInitAllocate(Device); if (pDeviceInit == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfPdoInitAllocate", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } // // Mark the device RAW so that the child device can be started // and accessed without requiring a function driver. Since we are // creating a RAW PDO, we must provide a class guid. // status = WdfPdoInitAssignRawDevice(pDeviceInit, &GUID_DEVINTERFACE_VJOY); if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfPdoInitAssignRawDevice", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } // TODO: Assign correct SDDL //// status = WdfDeviceInitAssignSDDLString(pDeviceInit, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_R); if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfDeviceInitAssignSDDLString", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } // // Assign DeviceID - This will be reported to IRP_MN_QUERY_ID/BusQueryDeviceID // status = WdfPdoInitAssignDeviceID(pDeviceInit, &deviceId); if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfPdoInitAssignDeviceID", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } // // We could be enumerating more than one children if the filter attaches // to multiple instances of keyboard, so we must provide a // BusQueryInstanceID. If we don't, system will throw CA bugcheck. // status = RtlUnicodeStringPrintf(&buffer, VJOY_DEVICE_INSTANCE, InstanceNo); if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"RtlUnicodeStringPrintf (1)", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } status = WdfPdoInitAssignInstanceID(pDeviceInit, &buffer); if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfPdoInitAssignInstanceID", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } // // Provide a description about the device. This text is usually read from // the device. This text is displayed momentarily by the PnP manager while // it's looking for a matching INF. If it finds one, it uses the Device // Description from the INF file to display in the device manager. // Since our device is raw device and we don't provide any hardware ID // to match with an INF, this text will be displayed in the device manager. // status = RtlUnicodeStringPrintf(&buffer,VJOY_DEVICE_TEXT_409 ); // English - United States if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"RtlUnicodeStringPrintf (2)", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } // // You can call WdfPdoInitAddDeviceText multiple times, adding device // text for multiple locales. When the system displays the text, it // chooses the text that matches the current locale, if available. // Otherwise it will use the string for the default locale. // The driver can specify the driver's default locale by calling // WdfPdoInitSetDefaultLocale. // status = WdfPdoInitAddDeviceText(pDeviceInit, &buffer, &deviceLocation, 0x409 // English - United States ); if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfPdoInitAddDeviceText (1)", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } #if 0 // Hebrew (No real ned - just for fun) status = RtlUnicodeStringPrintf(&buffer,VJOY_DEVICE_TEXT_40D, InstanceNo ); // Hebrew if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"RtlUnicodeStringPrintf (3)", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } status = WdfPdoInitAddDeviceText(pDeviceInit, &buffer, &deviceLocation, 0x40D // Hebrew ); if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfPdoInitAddDeviceText (2)", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } #endif // 0 WdfPdoInitSetDefaultLocale(pDeviceInit, 0x409); // English - United States WdfDeviceInitSetExclusive(pDeviceInit, FALSE); // Create a WDFFILEOBJECT WDF_OBJECT_ATTRIBUTES_INIT(&FileObjAttributes); WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&FileObjAttributes, FILEOBJECT_EXTENSION); WDF_FILEOBJECT_CONFIG_INIT(&FileObjInit, vJoy_EvtDeviceFileCreate, WDF_NO_EVENT_CALLBACK, vJoy_EvtFileCleanup); WdfDeviceInitSetFileObjectConfig(pDeviceInit, &FileObjInit, &FileObjAttributes); // // Initialize the attributes to specify the size of PDO device extension. // All the state information private to the PDO will be tracked here. // WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, RPDO_DEVICE_DATA); pdoAttributes.EvtCleanupCallback = rawEvtCleanupCallback; // // Set up our queue to allow forwarding of requests to the parent // This is done so that the cached data can be retrieved // //WdfPdoInitAllowForwardingRequestToParent(pDeviceInit); // TODO: Replace the above because it is needed for WdfRequestForwardToParentDeviceIoQueue() status = WdfDeviceCreate(&pDeviceInit, &pdoAttributes, &hChild); if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfDeviceCreate", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } // // Get the device context. // pdoData = PdoGetData(hChild); pdoData->InstanceNo = InstanceNo; pdoData->hParentDevice = Device; // // Save the I/O target handle and adjust the I/O stack size: // devExt = GetDeviceContext(Device); pdoData->IoTargetToParent = devExt->IoTargetToSelf; ChildDeviceObject = WdfDeviceWdmGetDeviceObject(hChild); ParentDeviceObject = WdfDeviceWdmGetDeviceObject(Device); ChildDeviceObject->StackSize = ParentDeviceObject->StackSize+1; // Create a wait-lock object that will be used to synch access to positions[i] // The lock is created when the raw device is created so the raw device is set to be its parent WDF_OBJECT_ATTRIBUTES_INIT(&LockAttributes); LockAttributes.ParentObject = hChild; status = WdfWaitLockCreate(&LockAttributes, &(devExt->positionLock)); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfWaitLockCreate failed 0x%x\n", status); LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfWaitLockCreate", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } // // Configure the default queue associated with the control device object // to be Serial so that request passed to EvtIoDeviceControl are serialized. // A default queue gets all the requests that are not // configure-fowarded using WdfDeviceConfigureRequestDispatching. // WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential); ioQueueConfig.EvtIoDeviceControl = vJoy_EvtIoDeviceControlForRawPdo; status = WdfIoQueueCreate(hChild, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue // pointer to default queue ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfIoQueueCreate failed 0x%x\n", status); LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfIoQueueCreate", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } // // Set some properties for the child device. // WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); pnpCaps.Removable = WdfTrue; // Remove Icon from " Devices and Printers" pnpCaps.SurpriseRemovalOK = WdfTrue; pnpCaps.NoDisplayInUI = WdfTrue; // pnpCaps.Address = InstanceNo; pnpCaps.UINumber = 0; WdfDeviceSetPnpCapabilities(hChild, &pnpCaps); // // TODO: In addition to setting NoDisplayInUI in DeviceCaps, we // have to do the following to hide the device. Following call // tells the framework to report the device state in // IRP_MN_QUERY_DEVICE_STATE request. // WDF_DEVICE_STATE_INIT(&deviceState); deviceState.DontDisplayInUI = WdfTrue; // Remove Icon from Device manager WdfDeviceSetDeviceState(hChild, &deviceState); // // Create 16 interfaces // for (iInterface=1 ; iInterface <= MAX_N_DEVICES; iInterface++) { RtlStringCchPrintfW((NTSTRSAFE_PWSTR)RefStr, 20, VJOY_INTERFACE L"%03d", iInterface); RtlInitUnicodeString(&RefStr2, (PCWSTR)RefStr); status = WdfDeviceCreateDeviceInterface(hChild,&GUID_DEVINTERFACE_VJOY,&RefStr2); if (!NT_SUCCESS (status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDeviceCreateDeviceInterface number %d failed 0x%x\n", iInterface, status); LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfDeviceCreateDeviceInterface", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } }; // Mark all interfaces as unused pdoData->UsedInterfacesMask=0; // // Add this device to the FDO's collection of children. // After the child device is added to the static collection successfully, // driver must call WdfPdoMarkMissing to get the device deleted. It // shouldn't delete the child device directly by calling WdfObjectDelete. // status = WdfFdoAddStaticChild(Device, hChild); if (!NT_SUCCESS(status)) { LogEventWithStatus(ERRLOG_RAW_DEV_FAILED ,L"WdfFdoAddStaticChild", WdfDriverWdmGetDriverObject(WdfGetDriver()), status); goto Cleanup; } return STATUS_SUCCESS; Cleanup: TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "KbFiltr_CreatePdo failed %x\n", status); // // Call WdfDeviceInitFree if you encounter an error while initializing // a new framework device object. If you call WdfDeviceInitFree, // do not call WdfDeviceCreate. // if (pDeviceInit != NULL) { WdfDeviceInitFree(pDeviceInit); } if(hChild) { WdfObjectDelete(hChild); } return status; }