DeviceIoControl in HIDAPI fail with "(0x00000057) The parameter is incorrect" #712

Closed
opened 2024-12-08 18:33:11 -06:00 by ghost · 23 comments
ghost commented 2024-12-08 18:33:11 -06:00 (Migrated from github.com)

I’ve made this project using C++ and HIDAPI to interact with a dualsense controller.

#include <hidapi/hidapi.h>
#include <iostream>
#include <vector>

hid_device* find_device(unsigned short vendor_id, unsigned short product_id) {
    struct hid_device_info* devices = hid_enumerate(0x0, 0x0);
    hid_device* target_device = nullptr;

    for (struct hid_device_info* cur = devices; cur != nullptr; cur = cur->next) {
        if (cur->vendor_id == vendor_id && cur->product_id == product_id) {
            std::wcout << L"Found device: " << cur->manufacturer_string
                << L" - " << cur->product_string << std::endl;
            target_device = hid_open_path(cur->path);
            break;
        }
    }

    hid_free_enumeration(devices);
    return target_device;
}

bool send_feature_report(hid_device* dev, unsigned char report_id, const std::vector<unsigned char>& data) {
    std::vector<unsigned char> buffer(data.size() + 1);
    buffer[0] = report_id;
    std::copy(data.begin(), data.end(), buffer.begin() + 1);
    return hid_send_feature_report(dev, buffer.data(), buffer.size()) >= 0;
}

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;
    if (hid_get_feature_report(dev, buffer.data(), buffer.size()) < 0) {
        return {};
    }
    return std::vector<unsigned char>(buffer.begin() + 1, buffer.end());
}

int nvstatus(hid_device* dev) {
    send_feature_report(dev, 0x80, {3, 3});
    auto response = receive_feature_report(dev, 0x81, 8);
    if (response.size() < 4) return 2;
    unsigned int ret = (response[0] << 24) | (response[1] << 16) | (response[2] << 8) | response[3];
    return (ret == 0x03030201) ? 1 : (ret == 0x03030200) ? 0 : ret;
}

int main() {
    const unsigned short vendor_id = 0x054C;
    const unsigned short product_id = 0x0CE6;

    hid_init();
    hid_device* dev = find_device(vendor_id, product_id);
    if (dev) {
        int status = nvstatus(dev);
        std::cout << "nvstatus: " << status << '\n';
        hid_close(dev);
    }
    hid_exit();
}

hid_get_feature_report fails because DeviceIoControl 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

I’ve made this project using C++ and HIDAPI to interact with a dualsense controller. ```cpp #include <hidapi/hidapi.h> #include <iostream> #include <vector> hid_device* find_device(unsigned short vendor_id, unsigned short product_id) { struct hid_device_info* devices = hid_enumerate(0x0, 0x0); hid_device* target_device = nullptr; for (struct hid_device_info* cur = devices; cur != nullptr; cur = cur->next) { if (cur->vendor_id == vendor_id && cur->product_id == product_id) { std::wcout << L"Found device: " << cur->manufacturer_string << L" - " << cur->product_string << std::endl; target_device = hid_open_path(cur->path); break; } } hid_free_enumeration(devices); return target_device; } bool send_feature_report(hid_device* dev, unsigned char report_id, const std::vector<unsigned char>& data) { std::vector<unsigned char> buffer(data.size() + 1); buffer[0] = report_id; std::copy(data.begin(), data.end(), buffer.begin() + 1); return hid_send_feature_report(dev, buffer.data(), buffer.size()) >= 0; } 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; if (hid_get_feature_report(dev, buffer.data(), buffer.size()) < 0) { return {}; } return std::vector<unsigned char>(buffer.begin() + 1, buffer.end()); } int nvstatus(hid_device* dev) { send_feature_report(dev, 0x80, {3, 3}); auto response = receive_feature_report(dev, 0x81, 8); if (response.size() < 4) return 2; unsigned int ret = (response[0] << 24) | (response[1] << 16) | (response[2] << 8) | response[3]; return (ret == 0x03030201) ? 1 : (ret == 0x03030200) ? 0 : ret; } int main() { const unsigned short vendor_id = 0x054C; const unsigned short product_id = 0x0CE6; hid_init(); hid_device* dev = find_device(vendor_id, product_id); if (dev) { int status = nvstatus(dev); std::cout << "nvstatus: " << status << '\n'; hid_close(dev); } hid_exit(); } ``` `hid_get_feature_report` fails because `DeviceIoControl` 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](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol)
mcuee commented 2024-12-09 05:36:44 -06:00 (Migrated from github.com)

Do you know the communication protocol of the device and if you are sure the feature report exists?

Do you know the communication protocol of the device and if you are sure the feature report exists?
ghost commented 2024-12-09 11:44:50 -06:00 (Migrated from github.com)

@mcuee

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.

@mcuee > 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](https://github.com/dualshock-tools/dualshock-tools.github.io/blob/main/core.js#L418). You can also find it running at [DualShock Tools](https://dualshock-tools.github.io/#). 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.
Youw commented 2024-12-11 05:33:10 -06:00 (Migrated from github.com)

This is something likely device-specific. Need to debug further to know for sure.

This is something likely device-specific. Need to debug further to know for sure.
ghost commented 2024-12-11 06:03:25 -06:00 (Migrated from github.com)

@Youw

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?

@Youw > 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?
Youw commented 2024-12-11 06:12:54 -06:00 (Migrated from github.com)

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.

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.
JoergAtGithub commented 2024-12-12 01:16:36 -06:00 (Migrated from github.com)

Do you've the WebHID solution running on the same Windows computer with the same HID device?

Do you've the WebHID solution running on the same Windows computer with the same HID device?
ghost commented 2024-12-12 07:08:16 -06:00 (Migrated from github.com)

@JoergAtGithub Yes

@JoergAtGithub Yes
ghost commented 2024-12-12 07:38:52 -06:00 (Migrated from github.com)

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.

@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.

Sending feature report: 80 03 03
Receiving feature report with Report ID: 0x81
Received 5 bytes: 81 03 03 02 01
nvstatus: 1
> 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. @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. ```cpp #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. ``` Sending feature report: 80 03 03 Receiving feature report with Report ID: 0x81 Received 5 bytes: 81 03 03 02 01 nvstatus: 1 ```
Youw commented 2024-12-12 08:13:57 -06:00 (Migrated from github.com)

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.

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 hid_devices for different reports.

And that is not a HIDAPI quirk - that's WinAPI behavior, and HIDAPI is only trying to be as thin implementation layer as possible.

> 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. 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 `hid_device`s for different reports. And that is not a HIDAPI quirk - that's WinAPI behavior, and HIDAPI is only trying to be as thin implementation layer as possible.
ghost commented 2024-12-12 18:40:26 -06:00 (Migrated from github.com)

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 hid_devices for different reports.

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 hid_devices using hid_enumerate to identify the different usage pages and usages associated with the device. However, based on my findings, the device exposes only one logical interface with a single usage page and usage combination (Usage Page: 0x1, Usage: 0x5).

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

----------------------------------
Path: \\?\HID#VID_054C&PID_0CE6&MI_03#8&251f98e4&1&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
Vendor ID: 0x54c Product ID: 0xce6
Manufacturer: Sony Interactive Entertainment
Product: DualSense Wireless Controller
Usage Page: 0x1 Usage: 0x5
----------------------------------
Path: \\?\HID#VID_046D&PID_C54D&MI_01&Col01#8&71e71c1&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\KBD
Vendor ID: 0x46d Product ID: 0xc54d
Manufacturer: Logitech
Product: USB Receiver
Usage Page: 0x0 Usage: 0x0
----------------------------------
Path: \\?\HID#VID_046D&PID_C54D&MI_02&Col01#8&1a4e8dff&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
Vendor ID: 0x46d Product ID: 0xc54d
Manufacturer: Logitech
Product: USB Receiver
Usage Page: 0xff00 Usage: 0x1
----------------------------------
Path: \\?\HID#VID_1462&PID_7B85#7&310d377a&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
Vendor ID: 0x1462 Product ID: 0x7b85
Manufacturer: MSI
Product: PRO CARBON
Usage Page: 0x1 Usage: 0x0
----------------------------------
> 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 `hid_device`s for different reports. > > 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 `hid_device`s using `hid_enumerate` to identify the different usage pages and usages associated with the device. However, based on my findings, the device exposes only one logical interface with a single usage page and usage combination (Usage Page: `0x1`, Usage: `0x5`). Here is the code used ```cpp #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 ``` ---------------------------------- Path: \\?\HID#VID_054C&PID_0CE6&MI_03#8&251f98e4&1&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} Vendor ID: 0x54c Product ID: 0xce6 Manufacturer: Sony Interactive Entertainment Product: DualSense Wireless Controller Usage Page: 0x1 Usage: 0x5 ---------------------------------- Path: \\?\HID#VID_046D&PID_C54D&MI_01&Col01#8&71e71c1&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\KBD Vendor ID: 0x46d Product ID: 0xc54d Manufacturer: Logitech Product: USB Receiver Usage Page: 0x0 Usage: 0x0 ---------------------------------- Path: \\?\HID#VID_046D&PID_C54D&MI_02&Col01#8&1a4e8dff&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} Vendor ID: 0x46d Product ID: 0xc54d Manufacturer: Logitech Product: USB Receiver Usage Page: 0xff00 Usage: 0x1 ---------------------------------- Path: \\?\HID#VID_1462&PID_7B85#7&310d377a&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} Vendor ID: 0x1462 Product ID: 0x7b85 Manufacturer: MSI Product: PRO CARBON Usage Page: 0x1 Usage: 0x0 ---------------------------------- ```
mcuee commented 2024-12-13 22:35:47 -06:00 (Migrated from github.com)

Just to double confirm, please run hidapi's hidtest application and not your application under Windows to check the output under Windows.
https://github.com/libusb/hidapi/tree/master/hidtest

Just to double confirm, please run hidapi's hidtest application and not your application under Windows to check the output under Windows. https://github.com/libusb/hidapi/tree/master/hidtest
ghost commented 2024-12-15 02:06:17 -06:00 (Migrated from github.com)

Sure @mcuee, here’s the output. Sorry for the delay.

hidapi test/example tool. Compiled with hidapi version 0.14.0, runtime version 0.14.0.
Compile-time version matches runtime version of hidapi.

Device Found
  type: 054c 0ce6
  path: \\?\HID#VID_054C&PID_0CE6&MI_03#8&251f98e4&1&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
  serial_number:
  Manufacturer: Sony Interactive Entertainment
  Product:      DualSense Wireless Controller
  Release:      100
  Interface:    3
  Usage (page): 0x5 (0x1)
  Bus type: 1 (USB)

  Report Descriptor: (437 bytes)
0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30,
0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x09, 0x33, 0x09, 0x34,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x06, 0x81,
0x02, 0x06, 0x00, 0xff, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff,
0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0x05, 0x01, 0x09,
0x39, 0x15, 0x00, 0x25, 0x07, 0x35, 0x00, 0x46, 0x3b, 0x01,
0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x05, 0x09,
0x19, 0x01, 0x29, 0x0f, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
0x95, 0x0f, 0x45, 0x00, 0x65, 0x00, 0x81, 0x02, 0x06, 0x00,
0xff, 0x09, 0x21, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95,
0x0d, 0x81, 0x02, 0x09, 0x22, 0x15, 0x00, 0x26, 0xff, 0x00,
0x35, 0x00, 0x46, 0x3b, 0x01, 0x75, 0x08, 0x95, 0x34, 0x81,
0x02, 0x85, 0x02, 0x09, 0x23, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x2f, 0x91, 0x02, 0x85, 0x05, 0x09, 0x33,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x28, 0xb1,
0x02, 0x85, 0x08, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x2f, 0xb1, 0x02, 0x85, 0x09, 0x09, 0x24,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x13, 0xb1,
0x02, 0x85, 0x0a, 0x09, 0x25, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x1a, 0xb1, 0x02, 0x85, 0x20, 0x09, 0x26,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1,
0x02, 0x85, 0x21, 0x09, 0x27, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x04, 0xb1, 0x02, 0x85, 0x22, 0x09, 0x40,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1,
0x02, 0x85, 0x80, 0x09, 0x28, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x81, 0x09, 0x29,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1,
0x02, 0x85, 0x82, 0x09, 0x2a, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x09, 0xb1, 0x02, 0x85, 0x83, 0x09, 0x2b,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1,
0x02, 0x85, 0x84, 0x09, 0x2c, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x85, 0x09, 0x2d,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x02, 0xb1,
0x02, 0x85, 0xa0, 0x09, 0x2e, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x01, 0xb1, 0x02, 0x85, 0xe0, 0x09, 0x2f,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1,
0x02, 0x85, 0xf0, 0x09, 0x30, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf1, 0x09, 0x31,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1,
0x02, 0x85, 0xf2, 0x09, 0x32, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x0f, 0xb1, 0x02, 0x85, 0xf4, 0x09, 0x35,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1,
0x02, 0x85, 0xf5, 0x09, 0x36, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x03, 0xb1, 0x02, 0xc0,
Device Found
  type: 046d c232
  path: \\?\HID#VID_046D&PID_C232#2&1a87f3a8&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\KBD
  serial_number:
  Manufacturer:
  Product:
  Release:      1
  Interface:    -1
  Usage (page): 0x6 (0x1)
  Bus type: 0 (Unknown)

  Report Descriptor: (65 bytes)
0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0,
0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08,
0x81, 0x02, 0x75, 0x08, 0x95, 0x01, 0x81, 0x03, 0x19, 0x00,
0x29, 0xe7, 0x15, 0x00, 0x25, 0xe7, 0x75, 0x08, 0x95, 0x06,
0x81, 0x00, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x15, 0x00,
0x25, 0x01, 0x75, 0x01, 0x95, 0x05, 0x91, 0x02, 0x75, 0x03,
0x95, 0x01, 0x91, 0x03, 0xc0,

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.

Sure @mcuee, here’s the output. Sorry for the delay. ``` hidapi test/example tool. Compiled with hidapi version 0.14.0, runtime version 0.14.0. Compile-time version matches runtime version of hidapi. Device Found type: 054c 0ce6 path: \\?\HID#VID_054C&PID_0CE6&MI_03#8&251f98e4&1&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} serial_number: Manufacturer: Sony Interactive Entertainment Product: DualSense Wireless Controller Release: 100 Interface: 3 Usage (page): 0x5 (0x1) Bus type: 1 (USB) Report Descriptor: (437 bytes) 0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x06, 0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0x05, 0x01, 0x09, 0x39, 0x15, 0x00, 0x25, 0x07, 0x35, 0x00, 0x46, 0x3b, 0x01, 0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0f, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0f, 0x45, 0x00, 0x65, 0x00, 0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x21, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0d, 0x81, 0x02, 0x09, 0x22, 0x15, 0x00, 0x26, 0xff, 0x00, 0x35, 0x00, 0x46, 0x3b, 0x01, 0x75, 0x08, 0x95, 0x34, 0x81, 0x02, 0x85, 0x02, 0x09, 0x23, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x2f, 0x91, 0x02, 0x85, 0x05, 0x09, 0x33, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x28, 0xb1, 0x02, 0x85, 0x08, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x2f, 0xb1, 0x02, 0x85, 0x09, 0x09, 0x24, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x13, 0xb1, 0x02, 0x85, 0x0a, 0x09, 0x25, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x1a, 0xb1, 0x02, 0x85, 0x20, 0x09, 0x26, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x21, 0x09, 0x27, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0xb1, 0x02, 0x85, 0x22, 0x09, 0x40, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x80, 0x09, 0x28, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x81, 0x09, 0x29, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x82, 0x09, 0x2a, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x09, 0xb1, 0x02, 0x85, 0x83, 0x09, 0x2b, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x84, 0x09, 0x2c, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x85, 0x09, 0x2d, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x02, 0xb1, 0x02, 0x85, 0xa0, 0x09, 0x2e, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0xb1, 0x02, 0x85, 0xe0, 0x09, 0x2f, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf0, 0x09, 0x30, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf1, 0x09, 0x31, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf2, 0x09, 0x32, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x0f, 0xb1, 0x02, 0x85, 0xf4, 0x09, 0x35, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf5, 0x09, 0x36, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x03, 0xb1, 0x02, 0xc0, ``` ``` Device Found type: 046d c232 path: \\?\HID#VID_046D&PID_C232#2&1a87f3a8&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\KBD serial_number: Manufacturer: Product: Release: 1 Interface: -1 Usage (page): 0x6 (0x1) Bus type: 0 (Unknown) Report Descriptor: (65 bytes) 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x75, 0x08, 0x95, 0x01, 0x81, 0x03, 0x19, 0x00, 0x29, 0xe7, 0x15, 0x00, 0x25, 0xe7, 0x75, 0x08, 0x95, 0x06, 0x81, 0x00, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x05, 0x91, 0x02, 0x75, 0x03, 0x95, 0x01, 0x91, 0x03, 0xc0, ``` 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.
Youw commented 2024-12-17 10:46:11 -06:00 (Migrated from github.com)

Here is the code used

Interesting. You didn't have to use WinAPI to get usage/usage_page - hid_device_info already has these fileds.

> Here is the code used Interesting. You didn't have to use WinAPI to get usage/usage_page - `hid_device_info` already has these fileds.
Youw commented 2024-12-17 10:50:37 -06:00 (Migrated from github.com)

And to double-check - what version of HIDAPI are you using? Latest release?

And to double-check - what version of HIDAPI are you using? Latest release?
Youw commented 2024-12-17 11:02:30 -06:00 (Migrated from github.com)

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.

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.
ghost commented 2024-12-17 23:15:17 -06:00 (Migrated from github.com)

And to double-check - what version of HIDAPI are you using? Latest release?

Yes, I'm 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.

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:

Receiving feature report with Report ID: 0x81
Failed to receive feature report. HIDAPI Error: Get Input/Feature Report DeviceIoControl: (0x00000057) The parameter is incorrect.
Received insufficient data for nvstatus.
nvstatus: 2

I'm really not sure what it could be at this point.

> And to double-check - what version of HIDAPI are you using? Latest release? Yes, I'm 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. I changed it to 8 (+ report ID). ```cpp #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: ``` Receiving feature report with Report ID: 0x81 Failed to receive feature report. HIDAPI Error: Get Input/Feature Report DeviceIoControl: (0x00000057) The parameter is incorrect. Received insufficient data for nvstatus. nvstatus: 2 ``` I'm really not sure what it could be at this point.
Youw commented 2024-12-18 05:54:13 -06:00 (Migrated from github.com)

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:
https://chromium.googlesource.com/chromium/src/+/32352ad08ee673a4d43e8593ce988b224f6482d3/device/hid/hid_connection_win.cc#167

It doesn't differ much from what we do in HIDAPI (at least up to the point of DeviceIoControl call. The only difference I see is that we provide both inout and output buffers, but Chrome implementation only passes the output buffer to DeviceIoControl.

@92half99 if you're up to it, you could try to remove the input buffer from DeviceIoControl here, just to try it locally and check if it makes any difference. And as per Chrome implementation, the buffer has to be FeatureReportByteLength + 1 bytes, so your latest attempt should have better chance to succeed.

> 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: https://chromium.googlesource.com/chromium/src/+/32352ad08ee673a4d43e8593ce988b224f6482d3/device/hid/hid_connection_win.cc#167 It doesn't differ much from what we do [in HIDAPI](https://github.com/libusb/hidapi/blob/ff67c77daddbd8e61ad3873ac16f8edc005f943f/windows/hid.c#L1291) (at least up to the point of `DeviceIoControl` call. The only difference I see is that we provide both inout and output buffers, but Chrome implementation only passes the output buffer to `DeviceIoControl`. @92half99 if you're up to it, you could try to remove the input buffer from `DeviceIoControl` [here](https://github.com/libusb/hidapi/blob/ff67c77daddbd8e61ad3873ac16f8edc005f943f/windows/hid.c#L1308), just to try it locally and check if it makes any difference. And as per Chrome implementation, the buffer has to be `FeatureReportByteLength` + 1 bytes, so your latest attempt should have better chance to succeed.
ghost commented 2024-12-18 06:27:34 -06:00 (Migrated from github.com)

@92half99 if you're up to it, you could try to remove the input buffer from DeviceIoControl here, just to try it locally and check if it makes any difference. And as per Chrome implementation, the buffer has to be FeatureReportByteLength + 1 bytes, so your latest attempt should have better chance to succeed.

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.

> @92half99 if you're up to it, you could try to remove the input buffer from `DeviceIoControl` [here](https://github.com/libusb/hidapi/blob/ff67c77daddbd8e61ad3873ac16f8edc005f943f/windows/hid.c#L1308), just to try it locally and check if it makes any difference. And as per Chrome implementation, the buffer has to be `FeatureReportByteLength` + 1 bytes, so your latest attempt should have better chance to succeed. 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.
ghost commented 2024-12-18 07:35:52 -06:00 (Migrated from github.com)

@Youw I tried it.
Here's the altered code.

	res = DeviceIoControl(dev->device_handle,
		report_type,
		NULL, 0,
		data, (DWORD) length,
		&bytes_returned, &ol);

Same output:

Receiving feature report with Report ID: 0x81
Failed to receive feature report. HIDAPI Error: Get Input/Feature Report DeviceIoControl: (0x00000057) The parameter is incorrect.
Received insufficient data for nvstatus.
nvstatus: 2
@Youw I tried it. Here's the altered code. ```c res = DeviceIoControl(dev->device_handle, report_type, NULL, 0, data, (DWORD) length, &bytes_returned, &ol); ``` Same output: ``` Receiving feature report with Report ID: 0x81 Failed to receive feature report. HIDAPI Error: Get Input/Feature Report DeviceIoControl: (0x00000057) The parameter is incorrect. Received insufficient data for nvstatus. nvstatus: 2 ```
Youw commented 2024-12-18 08:38:47 -06:00 (Migrated from github.com)

At this point, I no longer have suggestions. From what it looks - all should work.
If you discover anything else - feel free to share.

BTW: there is also an experimental windows backend: winrt. I suspect it should have the same behavior as win32 backend. If you're up to it - go try it as well.

At this point, I no longer have suggestions. From what it looks - all should work. If you discover anything else - feel free to share. BTW: there is also an experimental windows backend: [`winrt`](https://github.com/libusb/hidapi/tree/winrt). I suspect it should have the same behavior as win32 backend. If you're up to it - go try it as well.
ghost commented 2024-12-18 18:41:15 -06:00 (Migrated from github.com)

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 winrt build worked while the main build didn’t. If you could share your thoughts on this, I would greatly appreciate it.

Receiving feature report with Report ID: 0x81
Received 9 bytes: nvstatus: 0
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 [`winrt`](https://github.com/libusb/hidapi/tree/winrt) build worked while the [`main`](https://github.com/libusb/hidapi) build didn’t. If you could share your thoughts on this, I would greatly appreciate it. ``` Receiving feature report with Report ID: 0x81 Received 9 bytes: nvstatus: 0 ```
Youw commented 2024-12-19 04:33:35 -06:00 (Migrated from github.com)

Those are different backends. Something about the implementayion must be different. I'm not sure what, as it is propriatary MS implementation.

Those are different backends. Something about the implementayion must be different. I'm not sure what, as it is propriatary MS implementation.
ghost commented 2024-12-19 19:40:37 -06:00 (Migrated from github.com)

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.
code:

// #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:
winrt

Receiving feature report with Report ID: 0x81
Received 9 bytes: nvstatus: 0
Attempting ds5_nvlock...
Sending feature report (3 bytes): Send feature report failed. HIDAPI Error: hid_send_feature_report error: The parameter is incorrect.NVS Lock failed: sendFeatureReport error
ds5_nvlock result: failure
Receiving feature report with Report ID: 0x81
Received 9 bytes: nvstatus: 0

hidapi

Receiving feature report with Report ID: 0x81
Failed to receive feature report. HIDAPI Error: Get Input/Feature Report DeviceIoControl: (0x00000057) The parameter is incorrect.Received insufficient data for nvstatus.
nvstatus: 2
Attempting ds5_nvlock...
Sending feature report (3 bytes): Receiving feature report with Report ID: 0x83
Failed to receive feature report. HIDAPI Error: Get Input/Feature Report DeviceIoControl: (0x00000057) The parameter is incorrect.NVS Lock failed: receiveFeatureReport error
ds5_nvlock result: failure
Receiving feature report with Report ID: 0x81
Failed to receive feature report. HIDAPI Error: Get Input/Feature Report DeviceIoControl: (0x00000057) The parameter is incorrect.Received insufficient data for nvstatus.
nvstatus: 2
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. code: ```cpp // #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: winrt ``` Receiving feature report with Report ID: 0x81 Received 9 bytes: nvstatus: 0 Attempting ds5_nvlock... Sending feature report (3 bytes): Send feature report failed. HIDAPI Error: hid_send_feature_report error: The parameter is incorrect.NVS Lock failed: sendFeatureReport error ds5_nvlock result: failure Receiving feature report with Report ID: 0x81 Received 9 bytes: nvstatus: 0 ``` hidapi ``` Receiving feature report with Report ID: 0x81 Failed to receive feature report. HIDAPI Error: Get Input/Feature Report DeviceIoControl: (0x00000057) The parameter is incorrect.Received insufficient data for nvstatus. nvstatus: 2 Attempting ds5_nvlock... Sending feature report (3 bytes): Receiving feature report with Report ID: 0x83 Failed to receive feature report. HIDAPI Error: Get Input/Feature Report DeviceIoControl: (0x00000057) The parameter is incorrect.NVS Lock failed: receiveFeatureReport error ds5_nvlock result: failure Receiving feature report with Report ID: 0x81 Failed to receive feature report. HIDAPI Error: Get Input/Feature Report DeviceIoControl: (0x00000057) The parameter is incorrect.Received insufficient data for nvstatus. nvstatus: 2 ```
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-libusb/hidapi#712
No description provided.