-
Notifications
You must be signed in to change notification settings - Fork 408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DeviceIoControl in HIDAPI fail with "(0x00000057) The parameter is incorrect" #712
Comments
Do you know the communication protocol of the device and if you are sure the feature report exists? |
Yes, I’ve seen similar web-based projects that work. Here is one: core.js line 418. You can also find it running at DualShock Tools. I’ve looked into how WebHID interacts with the device compared to HIDAPI, reviewed the documentation for both, and still can’t figure out why it isn’t working. |
This is something likely device-specific. Need to debug further to know for sure. |
Any suggestions on what or how to debug to be sure? |
I'd start with getting the HID Device descriptor of that device and verifying it supports that output feature report. There is also a chance that that report is on a different usage page, which is a different logical hid_device on Windows. This one is easy to try if you try your code sample on Linux. |
Do you've the WebHID solution running on the same Windows computer with the same HID device? |
@JoergAtGithub Yes |
@Youw I ran it on a fresh Ubuntu install with this code, and you were right—it worked. I'm just not sure how to replicate this on Windows now. #include <hidapi/hidapi.h>
#include <iostream>
#include <vector>
#include <iomanip>
// Function to send a feature report
bool send_feature_report(hid_device* dev, const unsigned char* data, size_t length) {
std::cout << "Sending feature report: ";
for (size_t i = 0; i < length; ++i) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(data[i]) << " ";
}
std::cout << std::dec << std::endl;
int result = hid_send_feature_report(dev, data, length);
if (result < 0) {
std::cerr << "Send feature report failed. HIDAPI Error: " << hid_error(dev) << std::endl;
return false;
}
return true;
}
// Function to receive a feature report
std::vector<unsigned char> receive_feature_report(hid_device* dev, unsigned char report_id, size_t expected_length) {
std::vector<unsigned char> buffer(expected_length + 1, 0);
buffer[0] = report_id;
std::cout << "Receiving feature report with Report ID: 0x" << std::hex << static_cast<int>(report_id) << std::dec << std::endl;
int res = hid_get_feature_report(dev, buffer.data(), buffer.size());
if (res < 0) {
std::cerr << "Failed to receive feature report. HIDAPI Error: " << hid_error(dev) << std::endl;
return {};
}
std::cout << "Received " << res << " bytes: ";
for (int i = 0; i < res; ++i) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(buffer[i]) << " ";
}
std::cout << std::dec << std::endl;
return std::vector<unsigned char>(buffer.begin() + 1, buffer.begin() + res);
}
// Function to perform the nvstatus check
int nvstatus(hid_device* dev) {
unsigned char send_data[3] = { 0x80, 0x03, 0x03 }; // Report ID + data
if (!send_feature_report(dev, send_data, sizeof(send_data))) {
return 2; // Error
}
std::vector<unsigned char> response = receive_feature_report(dev, 0x81, 4);
if (response.empty()) {
return 2; // Error
}
unsigned int ret = (response[0] << 24) | (response[1] << 16) | (response[2] << 8) | response[3];
if (ret == 0x03030201) return 1; // Locked
if (ret == 0x03030200) return 0; // Unlocked
return ret; // Unknown
}
int main() {
const unsigned short vendor_id = 0x054C; // Sony
const unsigned short product_id = 0x0CE6; // DualSense
if (hid_init() != 0) {
std::cerr << "Failed to initialize HIDAPI" << std::endl;
return 1;
}
hid_device* dev = hid_open(vendor_id, product_id, nullptr);
if (!dev) {
std::cerr << "Failed to find or open device" << std::endl;
hid_exit();
return 1;
}
int status = nvstatus(dev);
std::cout << "nvstatus: " << status << std::endl;
hid_close(dev);
hid_exit();
return status;
} This is the output from that code.
|
This only supports my assumption that your output feature report is on a different usage/usage_page. On Windows different usage/usage_page combinations are different logical devices (e.g. listed as different devices under hid_enumerate with identical VID/PID/SerialNumber). You may need to open different And that is not a HIDAPI quirk - that's WinAPI behavior, and HIDAPI is only trying to be as thin implementation layer as possible. |
Thank you for the clarification, sorry it took me a minute to get it. I’ve taken your advice and enumerated all possible Here is the code used #include <windows.h>
#include <setupapi.h>
#include <hidsdi.h>
#include <hidapi.h>
#include <iostream>
#include <string>
// Link with setupapi.lib and hid.lib
#pragma comment(lib, "setupapi.lib")
#pragma comment(lib, "hid.lib")
// Helper function to convert char* to std::wstring
std::wstring convert_to_wstring(const char* str) {
if (!str) return L"";
int size_needed = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
if (size_needed <= 0) return L"";
std::wstring wstr(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, str, -1, &wstr[0], size_needed);
return wstr;
}
// Retrieve Usage Page and Usage using native Windows HID APIs
bool get_usage_page_and_usage(const wchar_t* device_path, USHORT& usage_page, USHORT& usage) {
HANDLE device_handle = CreateFileW(device_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (device_handle == INVALID_HANDLE_VALUE) return false;
PHIDP_PREPARSED_DATA preparsed_data = nullptr;
if (!HidD_GetPreparsedData(device_handle, &preparsed_data)) {
CloseHandle(device_handle);
return false;
}
HIDP_CAPS caps;
if (HidP_GetCaps(preparsed_data, &caps) != HIDP_STATUS_SUCCESS) {
HidD_FreePreparsedData(preparsed_data);
CloseHandle(device_handle);
return false;
}
usage_page = caps.UsagePage;
usage = caps.Usage;
HidD_FreePreparsedData(preparsed_data);
CloseHandle(device_handle);
return true;
}
// Print HID device details
void print_device_details(const struct hid_device_info* dev) {
USHORT usage_page = 0, usage = 0;
std::wstring device_path = convert_to_wstring(dev->path);
if (!device_path.empty()) {
get_usage_page_and_usage(device_path.c_str(), usage_page, usage);
}
std::wcout << L"----------------------------------" << std::endl;
std::wcout << L"Path: " << (device_path.empty() ? L"N/A" : device_path) << std::endl;
std::wcout << L"Vendor ID: 0x" << std::hex << dev->vendor_id
<< L" Product ID: 0x" << dev->product_id << std::dec << std::endl;
std::wcout << L"Manufacturer: " << (dev->manufacturer_string ? dev->manufacturer_string : L"Unknown") << std::endl;
std::wcout << L"Product: " << (dev->product_string ? dev->product_string : L"Unknown") << std::endl;
std::wcout << L"Usage Page: 0x" << std::hex << usage_page << L" Usage: 0x" << usage << std::dec << std::endl;
}
int main() {
struct hid_device_info* devices = hid_enumerate(0x0, 0x0);
for (struct hid_device_info* dev = devices; dev != nullptr; dev = dev->next) {
print_device_details(dev);
}
hid_free_enumeration(devices);
hid_exit();
} Console Output
|
Just to double confirm, please run hidapi's hidtest application and not your application under Windows to check the output under Windows. |
Sure @mcuee, here’s the output. Sorry for the delay.
There were other reports, but they were related to my Logitech mouse and keyboard. I’m not sure about this unknown one, so I’ve included it just in case. If you would like the entire report page feel free to let me know. |
Interesting. You didn't have to use WinAPI to get usage/usage_page - |
And to double-check - what version of HIDAPI are you using? Latest release? |
So far I don't see any obvious reason whay the sample you have shouldn't work. Report descriptor does have the 0x80 feature report. The only thing that comes to my mind, is that as per HID Report Descriptor, your report size is 8 (+ report ID), and you only provide a buffer of 4 bytes. |
Yes, I'm using latest release.
I changed it to 8 (+ report ID). #include <hidapi/hidapi.h>
#include <iostream>
#include <vector>
bool send_feature_report(hid_device* dev, const unsigned char* data, size_t length) {
std::cout << "Sending feature report (" << length << " bytes): ";
int result = hid_send_feature_report(dev, data, length);
if (result < 0) {
std::wcerr << "Send feature report failed. HIDAPI Error: "
<< (hid_error(dev) ? hid_error(dev) : L"Unknown error") << std::endl;
return false;
}
return true;
}
std::vector<unsigned char> receive_feature_report(hid_device* dev, unsigned char report_id, size_t expected_data_length) {
std::vector<unsigned char> buffer(expected_data_length + 1, 0);
buffer[0] = report_id;
std::cout << "Receiving feature report with Report ID: 0x"
<< std::hex << static_cast<int>(report_id) << std::dec << std::endl;
int res = hid_get_feature_report(dev, buffer.data(), static_cast<size_t>(buffer.size()));
if (res < 0) {
std::wcerr << "Failed to receive feature report. HIDAPI Error: "
<< (hid_error(dev) ? hid_error(dev) : L"Unknown error") << std::endl;
return {};
}
std::cout << "Received " << res << " bytes: ";
if (res > 1) {
return std::vector<unsigned char>(buffer.begin() + 1, buffer.begin() + res);
}
return {};
}
int nvstatus(hid_device* dev) {
unsigned char send_data[3] = { 0x80, 0x03, 0x03 };
std::vector<unsigned char> response = receive_feature_report(dev, 0x81, 8);
if (response.size() < 4) {
std::cerr << "Received insufficient data for nvstatus." << std::endl;
return 2;
}
unsigned int ret = (response[0] << 24) | (response[1] << 16) | (response[2] << 8) | response[3];
if (ret == 0x03030201) return 1; // Locked
if (ret == 0x03030200) return 0; // Unlocked
return static_cast<int>(ret); // Unknown
}
int main() {
const unsigned short vendor_id = 0x054C; // Sony
const unsigned short product_id = 0x0CE6; // DualSense
hid_device* dev = hid_open(vendor_id, product_id, nullptr);
int status = nvstatus(dev);
std::cout << "nvstatus: " << status << std::endl;
hid_close(dev);
hid_exit();
return status;
} Output:
I'm really not sure what it could be at this point. |
Neither do I. For historical reasons I'll leave the WebHID implementation if GetFeatureReport on Windows in Chrome: It doesn't differ much from what we do in HIDAPI (at least up to the point of @92half99 if you're up to it, you could try to remove the input buffer from |
I'll give it a shot and let you know how it goes. I’m still just curious why it works on Linux but not on Windows. |
@Youw I tried it. res = DeviceIoControl(dev->device_handle,
report_type,
NULL, 0,
data, (DWORD) length,
&bytes_returned, &ol); Same output:
|
At this point, I no longer have suggestions. From what it looks - all should work. BTW: there is also an experimental windows backend: |
It worked! To be perfectly honest, I'm still relatively new to programming. I'm an undergraduate CS student, and I don't fully understand why the
|
Those are different backends. Something about the implementayion must be different. I'm not sure what, as it is propriatary MS implementation. |
I am now encountering a different error regardless of the backend I use. I think I might have to accept that this device is too difficult to work with. // #include <winrt/hidapi_winrt.h>
#include <hidapi/hidapi.h>
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>
bool send_feature_report(hid_device* dev, const unsigned char* data, size_t length) {
std::cout << "Sending feature report (" << length << " bytes): ";
int result = hid_send_feature_report(dev, data, length);
if (result < 0) {
std::wcerr << "Send feature report failed. HIDAPI Error: "
<< (hid_error(dev) ? hid_error(dev) : L"Unknown error\n");
return false;
}
return true;
}
std::vector<unsigned char> receive_feature_report(hid_device* dev, unsigned char report_id, size_t expected_data_length) {
std::vector<unsigned char> buffer(expected_data_length + 1, 0);
buffer[0] = report_id;
std::cout << "Receiving feature report with Report ID: 0x"
<< std::hex << static_cast<int>(report_id) << std::dec << '\n';
int res = hid_get_feature_report(dev, buffer.data(), static_cast<size_t>(buffer.size()));
if (res < 0) {
std::wcerr << "Failed to receive feature report. HIDAPI Error: "
<< (hid_error(dev) ? hid_error(dev) : L"Unknown error\n");
return {};
}
std::cout << "Received " << res << " bytes: ";
if (res > 1) {
return std::vector<unsigned char>(buffer.begin() + 1, buffer.begin() + res);
}
return {};
}
int nvstatus(hid_device* dev) {
unsigned char send_data[3] = { 0x80, 0x03, 0x03 };
std::vector<unsigned char> response = receive_feature_report(dev, 0x81, 8);
if (response.size() < 4) {
std::cerr << "Received insufficient data for nvstatus.\n";
return 2;
}
unsigned int ret = (response[0] << 24) | (response[1] << 16) | (response[2] << 8) | response[3];
if (ret == 0x03030201) return 1; // Locked
if (ret == 0x03030200) return 0; // Unlocked
return static_cast<int>(ret); // Unknown
}
bool ds5_nvlock(hid_device* device) {
unsigned char send_data[3] = { 0x80, 0x03, 0x01 };
std::cout << "Attempting ds5_nvlock...\n";
if (!send_feature_report(device, send_data, sizeof(send_data))) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cerr << "NVS Lock failed: sendFeatureReport error\n";
return false;
}
const size_t expected_length = 32;
auto data = receive_feature_report(device, 0x83, expected_length);
if (data.empty()) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cerr << "NVS Lock failed: receiveFeatureReport error\n";
return false;
}
std::cout << "ds5_nvlock succeeded.\n";
return true;
}
int main() {
const unsigned short vendor_id = 0x054C; // Sony
const unsigned short product_id = 0x0CE6; // DualSense
hid_device* dev = hid_open(vendor_id, product_id, nullptr);
int status = nvstatus(dev);
std::cout << "nvstatus: " << status << '\n';
bool result = ds5_nvlock(dev);
std::cout << "ds5_nvlock result: " << (result ? "success\n" : "failure\n");
int status2 = nvstatus(dev);
std::cout << "nvstatus: " << status2 << '\n';
hid_close(dev);
hid_exit();
return status;
} Output:
hidapi
|
I’ve made this project using C++ and HIDAPI to interact with a dualsense controller.
hid_get_feature_report
fails becauseDeviceIoControl
returns an error.When the
DeviceIoControl
call is made, it fails with the error:Error Code: 0x00000057 (The parameter is incorrect)
The device is accessible, and I can use
hid_open
without issues. Other HID operations (like sending reports) work fine.Reference: deviceiocontrol
The text was updated successfully, but these errors were encountered: