I'm updating a functioning KMDF driver for a PCI device, using WinDDK 7600.16385.1 and OSR's ddkbuild.cmd, targeting WLH, testing on Win7 x86 and x64.
I'm attempting to retrieve the DEVPKEY_Device_LocationPaths property.
The Device Manager Device Properties Details tab displays the expected value in "Location Paths" ...
PCIROOT(0)#PCI(1C00)#PCI(0000)#PCI(0200)#PCI(0000)#PCI(0900)#PCI(0000)#PCI(0400)
... but calling IoGetDevicePropertyData() from the EvtDriverDeviceAdd handler ...
PDEVICE_OBJECT pWdmPDO = WdfFdoInitWdmGetPhysicalDevice( pDeviceInit );
[... WdfDeviceCreate succeeds ...]
WCHAR wszLocationStrings[128] = { 0 }; // Temporary, just to confirm DDI works
ULONG ulRequiredSize = 0;
DEVPROPTYPE devPropType = 0;
status = IoGetDevicePropertyData( pWdmPDO,
&DEVPKEY_Device_LocationPaths,
LOCALE_NEUTRAL,
/*ulFlags*/ 0,
sizeof(wszLocationStrings),
wszLocationStrings,
&ulRequiredSize,
&devPropType );
... always returns STATUS_OBJECT_NAME_NOT_FOUND.
I have tried ...
calling IoGetDevicePropertyData() for other DEVPKEY_Device_* values. The result is the same.
calling IoGetDevicePropertyData() in the EvtDevicePrepareHardware handler. The result is the same.
calling WdfDeviceWdmGetPhysicalDevice() to retrieve the PDO. The result is the same
WdfDeviceAllocAndQueryProperty(). It works correctly but does not provide the info I require.
WdfFdoQueryForInterface( GUID_PNP_LOCATION_INTERFACE ). It works correctly but only provides the current node (i.e. "PCI(0400)")
Searching for sample code that calls IoGetDevicePropertyData. I found the Windows CDROM Storage Class Driver sample but it doesn't appear to do anything I haven't tried already.
So ... what am I missing?
I found a MS PnP support email address in a WinHEC 2006 powerpoint and received a response from Microsoft ...
On Vista and Windows 7, IoGetDevicePropertyData cannot retrieve all the DEVPKEY
properties that user-mode can. There is no way for you to directly retrieve that
full path as created by the operating system from kernel mode.
For further questions, the Windows Hardware WDK and Driver Development forum
(http://social.msdn.microsoft.com/Forums/en-US/wdk/threads ) is a good place to
ask driver development questions.
... to which I responded ...
Thank you very much to your quick response!
It would be very helpful if the IoGetDevicePropertyData online docs could be updated
to include a list of the DEVPKEY properties that are supported and restrictions on
their availability (i.e. only after Start IRP completes as noted in the WinHEC
powerpoint)
And thanks for the link to the forum, I'll start there next time.
Related
EDIT: Issue reported here: https://github.com/signal11/hidapi/issues/276
Inkling is a pen-device from Wacom. (InklingReader) is an open source project that gets real-time data from it.
I'm trying to tidy up InklingReader to use HIDAPI rather than libusb (as it works at higher level: HID rather than raw USB, so is much more compact & suitable. Also libusb fails on recent OSX).
HID API a small lib: one .h, one (per-platform) .c.
My code looks like this:
unsigned short inklingVendorId = 0x056a, inklingProductId = 0x0221;
if (hid_init() == FAIL) return;
handle = hid_open(inklingVendorId, inklingProductId, nullptr);
On Windows hid_open fails. Single stepping reveals the fail-point here:
// path = "\\\\?\\hid#vid_056a&pid_0221&mi_00&col01#8&1ea90857&0&0000#"
// "{4d1e55b2-f16f-11cf-88cb-001111000030}"
//
static HANDLE open_device(const char *path, BOOL enumerate)
{
HANDLE handle;
DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;
// enumerate = 0
handle = CreateFileA(path,
desired_access,
share_mode,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
0);
int err = GetLastError(); // 5 i.e. ERROR_ACCESS_DENIED
return handle; // returns 0xffffffff i.e. INVALID_HANDLE
}
Now the HIDAPI author says "HIDAPI won't work with keyboards and mice on Windows. Windows as a security measure doesn't allow the opening of Mouse and Keyboard HIDs." (here)
And if I enumerate HID devices:
struct hid_device_info *devs, *cur_dev;
devs = hid_enumerate(inklingVendorId, inklingProductId);
cur_dev = devs;
while (cur_dev) {
DBG2("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
DBG2("");
DBG2(" Manufacturer: %ls", cur_dev->manufacturer_string);
DBG2(" Product: %ls", cur_dev->product_string);
DBG2(" Release: %hx", cur_dev->release_number);
DBG2(" Interface: %d", cur_dev->interface_number);
DBG2(" Usage Page: %d", cur_dev->usage_page);
DBG2(" Usage: %d", cur_dev->usage);
DBG2("");
cur_dev = cur_dev->next;
}
hid_free_enumeration(devs);
... I get not one but TWO entries:
Device Found
type: 056a 0221
path: \\?\hid#vid_056a&pid_0221&mi_00&col01#8&1ea90857&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
serial_number: 2B0400001C90C22A0002DD07FE8B022A
Manufacturer: Wacom, Inc.
Product: MSC Device
Release: 1256
Interface: 0
Usage Page: 1
Usage: 2
Device Found
type: 056a 0221
path: \\?\hid#vid_056a&pid_0221&mi_00&col02#8&1ea90857&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
serial_number: 2B0400001C90C22A0002DD07FE8B022A
Manufacturer: Wacom, Inc.
Product: MSC Device
Release: 1256
Interface: 0
Usage Page: 13
Usage: 2
(Note: OSX only reports the SECOND entry! On OSX there is no problem!)
Comparing path:
path: \?\hid#vid_056a&pid_0221&mi_00&col01#8&1ea90857&0&0000#...
path: \?\hid#vid_056a&pid_0221&mi_00&col02#8&1ea90857&0&0001#...
As per http://www.usb.org/developers/hidpage/Hut1_12v2.pdf,
UsagePage/Usage = 1/2 = {Generic Desktop Controls}/{Mouse}.
UsagePage/Usage = 13/2 = {Digitizers}/{Pen}.
(EDIT: Sometimes the first path is the 1/2 and the second is the 13/2, other times it's swapped).
And HIDAPI is only taking the first one it finds.
So it looks like this should be the solution. The Inkling was exposing 2 'devices' and hidapi was taking the wrong (mouse) one, and Windows doesn't allow access to Mouse or Keyboard Devices.
So I tweak the code...
while (cur_dev) {
if (cur_dev->vendor_id == vendor_id &&
cur_dev->product_id == product_id &&
cur_dev->usage_page == 13)
{
... to get the correct entry, it should work right?
Nope, CreateFileA just raises a different error:
usage_page== 1 => Error code 5 (ERROR_ACCESS_DENIED)
usage_page==13 => Error code 32 (ERROR_SHARING_VIOLATION)
Meh. This is rather upsetting. I seem to be at a dead-end!
I've tried fiddling with CreateFileA's params, e.g. replacing GENERIC_READ | GENERIC_WRITE with STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE -- now it happily creates a handle. But subsequent hid_read-s fail to collect any data.
Googling, https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/af869f90-7bda-483d-ba2d-51680073fe9f/createfile-returns-invalid-handle-while-trying-to-access-hid-device-on-windows-8-desktop-app?forum=wdk seems to contain a couple of suggested workarounds:
both toaster and firefly can work in the HID stack. toaster shows how
to address the filter through a raw PDO, firefly shows how to access
it with WMI. From a C perspective, I think the raw PDO is much simpler
to code to, WMI is a bit nasty and complicated.
firefly
toaster
The author is recommending something in toaster, but it is a big CodeBase and I don't have experience with Windows Driver programming.
It looks as though I'm going to have to dig through a lot of very unfamiliar territory to get anything working, so before a start out I am asking here. If nobody answers and I figure it out, I will answer my own question.
The only other thing I can think of it is that maybe another process is already engaging this path. Maybe if I can terminate this process, the CreateFileA might succeed? Roel's libusb approach involves detaching kernel driver: https://github.com/roelj/inklingreader/blob/master/src/usb/online-mode.c#L98
PS Somewhere I read that if another process has already opened this device, our open has to match the permissions of this previous open. And I also read that Windows automatically opens all HID Devices upon detection.
Find out which process has an exclusive lock on a USB device handle
PPS maybe one idea is to try an alternative HID lib What is the best usb library to communicate with usb HID devices on Windows?
PPPS maybe I need to run my code as admin. But that's not a good solution.
I have seen similar behavior. The ERROR_SHARING_VIOLATION problem started to occur after upgrading to Windows 10 Anniversary Edition. The problem is only seen for USB HID devices connected when Windows is started. If you unplug and plug the USB device after Windows has started then CreateFile is successful. I haven't yet found a root cause or a solution.
You're right: ERROR_SHARING_VIOLATION will occur if some other app already opened this device. You need to call CreateFileW API like this:
DWORD desired_access = GENERIC_WRITE | GENERIC_READ;
DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
::CreateFileW(deviceInterfacePath, desired_access, share_mode, 0, OPEN_EXISTING, 0, 0);
If you don't provide dwShareMode then it means that you're trying to open device exclusively. Which can fail if other app (new Windows version that maybe supports these kind of devices natively) already opened this device for its use.
Note about Keyboard and Mouse devices: you can also call ::CreateFileW without even setting desired_access (use zero value): in this case you can use HidD_GetManufacturerString/HidD_GetProductString/HidD_GetSerialNumberString/HidD_GetAttributes (and maybe some others) HID methods with returned handle. But you cannot read/write data to such device. This should be useful if you need to acquire name or VID/PID for HID keyboard/mouse.
Here is list of HID device types and their access modes on Windows.
The docs for the SetupDiSetDeviceRegistryProperty function say,
The following values are reserved for use by the operating system and
cannot be used in the Property parameter ...
SPDRP_HARDWAREID
However, there are lots of examples of code out there, including MS DevCon utility which uses this function with the SPDRP_HARDWAREID parameter, ie:
SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
&DeviceInfoData,
SPDRP_HARDWAREID,
(LPBYTE)hwIdList,
(lstrlen(hwIdList)+1+1)*sizeof(TCHAR)))
They also have an article which suggests doing so:
If an installer detects a non-PnP device, the installer should select a driver for the device as follows: create a device information element (SetupDiCreateDeviceInfo), set the SPDRP_HARDWAREID property by calling SetupDiSetDeviceRegistryProperty
I'd like to (and do) use this function to set Hardware ID for my virtual device. The question is - is it a typo in the manual, or it's some sort of unsupported behavior and therefore it can stop working any time?
TL;DR: If you're creating a root-enumerated device node, you're free to set SPDRP_HARDWAREID/SPDRP_COMPATIBLEIDS yourself by calling SetupDiSetDeviceRegistryProperty. Otherwise you're not allowed to do so.
This was an error in the docs that was fixed at some point.
Today the docs of SetupDiSetDeviceRegistryProperty read:
SPDRP_HARDWAREID or SPDRP_COMPATIBLEIDS can only be used when DeviceInfoData represents a root-enumerated device. For other devices, the bus driver reports hardware and compatible IDs when enumerating a child device after receiving IRP_MN_QUERY_ID. [Emphasis mine]
... which is exactly what DevCon does.
We have some HID devices (touch digitizers) that communicate with an internal R&D tool. This tool parses the raw feature reports from the devices to draw the touch reports along with some additional data that are present in the raw feature report but filtered out by the HID driver of Windows 7 (eg, pressure data is not present in WM_TOUCH messages).
However, we have started working with some devices that may have different firmware variants, and thus that do not share the same ordering or bytelength of the fields and I need to modify our R&D tool so that it will adapt transparently to all the devices.
The devices come from the same manufacturer (ourselves) and share the same device info, so using these fields to differentiate between the different firmwares is not an option. What I would like to do is to get the HID feature report descriptor sent by the device and update dynamically our feature report parsing method based on this information.
However, I didn't manage to find the correct method to call in order to get this descriptor when browsing the Windows API. What I have found so far is the Raw Input page on MSDN, but I'm not sure what to do next. Can I find the required information in the RID_DEVICE_HID structure ? Or do I need to call a completely different API ?
Thanks in advance for your help!
Ok, finally I've got something (almost completely) functional. As inferred by mcoill, I used the HidP_xxx() family of functions, but it needs a little bit of data preparation first.
I based my solution on this example code that targets USB joysticks and adapted it to touch digitizer devices.
If someone else also gets confused by the online doc, here are the required steps involved in the process:
registering the application for a Raw Input device at launch.
This is done by calling the function RegisterRawInputDevice(&Rid, 1, sizeof(Rid)), where Rid is a RAWINPUTDEVICE with the following properties set (in order to get a touch digitizer) :
Rid.usUsage = 0x04;
Rid.usUsagePage = 0x0d;
Rid.dwFlags = RIDEV_INPUT_SINK;
registering a callback OnInput(LPARAM lParam) for the events WM_INPUT since the Rid device will generate this type of events;
the OnInput(LPARAM lParam) method will get the data from this event in two steps:
// Parse the raw input header to read its size.
UINT bufferSize;
GetRawInputData(HRAWINPUT)lParam, RID_INPUT, NULL, &bufferSize, sizeof(RAWINPUTHEADER));
// Allocate memory for the raw input data and retrieve it
PRAWINPUT = (PRAWINPUT)HeapAlloc(GetProcessHeap(), 0, bufferSize);
GetRawInputData(HRAWINPUT)lParam, RID_INPUT, rawInput /* NOT NULL */, &bufferSize, sizeof(RAWINPUTHEADER));
it then calls a parsing method that creates the HIDP_PREPARSED_DATA structure required by the lookup functions:
// Again, read the data size, allocate then retrieve
GetRawInputDeviceInfo(rawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize);
PHIDP_PREPARSED_DATA preparsedData = (PHIDP_PREPARSED_DATA)HeapAlloc(heap, 0, bufferSize);
GetRawInputDeviceInfo(rawInput->header.hDevice, RIDI_PREPARSEDDATA, preparsedData, &bufferSize);
The preparsed data is split into capabilities:
// Create a structure that will hold the values
HidP_GetCaps(preparsedData, &caps);
USHORT capsLength = caps.NumberInputValueCaps;
PHIDP_VALUE_CAPS valueCaps = (PHIDP_VALUE_CAPS)HeapAlloc(heap, 0, capsLength*sizeof(HIDP_VALUE_CAPS));
HidP_GetValueCaps(HidP_Input, valueCaps, &capsLength, preparsedData);
And capabilities can be asked for their value:
// Read sample value
HidP_GetUsageValue(HidP_Input, valueCaps[i].UsagePage, 0, valueCaps[i].Range.UsageMin, &value, preparsedData, (PCHAR)rawInput->data.hid.bRawData, rawInput->data.hid.dwSizeHid);
Wouldn't HidP_GetPReparsedData(...), HidP_GetValueCaps(HidP_Feature, ...) and their ilk give you enough information without having to get the raw feature report?
HIDClass Support Routines on MSDN
I have a driver that runs in the kernel of a Windows Embedded Compact 2013. The driver is loaded with the "Drivers\BuiltIn" registry key. It accesses a set of HW-registers that are mapped with MmMapIoSpace.
The access to the hardware has some problems. That's why I would like to develop the hardware access in a user mode program and debug the problems. I created a program with VS2013 for that purpose. That's the way we used to go with Windows CE 5.0.
The driver maps the physical address with MmMapIoSpace to the process address space. My program should do the same or something similar. Unfortunately this doesn't work in my program. MmMapIoSpace returns NULL, LastError=87 (invalid parameters). Even CreateStaticMapping returns NULL.
How can I access memory mapped register in WEC2013 without building a new platform for each iteration?
MmMapIoSpace doesn't work in applications anymore since WinCE6.
You maybe could create a driver which maps your hw-register to your user process. Your user process would then obtain this pointer by an ioctl call to this driver.
We mapped some external memory to an application with this method.
VirtualAllocCopyEx() can create a mapping to a specified process.
Hope, this helps. Greetings.
Corresponding to timmfs answer I implemented this code in the driver's XXX_IOControl function:
PHYSICAL_ADDRESS PhysAddress = { 0 };
PhysAddress.LowPart = phys_address;
PVOID pRegister = MmMapIoSpace(PhysAddress, phys_size, FALSE);
HANDLE hCallerProcess = (HANDLE)GetCallerVMProcessId();
HANDLE hCurrentProcess = (HANDLE)GetCurrentProcessId();
PVOID UserSpaceAddress = VirtualAllocCopyEx(hCurrentProcess, hCallerProcess, pRegister, shys_size, PAGE_NOCACHE);
This excerpt shows some solutions for pitfalls I met. So I show how I get all the parameters.
Does anyone know if there is an API to get the current monitor state (on or off) in Windows (XP/Vista/2000/2003)?
All of my searches seem to indicate there is no real way of doing this.
This thread tries to use GetDevicePowerState which according to Microsoft's docs does not work for display devices.
In Vista I can listen to GUID_MONITOR_POWER_ON but I do not seem to get events when the monitor is turned off manually.
In XP I can hook into WM_SYSCOMMAND SC_MONITORPOWER, looking for status 2. This only works for situations where the system triggers the power off.
The WMI Win32_DesktopMonitor class does not seem to help out as well.
Edit: Here is a discussion on comp.os.ms-windows.programmer.win32 indicating there is no reliable way of doing this.
Anyone else have any other ideas?
GetDevicePowerState sometimes works for monitors. If it's present, you can open the \\.\LCD device. Close it immediately after you've finished with it.
Essentially, you're out of luck—there is no reliable way to detect the monitor power state, short of writing a device driver and filtering all of the power IRPs up and down the display driver chain. And that's not very reliable either.
You could hook up a webcam, point it at your screen and do some analysis on the images you receive ;)
Before doing anything based on the monitor state, just remember that users can use a machine with remote desktop of other systems that don't require a monitor connected to the machine - so don't turn off any visualization based on the monitor state.
You can't.
Look like all monitor power capabilities connected to the "power safe mode"
After searching i found here code that connecting between SC_MONITORPOWER message and system values (post number 2)
I use the code to testing if the system values is changing when i am manually switch off the monitor.
int main()
{
for(;monitorOff()!=1;)
Sleep(500);
return 0;
}//main
And the code is never stopped, no matter how long i am switch off my monitor.
There the code of monitorOff function:
int monitorOff()
{
const GUID MonitorClassGuid =
{0x4d36e96e, 0xe325, 0x11ce,
{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}};
list<DevData> monitors;
ListDeviceClassData(&MonitorClassGuid, monitors);
list<DevData>::iterator it = monitors.begin(),
it_end = monitors.end();
for (; it != it_end; ++it)
{
const char *off_msg = "";
//it->PowerData.PD_PowerStateMapping
if (it->PowerData.PD_MostRecentPowerState != PowerDeviceD0)
{
return 1;
}
}//for
return 0;
}//monitorOff
Conclusion : when you manually switch of the the monitor, you cant catch it by windows (if there is no unusual driver interface for this), because all windows capabilities is connected to "power safe mode".
In Windows XP or later you can use the IMSVidDevice Interface.
See
http://msdn.microsoft.com/en-us/library/dd376775(VS.85).aspx
(not sure if this works in Sever 2003)
With Delphi code, you can detect invalid monitor geomerty while standby in progress:
i := 0
('Monitor'+IntToStr(i)+': '+IntToStr(Screen.Monitors[i].BoundsRect.Left)+', '+
IntToStr(Screen.Monitors[i].BoundsRect.Top)+', '+
IntToStr(Screen.Monitors[i].BoundsRect.Right)+', '+
IntToStr(Screen.Monitors[i].BoundsRect.Bottom))
Results:
Monitor geometry before standby:
Monitor0: 0, 0, 1600, 900
Monitor geometry while standby in Deplhi7:
Monitor0: 1637792, 4210405, 31266576, 1637696
Monitor geometry while standby in DeplhiXE:
Monitor0: 4211194, 40, 1637668, 1637693
This is a really old post but if it can help someone, I have found a solution to detect a screen being available or not : the Connecting and Configuring Displays (CCD) API of Windows.
It's part of User32.ddl and the interesting functions are GetDisplayConfigBufferSizes and QueryDisplayConfig. It give us all informations that can be viewed in the Configuration Panel of windows.
In particular the PathInfo contains a TargetInfo property that have a targetAvailable flag. This flag seems to be correctly updated on all the configurations I have tried so far.
This allow you to know the state of every screens connected to the PC and set their configurations.
Here a CCD wrapper for .Net
If your monitor has some sort of built-in USB hub, you could try and use that to detect if the monitor is off/on.
This will of course only work if the USB hub doesn't stay connected when the monitor is consider "off".