| | |
| | | files located at the root of the source distribution. |
| | | These files may also be found in the public source |
| | | code repository located at: |
| | | http://github.com/signal11/hidapi . |
| | | https://github.com/libusb/hidapi . |
| | | ********************************************************/ |
| | | #include "../../SDL_internal.h" |
| | | |
| | |
| | | #define MAX_STRING_WCHARS 0xFFF |
| | | |
| | | /*#define HIDAPI_USE_DDK*/ |
| | | |
| | | /* The timeout in milliseconds for waiting on WriteFile to |
| | | complete in hid_write. The longest observed time to do a output |
| | | report that we've seen is ~200-250ms so let's double that */ |
| | | #define HID_WRITE_TIMEOUT_MILLISECONDS 500 |
| | | |
| | | /* We will only enumerate devices that match these usages */ |
| | | #define USAGE_PAGE_GENERIC_DESKTOP 0x0001 |
| | | #define USAGE_JOYSTICK 0x0004 |
| | | #define USAGE_GAMEPAD 0x0005 |
| | | #define USAGE_MULTIAXISCONTROLLER 0x0008 |
| | | #define USB_VENDOR_VALVE 0x28de |
| | | |
| | | #ifdef __cplusplus |
| | | extern "C" { |
| | |
| | | BOOL read_pending; |
| | | char *read_buf; |
| | | OVERLAPPED ol; |
| | | OVERLAPPED write_ol; |
| | | }; |
| | | |
| | | static hid_device *new_hid_device() |
| | |
| | | dev->read_buf = NULL; |
| | | memset(&dev->ol, 0, sizeof(dev->ol)); |
| | | dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); |
| | | memset(&dev->write_ol, 0, sizeof(dev->write_ol)); |
| | | dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); |
| | | |
| | | return dev; |
| | | } |
| | |
| | | static void free_hid_device(hid_device *dev) |
| | | { |
| | | CloseHandle(dev->ol.hEvent); |
| | | CloseHandle(dev->write_ol.hEvent); |
| | | CloseHandle(dev->device_handle); |
| | | LocalFree(dev->last_error_str); |
| | | free(dev->read_buf); |
| | |
| | | return 0; |
| | | } |
| | | |
| | | int hid_blacklist(unsigned short vendor_id, unsigned short product_id) |
| | | { |
| | | size_t i; |
| | | static const struct { unsigned short vid; unsigned short pid; } known_bad[] = { |
| | | /* Causes deadlock when asking for device details... */ |
| | | { 0x1B1C, 0x1B3D }, /* Corsair Gaming keyboard */ |
| | | { 0x1532, 0x0109 }, /* Razer Lycosa Gaming keyboard */ |
| | | { 0x1532, 0x010B }, /* Razer Arctosa Gaming keyboard */ |
| | | { 0x045E, 0x0822 }, /* Microsoft Precision Mouse */ |
| | | |
| | | /* Turns into an Android controller when enumerated... */ |
| | | { 0x0738, 0x2217 } /* SPEEDLINK COMPETITION PRO */ |
| | | }; |
| | | |
| | | for (i = 0; i < (sizeof(known_bad)/sizeof(known_bad[0])); i++) { |
| | | if ((vendor_id == known_bad[i].vid) && (product_id == known_bad[i].pid)) { |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) |
| | | { |
| | | BOOL res; |
| | |
| | | SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; |
| | | HDEVINFO device_info_set = INVALID_HANDLE_VALUE; |
| | | int device_index = 0; |
| | | int i; |
| | | |
| | | if (hid_init() < 0) |
| | | return NULL; |
| | |
| | | goto cont; |
| | | } |
| | | |
| | | /* XInput devices don't get real HID reports and are better handled by the raw input driver */ |
| | | if (strstr(device_interface_detail_data->DevicePath, "&ig_") != NULL) { |
| | | goto cont; |
| | | } |
| | | |
| | | /* Make sure this device is of Setup Class "HIDClass" and has a |
| | | driver bound to it. */ |
| | | for (i = 0; ; i++) { |
| | | /* In the main HIDAPI tree this is a loop which will erroneously open |
| | | devices that aren't HID class. Please preserve this delta if we ever |
| | | update to take new changes */ |
| | | { |
| | | char driver_name[256]; |
| | | |
| | | /* Populate devinfo_data. This function will return failure |
| | | when there are no more interfaces left. */ |
| | | res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); |
| | | res = SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data); |
| | | |
| | | if (!res) |
| | | goto cont; |
| | | |
| | |
| | | /* See if there's a driver bound. */ |
| | | res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, |
| | | SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); |
| | | if (res) |
| | | break; |
| | | if (!res) |
| | | goto cont; |
| | | } |
| | | else |
| | | { |
| | | goto cont; |
| | | } |
| | | } |
| | | |
| | |
| | | if (write_handle == INVALID_HANDLE_VALUE) { |
| | | /* Unable to open the device. */ |
| | | //register_error(dev, "CreateFile"); |
| | | goto cont_close; |
| | | goto cont; |
| | | } |
| | | |
| | | |
| | |
| | | /* Check the VID/PID to see if we should add this |
| | | device to the enumeration list. */ |
| | | if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && |
| | | (product_id == 0x0 || attrib.ProductID == product_id)) { |
| | | (product_id == 0x0 || attrib.ProductID == product_id) && |
| | | !hid_blacklist(attrib.VendorID, attrib.ProductID)) { |
| | | |
| | | #define WSTR_LEN 512 |
| | | const char *str; |
| | |
| | | wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ |
| | | size_t len; |
| | | |
| | | /* Get the Usage Page and Usage for this device. */ |
| | | hidp_res = HidD_GetPreparsedData(write_handle, &pp_data); |
| | | if (hidp_res) { |
| | | nt_res = HidP_GetCaps(pp_data, &caps); |
| | | HidD_FreePreparsedData(pp_data); |
| | | if (nt_res != HIDP_STATUS_SUCCESS) { |
| | | goto cont_close; |
| | | } |
| | | } |
| | | else { |
| | | goto cont_close; |
| | | } |
| | | |
| | | /* SDL Modification: Ignore the device if it's not a gamepad. This limits compatibility |
| | | risk from devices that may respond poorly to our string queries below. */ |
| | | if (attrib.VendorID != USB_VENDOR_VALVE) { |
| | | if (caps.UsagePage != USAGE_PAGE_GENERIC_DESKTOP) { |
| | | goto cont_close; |
| | | } |
| | | if (caps.Usage != USAGE_JOYSTICK && caps.Usage != USAGE_GAMEPAD && caps.Usage != USAGE_MULTIAXISCONTROLLER) { |
| | | goto cont_close; |
| | | } |
| | | } |
| | | |
| | | /* VID/PID match. Create the record. */ |
| | | tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); |
| | | if (cur_dev) { |
| | |
| | | root = tmp; |
| | | } |
| | | cur_dev = tmp; |
| | | |
| | | /* Get the Usage Page and Usage for this device. */ |
| | | hidp_res = HidD_GetPreparsedData(write_handle, &pp_data); |
| | | if (hidp_res) { |
| | | nt_res = HidP_GetCaps(pp_data, &caps); |
| | | if (nt_res == HIDP_STATUS_SUCCESS) { |
| | | cur_dev->usage_page = caps.UsagePage; |
| | | cur_dev->usage = caps.Usage; |
| | | } |
| | | |
| | | HidD_FreePreparsedData(pp_data); |
| | | } |
| | | |
| | | /* Fill out the record */ |
| | | cur_dev->usage_page = caps.UsagePage; |
| | | cur_dev->usage = caps.Usage; |
| | | cur_dev->next = NULL; |
| | | str = device_interface_detail_data->DevicePath; |
| | | if (str) { |
| | |
| | | return -1; |
| | | } |
| | | |
| | | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) |
| | | static int hid_write_timeout(hid_device *dev, const unsigned char *data, size_t length, int milliseconds) |
| | | { |
| | | DWORD bytes_written; |
| | | BOOL res; |
| | | size_t stashed_length = length; |
| | | OVERLAPPED ol; |
| | | unsigned char *buf; |
| | | memset(&ol, 0, sizeof(ol)); |
| | | |
| | | #if 1 |
| | | /* If the application is writing to the device, it knows how much data to write. |
| | | * This matches the behavior on other platforms. It's also important when writing |
| | | * to Sony game controllers over Bluetooth, where there's a CRC at the end which |
| | | * must not be tampered with. |
| | | */ |
| | | buf = (unsigned char *) data; |
| | | #else |
| | | /* Make sure the right number of bytes are passed to WriteFile. Windows |
| | | expects the number of bytes which are in the _longest_ report (plus |
| | | one for the report number) bytes even if the data is a report |
| | |
| | | memset(buf + length, 0, dev->output_report_length - length); |
| | | length = dev->output_report_length; |
| | | } |
| | | #endif |
| | | if (length > 512) |
| | | { |
| | | return hid_write_output_report( dev, data, stashed_length ); |
| | | } |
| | | else |
| | | { |
| | | res = WriteFile( dev->device_handle, buf, ( DWORD ) length, NULL, &ol ); |
| | | res = WriteFile( dev->device_handle, buf, ( DWORD ) length, NULL, &dev->write_ol ); |
| | | if (!res) { |
| | | if (GetLastError() != ERROR_IO_PENDING) { |
| | | /* WriteFile() failed. Return error. */ |
| | |
| | | |
| | | /* Wait here until the write is done. This makes |
| | | hid_write() synchronous. */ |
| | | res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); |
| | | res = WaitForSingleObject(dev->write_ol.hEvent, milliseconds); |
| | | if (res != WAIT_OBJECT_0) |
| | | { |
| | | // There was a Timeout. |
| | | bytes_written = (DWORD) -1; |
| | | register_error(dev, "WriteFile/WaitForSingleObject Timeout"); |
| | | goto end_of_function; |
| | | } |
| | | |
| | | res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE/*F=don't_wait*/); |
| | | if (!res) { |
| | | /* The Write operation failed. */ |
| | | register_error(dev, "WriteFile"); |
| | |
| | | return bytes_written; |
| | | } |
| | | |
| | | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) |
| | | { |
| | | return hid_write_timeout(dev, data, length, HID_WRITE_TIMEOUT_MILLISECONDS); |
| | | } |
| | | |
| | | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) |
| | | { |
| | |
| | | return -1; |
| | | } |
| | | |
| | | /* bytes_returned does not include the first byte which contains the |
| | | report ID. The data buffer actually contains one more byte than |
| | | bytes_returned. */ |
| | | bytes_returned++; |
| | | |
| | | |
| | | return bytes_returned; |
| | | #endif |
| | | } |
| | |
| | | /*#define S11*/ |
| | | #define P32 |
| | | #ifdef S11 |
| | | unsigned short VendorID = 0xa0a0; |
| | | unsigned short VendorID = 0xa0a0; |
| | | unsigned short ProductID = 0x0001; |
| | | #endif |
| | | |
| | | #ifdef P32 |
| | | unsigned short VendorID = 0x04d8; |
| | | unsigned short VendorID = 0x04d8; |
| | | unsigned short ProductID = 0x3f; |
| | | #endif |
| | | |
| | | #ifdef PICPGM |
| | | unsigned short VendorID = 0x04d8; |
| | | unsigned short ProductID = 0x0033; |
| | | unsigned short VendorID = 0x04d8; |
| | | unsigned short ProductID = 0x0033; |
| | | #endif |
| | | |
| | | int __cdecl main(int argc, char* argv[]) |
| | | { |
| | | int res; |
| | | int i, res; |
| | | unsigned char buf[65]; |
| | | |
| | | UNREFERENCED_PARAMETER(argc); |
| | |
| | | printf("Unable to read()\n"); |
| | | |
| | | /* Print out the returned buffer. */ |
| | | for (int i = 0; i < 4; i++) |
| | | for (i = 0; i < 4; i++) |
| | | printf("buf[%d]: %d\n", i, buf[i]); |
| | | |
| | | return 0; |