Skip to content
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

Open
92half99 opened this issue Dec 9, 2024 · 23 comments
Labels
device-specific Issues/PRs related to a specific device question Information is requested Windows Related to Windows backend

Comments

@92half99
Copy link

92half99 commented Dec 9, 2024

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

@mcuee mcuee added Windows Related to Windows backend question Information is requested labels Dec 9, 2024
@mcuee
Copy link
Member

mcuee commented Dec 9, 2024

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

@92half99
Copy link
Author

92half99 commented Dec 9, 2024

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

@Youw Youw added the device-specific Issues/PRs related to a specific device label Dec 11, 2024
@Youw
Copy link
Member

Youw commented Dec 11, 2024

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

@92half99
Copy link
Author

@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
Copy link
Member

Youw commented Dec 11, 2024

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
Copy link
Contributor

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

@92half99
Copy link
Author

@JoergAtGithub Yes

@92half99
Copy link
Author

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

@Youw
Copy link
Member

Youw commented Dec 12, 2024

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.

@92half99
Copy link
Author

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

@92half99 92half99 reopened this Dec 13, 2024
@mcuee
Copy link
Member

mcuee commented Dec 14, 2024

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

@92half99
Copy link
Author

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
Copy link
Member

Youw commented Dec 17, 2024

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
Copy link
Member

Youw commented Dec 17, 2024

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

@Youw
Copy link
Member

Youw commented Dec 17, 2024

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.

@92half99
Copy link
Author

92half99 commented Dec 18, 2024

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.

@Youw
Copy link
Member

Youw commented Dec 18, 2024

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.

@92half99
Copy link
Author

92half99 commented Dec 18, 2024

@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
Copy link
Author

@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
Copy link
Member

Youw commented Dec 18, 2024

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.

@92half99
Copy link
Author

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

@Youw
Copy link
Member

Youw commented Dec 19, 2024

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

@92half99 92half99 reopened this Dec 20, 2024
@92half99
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
device-specific Issues/PRs related to a specific device question Information is requested Windows Related to Windows backend
Projects
None yet
Development

No branches or pull requests

4 participants