diff options
Diffstat (limited to 'contrib/SDL-3.2.8/src/hidapi/netbsd')
| -rw-r--r-- | contrib/SDL-3.2.8/src/hidapi/netbsd/CMakeLists.txt | 35 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/hidapi/netbsd/README.md | 29 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/hidapi/netbsd/hid.c | 1173 |
3 files changed, 1237 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/hidapi/netbsd/CMakeLists.txt b/contrib/SDL-3.2.8/src/hidapi/netbsd/CMakeLists.txt new file mode 100644 index 0000000..3b3e4d0 --- /dev/null +++ b/contrib/SDL-3.2.8/src/hidapi/netbsd/CMakeLists.txt | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | cmake_minimum_required(VERSION 3.6.3...3.25 FATAL_ERROR) | ||
| 2 | |||
| 3 | add_library(hidapi_netbsd | ||
| 4 | ${HIDAPI_PUBLIC_HEADERS} | ||
| 5 | hid.c | ||
| 6 | ) | ||
| 7 | target_link_libraries(hidapi_netbsd PUBLIC hidapi_include) | ||
| 8 | |||
| 9 | find_package(Threads REQUIRED) | ||
| 10 | |||
| 11 | target_link_libraries(hidapi_netbsd PRIVATE Threads::Threads) | ||
| 12 | |||
| 13 | set_target_properties(hidapi_netbsd | ||
| 14 | PROPERTIES | ||
| 15 | EXPORT_NAME "netbsd" | ||
| 16 | OUTPUT_NAME "hidapi-netbsd" | ||
| 17 | VERSION ${PROJECT_VERSION} | ||
| 18 | SOVERSION ${PROJECT_VERSION_MAJOR} | ||
| 19 | PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}" | ||
| 20 | ) | ||
| 21 | |||
| 22 | # compatibility with find_package() | ||
| 23 | add_library(hidapi::netbsd ALIAS hidapi_netbsd) | ||
| 24 | # compatibility with raw library link | ||
| 25 | add_library(hidapi-netbsd ALIAS hidapi_netbsd) | ||
| 26 | |||
| 27 | if(HIDAPI_INSTALL_TARGETS) | ||
| 28 | install(TARGETS hidapi_netbsd EXPORT hidapi | ||
| 29 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" | ||
| 30 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" | ||
| 31 | PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hidapi" | ||
| 32 | ) | ||
| 33 | endif() | ||
| 34 | |||
| 35 | hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi-netbsd.pc.in") | ||
diff --git a/contrib/SDL-3.2.8/src/hidapi/netbsd/README.md b/contrib/SDL-3.2.8/src/hidapi/netbsd/README.md new file mode 100644 index 0000000..f1b12a0 --- /dev/null +++ b/contrib/SDL-3.2.8/src/hidapi/netbsd/README.md | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | Implementation Notes | ||
| 2 | -------------------- | ||
| 3 | NetBSD maps every `uhidev` device to one or more `uhid` | ||
| 4 | devices. Each `uhid` device only supports one report ID. | ||
| 5 | The parent device `uhidev` creates one `uhid` device per | ||
| 6 | report ID found in the hardware's report descriptor. | ||
| 7 | |||
| 8 | In the event there are no report ID(s) found within the | ||
| 9 | report descriptor, only one `uhid` device with a report ID | ||
| 10 | of `0` is created. | ||
| 11 | |||
| 12 | In order to remain compatible with existing `hidapi` APIs, | ||
| 13 | all the `uhid` devices created by the parent `uhidev` device | ||
| 14 | must be opened under the same `hid_device` instance to ensure | ||
| 15 | that we can route reports to their appropriate `uhid` device. | ||
| 16 | |||
| 17 | Internally the `uhid` driver will insert the report ID as | ||
| 18 | needed so we must also omit the report ID in any situation | ||
| 19 | where the `hidapi` API expects it to be included in the | ||
| 20 | report data stream. | ||
| 21 | |||
| 22 | Given the design of `uhid`, it must be augmented with extra | ||
| 23 | platform specific APIs to ensure that the exact relationship | ||
| 24 | between `uhidev` devices and `uhid` devices can be determined. | ||
| 25 | |||
| 26 | The NetBSD implementation does this via the `drvctl` kernel | ||
| 27 | driver. At present there is no known way to do this on OpenBSD | ||
| 28 | for a `uhid` implementation to be at the same level as the | ||
| 29 | NetBSD one. | ||
diff --git a/contrib/SDL-3.2.8/src/hidapi/netbsd/hid.c b/contrib/SDL-3.2.8/src/hidapi/netbsd/hid.c new file mode 100644 index 0000000..82f34d4 --- /dev/null +++ b/contrib/SDL-3.2.8/src/hidapi/netbsd/hid.c | |||
| @@ -0,0 +1,1173 @@ | |||
| 1 | /******************************************************* | ||
| 2 | HIDAPI - Multi-Platform library for | ||
| 3 | communication with HID devices. | ||
| 4 | |||
| 5 | James Buren | ||
| 6 | libusb/hidapi Team | ||
| 7 | |||
| 8 | Copyright 2023, All Rights Reserved. | ||
| 9 | |||
| 10 | At the discretion of the user of this library, | ||
| 11 | this software may be licensed under the terms of the | ||
| 12 | GNU General Public License v3, a BSD-Style license, or the | ||
| 13 | original HIDAPI license as outlined in the LICENSE.txt, | ||
| 14 | LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt | ||
| 15 | files located at the root of the source distribution. | ||
| 16 | These files may also be found in the public source | ||
| 17 | code repository located at: | ||
| 18 | https://github.com/libusb/hidapi . | ||
| 19 | ********************************************************/ | ||
| 20 | |||
| 21 | /* C */ | ||
| 22 | #include <stdlib.h> | ||
| 23 | #include <stdarg.h> | ||
| 24 | #include <string.h> | ||
| 25 | #include <locale.h> | ||
| 26 | #include <ctype.h> | ||
| 27 | #include <errno.h> | ||
| 28 | |||
| 29 | /* Unix */ | ||
| 30 | #include <unistd.h> | ||
| 31 | #include <fcntl.h> | ||
| 32 | #include <iconv.h> | ||
| 33 | #include <poll.h> | ||
| 34 | |||
| 35 | /* NetBSD */ | ||
| 36 | #include <sys/drvctlio.h> | ||
| 37 | #include <dev/usb/usb.h> | ||
| 38 | #include <dev/usb/usbhid.h> | ||
| 39 | |||
| 40 | #include "../hidapi/hidapi.h" | ||
| 41 | |||
| 42 | #define HIDAPI_MAX_CHILD_DEVICES 256 | ||
| 43 | |||
| 44 | struct hid_device_ { | ||
| 45 | int device_handle; | ||
| 46 | int blocking; | ||
| 47 | wchar_t *last_error_str; | ||
| 48 | struct hid_device_info *device_info; | ||
| 49 | size_t poll_handles_length; | ||
| 50 | struct pollfd poll_handles[256]; | ||
| 51 | int report_handles[256]; | ||
| 52 | char path[USB_MAX_DEVNAMELEN]; | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct hid_enumerate_data { | ||
| 56 | struct hid_device_info *root; | ||
| 57 | struct hid_device_info *end; | ||
| 58 | int drvctl; | ||
| 59 | uint16_t vendor_id; | ||
| 60 | uint16_t product_id; | ||
| 61 | }; | ||
| 62 | |||
| 63 | typedef void (*enumerate_devices_callback) (const struct usb_device_info *, void *); | ||
| 64 | |||
| 65 | static wchar_t *last_global_error_str = NULL; | ||
| 66 | |||
| 67 | /* The caller must free the returned string with free(). */ | ||
| 68 | static wchar_t *utf8_to_wchar_t(const char *utf8) | ||
| 69 | { | ||
| 70 | wchar_t *ret = NULL; | ||
| 71 | |||
| 72 | if (utf8) { | ||
| 73 | size_t wlen = mbstowcs(NULL, utf8, 0); | ||
| 74 | if ((size_t) -1 == wlen) { | ||
| 75 | return wcsdup(L""); | ||
| 76 | } | ||
| 77 | ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t)); | ||
| 78 | if (ret == NULL) { | ||
| 79 | /* as much as we can do at this point */ | ||
| 80 | return NULL; | ||
| 81 | } | ||
| 82 | mbstowcs(ret, utf8, wlen+1); | ||
| 83 | ret[wlen] = 0x0000; | ||
| 84 | } | ||
| 85 | |||
| 86 | return ret; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* Makes a copy of the given error message (and decoded according to the | ||
| 90 | * currently locale) into the wide string pointer pointed by error_str. | ||
| 91 | * The last stored error string is freed. | ||
| 92 | * Use register_error_str(NULL) to free the error message completely. */ | ||
| 93 | static void register_error_str(wchar_t **error_str, const char *msg) | ||
| 94 | { | ||
| 95 | free(*error_str); | ||
| 96 | *error_str = utf8_to_wchar_t(msg); | ||
| 97 | } | ||
| 98 | |||
| 99 | /* Semilar to register_error_str, but allows passing a format string with va_list args into this function. */ | ||
| 100 | static void register_error_str_vformat(wchar_t **error_str, const char *format, va_list args) | ||
| 101 | { | ||
| 102 | char msg[256]; | ||
| 103 | vsnprintf(msg, sizeof(msg), format, args); | ||
| 104 | |||
| 105 | register_error_str(error_str, msg); | ||
| 106 | } | ||
| 107 | |||
| 108 | /* Set the last global error to be reported by hid_error(NULL). | ||
| 109 | * The given error message will be copied (and decoded according to the | ||
| 110 | * currently locale, so do not pass in string constants). | ||
| 111 | * The last stored global error message is freed. | ||
| 112 | * Use register_global_error(NULL) to indicate "no error". */ | ||
| 113 | static void register_global_error(const char *msg) | ||
| 114 | { | ||
| 115 | register_error_str(&last_global_error_str, msg); | ||
| 116 | } | ||
| 117 | |||
| 118 | /* Similar to register_global_error, but allows passing a format string into this function. */ | ||
| 119 | static void register_global_error_format(const char *format, ...) | ||
| 120 | { | ||
| 121 | va_list args; | ||
| 122 | va_start(args, format); | ||
| 123 | register_error_str_vformat(&last_global_error_str, format, args); | ||
| 124 | va_end(args); | ||
| 125 | } | ||
| 126 | |||
| 127 | /* Set the last error for a device to be reported by hid_error(dev). | ||
| 128 | * The given error message will be copied (and decoded according to the | ||
| 129 | * currently locale, so do not pass in string constants). | ||
| 130 | * The last stored device error message is freed. | ||
| 131 | * Use register_device_error(dev, NULL) to indicate "no error". */ | ||
| 132 | static void register_device_error(hid_device *dev, const char *msg) | ||
| 133 | { | ||
| 134 | register_error_str(&dev->last_error_str, msg); | ||
| 135 | } | ||
| 136 | |||
| 137 | /* Similar to register_device_error, but you can pass a format string into this function. */ | ||
| 138 | static void register_device_error_format(hid_device *dev, const char *format, ...) | ||
| 139 | { | ||
| 140 | va_list args; | ||
| 141 | va_start(args, format); | ||
| 142 | register_error_str_vformat(&dev->last_error_str, format, args); | ||
| 143 | va_end(args); | ||
| 144 | } | ||
| 145 | |||
| 146 | |||
| 147 | /* | ||
| 148 | * Gets the size of the HID item at the given position | ||
| 149 | * Returns 1 if successful, 0 if an invalid key | ||
| 150 | * Sets data_len and key_size when successful | ||
| 151 | */ | ||
| 152 | static int get_hid_item_size(const uint8_t *report_descriptor, uint32_t size, unsigned int pos, int *data_len, int *key_size) | ||
| 153 | { | ||
| 154 | int key = report_descriptor[pos]; | ||
| 155 | int size_code; | ||
| 156 | |||
| 157 | /* | ||
| 158 | * This is a Long Item. The next byte contains the | ||
| 159 | * length of the data section (value) for this key. | ||
| 160 | * See the HID specification, version 1.11, section | ||
| 161 | * 6.2.2.3, titled "Long Items." | ||
| 162 | */ | ||
| 163 | if ((key & 0xf0) == 0xf0) { | ||
| 164 | if (pos + 1 < size) | ||
| 165 | { | ||
| 166 | *data_len = report_descriptor[pos + 1]; | ||
| 167 | *key_size = 3; | ||
| 168 | return 1; | ||
| 169 | } | ||
| 170 | *data_len = 0; /* malformed report */ | ||
| 171 | *key_size = 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | /* | ||
| 175 | * This is a Short Item. The bottom two bits of the | ||
| 176 | * key contain the size code for the data section | ||
| 177 | * (value) for this key. Refer to the HID | ||
| 178 | * specification, version 1.11, section 6.2.2.2, | ||
| 179 | * titled "Short Items." | ||
| 180 | */ | ||
| 181 | size_code = key & 0x3; | ||
| 182 | switch (size_code) { | ||
| 183 | case 0: | ||
| 184 | case 1: | ||
| 185 | case 2: | ||
| 186 | *data_len = size_code; | ||
| 187 | *key_size = 1; | ||
| 188 | return 1; | ||
| 189 | case 3: | ||
| 190 | *data_len = 4; | ||
| 191 | *key_size = 1; | ||
| 192 | return 1; | ||
| 193 | default: | ||
| 194 | /* Can't ever happen since size_code is & 0x3 */ | ||
| 195 | *data_len = 0; | ||
| 196 | *key_size = 0; | ||
| 197 | break; | ||
| 198 | }; | ||
| 199 | |||
| 200 | /* malformed report */ | ||
| 201 | return 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | /* | ||
| 205 | * Get bytes from a HID Report Descriptor. | ||
| 206 | * Only call with a num_bytes of 0, 1, 2, or 4. | ||
| 207 | */ | ||
| 208 | static uint32_t get_hid_report_bytes(const uint8_t *rpt, size_t len, size_t num_bytes, size_t cur) | ||
| 209 | { | ||
| 210 | /* Return if there aren't enough bytes. */ | ||
| 211 | if (cur + num_bytes >= len) | ||
| 212 | return 0; | ||
| 213 | |||
| 214 | if (num_bytes == 0) | ||
| 215 | return 0; | ||
| 216 | else if (num_bytes == 1) | ||
| 217 | return rpt[cur + 1]; | ||
| 218 | else if (num_bytes == 2) | ||
| 219 | return (rpt[cur + 2] * 256 + rpt[cur + 1]); | ||
| 220 | else if (num_bytes == 4) | ||
| 221 | return ( | ||
| 222 | rpt[cur + 4] * 0x01000000 + | ||
| 223 | rpt[cur + 3] * 0x00010000 + | ||
| 224 | rpt[cur + 2] * 0x00000100 + | ||
| 225 | rpt[cur + 1] * 0x00000001 | ||
| 226 | ); | ||
| 227 | else | ||
| 228 | return 0; | ||
| 229 | } | ||
| 230 | |||
| 231 | /* | ||
| 232 | * Iterates until the end of a Collection. | ||
| 233 | * Assumes that *pos is exactly at the beginning of a Collection. | ||
| 234 | * Skips all nested Collection, i.e. iterates until the end of current level Collection. | ||
| 235 | * | ||
| 236 | * The return value is non-0 when an end of current Collection is found, | ||
| 237 | * 0 when error is occurred (broken Descriptor, end of a Collection is found before its begin, | ||
| 238 | * or no Collection is found at all). | ||
| 239 | */ | ||
| 240 | static int hid_iterate_over_collection(const uint8_t *report_descriptor, uint32_t size, unsigned int *pos, int *data_len, int *key_size) | ||
| 241 | { | ||
| 242 | int collection_level = 0; | ||
| 243 | |||
| 244 | while (*pos < size) { | ||
| 245 | int key = report_descriptor[*pos]; | ||
| 246 | int key_cmd = key & 0xfc; | ||
| 247 | |||
| 248 | /* Determine data_len and key_size */ | ||
| 249 | if (!get_hid_item_size(report_descriptor, size, *pos, data_len, key_size)) | ||
| 250 | return 0; /* malformed report */ | ||
| 251 | |||
| 252 | switch (key_cmd) { | ||
| 253 | case 0xa0: /* Collection 6.2.2.4 (Main) */ | ||
| 254 | collection_level++; | ||
| 255 | break; | ||
| 256 | case 0xc0: /* End Collection 6.2.2.4 (Main) */ | ||
| 257 | collection_level--; | ||
| 258 | break; | ||
| 259 | } | ||
| 260 | |||
| 261 | if (collection_level < 0) { | ||
| 262 | /* Broken descriptor or someone is using this function wrong, | ||
| 263 | * i.e. should be called exactly at the collection start */ | ||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | |||
| 267 | if (collection_level == 0) { | ||
| 268 | /* Found it! | ||
| 269 | * Also possible when called not at the collection start, but should not happen if used correctly */ | ||
| 270 | return 1; | ||
| 271 | } | ||
| 272 | |||
| 273 | *pos += *data_len + *key_size; | ||
| 274 | } | ||
| 275 | |||
| 276 | return 0; /* Did not find the end of a Collection */ | ||
| 277 | } | ||
| 278 | |||
| 279 | struct hid_usage_iterator { | ||
| 280 | unsigned int pos; | ||
| 281 | int usage_page_found; | ||
| 282 | unsigned short usage_page; | ||
| 283 | }; | ||
| 284 | |||
| 285 | /* | ||
| 286 | * Retrieves the device's Usage Page and Usage from the report descriptor. | ||
| 287 | * The algorithm returns the current Usage Page/Usage pair whenever a new | ||
| 288 | * Collection is found and a Usage Local Item is currently in scope. | ||
| 289 | * Usage Local Items are consumed by each Main Item (See. 6.2.2.8). | ||
| 290 | * The algorithm should give similar results as Apple's: | ||
| 291 | * https://developer.apple.com/documentation/iokit/kiohiddeviceusagepairskey?language=objc | ||
| 292 | * Physical Collections are also matched (macOS does the same). | ||
| 293 | * | ||
| 294 | * This function can be called repeatedly until it returns non-0 | ||
| 295 | * Usage is found. pos is the starting point (initially 0) and will be updated | ||
| 296 | * to the next search position. | ||
| 297 | * | ||
| 298 | * The return value is 0 when a pair is found. | ||
| 299 | * 1 when finished processing descriptor. | ||
| 300 | * -1 on a malformed report. | ||
| 301 | */ | ||
| 302 | static int get_next_hid_usage(const uint8_t *report_descriptor, uint32_t size, struct hid_usage_iterator *ctx, unsigned short *usage_page, unsigned short *usage) | ||
| 303 | { | ||
| 304 | int data_len, key_size; | ||
| 305 | int initial = ctx->pos == 0; /* Used to handle case where no top-level application collection is defined */ | ||
| 306 | |||
| 307 | int usage_found = 0; | ||
| 308 | |||
| 309 | while (ctx->pos < size) { | ||
| 310 | int key = report_descriptor[ctx->pos]; | ||
| 311 | int key_cmd = key & 0xfc; | ||
| 312 | |||
| 313 | /* Determine data_len and key_size */ | ||
| 314 | if (!get_hid_item_size(report_descriptor, size, ctx->pos, &data_len, &key_size)) | ||
| 315 | return -1; /* malformed report */ | ||
| 316 | |||
| 317 | switch (key_cmd) { | ||
| 318 | case 0x4: /* Usage Page 6.2.2.7 (Global) */ | ||
| 319 | ctx->usage_page = get_hid_report_bytes(report_descriptor, size, data_len, ctx->pos); | ||
| 320 | ctx->usage_page_found = 1; | ||
| 321 | break; | ||
| 322 | |||
| 323 | case 0x8: /* Usage 6.2.2.8 (Local) */ | ||
| 324 | if (data_len == 4) { /* Usages 5.5 / Usage Page 6.2.2.7 */ | ||
| 325 | ctx->usage_page = get_hid_report_bytes(report_descriptor, size, 2, ctx->pos + 2); | ||
| 326 | ctx->usage_page_found = 1; | ||
| 327 | *usage = get_hid_report_bytes(report_descriptor, size, 2, ctx->pos); | ||
| 328 | usage_found = 1; | ||
| 329 | } | ||
| 330 | else { | ||
| 331 | *usage = get_hid_report_bytes(report_descriptor, size, data_len, ctx->pos); | ||
| 332 | usage_found = 1; | ||
| 333 | } | ||
| 334 | break; | ||
| 335 | |||
| 336 | case 0xa0: /* Collection 6.2.2.4 (Main) */ | ||
| 337 | if (!hid_iterate_over_collection(report_descriptor, size, &ctx->pos, &data_len, &key_size)) { | ||
| 338 | return -1; | ||
| 339 | } | ||
| 340 | |||
| 341 | /* A pair is valid - to be reported when Collection is found */ | ||
| 342 | if (usage_found && ctx->usage_page_found) { | ||
| 343 | *usage_page = ctx->usage_page; | ||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 347 | break; | ||
| 348 | } | ||
| 349 | |||
| 350 | /* Skip over this key and its associated data */ | ||
| 351 | ctx->pos += data_len + key_size; | ||
| 352 | } | ||
| 353 | |||
| 354 | /* If no top-level application collection is found and usage page/usage pair is found, pair is valid | ||
| 355 | https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/top-level-collections */ | ||
| 356 | if (initial && usage_found && ctx->usage_page_found) { | ||
| 357 | *usage_page = ctx->usage_page; | ||
| 358 | return 0; /* success */ | ||
| 359 | } | ||
| 360 | |||
| 361 | return 1; /* finished processing */ | ||
| 362 | } | ||
| 363 | |||
| 364 | static struct hid_device_info *create_device_info(const struct usb_device_info *udi, const char *path, const struct usb_ctl_report_desc *ucrd) | ||
| 365 | { | ||
| 366 | struct hid_device_info *root; | ||
| 367 | struct hid_device_info *end; | ||
| 368 | |||
| 369 | root = (struct hid_device_info *) calloc(1, sizeof(struct hid_device_info)); | ||
| 370 | if (!root) | ||
| 371 | return NULL; | ||
| 372 | |||
| 373 | end = root; | ||
| 374 | |||
| 375 | /* Path */ | ||
| 376 | end->path = (path) ? strdup(path) : NULL; | ||
| 377 | |||
| 378 | /* Vendor Id */ | ||
| 379 | end->vendor_id = udi->udi_vendorNo; | ||
| 380 | |||
| 381 | /* Product Id */ | ||
| 382 | end->product_id = udi->udi_productNo; | ||
| 383 | |||
| 384 | /* Serial Number */ | ||
| 385 | end->serial_number = utf8_to_wchar_t(udi->udi_serial); | ||
| 386 | |||
| 387 | /* Release Number */ | ||
| 388 | end->release_number = udi->udi_releaseNo; | ||
| 389 | |||
| 390 | /* Manufacturer String */ | ||
| 391 | end->manufacturer_string = utf8_to_wchar_t(udi->udi_vendor); | ||
| 392 | |||
| 393 | /* Product String */ | ||
| 394 | end->product_string = utf8_to_wchar_t(udi->udi_product); | ||
| 395 | |||
| 396 | /* Usage Page */ | ||
| 397 | end->usage_page = 0; | ||
| 398 | |||
| 399 | /* Usage */ | ||
| 400 | end->usage = 0; | ||
| 401 | |||
| 402 | /* Interface Number */ | ||
| 403 | end->interface_number = -1; | ||
| 404 | |||
| 405 | /* Next Device Info */ | ||
| 406 | end->next = NULL; | ||
| 407 | |||
| 408 | /* Bus Type */ | ||
| 409 | end->bus_type = HID_API_BUS_USB; | ||
| 410 | |||
| 411 | if (ucrd) { | ||
| 412 | uint16_t page; | ||
| 413 | uint16_t usage; | ||
| 414 | struct hid_usage_iterator usage_iterator; | ||
| 415 | |||
| 416 | page = usage = 0; | ||
| 417 | memset(&usage_iterator, 0, sizeof(usage_iterator)); | ||
| 418 | |||
| 419 | /* | ||
| 420 | * Parse the first usage and usage page | ||
| 421 | * out of the report descriptor. | ||
| 422 | */ | ||
| 423 | if (get_next_hid_usage(ucrd->ucrd_data, ucrd->ucrd_size, &usage_iterator, &page, &usage) == 0) { | ||
| 424 | end->usage_page = page; | ||
| 425 | end->usage = usage; | ||
| 426 | } | ||
| 427 | |||
| 428 | /* | ||
| 429 | * Parse any additional usage and usage pages | ||
| 430 | * out of the report descriptor. | ||
| 431 | */ | ||
| 432 | while (get_next_hid_usage(ucrd->ucrd_data, ucrd->ucrd_size, &usage_iterator, &page, &usage) == 0) { | ||
| 433 | /* Create new record for additional usage pairs */ | ||
| 434 | struct hid_device_info *node = (struct hid_device_info *) calloc(1, sizeof(struct hid_device_info)); | ||
| 435 | |||
| 436 | if (!node) | ||
| 437 | continue; | ||
| 438 | |||
| 439 | /* Update fields */ | ||
| 440 | node->path = (end->path) ? strdup(end->path) : NULL; | ||
| 441 | node->vendor_id = end->vendor_id; | ||
| 442 | node->product_id = end->product_id; | ||
| 443 | node->serial_number = (end->serial_number) ? wcsdup(end->serial_number) : NULL; | ||
| 444 | node->release_number = end->release_number; | ||
| 445 | node->manufacturer_string = (end->manufacturer_string) ? wcsdup(end->manufacturer_string) : NULL; | ||
| 446 | node->product_string = (end->product_string) ? wcsdup(end->product_string) : NULL; | ||
| 447 | node->usage_page = page; | ||
| 448 | node->usage = usage; | ||
| 449 | node->interface_number = end->interface_number; | ||
| 450 | node->next = NULL; | ||
| 451 | node->bus_type = end->bus_type; | ||
| 452 | |||
| 453 | /* Insert node */ | ||
| 454 | end->next = node; | ||
| 455 | end = node; | ||
| 456 | } | ||
| 457 | } | ||
| 458 | |||
| 459 | return root; | ||
| 460 | } | ||
| 461 | |||
| 462 | static int is_usb_controller(const char *s) | ||
| 463 | { | ||
| 464 | return (!strncmp(s, "usb", 3) && isdigit((int) s[3])); | ||
| 465 | } | ||
| 466 | |||
| 467 | static int is_uhid_parent_device(const char *s) | ||
| 468 | { | ||
| 469 | return (!strncmp(s, "uhidev", 6) && isdigit((int) s[6])); | ||
| 470 | } | ||
| 471 | |||
| 472 | static int is_uhid_device(const char *s) | ||
| 473 | { | ||
| 474 | return (!strncmp(s, "uhid", 4) && isdigit((int) s[4])); | ||
| 475 | } | ||
| 476 | |||
| 477 | static void walk_device_tree(int drvctl, const char *dev, int depth, char arr[static HIDAPI_MAX_CHILD_DEVICES][USB_MAX_DEVNAMELEN], size_t *len, int (*cmp) (const char *)) | ||
| 478 | { | ||
| 479 | int res; | ||
| 480 | char childname[HIDAPI_MAX_CHILD_DEVICES][USB_MAX_DEVNAMELEN]; | ||
| 481 | struct devlistargs dla; | ||
| 482 | |||
| 483 | if (depth && (!dev || !*dev)) | ||
| 484 | return; | ||
| 485 | |||
| 486 | if (cmp(dev) && *len < HIDAPI_MAX_CHILD_DEVICES) | ||
| 487 | strlcpy(arr[(*len)++], dev, sizeof(*arr)); | ||
| 488 | |||
| 489 | strlcpy(dla.l_devname, dev, sizeof(dla.l_devname)); | ||
| 490 | dla.l_childname = childname; | ||
| 491 | dla.l_children = HIDAPI_MAX_CHILD_DEVICES; | ||
| 492 | |||
| 493 | res = ioctl(drvctl, DRVLISTDEV, &dla); | ||
| 494 | if (res == -1) | ||
| 495 | return; | ||
| 496 | |||
| 497 | /* | ||
| 498 | * DO NOT CHANGE THIS. This is a fail-safe check | ||
| 499 | * for the unlikely event that a parent device has | ||
| 500 | * more than HIDAPI_MAX_CHILD_DEVICES child devices | ||
| 501 | * to prevent iterating over uninitialized data. | ||
| 502 | */ | ||
| 503 | if (dla.l_children > HIDAPI_MAX_CHILD_DEVICES) | ||
| 504 | return; | ||
| 505 | |||
| 506 | for (size_t i = 0; i < dla.l_children; i++) | ||
| 507 | walk_device_tree(drvctl, dla.l_childname[i], depth + 1, arr, len, cmp); | ||
| 508 | } | ||
| 509 | |||
| 510 | static void enumerate_usb_devices(int bus, uint8_t addr, enumerate_devices_callback func, void *data) | ||
| 511 | { | ||
| 512 | int res; | ||
| 513 | struct usb_device_info udi; | ||
| 514 | |||
| 515 | udi.udi_addr = addr; | ||
| 516 | |||
| 517 | res = ioctl(bus, USB_DEVICEINFO, &udi); | ||
| 518 | if (res == -1) | ||
| 519 | return; | ||
| 520 | |||
| 521 | for (int port = 0; port < udi.udi_nports; port++) { | ||
| 522 | addr = udi.udi_ports[port]; | ||
| 523 | if (addr >= USB_MAX_DEVICES) | ||
| 524 | continue; | ||
| 525 | |||
| 526 | enumerate_usb_devices(bus, addr, func, data); | ||
| 527 | } | ||
| 528 | |||
| 529 | func(&udi, data); | ||
| 530 | } | ||
| 531 | |||
| 532 | static void hid_enumerate_callback(const struct usb_device_info *udi, void *data) | ||
| 533 | { | ||
| 534 | struct hid_enumerate_data *hed; | ||
| 535 | |||
| 536 | hed = (struct hid_enumerate_data *) data; | ||
| 537 | |||
| 538 | if (hed->vendor_id != 0 && hed->vendor_id != udi->udi_vendorNo) | ||
| 539 | return; | ||
| 540 | |||
| 541 | if (hed->product_id != 0 && hed->product_id != udi->udi_productNo) | ||
| 542 | return; | ||
| 543 | |||
| 544 | for (size_t i = 0; i < USB_MAX_DEVNAMES; i++) { | ||
| 545 | const char *parent_dev; | ||
| 546 | char arr[HIDAPI_MAX_CHILD_DEVICES][USB_MAX_DEVNAMELEN]; | ||
| 547 | size_t len; | ||
| 548 | const char *child_dev; | ||
| 549 | char devpath[USB_MAX_DEVNAMELEN]; | ||
| 550 | int uhid; | ||
| 551 | struct usb_ctl_report_desc ucrd; | ||
| 552 | int use_ucrd; | ||
| 553 | struct hid_device_info *node; | ||
| 554 | |||
| 555 | parent_dev = udi->udi_devnames[i]; | ||
| 556 | if (!is_uhid_parent_device(parent_dev)) | ||
| 557 | continue; | ||
| 558 | |||
| 559 | len = 0; | ||
| 560 | walk_device_tree(hed->drvctl, parent_dev, 0, arr, &len, is_uhid_device); | ||
| 561 | |||
| 562 | if (len == 0) | ||
| 563 | continue; | ||
| 564 | |||
| 565 | child_dev = arr[0]; | ||
| 566 | strlcpy(devpath, "/dev/", sizeof(devpath)); | ||
| 567 | strlcat(devpath, child_dev, sizeof(devpath)); | ||
| 568 | |||
| 569 | uhid = open(devpath, O_RDONLY | O_CLOEXEC); | ||
| 570 | if (uhid >= 0) { | ||
| 571 | use_ucrd = (ioctl(uhid, USB_GET_REPORT_DESC, &ucrd) != -1); | ||
| 572 | close(uhid); | ||
| 573 | } else { | ||
| 574 | use_ucrd = 0; | ||
| 575 | } | ||
| 576 | |||
| 577 | node = create_device_info(udi, parent_dev, (use_ucrd) ? &ucrd : NULL); | ||
| 578 | if (!node) | ||
| 579 | continue; | ||
| 580 | |||
| 581 | if (!hed->root) { | ||
| 582 | hed->root = node; | ||
| 583 | hed->end = node; | ||
| 584 | } else { | ||
| 585 | hed->end->next = node; | ||
| 586 | hed->end = node; | ||
| 587 | } | ||
| 588 | |||
| 589 | while (hed->end->next) | ||
| 590 | hed->end = hed->end->next; | ||
| 591 | } | ||
| 592 | } | ||
| 593 | |||
| 594 | static int set_report(hid_device *dev, const uint8_t *data, size_t length, int report) | ||
| 595 | { | ||
| 596 | int res; | ||
| 597 | int device_handle; | ||
| 598 | struct usb_ctl_report ucr; | ||
| 599 | |||
| 600 | if (length < 1) { | ||
| 601 | register_device_error(dev, "report must be greater than 1 byte"); | ||
| 602 | return -1; | ||
| 603 | } | ||
| 604 | |||
| 605 | device_handle = dev->report_handles[*data]; | ||
| 606 | if (device_handle < 0) { | ||
| 607 | register_device_error_format(dev, "unsupported report id: %hhu", *data); | ||
| 608 | return -1; | ||
| 609 | } | ||
| 610 | |||
| 611 | length--; | ||
| 612 | data++; | ||
| 613 | |||
| 614 | if (length > sizeof(ucr.ucr_data)) { | ||
| 615 | register_device_error_format(dev, "report must be less than or equal to %zu bytes", sizeof(ucr.ucr_data)); | ||
| 616 | return -1; | ||
| 617 | } | ||
| 618 | |||
| 619 | ucr.ucr_report = report; | ||
| 620 | memcpy(ucr.ucr_data, data, length); | ||
| 621 | |||
| 622 | res = ioctl(device_handle, USB_SET_REPORT, &ucr); | ||
| 623 | if (res == -1) { | ||
| 624 | register_device_error_format(dev, "ioctl (USB_SET_REPORT): %s", strerror(errno)); | ||
| 625 | return -1; | ||
| 626 | } | ||
| 627 | |||
| 628 | return (int) (length + 1); | ||
| 629 | } | ||
| 630 | |||
| 631 | static int get_report(hid_device *dev, uint8_t *data, size_t length, int report) | ||
| 632 | { | ||
| 633 | int res; | ||
| 634 | int device_handle; | ||
| 635 | struct usb_ctl_report ucr; | ||
| 636 | |||
| 637 | if (length < 1) { | ||
| 638 | register_device_error(dev, "report must be greater than 1 byte"); | ||
| 639 | return -1; | ||
| 640 | } | ||
| 641 | |||
| 642 | device_handle = dev->report_handles[*data]; | ||
| 643 | if (device_handle < 0) { | ||
| 644 | register_device_error_format(dev, "unsupported report id: %hhu", *data); | ||
| 645 | return -1; | ||
| 646 | } | ||
| 647 | |||
| 648 | length--; | ||
| 649 | data++; | ||
| 650 | |||
| 651 | if (length > sizeof(ucr.ucr_data)) { | ||
| 652 | length = sizeof(ucr.ucr_data); | ||
| 653 | } | ||
| 654 | |||
| 655 | ucr.ucr_report = report; | ||
| 656 | |||
| 657 | res = ioctl(device_handle, USB_GET_REPORT, &ucr); | ||
| 658 | if (res == -1) { | ||
| 659 | register_device_error_format(dev, "ioctl (USB_GET_REPORT): %s", strerror(errno)); | ||
| 660 | return -1; | ||
| 661 | } | ||
| 662 | |||
| 663 | memcpy(data, ucr.ucr_data, length); | ||
| 664 | |||
| 665 | return (int) (length + 1); | ||
| 666 | } | ||
| 667 | |||
| 668 | int HID_API_EXPORT HID_API_CALL hid_init(void) | ||
| 669 | { | ||
| 670 | /* indicate no error */ | ||
| 671 | register_global_error(NULL); | ||
| 672 | |||
| 673 | return 0; | ||
| 674 | } | ||
| 675 | |||
| 676 | int HID_API_EXPORT HID_API_CALL hid_exit(void) | ||
| 677 | { | ||
| 678 | /* Free global error message */ | ||
| 679 | register_global_error(NULL); | ||
| 680 | |||
| 681 | return 0; | ||
| 682 | } | ||
| 683 | |||
| 684 | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) | ||
| 685 | { | ||
| 686 | int res; | ||
| 687 | int drvctl; | ||
| 688 | char arr[HIDAPI_MAX_CHILD_DEVICES][USB_MAX_DEVNAMELEN]; | ||
| 689 | size_t len; | ||
| 690 | struct hid_enumerate_data hed; | ||
| 691 | |||
| 692 | res = hid_init(); | ||
| 693 | if (res == -1) | ||
| 694 | return NULL; | ||
| 695 | |||
| 696 | drvctl = open(DRVCTLDEV, O_RDONLY | O_CLOEXEC); | ||
| 697 | if (drvctl == -1) { | ||
| 698 | register_global_error_format("failed to open drvctl: %s", strerror(errno)); | ||
| 699 | return NULL; | ||
| 700 | } | ||
| 701 | |||
| 702 | len = 0; | ||
| 703 | walk_device_tree(drvctl, "", 0, arr, &len, is_usb_controller); | ||
| 704 | |||
| 705 | hed.root = NULL; | ||
| 706 | hed.end = NULL; | ||
| 707 | hed.drvctl = drvctl; | ||
| 708 | hed.vendor_id = vendor_id; | ||
| 709 | hed.product_id = product_id; | ||
| 710 | |||
| 711 | for (size_t i = 0; i < len; i++) { | ||
| 712 | char devpath[USB_MAX_DEVNAMELEN]; | ||
| 713 | int bus; | ||
| 714 | |||
| 715 | strlcpy(devpath, "/dev/", sizeof(devpath)); | ||
| 716 | strlcat(devpath, arr[i], sizeof(devpath)); | ||
| 717 | |||
| 718 | bus = open(devpath, O_RDONLY | O_CLOEXEC); | ||
| 719 | if (bus == -1) | ||
| 720 | continue; | ||
| 721 | |||
| 722 | enumerate_usb_devices(bus, 0, hid_enumerate_callback, &hed); | ||
| 723 | |||
| 724 | close(bus); | ||
| 725 | } | ||
| 726 | |||
| 727 | close(drvctl); | ||
| 728 | |||
| 729 | return hed.root; | ||
| 730 | } | ||
| 731 | |||
| 732 | void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) | ||
| 733 | { | ||
| 734 | while (devs) { | ||
| 735 | struct hid_device_info *next = devs->next; | ||
| 736 | free(devs->path); | ||
| 737 | free(devs->serial_number); | ||
| 738 | free(devs->manufacturer_string); | ||
| 739 | free(devs->product_string); | ||
| 740 | free(devs); | ||
| 741 | devs = next; | ||
| 742 | } | ||
| 743 | } | ||
| 744 | |||
| 745 | HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) | ||
| 746 | { | ||
| 747 | struct hid_device_info *devs; | ||
| 748 | struct hid_device_info *dev; | ||
| 749 | char path[USB_MAX_DEVNAMELEN]; | ||
| 750 | |||
| 751 | devs = hid_enumerate(vendor_id, product_id); | ||
| 752 | if (!devs) | ||
| 753 | return NULL; | ||
| 754 | |||
| 755 | *path = '\0'; | ||
| 756 | |||
| 757 | for (dev = devs; dev; dev = dev->next) { | ||
| 758 | if (dev->vendor_id != vendor_id) | ||
| 759 | continue; | ||
| 760 | |||
| 761 | if (dev->product_id != product_id) | ||
| 762 | continue; | ||
| 763 | |||
| 764 | if (serial_number && wcscmp(dev->serial_number, serial_number)) | ||
| 765 | continue; | ||
| 766 | |||
| 767 | strlcpy(path, dev->path, sizeof(path)); | ||
| 768 | |||
| 769 | break; | ||
| 770 | } | ||
| 771 | |||
| 772 | hid_free_enumeration(devs); | ||
| 773 | |||
| 774 | if (*path == '\0') { | ||
| 775 | register_global_error("Device with requested VID/PID/(SerialNumber) not found"); | ||
| 776 | return NULL; | ||
| 777 | } | ||
| 778 | |||
| 779 | return hid_open_path(path); | ||
| 780 | } | ||
| 781 | |||
| 782 | HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) | ||
| 783 | { | ||
| 784 | int res; | ||
| 785 | hid_device *dev; | ||
| 786 | int drvctl; | ||
| 787 | char arr[HIDAPI_MAX_CHILD_DEVICES][USB_MAX_DEVNAMELEN]; | ||
| 788 | size_t len; | ||
| 789 | |||
| 790 | res = hid_init(); | ||
| 791 | if (res == -1) | ||
| 792 | goto err_0; | ||
| 793 | |||
| 794 | dev = (hid_device *) calloc(1, sizeof(hid_device)); | ||
| 795 | if (!dev) { | ||
| 796 | register_global_error("could not allocate hid_device"); | ||
| 797 | goto err_0; | ||
| 798 | } | ||
| 799 | |||
| 800 | drvctl = open(DRVCTLDEV, O_RDONLY | O_CLOEXEC); | ||
| 801 | if (drvctl == -1) { | ||
| 802 | register_global_error_format("failed to open drvctl: %s", strerror(errno)); | ||
| 803 | goto err_1; | ||
| 804 | } | ||
| 805 | |||
| 806 | if (!is_uhid_parent_device(path)) { | ||
| 807 | register_global_error("not a uhidev device"); | ||
| 808 | goto err_2; | ||
| 809 | } | ||
| 810 | |||
| 811 | len = 0; | ||
| 812 | walk_device_tree(drvctl, path, 0, arr, &len, is_uhid_device); | ||
| 813 | |||
| 814 | dev->poll_handles_length = 0; | ||
| 815 | memset(dev->poll_handles, 0x00, sizeof(dev->poll_handles)); | ||
| 816 | memset(dev->report_handles, 0xff, sizeof(dev->report_handles)); | ||
| 817 | |||
| 818 | for (size_t i = 0; i < len; i++) { | ||
| 819 | const char *child_dev; | ||
| 820 | char devpath[USB_MAX_DEVNAMELEN]; | ||
| 821 | int uhid; | ||
| 822 | int rep_id; | ||
| 823 | struct pollfd *ph; | ||
| 824 | |||
| 825 | child_dev = arr[i]; | ||
| 826 | strlcpy(devpath, "/dev/", sizeof(devpath)); | ||
| 827 | strlcat(devpath, child_dev, sizeof(devpath)); | ||
| 828 | |||
| 829 | uhid = open(devpath, O_RDWR | O_CLOEXEC); | ||
| 830 | if (uhid == -1) { | ||
| 831 | register_global_error_format("failed to open %s: %s", child_dev, strerror(errno)); | ||
| 832 | goto err_3; | ||
| 833 | } | ||
| 834 | |||
| 835 | res = ioctl(uhid, USB_GET_REPORT_ID, &rep_id); | ||
| 836 | if (res == -1) { | ||
| 837 | close(uhid); | ||
| 838 | register_global_error_format("failed to get report id %s: %s", child_dev, strerror(errno)); | ||
| 839 | goto err_3; | ||
| 840 | } | ||
| 841 | |||
| 842 | ph = &dev->poll_handles[dev->poll_handles_length++]; | ||
| 843 | ph->fd = uhid; | ||
| 844 | ph->events = POLLIN; | ||
| 845 | ph->revents = 0; | ||
| 846 | dev->report_handles[rep_id] = uhid; | ||
| 847 | dev->device_handle = uhid; | ||
| 848 | } | ||
| 849 | |||
| 850 | dev->blocking = 1; | ||
| 851 | dev->last_error_str = NULL; | ||
| 852 | dev->device_info = NULL; | ||
| 853 | strlcpy(dev->path, path, sizeof(dev->path)); | ||
| 854 | |||
| 855 | register_global_error(NULL); | ||
| 856 | return dev; | ||
| 857 | |||
| 858 | err_3: | ||
| 859 | for (size_t i = 0; i < dev->poll_handles_length; i++) | ||
| 860 | close(dev->poll_handles[i].fd); | ||
| 861 | err_2: | ||
| 862 | close(drvctl); | ||
| 863 | err_1: | ||
| 864 | free(dev); | ||
| 865 | err_0: | ||
| 866 | return NULL; | ||
| 867 | } | ||
| 868 | |||
| 869 | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) | ||
| 870 | { | ||
| 871 | return set_report(dev, data, length, UHID_OUTPUT_REPORT); | ||
| 872 | } | ||
| 873 | |||
| 874 | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) | ||
| 875 | { | ||
| 876 | int res; | ||
| 877 | size_t i; | ||
| 878 | struct pollfd *ph; | ||
| 879 | ssize_t n; | ||
| 880 | |||
| 881 | res = poll(dev->poll_handles, dev->poll_handles_length, milliseconds); | ||
| 882 | if (res == -1) { | ||
| 883 | register_device_error_format(dev, "error while polling: %s", strerror(errno)); | ||
| 884 | return -1; | ||
| 885 | } | ||
| 886 | |||
| 887 | if (res == 0) | ||
| 888 | return 0; | ||
| 889 | |||
| 890 | for (i = 0; i < dev->poll_handles_length; i++) { | ||
| 891 | ph = &dev->poll_handles[i]; | ||
| 892 | |||
| 893 | if (ph->revents & (POLLERR | POLLHUP | POLLNVAL)) { | ||
| 894 | register_device_error(dev, "device IO error while polling"); | ||
| 895 | return -1; | ||
| 896 | } | ||
| 897 | |||
| 898 | if (ph->revents & POLLIN) | ||
| 899 | break; | ||
| 900 | } | ||
| 901 | |||
| 902 | if (i == dev->poll_handles_length) | ||
| 903 | return 0; | ||
| 904 | |||
| 905 | n = read(ph->fd, data, length); | ||
| 906 | if (n == -1) { | ||
| 907 | if (errno == EAGAIN || errno == EINPROGRESS) | ||
| 908 | n = 0; | ||
| 909 | else | ||
| 910 | register_device_error_format(dev, "error while reading: %s", strerror(errno)); | ||
| 911 | } | ||
| 912 | |||
| 913 | return n; | ||
| 914 | } | ||
| 915 | |||
| 916 | int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) | ||
| 917 | { | ||
| 918 | return hid_read_timeout(dev, data, length, (dev->blocking) ? -1 : 0); | ||
| 919 | } | ||
| 920 | |||
| 921 | int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) | ||
| 922 | { | ||
| 923 | dev->blocking = !nonblock; | ||
| 924 | return 0; | ||
| 925 | } | ||
| 926 | |||
| 927 | int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) | ||
| 928 | { | ||
| 929 | return set_report(dev, data, length, UHID_FEATURE_REPORT); | ||
| 930 | } | ||
| 931 | |||
| 932 | int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) | ||
| 933 | { | ||
| 934 | return get_report(dev, data, length, UHID_FEATURE_REPORT); | ||
| 935 | } | ||
| 936 | |||
| 937 | int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) | ||
| 938 | { | ||
| 939 | return get_report(dev, data, length, UHID_INPUT_REPORT); | ||
| 940 | } | ||
| 941 | |||
| 942 | void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) | ||
| 943 | { | ||
| 944 | if (!dev) | ||
| 945 | return; | ||
| 946 | |||
| 947 | /* Free the device error message */ | ||
| 948 | register_device_error(dev, NULL); | ||
| 949 | |||
| 950 | hid_free_enumeration(dev->device_info); | ||
| 951 | |||
| 952 | for (size_t i = 0; i < dev->poll_handles_length; i++) | ||
| 953 | close(dev->poll_handles[i].fd); | ||
| 954 | |||
| 955 | free(dev); | ||
| 956 | } | ||
| 957 | |||
| 958 | int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) | ||
| 959 | { | ||
| 960 | struct hid_device_info *hdi; | ||
| 961 | |||
| 962 | if (!string || !maxlen) { | ||
| 963 | register_device_error(dev, "Zero buffer/length"); | ||
| 964 | return -1; | ||
| 965 | } | ||
| 966 | |||
| 967 | hdi = hid_get_device_info(dev); | ||
| 968 | if (!dev) | ||
| 969 | return -1; | ||
| 970 | |||
| 971 | if (hdi->manufacturer_string) { | ||
| 972 | wcsncpy(string, hdi->manufacturer_string, maxlen); | ||
| 973 | string[maxlen - 1] = L'\0'; | ||
| 974 | } else { | ||
| 975 | string[0] = L'\0'; | ||
| 976 | } | ||
| 977 | |||
| 978 | return 0; | ||
| 979 | } | ||
| 980 | |||
| 981 | int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) | ||
| 982 | { | ||
| 983 | struct hid_device_info *hdi; | ||
| 984 | |||
| 985 | if (!string || !maxlen) { | ||
| 986 | register_device_error(dev, "Zero buffer/length"); | ||
| 987 | return -1; | ||
| 988 | } | ||
| 989 | |||
| 990 | hdi = hid_get_device_info(dev); | ||
| 991 | if (!dev) | ||
| 992 | return -1; | ||
| 993 | |||
| 994 | if (hdi->product_string) { | ||
| 995 | wcsncpy(string, hdi->product_string, maxlen); | ||
| 996 | string[maxlen - 1] = L'\0'; | ||
| 997 | } else { | ||
| 998 | string[0] = L'\0'; | ||
| 999 | } | ||
| 1000 | |||
| 1001 | return 0; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) | ||
| 1005 | { | ||
| 1006 | struct hid_device_info *hdi; | ||
| 1007 | |||
| 1008 | if (!string || !maxlen) { | ||
| 1009 | register_device_error(dev, "Zero buffer/length"); | ||
| 1010 | return -1; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | hdi = hid_get_device_info(dev); | ||
| 1014 | if (!dev) | ||
| 1015 | return -1; | ||
| 1016 | |||
| 1017 | if (hdi->serial_number) { | ||
| 1018 | wcsncpy(string, hdi->serial_number, maxlen); | ||
| 1019 | string[maxlen - 1] = L'\0'; | ||
| 1020 | } else { | ||
| 1021 | string[0] = L'\0'; | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | return 0; | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_get_device_info(hid_device *dev) | ||
| 1028 | { | ||
| 1029 | int res; | ||
| 1030 | struct usb_device_info udi; | ||
| 1031 | struct usb_ctl_report_desc ucrd; | ||
| 1032 | int use_ucrd; | ||
| 1033 | struct hid_device_info *hdi; | ||
| 1034 | |||
| 1035 | if (dev->device_info) | ||
| 1036 | return dev->device_info; | ||
| 1037 | |||
| 1038 | res = ioctl(dev->device_handle, USB_GET_DEVICEINFO, &udi); | ||
| 1039 | if (res == -1) { | ||
| 1040 | register_device_error_format(dev, "ioctl (USB_GET_DEVICEINFO): %s", strerror(errno)); | ||
| 1041 | return NULL; | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | use_ucrd = (ioctl(dev->device_handle, USB_GET_REPORT_DESC, &ucrd) != -1); | ||
| 1045 | |||
| 1046 | hdi = create_device_info(&udi, dev->path, (use_ucrd) ? &ucrd : NULL); | ||
| 1047 | if (!hdi) { | ||
| 1048 | register_device_error(dev, "failed to create device info"); | ||
| 1049 | return NULL; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | dev->device_info = hdi; | ||
| 1053 | |||
| 1054 | return hdi; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) | ||
| 1058 | { | ||
| 1059 | int res; | ||
| 1060 | struct usb_string_desc usd; | ||
| 1061 | usb_string_descriptor_t *str; | ||
| 1062 | iconv_t ic; | ||
| 1063 | char *src; | ||
| 1064 | size_t srcleft; | ||
| 1065 | char *dst; | ||
| 1066 | size_t dstleft; | ||
| 1067 | size_t ic_res; | ||
| 1068 | |||
| 1069 | /* First let us get the supported language IDs. */ | ||
| 1070 | usd.usd_string_index = 0; | ||
| 1071 | usd.usd_language_id = 0; | ||
| 1072 | |||
| 1073 | res = ioctl(dev->device_handle, USB_GET_STRING_DESC, &usd); | ||
| 1074 | if (res == -1) { | ||
| 1075 | register_device_error_format(dev, "ioctl (USB_GET_STRING_DESC): %s", strerror(errno)); | ||
| 1076 | return -1; | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | str = &usd.usd_desc; | ||
| 1080 | |||
| 1081 | if (str->bLength < 4) { | ||
| 1082 | register_device_error(dev, "failed to get supported language IDs"); | ||
| 1083 | return -1; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | /* Now we can get the requested string. */ | ||
| 1087 | usd.usd_string_index = string_index; | ||
| 1088 | usd.usd_language_id = UGETW(str->bString[0]); | ||
| 1089 | |||
| 1090 | res = ioctl(dev->device_handle, USB_GET_STRING_DESC, &usd); | ||
| 1091 | if (res == -1) { | ||
| 1092 | register_device_error_format(dev, "ioctl (USB_GET_STRING_DESC): %s", strerror(errno)); | ||
| 1093 | return -1; | ||
| 1094 | } | ||
| 1095 | |||
| 1096 | /* Now we need to convert it, using iconv. */ | ||
| 1097 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | ||
| 1098 | ic = iconv_open("utf-32le", "utf-16le"); | ||
| 1099 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | ||
| 1100 | ic = iconv_open("utf-32be", "utf-16le"); | ||
| 1101 | #endif | ||
| 1102 | if (ic == (iconv_t) -1) { | ||
| 1103 | register_device_error_format(dev, "iconv_open failed: %s", strerror(errno)); | ||
| 1104 | return -1; | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | src = (char *) str->bString; | ||
| 1108 | srcleft = str->bLength - 2; | ||
| 1109 | dst = (char *) string; | ||
| 1110 | dstleft = sizeof(wchar_t[maxlen]); | ||
| 1111 | |||
| 1112 | ic_res = iconv(ic, &src, &srcleft, &dst, &dstleft); | ||
| 1113 | iconv_close(ic); | ||
| 1114 | if (ic_res == (size_t) -1) { | ||
| 1115 | register_device_error_format(dev, "iconv failed: %s", strerror(errno)); | ||
| 1116 | return -1; | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | /* Write the terminating NULL. */ | ||
| 1120 | string[maxlen - 1] = L'\0'; | ||
| 1121 | if (dstleft >= sizeof(wchar_t)) | ||
| 1122 | *((wchar_t *) dst) = L'\0'; | ||
| 1123 | |||
| 1124 | return 0; | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size) | ||
| 1128 | { | ||
| 1129 | int res; | ||
| 1130 | struct usb_ctl_report_desc ucrd; | ||
| 1131 | |||
| 1132 | res = ioctl(dev->device_handle, USB_GET_REPORT_DESC, &ucrd); | ||
| 1133 | if (res == -1) { | ||
| 1134 | register_device_error_format(dev, "ioctl (USB_GET_REPORT_DESC): %s", strerror(errno)); | ||
| 1135 | return -1; | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | if ((size_t) ucrd.ucrd_size < buf_size) | ||
| 1139 | buf_size = (size_t) ucrd.ucrd_size; | ||
| 1140 | |||
| 1141 | memcpy(buf, ucrd.ucrd_data, buf_size); | ||
| 1142 | |||
| 1143 | return (int) buf_size; | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev) | ||
| 1147 | { | ||
| 1148 | if (dev) { | ||
| 1149 | if (dev->last_error_str == NULL) | ||
| 1150 | return L"Success"; | ||
| 1151 | return dev->last_error_str; | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | if (last_global_error_str == NULL) | ||
| 1155 | return L"Success"; | ||
| 1156 | return last_global_error_str; | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void) | ||
| 1160 | { | ||
| 1161 | static const struct hid_api_version api_version = { | ||
| 1162 | .major = HID_API_VERSION_MAJOR, | ||
| 1163 | .minor = HID_API_VERSION_MINOR, | ||
| 1164 | .patch = HID_API_VERSION_PATCH | ||
| 1165 | }; | ||
| 1166 | |||
| 1167 | return &api_version; | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | HID_API_EXPORT const char* HID_API_CALL hid_version_str(void) | ||
| 1171 | { | ||
| 1172 | return HID_API_VERSION_STR; | ||
| 1173 | } | ||
