1 |
jakw |
1 |
/*******************************************************
|
|
|
2 |
HIDAPI - Multi-Platform library for
|
|
|
3 |
communication with HID devices.
|
|
|
4 |
|
|
|
5 |
Alan Ott
|
|
|
6 |
Signal 11 Software
|
|
|
7 |
|
|
|
8 |
8/22/2009
|
|
|
9 |
Linux Version - 6/2/2010
|
|
|
10 |
Libusb Version - 8/13/2010
|
|
|
11 |
FreeBSD Version - 11/1/2011
|
|
|
12 |
|
|
|
13 |
Copyright 2009, All Rights Reserved.
|
|
|
14 |
|
|
|
15 |
At the discretion of the user of this library,
|
|
|
16 |
this software may be licensed under the terms of the
|
|
|
17 |
GNU Public License v3, a BSD-Style license, or the
|
|
|
18 |
original HIDAPI license as outlined in the LICENSE.txt,
|
|
|
19 |
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
|
|
20 |
files located at the root of the source distribution.
|
|
|
21 |
These files may also be found in the public source
|
|
|
22 |
code repository located at:
|
|
|
23 |
http://github.com/signal11/hidapi .
|
|
|
24 |
********************************************************/
|
|
|
25 |
|
|
|
26 |
#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */
|
|
|
27 |
|
|
|
28 |
/* C */
|
|
|
29 |
#include <stdio.h>
|
|
|
30 |
#include <string.h>
|
|
|
31 |
#include <stdlib.h>
|
|
|
32 |
#include <ctype.h>
|
|
|
33 |
#include <locale.h>
|
|
|
34 |
#include <errno.h>
|
|
|
35 |
|
|
|
36 |
/* Unix */
|
|
|
37 |
#include <unistd.h>
|
|
|
38 |
#include <sys/types.h>
|
|
|
39 |
#include <sys/stat.h>
|
|
|
40 |
#include <sys/ioctl.h>
|
|
|
41 |
#include <sys/utsname.h>
|
|
|
42 |
#include <fcntl.h>
|
|
|
43 |
#include <pthread.h>
|
|
|
44 |
#include <wchar.h>
|
|
|
45 |
|
|
|
46 |
/* GNU / LibUSB */
|
|
|
47 |
#include "libusb.h"
|
|
|
48 |
#include "iconv.h"
|
|
|
49 |
|
|
|
50 |
#include "hidapi.h"
|
|
|
51 |
|
|
|
52 |
#ifdef __cplusplus
|
|
|
53 |
extern "C" {
|
|
|
54 |
#endif
|
|
|
55 |
|
|
|
56 |
#ifdef DEBUG_PRINTF
|
|
|
57 |
#define LOG(...) fprintf(stderr, __VA_ARGS__)
|
|
|
58 |
#else
|
|
|
59 |
#define LOG(...) do {} while (0)
|
|
|
60 |
#endif
|
|
|
61 |
|
|
|
62 |
#ifndef __FreeBSD__
|
|
|
63 |
#define DETACH_KERNEL_DRIVER
|
|
|
64 |
#endif
|
|
|
65 |
|
|
|
66 |
/* Uncomment to enable the retrieval of Usage and Usage Page in
|
|
|
67 |
hid_enumerate(). Warning, on platforms different from FreeBSD
|
|
|
68 |
this is very invasive as it requires the detach
|
|
|
69 |
and re-attach of the kernel driver. See comments inside hid_enumerate().
|
|
|
70 |
libusb HIDAPI programs are encouraged to use the interface number
|
|
|
71 |
instead to differentiate between interfaces on a composite HID device. */
|
|
|
72 |
/*#define INVASIVE_GET_USAGE*/
|
|
|
73 |
|
|
|
74 |
/* Linked List of input reports received from the device. */
|
|
|
75 |
struct input_report {
|
|
|
76 |
uint8_t *data;
|
|
|
77 |
size_t len;
|
|
|
78 |
struct input_report *next;
|
|
|
79 |
};
|
|
|
80 |
|
|
|
81 |
|
|
|
82 |
struct hid_device_ {
|
|
|
83 |
/* Handle to the actual device. */
|
|
|
84 |
libusb_device_handle *device_handle;
|
|
|
85 |
|
|
|
86 |
/* Endpoint information */
|
|
|
87 |
int input_endpoint;
|
|
|
88 |
int output_endpoint;
|
|
|
89 |
int input_ep_max_packet_size;
|
|
|
90 |
|
|
|
91 |
/* The interface number of the HID */
|
|
|
92 |
int interface;
|
|
|
93 |
|
|
|
94 |
/* Indexes of Strings */
|
|
|
95 |
int manufacturer_index;
|
|
|
96 |
int product_index;
|
|
|
97 |
int serial_index;
|
|
|
98 |
|
|
|
99 |
/* Whether blocking reads are used */
|
|
|
100 |
int blocking; /* boolean */
|
|
|
101 |
|
|
|
102 |
/* Read thread objects */
|
|
|
103 |
pthread_t thread;
|
|
|
104 |
pthread_mutex_t mutex; /* Protects input_reports */
|
|
|
105 |
pthread_cond_t condition;
|
|
|
106 |
pthread_barrier_t barrier; /* Ensures correct startup sequence */
|
|
|
107 |
int shutdown_thread;
|
|
|
108 |
struct libusb_transfer *transfer;
|
|
|
109 |
|
|
|
110 |
/* List of received input reports. */
|
|
|
111 |
struct input_report *input_reports;
|
|
|
112 |
};
|
|
|
113 |
|
|
|
114 |
static libusb_context *usb_context = NULL;
|
|
|
115 |
|
|
|
116 |
uint16_t get_usb_code_for_current_locale(void);
|
|
|
117 |
static int return_data(hid_device *dev, unsigned char *data, size_t length);
|
|
|
118 |
|
|
|
119 |
static hid_device *new_hid_device(void)
|
|
|
120 |
{
|
|
|
121 |
hid_device *dev = calloc(1, sizeof(hid_device));
|
|
|
122 |
dev->blocking = 1;
|
|
|
123 |
|
|
|
124 |
pthread_mutex_init(&dev->mutex, NULL);
|
|
|
125 |
pthread_cond_init(&dev->condition, NULL);
|
|
|
126 |
pthread_barrier_init(&dev->barrier, NULL, 2);
|
|
|
127 |
|
|
|
128 |
return dev;
|
|
|
129 |
}
|
|
|
130 |
|
|
|
131 |
static void free_hid_device(hid_device *dev)
|
|
|
132 |
{
|
|
|
133 |
/* Clean up the thread objects */
|
|
|
134 |
pthread_barrier_destroy(&dev->barrier);
|
|
|
135 |
pthread_cond_destroy(&dev->condition);
|
|
|
136 |
pthread_mutex_destroy(&dev->mutex);
|
|
|
137 |
|
|
|
138 |
/* Free the device itself */
|
|
|
139 |
free(dev);
|
|
|
140 |
}
|
|
|
141 |
|
|
|
142 |
#if 0
|
|
|
143 |
/*TODO: Implement this funciton on hidapi/libusb.. */
|
|
|
144 |
static void register_error(hid_device *device, const char *op)
|
|
|
145 |
{
|
|
|
146 |
|
|
|
147 |
}
|
|
|
148 |
#endif
|
|
|
149 |
|
|
|
150 |
#ifdef INVASIVE_GET_USAGE
|
|
|
151 |
/* Get bytes from a HID Report Descriptor.
|
|
|
152 |
Only call with a num_bytes of 0, 1, 2, or 4. */
|
|
|
153 |
static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur)
|
|
|
154 |
{
|
|
|
155 |
/* Return if there aren't enough bytes. */
|
|
|
156 |
if (cur + num_bytes >= len)
|
|
|
157 |
return 0;
|
|
|
158 |
|
|
|
159 |
if (num_bytes == 0)
|
|
|
160 |
return 0;
|
|
|
161 |
else if (num_bytes == 1) {
|
|
|
162 |
return rpt[cur+1];
|
|
|
163 |
}
|
|
|
164 |
else if (num_bytes == 2) {
|
|
|
165 |
return (rpt[cur+2] * 256 + rpt[cur+1]);
|
|
|
166 |
}
|
|
|
167 |
else if (num_bytes == 4) {
|
|
|
168 |
return (rpt[cur+4] * 0x01000000 +
|
|
|
169 |
rpt[cur+3] * 0x00010000 +
|
|
|
170 |
rpt[cur+2] * 0x00000100 +
|
|
|
171 |
rpt[cur+1] * 0x00000001);
|
|
|
172 |
}
|
|
|
173 |
else
|
|
|
174 |
return 0;
|
|
|
175 |
}
|
|
|
176 |
|
|
|
177 |
/* Retrieves the device's Usage Page and Usage from the report
|
|
|
178 |
descriptor. The algorithm is simple, as it just returns the first
|
|
|
179 |
Usage and Usage Page that it finds in the descriptor.
|
|
|
180 |
The return value is 0 on success and -1 on failure. */
|
|
|
181 |
static int get_usage(uint8_t *report_descriptor, size_t size,
|
|
|
182 |
unsigned short *usage_page, unsigned short *usage)
|
|
|
183 |
{
|
|
|
184 |
unsigned int i = 0;
|
|
|
185 |
int size_code;
|
|
|
186 |
int data_len, key_size;
|
|
|
187 |
int usage_found = 0, usage_page_found = 0;
|
|
|
188 |
|
|
|
189 |
while (i < size) {
|
|
|
190 |
int key = report_descriptor[i];
|
|
|
191 |
int key_cmd = key & 0xfc;
|
|
|
192 |
|
|
|
193 |
//printf("key: %02hhx\n", key);
|
|
|
194 |
|
|
|
195 |
if ((key & 0xf0) == 0xf0) {
|
|
|
196 |
/* This is a Long Item. The next byte contains the
|
|
|
197 |
length of the data section (value) for this key.
|
|
|
198 |
See the HID specification, version 1.11, section
|
|
|
199 |
6.2.2.3, titled "Long Items." */
|
|
|
200 |
if (i+1 < size)
|
|
|
201 |
data_len = report_descriptor[i+1];
|
|
|
202 |
else
|
|
|
203 |
data_len = 0; /* malformed report */
|
|
|
204 |
key_size = 3;
|
|
|
205 |
}
|
|
|
206 |
else {
|
|
|
207 |
/* This is a Short Item. The bottom two bits of the
|
|
|
208 |
key contain the size code for the data section
|
|
|
209 |
(value) for this key. Refer to the HID
|
|
|
210 |
specification, version 1.11, section 6.2.2.2,
|
|
|
211 |
titled "Short Items." */
|
|
|
212 |
size_code = key & 0x3;
|
|
|
213 |
switch (size_code) {
|
|
|
214 |
case 0:
|
|
|
215 |
case 1:
|
|
|
216 |
case 2:
|
|
|
217 |
data_len = size_code;
|
|
|
218 |
break;
|
|
|
219 |
case 3:
|
|
|
220 |
data_len = 4;
|
|
|
221 |
break;
|
|
|
222 |
default:
|
|
|
223 |
/* Can't ever happen since size_code is & 0x3 */
|
|
|
224 |
data_len = 0;
|
|
|
225 |
break;
|
|
|
226 |
};
|
|
|
227 |
key_size = 1;
|
|
|
228 |
}
|
|
|
229 |
|
|
|
230 |
if (key_cmd == 0x4) {
|
|
|
231 |
*usage_page = get_bytes(report_descriptor, size, data_len, i);
|
|
|
232 |
usage_page_found = 1;
|
|
|
233 |
//printf("Usage Page: %x\n", (uint32_t)*usage_page);
|
|
|
234 |
}
|
|
|
235 |
if (key_cmd == 0x8) {
|
|
|
236 |
*usage = get_bytes(report_descriptor, size, data_len, i);
|
|
|
237 |
usage_found = 1;
|
|
|
238 |
//printf("Usage: %x\n", (uint32_t)*usage);
|
|
|
239 |
}
|
|
|
240 |
|
|
|
241 |
if (usage_page_found && usage_found)
|
|
|
242 |
return 0; /* success */
|
|
|
243 |
|
|
|
244 |
/* Skip over this key and it's associated data */
|
|
|
245 |
i += data_len + key_size;
|
|
|
246 |
}
|
|
|
247 |
|
|
|
248 |
return -1; /* failure */
|
|
|
249 |
}
|
|
|
250 |
#endif /* INVASIVE_GET_USAGE */
|
|
|
251 |
|
|
|
252 |
#ifdef __FreeBSD__
|
|
|
253 |
/* The FreeBSD version of libusb doesn't have this funciton. In mainline
|
|
|
254 |
libusb, it's inlined in libusb.h. This function will bear a striking
|
|
|
255 |
resemblence to that one, because there's about one way to code it.
|
|
|
256 |
|
|
|
257 |
Note that the data parameter is Unicode in UTF-16LE encoding.
|
|
|
258 |
Return value is the number of bytes in data, or LIBUSB_ERROR_*.
|
|
|
259 |
*/
|
|
|
260 |
static inline int libusb_get_string_descriptor(libusb_device_handle *dev,
|
|
|
261 |
uint8_t descriptor_index, uint16_t lang_id,
|
|
|
262 |
unsigned char *data, int length)
|
|
|
263 |
{
|
|
|
264 |
return libusb_control_transfer(dev,
|
|
|
265 |
LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */
|
|
|
266 |
LIBUSB_REQUEST_GET_DESCRIPTOR,
|
|
|
267 |
(LIBUSB_DT_STRING << 8) | descriptor_index,
|
|
|
268 |
lang_id, data, (uint16_t) length, 1000);
|
|
|
269 |
}
|
|
|
270 |
|
|
|
271 |
#endif
|
|
|
272 |
|
|
|
273 |
|
|
|
274 |
/* Get the first language the device says it reports. This comes from
|
|
|
275 |
USB string #0. */
|
|
|
276 |
static uint16_t get_first_language(libusb_device_handle *dev)
|
|
|
277 |
{
|
|
|
278 |
uint16_t buf[32];
|
|
|
279 |
int len;
|
|
|
280 |
|
|
|
281 |
/* Get the string from libusb. */
|
|
|
282 |
len = libusb_get_string_descriptor(dev,
|
|
|
283 |
0x0, /* String ID */
|
|
|
284 |
0x0, /* Language */
|
|
|
285 |
(unsigned char*)buf,
|
|
|
286 |
sizeof(buf));
|
|
|
287 |
if (len < 4)
|
|
|
288 |
return 0x0;
|
|
|
289 |
|
|
|
290 |
return buf[1]; /* First two bytes are len and descriptor type. */
|
|
|
291 |
}
|
|
|
292 |
|
|
|
293 |
static int is_language_supported(libusb_device_handle *dev, uint16_t lang)
|
|
|
294 |
{
|
|
|
295 |
uint16_t buf[32];
|
|
|
296 |
int len;
|
|
|
297 |
int i;
|
|
|
298 |
|
|
|
299 |
/* Get the string from libusb. */
|
|
|
300 |
len = libusb_get_string_descriptor(dev,
|
|
|
301 |
0x0, /* String ID */
|
|
|
302 |
0x0, /* Language */
|
|
|
303 |
(unsigned char*)buf,
|
|
|
304 |
sizeof(buf));
|
|
|
305 |
if (len < 4)
|
|
|
306 |
return 0x0;
|
|
|
307 |
|
|
|
308 |
|
|
|
309 |
len /= 2; /* language IDs are two-bytes each. */
|
|
|
310 |
/* Start at index 1 because there are two bytes of protocol data. */
|
|
|
311 |
for (i = 1; i < len; i++) {
|
|
|
312 |
if (buf[i] == lang)
|
|
|
313 |
return 1;
|
|
|
314 |
}
|
|
|
315 |
|
|
|
316 |
return 0;
|
|
|
317 |
}
|
|
|
318 |
|
|
|
319 |
|
|
|
320 |
/* This function returns a newly allocated wide string containing the USB
|
|
|
321 |
device string numbered by the index. The returned string must be freed
|
|
|
322 |
by using free(). */
|
|
|
323 |
static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx)
|
|
|
324 |
{
|
|
|
325 |
char buf[512];
|
|
|
326 |
int len;
|
|
|
327 |
wchar_t *str = NULL;
|
|
|
328 |
wchar_t wbuf[256];
|
|
|
329 |
|
|
|
330 |
/* iconv variables */
|
|
|
331 |
iconv_t ic;
|
|
|
332 |
size_t inbytes;
|
|
|
333 |
size_t outbytes;
|
|
|
334 |
size_t res;
|
|
|
335 |
#ifdef __FreeBSD__
|
|
|
336 |
const char *inptr;
|
|
|
337 |
#else
|
|
|
338 |
char *inptr;
|
|
|
339 |
#endif
|
|
|
340 |
char *outptr;
|
|
|
341 |
|
|
|
342 |
/* Determine which language to use. */
|
|
|
343 |
uint16_t lang;
|
|
|
344 |
lang = get_usb_code_for_current_locale();
|
|
|
345 |
if (!is_language_supported(dev, lang))
|
|
|
346 |
lang = get_first_language(dev);
|
|
|
347 |
|
|
|
348 |
/* Get the string from libusb. */
|
|
|
349 |
len = libusb_get_string_descriptor(dev,
|
|
|
350 |
idx,
|
|
|
351 |
lang,
|
|
|
352 |
(unsigned char*)buf,
|
|
|
353 |
sizeof(buf));
|
|
|
354 |
if (len < 0)
|
|
|
355 |
return NULL;
|
|
|
356 |
|
|
|
357 |
/* buf does not need to be explicitly NULL-terminated because
|
|
|
358 |
it is only passed into iconv() which does not need it. */
|
|
|
359 |
|
|
|
360 |
/* Initialize iconv. */
|
|
|
361 |
ic = iconv_open("WCHAR_T", "UTF-16LE");
|
|
|
362 |
if (ic == (iconv_t)-1) {
|
|
|
363 |
LOG("iconv_open() failed\n");
|
|
|
364 |
return NULL;
|
|
|
365 |
}
|
|
|
366 |
|
|
|
367 |
/* Convert to native wchar_t (UTF-32 on glibc/BSD systems).
|
|
|
368 |
Skip the first character (2-bytes). */
|
|
|
369 |
inptr = buf+2;
|
|
|
370 |
inbytes = len-2;
|
|
|
371 |
outptr = (char*) wbuf;
|
|
|
372 |
outbytes = sizeof(wbuf);
|
|
|
373 |
res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes);
|
|
|
374 |
if (res == (size_t)-1) {
|
|
|
375 |
LOG("iconv() failed\n");
|
|
|
376 |
goto err;
|
|
|
377 |
}
|
|
|
378 |
|
|
|
379 |
/* Write the terminating NULL. */
|
|
|
380 |
wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000;
|
|
|
381 |
if (outbytes >= sizeof(wbuf[0]))
|
|
|
382 |
*((wchar_t*)outptr) = 0x00000000;
|
|
|
383 |
|
|
|
384 |
/* Allocate and copy the string. */
|
|
|
385 |
str = wcsdup(wbuf);
|
|
|
386 |
|
|
|
387 |
err:
|
|
|
388 |
iconv_close(ic);
|
|
|
389 |
|
|
|
390 |
return str;
|
|
|
391 |
}
|
|
|
392 |
|
|
|
393 |
static char *make_path(libusb_device *dev, int interface_number)
|
|
|
394 |
{
|
|
|
395 |
char str[64];
|
|
|
396 |
snprintf(str, sizeof(str), "%04x:%04x:%02x",
|
|
|
397 |
libusb_get_bus_number(dev),
|
|
|
398 |
libusb_get_device_address(dev),
|
|
|
399 |
interface_number);
|
|
|
400 |
str[sizeof(str)-1] = '\0';
|
|
|
401 |
|
|
|
402 |
return strdup(str);
|
|
|
403 |
}
|
|
|
404 |
|
|
|
405 |
|
|
|
406 |
int HID_API_EXPORT hid_init(void)
|
|
|
407 |
{
|
|
|
408 |
if (!usb_context) {
|
|
|
409 |
const char *locale;
|
|
|
410 |
|
|
|
411 |
/* Init Libusb */
|
|
|
412 |
if (libusb_init(&usb_context))
|
|
|
413 |
return -1;
|
|
|
414 |
|
|
|
415 |
/* Set the locale if it's not set. */
|
|
|
416 |
locale = setlocale(LC_CTYPE, NULL);
|
|
|
417 |
if (!locale)
|
|
|
418 |
setlocale(LC_CTYPE, "");
|
|
|
419 |
}
|
|
|
420 |
|
|
|
421 |
return 0;
|
|
|
422 |
}
|
|
|
423 |
|
|
|
424 |
int HID_API_EXPORT hid_exit(void)
|
|
|
425 |
{
|
|
|
426 |
if (usb_context) {
|
|
|
427 |
libusb_exit(usb_context);
|
|
|
428 |
usb_context = NULL;
|
|
|
429 |
}
|
|
|
430 |
|
|
|
431 |
return 0;
|
|
|
432 |
}
|
|
|
433 |
|
|
|
434 |
struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
|
|
|
435 |
{
|
|
|
436 |
libusb_device **devs;
|
|
|
437 |
libusb_device *dev;
|
|
|
438 |
libusb_device_handle *handle;
|
|
|
439 |
ssize_t num_devs;
|
|
|
440 |
int i = 0;
|
|
|
441 |
|
|
|
442 |
struct hid_device_info *root = NULL; /* return object */
|
|
|
443 |
struct hid_device_info *cur_dev = NULL;
|
|
|
444 |
|
|
|
445 |
if(hid_init() < 0)
|
|
|
446 |
return NULL;
|
|
|
447 |
|
|
|
448 |
num_devs = libusb_get_device_list(usb_context, &devs);
|
|
|
449 |
if (num_devs < 0)
|
|
|
450 |
return NULL;
|
|
|
451 |
while ((dev = devs[i++]) != NULL) {
|
|
|
452 |
struct libusb_device_descriptor desc;
|
|
|
453 |
struct libusb_config_descriptor *conf_desc = NULL;
|
|
|
454 |
int j, k;
|
|
|
455 |
int interface_num = 0;
|
|
|
456 |
|
|
|
457 |
int res = libusb_get_device_descriptor(dev, &desc);
|
|
|
458 |
unsigned short dev_vid = desc.idVendor;
|
|
|
459 |
unsigned short dev_pid = desc.idProduct;
|
|
|
460 |
|
|
|
461 |
res = libusb_get_active_config_descriptor(dev, &conf_desc);
|
|
|
462 |
if (res < 0)
|
|
|
463 |
libusb_get_config_descriptor(dev, 0, &conf_desc);
|
|
|
464 |
if (conf_desc) {
|
|
|
465 |
for (j = 0; j < conf_desc->bNumInterfaces; j++) {
|
|
|
466 |
const struct libusb_interface *intf = &conf_desc->interface[j];
|
|
|
467 |
for (k = 0; k < intf->num_altsetting; k++) {
|
|
|
468 |
const struct libusb_interface_descriptor *intf_desc;
|
|
|
469 |
intf_desc = &intf->altsetting[k];
|
|
|
470 |
if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) {
|
|
|
471 |
interface_num = intf_desc->bInterfaceNumber;
|
|
|
472 |
|
|
|
473 |
/* Check the VID/PID against the arguments */
|
|
|
474 |
if ((vendor_id == 0x0 || vendor_id == dev_vid) &&
|
|
|
475 |
(product_id == 0x0 || product_id == dev_pid)) {
|
|
|
476 |
struct hid_device_info *tmp;
|
|
|
477 |
|
|
|
478 |
/* VID/PID match. Create the record. */
|
|
|
479 |
tmp = calloc(1, sizeof(struct hid_device_info));
|
|
|
480 |
if (cur_dev) {
|
|
|
481 |
cur_dev->next = tmp;
|
|
|
482 |
}
|
|
|
483 |
else {
|
|
|
484 |
root = tmp;
|
|
|
485 |
}
|
|
|
486 |
cur_dev = tmp;
|
|
|
487 |
|
|
|
488 |
/* Fill out the record */
|
|
|
489 |
cur_dev->next = NULL;
|
|
|
490 |
cur_dev->path = make_path(dev, interface_num);
|
|
|
491 |
|
|
|
492 |
res = libusb_open(dev, &handle);
|
|
|
493 |
|
|
|
494 |
if (res >= 0) {
|
|
|
495 |
/* Serial Number */
|
|
|
496 |
if (desc.iSerialNumber > 0)
|
|
|
497 |
cur_dev->serial_number =
|
|
|
498 |
get_usb_string(handle, desc.iSerialNumber);
|
|
|
499 |
|
|
|
500 |
/* Manufacturer and Product strings */
|
|
|
501 |
if (desc.iManufacturer > 0)
|
|
|
502 |
cur_dev->manufacturer_string =
|
|
|
503 |
get_usb_string(handle, desc.iManufacturer);
|
|
|
504 |
if (desc.iProduct > 0)
|
|
|
505 |
cur_dev->product_string =
|
|
|
506 |
get_usb_string(handle, desc.iProduct);
|
|
|
507 |
|
|
|
508 |
#ifdef INVASIVE_GET_USAGE
|
|
|
509 |
{
|
|
|
510 |
/*
|
|
|
511 |
This section is removed because it is too
|
|
|
512 |
invasive on the system. Getting a Usage Page
|
|
|
513 |
and Usage requires parsing the HID Report
|
|
|
514 |
descriptor. Getting a HID Report descriptor
|
|
|
515 |
involves claiming the interface. Claiming the
|
|
|
516 |
interface involves detaching the kernel driver.
|
|
|
517 |
Detaching the kernel driver is hard on the system
|
|
|
518 |
because it will unclaim interfaces (if another
|
|
|
519 |
app has them claimed) and the re-attachment of
|
|
|
520 |
the driver will sometimes change /dev entry names.
|
|
|
521 |
It is for these reasons that this section is
|
|
|
522 |
#if 0. For composite devices, use the interface
|
|
|
523 |
field in the hid_device_info struct to distinguish
|
|
|
524 |
between interfaces. */
|
|
|
525 |
unsigned char data[256];
|
|
|
526 |
#ifdef DETACH_KERNEL_DRIVER
|
|
|
527 |
int detached = 0;
|
|
|
528 |
/* Usage Page and Usage */
|
|
|
529 |
res = libusb_kernel_driver_active(handle, interface_num);
|
|
|
530 |
if (res == 1) {
|
|
|
531 |
res = libusb_detach_kernel_driver(handle, interface_num);
|
|
|
532 |
if (res < 0)
|
|
|
533 |
LOG("Couldn't detach kernel driver, even though a kernel driver was attached.");
|
|
|
534 |
else
|
|
|
535 |
detached = 1;
|
|
|
536 |
}
|
|
|
537 |
#endif
|
|
|
538 |
res = libusb_claim_interface(handle, interface_num);
|
|
|
539 |
if (res >= 0) {
|
|
|
540 |
/* Get the HID Report Descriptor. */
|
|
|
541 |
res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000);
|
|
|
542 |
if (res >= 0) {
|
|
|
543 |
unsigned short page=0, usage=0;
|
|
|
544 |
/* Parse the usage and usage page
|
|
|
545 |
out of the report descriptor. */
|
|
|
546 |
get_usage(data, res, &page, &usage);
|
|
|
547 |
cur_dev->usage_page = page;
|
|
|
548 |
cur_dev->usage = usage;
|
|
|
549 |
}
|
|
|
550 |
else
|
|
|
551 |
LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res);
|
|
|
552 |
|
|
|
553 |
/* Release the interface */
|
|
|
554 |
res = libusb_release_interface(handle, interface_num);
|
|
|
555 |
if (res < 0)
|
|
|
556 |
LOG("Can't release the interface.\n");
|
|
|
557 |
}
|
|
|
558 |
else
|
|
|
559 |
LOG("Can't claim interface %d\n", res);
|
|
|
560 |
#ifdef DETACH_KERNEL_DRIVER
|
|
|
561 |
/* Re-attach kernel driver if necessary. */
|
|
|
562 |
if (detached) {
|
|
|
563 |
res = libusb_attach_kernel_driver(handle, interface_num);
|
|
|
564 |
if (res < 0)
|
|
|
565 |
LOG("Couldn't re-attach kernel driver.\n");
|
|
|
566 |
}
|
|
|
567 |
#endif
|
|
|
568 |
}
|
|
|
569 |
#endif /* INVASIVE_GET_USAGE */
|
|
|
570 |
|
|
|
571 |
libusb_close(handle);
|
|
|
572 |
}
|
|
|
573 |
/* VID/PID */
|
|
|
574 |
cur_dev->vendor_id = dev_vid;
|
|
|
575 |
cur_dev->product_id = dev_pid;
|
|
|
576 |
|
|
|
577 |
/* Release Number */
|
|
|
578 |
cur_dev->release_number = desc.bcdDevice;
|
|
|
579 |
|
|
|
580 |
/* Interface Number */
|
|
|
581 |
cur_dev->interface_number = interface_num;
|
|
|
582 |
}
|
|
|
583 |
}
|
|
|
584 |
} /* altsettings */
|
|
|
585 |
} /* interfaces */
|
|
|
586 |
libusb_free_config_descriptor(conf_desc);
|
|
|
587 |
}
|
|
|
588 |
}
|
|
|
589 |
|
|
|
590 |
libusb_free_device_list(devs, 1);
|
|
|
591 |
|
|
|
592 |
return root;
|
|
|
593 |
}
|
|
|
594 |
|
|
|
595 |
void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
|
|
|
596 |
{
|
|
|
597 |
struct hid_device_info *d = devs;
|
|
|
598 |
while (d) {
|
|
|
599 |
struct hid_device_info *next = d->next;
|
|
|
600 |
free(d->path);
|
|
|
601 |
free(d->serial_number);
|
|
|
602 |
free(d->manufacturer_string);
|
|
|
603 |
free(d->product_string);
|
|
|
604 |
free(d);
|
|
|
605 |
d = next;
|
|
|
606 |
}
|
|
|
607 |
}
|
|
|
608 |
|
|
|
609 |
hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
|
|
|
610 |
{
|
|
|
611 |
struct hid_device_info *devs, *cur_dev;
|
|
|
612 |
const char *path_to_open = NULL;
|
|
|
613 |
hid_device *handle = NULL;
|
|
|
614 |
|
|
|
615 |
devs = hid_enumerate(vendor_id, product_id);
|
|
|
616 |
cur_dev = devs;
|
|
|
617 |
while (cur_dev) {
|
|
|
618 |
if (cur_dev->vendor_id == vendor_id &&
|
|
|
619 |
cur_dev->product_id == product_id) {
|
|
|
620 |
if (serial_number) {
|
|
|
621 |
if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
|
|
|
622 |
path_to_open = cur_dev->path;
|
|
|
623 |
break;
|
|
|
624 |
}
|
|
|
625 |
}
|
|
|
626 |
else {
|
|
|
627 |
path_to_open = cur_dev->path;
|
|
|
628 |
break;
|
|
|
629 |
}
|
|
|
630 |
}
|
|
|
631 |
cur_dev = cur_dev->next;
|
|
|
632 |
}
|
|
|
633 |
|
|
|
634 |
if (path_to_open) {
|
|
|
635 |
/* Open the device */
|
|
|
636 |
handle = hid_open_path(path_to_open);
|
|
|
637 |
}
|
|
|
638 |
|
|
|
639 |
hid_free_enumeration(devs);
|
|
|
640 |
|
|
|
641 |
return handle;
|
|
|
642 |
}
|
|
|
643 |
|
|
|
644 |
static void read_callback(struct libusb_transfer *transfer)
|
|
|
645 |
{
|
|
|
646 |
hid_device *dev = transfer->user_data;
|
|
|
647 |
int res;
|
|
|
648 |
|
|
|
649 |
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
|
|
650 |
|
|
|
651 |
struct input_report *rpt = malloc(sizeof(*rpt));
|
|
|
652 |
rpt->data = malloc(transfer->actual_length);
|
|
|
653 |
memcpy(rpt->data, transfer->buffer, transfer->actual_length);
|
|
|
654 |
rpt->len = transfer->actual_length;
|
|
|
655 |
rpt->next = NULL;
|
|
|
656 |
|
|
|
657 |
pthread_mutex_lock(&dev->mutex);
|
|
|
658 |
|
|
|
659 |
/* Attach the new report object to the end of the list. */
|
|
|
660 |
if (dev->input_reports == NULL) {
|
|
|
661 |
/* The list is empty. Put it at the root. */
|
|
|
662 |
dev->input_reports = rpt;
|
|
|
663 |
pthread_cond_signal(&dev->condition);
|
|
|
664 |
}
|
|
|
665 |
else {
|
|
|
666 |
/* Find the end of the list and attach. */
|
|
|
667 |
struct input_report *cur = dev->input_reports;
|
|
|
668 |
int num_queued = 0;
|
|
|
669 |
while (cur->next != NULL) {
|
|
|
670 |
cur = cur->next;
|
|
|
671 |
num_queued++;
|
|
|
672 |
}
|
|
|
673 |
cur->next = rpt;
|
|
|
674 |
|
|
|
675 |
/* Pop one off if we've reached 30 in the queue. This
|
|
|
676 |
way we don't grow forever if the user never reads
|
|
|
677 |
anything from the device. */
|
|
|
678 |
if (num_queued > 30) {
|
|
|
679 |
return_data(dev, NULL, 0);
|
|
|
680 |
}
|
|
|
681 |
}
|
|
|
682 |
pthread_mutex_unlock(&dev->mutex);
|
|
|
683 |
}
|
|
|
684 |
else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
|
|
|
685 |
dev->shutdown_thread = 1;
|
|
|
686 |
return;
|
|
|
687 |
}
|
|
|
688 |
else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
|
|
|
689 |
dev->shutdown_thread = 1;
|
|
|
690 |
return;
|
|
|
691 |
}
|
|
|
692 |
else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
|
|
|
693 |
//LOG("Timeout (normal)\n");
|
|
|
694 |
}
|
|
|
695 |
else {
|
|
|
696 |
LOG("Unknown transfer code: %d\n", transfer->status);
|
|
|
697 |
}
|
|
|
698 |
|
|
|
699 |
/* Re-submit the transfer object. */
|
|
|
700 |
res = libusb_submit_transfer(transfer);
|
|
|
701 |
if (res != 0) {
|
|
|
702 |
LOG("Unable to submit URB. libusb error code: %d\n", res);
|
|
|
703 |
dev->shutdown_thread = 1;
|
|
|
704 |
}
|
|
|
705 |
}
|
|
|
706 |
|
|
|
707 |
|
|
|
708 |
static void *read_thread(void *param)
|
|
|
709 |
{
|
|
|
710 |
hid_device *dev = param;
|
|
|
711 |
unsigned char *buf;
|
|
|
712 |
const size_t length = dev->input_ep_max_packet_size;
|
|
|
713 |
|
|
|
714 |
/* Set up the transfer object. */
|
|
|
715 |
buf = malloc(length);
|
|
|
716 |
dev->transfer = libusb_alloc_transfer(0);
|
|
|
717 |
libusb_fill_interrupt_transfer(dev->transfer,
|
|
|
718 |
dev->device_handle,
|
|
|
719 |
dev->input_endpoint,
|
|
|
720 |
buf,
|
|
|
721 |
length,
|
|
|
722 |
read_callback,
|
|
|
723 |
dev,
|
|
|
724 |
5000/*timeout*/);
|
|
|
725 |
|
|
|
726 |
/* Make the first submission. Further submissions are made
|
|
|
727 |
from inside read_callback() */
|
|
|
728 |
libusb_submit_transfer(dev->transfer);
|
|
|
729 |
|
|
|
730 |
/* Notify the main thread that the read thread is up and running. */
|
|
|
731 |
pthread_barrier_wait(&dev->barrier);
|
|
|
732 |
|
|
|
733 |
/* Handle all the events. */
|
|
|
734 |
while (!dev->shutdown_thread) {
|
|
|
735 |
int res;
|
|
|
736 |
res = libusb_handle_events(usb_context);
|
|
|
737 |
if (res < 0) {
|
|
|
738 |
/* There was an error. */
|
|
|
739 |
LOG("read_thread(): libusb reports error # %d\n", res);
|
|
|
740 |
|
|
|
741 |
/* Break out of this loop only on fatal error.*/
|
|
|
742 |
if (res != LIBUSB_ERROR_BUSY &&
|
|
|
743 |
res != LIBUSB_ERROR_TIMEOUT &&
|
|
|
744 |
res != LIBUSB_ERROR_OVERFLOW &&
|
|
|
745 |
res != LIBUSB_ERROR_INTERRUPTED) {
|
|
|
746 |
break;
|
|
|
747 |
}
|
|
|
748 |
}
|
|
|
749 |
}
|
|
|
750 |
|
|
|
751 |
/* Cancel any transfer that may be pending. This call will fail
|
|
|
752 |
if no transfers are pending, but that's OK. */
|
|
|
753 |
if (libusb_cancel_transfer(dev->transfer) == 0) {
|
|
|
754 |
/* The transfer was cancelled, so wait for its completion. */
|
|
|
755 |
libusb_handle_events(usb_context);
|
|
|
756 |
}
|
|
|
757 |
|
|
|
758 |
/* Now that the read thread is stopping, Wake any threads which are
|
|
|
759 |
waiting on data (in hid_read_timeout()). Do this under a mutex to
|
|
|
760 |
make sure that a thread which is about to go to sleep waiting on
|
|
|
761 |
the condition acutally will go to sleep before the condition is
|
|
|
762 |
signaled. */
|
|
|
763 |
pthread_mutex_lock(&dev->mutex);
|
|
|
764 |
pthread_cond_broadcast(&dev->condition);
|
|
|
765 |
pthread_mutex_unlock(&dev->mutex);
|
|
|
766 |
|
|
|
767 |
/* The dev->transfer->buffer and dev->transfer objects are cleaned up
|
|
|
768 |
in hid_close(). They are not cleaned up here because this thread
|
|
|
769 |
could end either due to a disconnect or due to a user
|
|
|
770 |
call to hid_close(). In both cases the objects can be safely
|
|
|
771 |
cleaned up after the call to pthread_join() (in hid_close()), but
|
|
|
772 |
since hid_close() calls libusb_cancel_transfer(), on these objects,
|
|
|
773 |
they can not be cleaned up here. */
|
|
|
774 |
|
|
|
775 |
return NULL;
|
|
|
776 |
}
|
|
|
777 |
|
|
|
778 |
|
|
|
779 |
hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
|
|
780 |
{
|
|
|
781 |
hid_device *dev = NULL;
|
|
|
782 |
|
|
|
783 |
libusb_device **devs;
|
|
|
784 |
libusb_device *usb_dev;
|
|
|
785 |
int res;
|
|
|
786 |
int d = 0;
|
|
|
787 |
int good_open = 0;
|
|
|
788 |
|
|
|
789 |
dev = new_hid_device();
|
|
|
790 |
|
|
|
791 |
if(hid_init() < 0)
|
|
|
792 |
return NULL;
|
|
|
793 |
|
|
|
794 |
libusb_get_device_list(usb_context, &devs);
|
|
|
795 |
while ((usb_dev = devs[d++]) != NULL) {
|
|
|
796 |
struct libusb_device_descriptor desc;
|
|
|
797 |
struct libusb_config_descriptor *conf_desc = NULL;
|
|
|
798 |
int i,j,k;
|
|
|
799 |
libusb_get_device_descriptor(usb_dev, &desc);
|
|
|
800 |
|
|
|
801 |
if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0)
|
|
|
802 |
continue;
|
|
|
803 |
for (j = 0; j < conf_desc->bNumInterfaces; j++) {
|
|
|
804 |
const struct libusb_interface *intf = &conf_desc->interface[j];
|
|
|
805 |
for (k = 0; k < intf->num_altsetting; k++) {
|
|
|
806 |
const struct libusb_interface_descriptor *intf_desc;
|
|
|
807 |
intf_desc = &intf->altsetting[k];
|
|
|
808 |
if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) {
|
|
|
809 |
char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber);
|
|
|
810 |
if (!strcmp(dev_path, path)) {
|
|
|
811 |
/* Matched Paths. Open this device */
|
|
|
812 |
|
|
|
813 |
/* OPEN HERE */
|
|
|
814 |
res = libusb_open(usb_dev, &dev->device_handle);
|
|
|
815 |
if (res < 0) {
|
|
|
816 |
LOG("can't open device\n");
|
|
|
817 |
free(dev_path);
|
|
|
818 |
break;
|
|
|
819 |
}
|
|
|
820 |
good_open = 1;
|
|
|
821 |
#ifdef DETACH_KERNEL_DRIVER
|
|
|
822 |
/* Detach the kernel driver, but only if the
|
|
|
823 |
device is managed by the kernel */
|
|
|
824 |
if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) {
|
|
|
825 |
res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber);
|
|
|
826 |
if (res < 0) {
|
|
|
827 |
libusb_close(dev->device_handle);
|
|
|
828 |
LOG("Unable to detach Kernel Driver\n");
|
|
|
829 |
free(dev_path);
|
|
|
830 |
good_open = 0;
|
|
|
831 |
break;
|
|
|
832 |
}
|
|
|
833 |
}
|
|
|
834 |
#endif
|
|
|
835 |
res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber);
|
|
|
836 |
if (res < 0) {
|
|
|
837 |
LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res);
|
|
|
838 |
free(dev_path);
|
|
|
839 |
libusb_close(dev->device_handle);
|
|
|
840 |
good_open = 0;
|
|
|
841 |
break;
|
|
|
842 |
}
|
|
|
843 |
|
|
|
844 |
/* Store off the string descriptor indexes */
|
|
|
845 |
dev->manufacturer_index = desc.iManufacturer;
|
|
|
846 |
dev->product_index = desc.iProduct;
|
|
|
847 |
dev->serial_index = desc.iSerialNumber;
|
|
|
848 |
|
|
|
849 |
/* Store off the interface number */
|
|
|
850 |
dev->interface = intf_desc->bInterfaceNumber;
|
|
|
851 |
|
|
|
852 |
/* Find the INPUT and OUTPUT endpoints. An
|
|
|
853 |
OUTPUT endpoint is not required. */
|
|
|
854 |
for (i = 0; i < intf_desc->bNumEndpoints; i++) {
|
|
|
855 |
const struct libusb_endpoint_descriptor *ep
|
|
|
856 |
= &intf_desc->endpoint[i];
|
|
|
857 |
|
|
|
858 |
/* Determine the type and direction of this
|
|
|
859 |
endpoint. */
|
|
|
860 |
int is_interrupt =
|
|
|
861 |
(ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
|
|
|
862 |
== LIBUSB_TRANSFER_TYPE_INTERRUPT;
|
|
|
863 |
int is_output =
|
|
|
864 |
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
|
|
|
865 |
== LIBUSB_ENDPOINT_OUT;
|
|
|
866 |
int is_input =
|
|
|
867 |
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
|
|
|
868 |
== LIBUSB_ENDPOINT_IN;
|
|
|
869 |
|
|
|
870 |
/* Decide whether to use it for intput or output. */
|
|
|
871 |
if (dev->input_endpoint == 0 &&
|
|
|
872 |
is_interrupt && is_input) {
|
|
|
873 |
/* Use this endpoint for INPUT */
|
|
|
874 |
dev->input_endpoint = ep->bEndpointAddress;
|
|
|
875 |
dev->input_ep_max_packet_size = ep->wMaxPacketSize;
|
|
|
876 |
}
|
|
|
877 |
if (dev->output_endpoint == 0 &&
|
|
|
878 |
is_interrupt && is_output) {
|
|
|
879 |
/* Use this endpoint for OUTPUT */
|
|
|
880 |
dev->output_endpoint = ep->bEndpointAddress;
|
|
|
881 |
}
|
|
|
882 |
}
|
|
|
883 |
|
|
|
884 |
pthread_create(&dev->thread, NULL, read_thread, dev);
|
|
|
885 |
|
|
|
886 |
/* Wait here for the read thread to be initialized. */
|
|
|
887 |
pthread_barrier_wait(&dev->barrier);
|
|
|
888 |
|
|
|
889 |
}
|
|
|
890 |
free(dev_path);
|
|
|
891 |
}
|
|
|
892 |
}
|
|
|
893 |
}
|
|
|
894 |
libusb_free_config_descriptor(conf_desc);
|
|
|
895 |
|
|
|
896 |
}
|
|
|
897 |
|
|
|
898 |
libusb_free_device_list(devs, 1);
|
|
|
899 |
|
|
|
900 |
/* If we have a good handle, return it. */
|
|
|
901 |
if (good_open) {
|
|
|
902 |
return dev;
|
|
|
903 |
}
|
|
|
904 |
else {
|
|
|
905 |
/* Unable to open any devices. */
|
|
|
906 |
free_hid_device(dev);
|
|
|
907 |
return NULL;
|
|
|
908 |
}
|
|
|
909 |
}
|
|
|
910 |
|
|
|
911 |
|
|
|
912 |
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
|
|
|
913 |
{
|
|
|
914 |
int res;
|
|
|
915 |
int report_number = data[0];
|
|
|
916 |
int skipped_report_id = 0;
|
|
|
917 |
|
|
|
918 |
if (report_number == 0x0) {
|
|
|
919 |
data++;
|
|
|
920 |
length--;
|
|
|
921 |
skipped_report_id = 1;
|
|
|
922 |
}
|
|
|
923 |
|
|
|
924 |
|
|
|
925 |
if (dev->output_endpoint <= 0) {
|
|
|
926 |
/* No interrput out endpoint. Use the Control Endpoint */
|
|
|
927 |
res = libusb_control_transfer(dev->device_handle,
|
|
|
928 |
LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT,
|
|
|
929 |
0x09/*HID Set_Report*/,
|
|
|
930 |
(2/*HID output*/ << 8) | report_number,
|
|
|
931 |
dev->interface,
|
|
|
932 |
(unsigned char *)data, length,
|
|
|
933 |
1000/*timeout millis*/);
|
|
|
934 |
|
|
|
935 |
if (res < 0)
|
|
|
936 |
return -1;
|
|
|
937 |
|
|
|
938 |
if (skipped_report_id)
|
|
|
939 |
length++;
|
|
|
940 |
|
|
|
941 |
return length;
|
|
|
942 |
}
|
|
|
943 |
else {
|
|
|
944 |
/* Use the interrupt out endpoint */
|
|
|
945 |
int actual_length;
|
|
|
946 |
res = libusb_interrupt_transfer(dev->device_handle,
|
|
|
947 |
dev->output_endpoint,
|
|
|
948 |
(unsigned char*)data,
|
|
|
949 |
length,
|
|
|
950 |
&actual_length, 1000);
|
|
|
951 |
|
|
|
952 |
if (res < 0)
|
|
|
953 |
return -1;
|
|
|
954 |
|
|
|
955 |
if (skipped_report_id)
|
|
|
956 |
actual_length++;
|
|
|
957 |
|
|
|
958 |
return actual_length;
|
|
|
959 |
}
|
|
|
960 |
}
|
|
|
961 |
|
|
|
962 |
/* Helper function, to simplify hid_read().
|
|
|
963 |
This should be called with dev->mutex locked. */
|
|
|
964 |
static int return_data(hid_device *dev, unsigned char *data, size_t length)
|
|
|
965 |
{
|
|
|
966 |
/* Copy the data out of the linked list item (rpt) into the
|
|
|
967 |
return buffer (data), and delete the liked list item. */
|
|
|
968 |
struct input_report *rpt = dev->input_reports;
|
|
|
969 |
size_t len = (length < rpt->len)? length: rpt->len;
|
|
|
970 |
if (len > 0)
|
|
|
971 |
memcpy(data, rpt->data, len);
|
|
|
972 |
dev->input_reports = rpt->next;
|
|
|
973 |
free(rpt->data);
|
|
|
974 |
free(rpt);
|
|
|
975 |
return len;
|
|
|
976 |
}
|
|
|
977 |
|
|
|
978 |
static void cleanup_mutex(void *param)
|
|
|
979 |
{
|
|
|
980 |
hid_device *dev = param;
|
|
|
981 |
pthread_mutex_unlock(&dev->mutex);
|
|
|
982 |
}
|
|
|
983 |
|
|
|
984 |
|
|
|
985 |
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
|
|
|
986 |
{
|
|
|
987 |
int bytes_read = -1;
|
|
|
988 |
|
|
|
989 |
#if 0
|
|
|
990 |
int transferred;
|
|
|
991 |
int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000);
|
|
|
992 |
LOG("transferred: %d\n", transferred);
|
|
|
993 |
return transferred;
|
|
|
994 |
#endif
|
|
|
995 |
|
|
|
996 |
pthread_mutex_lock(&dev->mutex);
|
|
|
997 |
pthread_cleanup_push(&cleanup_mutex, dev);
|
|
|
998 |
|
|
|
999 |
/* There's an input report queued up. Return it. */
|
|
|
1000 |
if (dev->input_reports) {
|
|
|
1001 |
/* Return the first one */
|
|
|
1002 |
bytes_read = return_data(dev, data, length);
|
|
|
1003 |
goto ret;
|
|
|
1004 |
}
|
|
|
1005 |
|
|
|
1006 |
if (dev->shutdown_thread) {
|
|
|
1007 |
/* This means the device has been disconnected.
|
|
|
1008 |
An error code of -1 should be returned. */
|
|
|
1009 |
bytes_read = -1;
|
|
|
1010 |
goto ret;
|
|
|
1011 |
}
|
|
|
1012 |
|
|
|
1013 |
if (milliseconds == -1) {
|
|
|
1014 |
/* Blocking */
|
|
|
1015 |
while (!dev->input_reports && !dev->shutdown_thread) {
|
|
|
1016 |
pthread_cond_wait(&dev->condition, &dev->mutex);
|
|
|
1017 |
}
|
|
|
1018 |
if (dev->input_reports) {
|
|
|
1019 |
bytes_read = return_data(dev, data, length);
|
|
|
1020 |
}
|
|
|
1021 |
}
|
|
|
1022 |
else if (milliseconds > 0) {
|
|
|
1023 |
/* Non-blocking, but called with timeout. */
|
|
|
1024 |
int res;
|
|
|
1025 |
struct timespec ts;
|
|
|
1026 |
clock_gettime(CLOCK_REALTIME, &ts);
|
|
|
1027 |
ts.tv_sec += milliseconds / 1000;
|
|
|
1028 |
ts.tv_nsec += (milliseconds % 1000) * 1000000;
|
|
|
1029 |
if (ts.tv_nsec >= 1000000000L) {
|
|
|
1030 |
ts.tv_sec++;
|
|
|
1031 |
ts.tv_nsec -= 1000000000L;
|
|
|
1032 |
}
|
|
|
1033 |
|
|
|
1034 |
while (!dev->input_reports && !dev->shutdown_thread) {
|
|
|
1035 |
res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts);
|
|
|
1036 |
if (res == 0) {
|
|
|
1037 |
if (dev->input_reports) {
|
|
|
1038 |
bytes_read = return_data(dev, data, length);
|
|
|
1039 |
break;
|
|
|
1040 |
}
|
|
|
1041 |
|
|
|
1042 |
/* If we're here, there was a spurious wake up
|
|
|
1043 |
or the read thread was shutdown. Run the
|
|
|
1044 |
loop again (ie: don't break). */
|
|
|
1045 |
}
|
|
|
1046 |
else if (res == ETIMEDOUT) {
|
|
|
1047 |
/* Timed out. */
|
|
|
1048 |
bytes_read = 0;
|
|
|
1049 |
break;
|
|
|
1050 |
}
|
|
|
1051 |
else {
|
|
|
1052 |
/* Error. */
|
|
|
1053 |
bytes_read = -1;
|
|
|
1054 |
break;
|
|
|
1055 |
}
|
|
|
1056 |
}
|
|
|
1057 |
}
|
|
|
1058 |
else {
|
|
|
1059 |
/* Purely non-blocking */
|
|
|
1060 |
bytes_read = 0;
|
|
|
1061 |
}
|
|
|
1062 |
|
|
|
1063 |
ret:
|
|
|
1064 |
pthread_mutex_unlock(&dev->mutex);
|
|
|
1065 |
pthread_cleanup_pop(0);
|
|
|
1066 |
|
|
|
1067 |
return bytes_read;
|
|
|
1068 |
}
|
|
|
1069 |
|
|
|
1070 |
int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
|
|
1071 |
{
|
|
|
1072 |
return hid_read_timeout(dev, data, length, dev->blocking ? -1 : 0);
|
|
|
1073 |
}
|
|
|
1074 |
|
|
|
1075 |
int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
|
|
|
1076 |
{
|
|
|
1077 |
dev->blocking = !nonblock;
|
|
|
1078 |
|
|
|
1079 |
return 0;
|
|
|
1080 |
}
|
|
|
1081 |
|
|
|
1082 |
|
|
|
1083 |
int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
|
|
|
1084 |
{
|
|
|
1085 |
int res = -1;
|
|
|
1086 |
int skipped_report_id = 0;
|
|
|
1087 |
int report_number = data[0];
|
|
|
1088 |
|
|
|
1089 |
if (report_number == 0x0) {
|
|
|
1090 |
data++;
|
|
|
1091 |
length--;
|
|
|
1092 |
skipped_report_id = 1;
|
|
|
1093 |
}
|
|
|
1094 |
|
|
|
1095 |
res = libusb_control_transfer(dev->device_handle,
|
|
|
1096 |
LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT,
|
|
|
1097 |
0x09/*HID set_report*/,
|
|
|
1098 |
(3/*HID feature*/ << 8) | report_number,
|
|
|
1099 |
dev->interface,
|
|
|
1100 |
(unsigned char *)data, length,
|
|
|
1101 |
1000/*timeout millis*/);
|
|
|
1102 |
|
|
|
1103 |
if (res < 0)
|
|
|
1104 |
return -1;
|
|
|
1105 |
|
|
|
1106 |
/* Account for the report ID */
|
|
|
1107 |
if (skipped_report_id)
|
|
|
1108 |
length++;
|
|
|
1109 |
|
|
|
1110 |
return length;
|
|
|
1111 |
}
|
|
|
1112 |
|
|
|
1113 |
int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
|
|
|
1114 |
{
|
|
|
1115 |
int res = -1;
|
|
|
1116 |
int skipped_report_id = 0;
|
|
|
1117 |
int report_number = data[0];
|
|
|
1118 |
|
|
|
1119 |
if (report_number == 0x0) {
|
|
|
1120 |
/* Offset the return buffer by 1, so that the report ID
|
|
|
1121 |
will remain in byte 0. */
|
|
|
1122 |
data++;
|
|
|
1123 |
length--;
|
|
|
1124 |
skipped_report_id = 1;
|
|
|
1125 |
}
|
|
|
1126 |
res = libusb_control_transfer(dev->device_handle,
|
|
|
1127 |
LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN,
|
|
|
1128 |
0x01/*HID get_report*/,
|
|
|
1129 |
(3/*HID feature*/ << 8) | report_number,
|
|
|
1130 |
dev->interface,
|
|
|
1131 |
(unsigned char *)data, length,
|
|
|
1132 |
1000/*timeout millis*/);
|
|
|
1133 |
|
|
|
1134 |
if (res < 0)
|
|
|
1135 |
return -1;
|
|
|
1136 |
|
|
|
1137 |
if (skipped_report_id)
|
|
|
1138 |
res++;
|
|
|
1139 |
|
|
|
1140 |
return res;
|
|
|
1141 |
}
|
|
|
1142 |
|
|
|
1143 |
|
|
|
1144 |
void HID_API_EXPORT hid_close(hid_device *dev)
|
|
|
1145 |
{
|
|
|
1146 |
if (!dev)
|
|
|
1147 |
return;
|
|
|
1148 |
|
|
|
1149 |
/* Cause read_thread() to stop. */
|
|
|
1150 |
dev->shutdown_thread = 1;
|
|
|
1151 |
libusb_cancel_transfer(dev->transfer);
|
|
|
1152 |
|
|
|
1153 |
/* Wait for read_thread() to end. */
|
|
|
1154 |
pthread_join(dev->thread, NULL);
|
|
|
1155 |
|
|
|
1156 |
/* Clean up the Transfer objects allocated in read_thread(). */
|
|
|
1157 |
free(dev->transfer->buffer);
|
|
|
1158 |
libusb_free_transfer(dev->transfer);
|
|
|
1159 |
|
|
|
1160 |
/* release the interface */
|
|
|
1161 |
libusb_release_interface(dev->device_handle, dev->interface);
|
|
|
1162 |
|
|
|
1163 |
/* Close the handle */
|
|
|
1164 |
libusb_close(dev->device_handle);
|
|
|
1165 |
|
|
|
1166 |
/* Clear out the queue of received reports. */
|
|
|
1167 |
pthread_mutex_lock(&dev->mutex);
|
|
|
1168 |
while (dev->input_reports) {
|
|
|
1169 |
return_data(dev, NULL, 0);
|
|
|
1170 |
}
|
|
|
1171 |
pthread_mutex_unlock(&dev->mutex);
|
|
|
1172 |
|
|
|
1173 |
free_hid_device(dev);
|
|
|
1174 |
}
|
|
|
1175 |
|
|
|
1176 |
|
|
|
1177 |
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
|
|
1178 |
{
|
|
|
1179 |
return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen);
|
|
|
1180 |
}
|
|
|
1181 |
|
|
|
1182 |
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
|
|
1183 |
{
|
|
|
1184 |
return hid_get_indexed_string(dev, dev->product_index, string, maxlen);
|
|
|
1185 |
}
|
|
|
1186 |
|
|
|
1187 |
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
|
|
1188 |
{
|
|
|
1189 |
return hid_get_indexed_string(dev, dev->serial_index, string, maxlen);
|
|
|
1190 |
}
|
|
|
1191 |
|
|
|
1192 |
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
|
|
|
1193 |
{
|
|
|
1194 |
wchar_t *str;
|
|
|
1195 |
|
|
|
1196 |
str = get_usb_string(dev->device_handle, string_index);
|
|
|
1197 |
if (str) {
|
|
|
1198 |
wcsncpy(string, str, maxlen);
|
|
|
1199 |
string[maxlen-1] = L'\0';
|
|
|
1200 |
free(str);
|
|
|
1201 |
return 0;
|
|
|
1202 |
}
|
|
|
1203 |
else
|
|
|
1204 |
return -1;
|
|
|
1205 |
}
|
|
|
1206 |
|
|
|
1207 |
|
|
|
1208 |
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
|
|
|
1209 |
{
|
|
|
1210 |
return NULL;
|
|
|
1211 |
}
|
|
|
1212 |
|
|
|
1213 |
|
|
|
1214 |
struct lang_map_entry {
|
|
|
1215 |
const char *name;
|
|
|
1216 |
const char *string_code;
|
|
|
1217 |
uint16_t usb_code;
|
|
|
1218 |
};
|
|
|
1219 |
|
|
|
1220 |
#define LANG(name,code,usb_code) { name, code, usb_code }
|
|
|
1221 |
static struct lang_map_entry lang_map[] = {
|
|
|
1222 |
LANG("Afrikaans", "af", 0x0436),
|
|
|
1223 |
LANG("Albanian", "sq", 0x041C),
|
|
|
1224 |
LANG("Arabic - United Arab Emirates", "ar_ae", 0x3801),
|
|
|
1225 |
LANG("Arabic - Bahrain", "ar_bh", 0x3C01),
|
|
|
1226 |
LANG("Arabic - Algeria", "ar_dz", 0x1401),
|
|
|
1227 |
LANG("Arabic - Egypt", "ar_eg", 0x0C01),
|
|
|
1228 |
LANG("Arabic - Iraq", "ar_iq", 0x0801),
|
|
|
1229 |
LANG("Arabic - Jordan", "ar_jo", 0x2C01),
|
|
|
1230 |
LANG("Arabic - Kuwait", "ar_kw", 0x3401),
|
|
|
1231 |
LANG("Arabic - Lebanon", "ar_lb", 0x3001),
|
|
|
1232 |
LANG("Arabic - Libya", "ar_ly", 0x1001),
|
|
|
1233 |
LANG("Arabic - Morocco", "ar_ma", 0x1801),
|
|
|
1234 |
LANG("Arabic - Oman", "ar_om", 0x2001),
|
|
|
1235 |
LANG("Arabic - Qatar", "ar_qa", 0x4001),
|
|
|
1236 |
LANG("Arabic - Saudi Arabia", "ar_sa", 0x0401),
|
|
|
1237 |
LANG("Arabic - Syria", "ar_sy", 0x2801),
|
|
|
1238 |
LANG("Arabic - Tunisia", "ar_tn", 0x1C01),
|
|
|
1239 |
LANG("Arabic - Yemen", "ar_ye", 0x2401),
|
|
|
1240 |
LANG("Armenian", "hy", 0x042B),
|
|
|
1241 |
LANG("Azeri - Latin", "az_az", 0x042C),
|
|
|
1242 |
LANG("Azeri - Cyrillic", "az_az", 0x082C),
|
|
|
1243 |
LANG("Basque", "eu", 0x042D),
|
|
|
1244 |
LANG("Belarusian", "be", 0x0423),
|
|
|
1245 |
LANG("Bulgarian", "bg", 0x0402),
|
|
|
1246 |
LANG("Catalan", "ca", 0x0403),
|
|
|
1247 |
LANG("Chinese - China", "zh_cn", 0x0804),
|
|
|
1248 |
LANG("Chinese - Hong Kong SAR", "zh_hk", 0x0C04),
|
|
|
1249 |
LANG("Chinese - Macau SAR", "zh_mo", 0x1404),
|
|
|
1250 |
LANG("Chinese - Singapore", "zh_sg", 0x1004),
|
|
|
1251 |
LANG("Chinese - Taiwan", "zh_tw", 0x0404),
|
|
|
1252 |
LANG("Croatian", "hr", 0x041A),
|
|
|
1253 |
LANG("Czech", "cs", 0x0405),
|
|
|
1254 |
LANG("Danish", "da", 0x0406),
|
|
|
1255 |
LANG("Dutch - Netherlands", "nl_nl", 0x0413),
|
|
|
1256 |
LANG("Dutch - Belgium", "nl_be", 0x0813),
|
|
|
1257 |
LANG("English - Australia", "en_au", 0x0C09),
|
|
|
1258 |
LANG("English - Belize", "en_bz", 0x2809),
|
|
|
1259 |
LANG("English - Canada", "en_ca", 0x1009),
|
|
|
1260 |
LANG("English - Caribbean", "en_cb", 0x2409),
|
|
|
1261 |
LANG("English - Ireland", "en_ie", 0x1809),
|
|
|
1262 |
LANG("English - Jamaica", "en_jm", 0x2009),
|
|
|
1263 |
LANG("English - New Zealand", "en_nz", 0x1409),
|
|
|
1264 |
LANG("English - Phillippines", "en_ph", 0x3409),
|
|
|
1265 |
LANG("English - Southern Africa", "en_za", 0x1C09),
|
|
|
1266 |
LANG("English - Trinidad", "en_tt", 0x2C09),
|
|
|
1267 |
LANG("English - Great Britain", "en_gb", 0x0809),
|
|
|
1268 |
LANG("English - United States", "en_us", 0x0409),
|
|
|
1269 |
LANG("Estonian", "et", 0x0425),
|
|
|
1270 |
LANG("Farsi", "fa", 0x0429),
|
|
|
1271 |
LANG("Finnish", "fi", 0x040B),
|
|
|
1272 |
LANG("Faroese", "fo", 0x0438),
|
|
|
1273 |
LANG("French - France", "fr_fr", 0x040C),
|
|
|
1274 |
LANG("French - Belgium", "fr_be", 0x080C),
|
|
|
1275 |
LANG("French - Canada", "fr_ca", 0x0C0C),
|
|
|
1276 |
LANG("French - Luxembourg", "fr_lu", 0x140C),
|
|
|
1277 |
LANG("French - Switzerland", "fr_ch", 0x100C),
|
|
|
1278 |
LANG("Gaelic - Ireland", "gd_ie", 0x083C),
|
|
|
1279 |
LANG("Gaelic - Scotland", "gd", 0x043C),
|
|
|
1280 |
LANG("German - Germany", "de_de", 0x0407),
|
|
|
1281 |
LANG("German - Austria", "de_at", 0x0C07),
|
|
|
1282 |
LANG("German - Liechtenstein", "de_li", 0x1407),
|
|
|
1283 |
LANG("German - Luxembourg", "de_lu", 0x1007),
|
|
|
1284 |
LANG("German - Switzerland", "de_ch", 0x0807),
|
|
|
1285 |
LANG("Greek", "el", 0x0408),
|
|
|
1286 |
LANG("Hebrew", "he", 0x040D),
|
|
|
1287 |
LANG("Hindi", "hi", 0x0439),
|
|
|
1288 |
LANG("Hungarian", "hu", 0x040E),
|
|
|
1289 |
LANG("Icelandic", "is", 0x040F),
|
|
|
1290 |
LANG("Indonesian", "id", 0x0421),
|
|
|
1291 |
LANG("Italian - Italy", "it_it", 0x0410),
|
|
|
1292 |
LANG("Italian - Switzerland", "it_ch", 0x0810),
|
|
|
1293 |
LANG("Japanese", "ja", 0x0411),
|
|
|
1294 |
LANG("Korean", "ko", 0x0412),
|
|
|
1295 |
LANG("Latvian", "lv", 0x0426),
|
|
|
1296 |
LANG("Lithuanian", "lt", 0x0427),
|
|
|
1297 |
LANG("F.Y.R.O. Macedonia", "mk", 0x042F),
|
|
|
1298 |
LANG("Malay - Malaysia", "ms_my", 0x043E),
|
|
|
1299 |
LANG("Malay – Brunei", "ms_bn", 0x083E),
|
|
|
1300 |
LANG("Maltese", "mt", 0x043A),
|
|
|
1301 |
LANG("Marathi", "mr", 0x044E),
|
|
|
1302 |
LANG("Norwegian - Bokml", "no_no", 0x0414),
|
|
|
1303 |
LANG("Norwegian - Nynorsk", "no_no", 0x0814),
|
|
|
1304 |
LANG("Polish", "pl", 0x0415),
|
|
|
1305 |
LANG("Portuguese - Portugal", "pt_pt", 0x0816),
|
|
|
1306 |
LANG("Portuguese - Brazil", "pt_br", 0x0416),
|
|
|
1307 |
LANG("Raeto-Romance", "rm", 0x0417),
|
|
|
1308 |
LANG("Romanian - Romania", "ro", 0x0418),
|
|
|
1309 |
LANG("Romanian - Republic of Moldova", "ro_mo", 0x0818),
|
|
|
1310 |
LANG("Russian", "ru", 0x0419),
|
|
|
1311 |
LANG("Russian - Republic of Moldova", "ru_mo", 0x0819),
|
|
|
1312 |
LANG("Sanskrit", "sa", 0x044F),
|
|
|
1313 |
LANG("Serbian - Cyrillic", "sr_sp", 0x0C1A),
|
|
|
1314 |
LANG("Serbian - Latin", "sr_sp", 0x081A),
|
|
|
1315 |
LANG("Setsuana", "tn", 0x0432),
|
|
|
1316 |
LANG("Slovenian", "sl", 0x0424),
|
|
|
1317 |
LANG("Slovak", "sk", 0x041B),
|
|
|
1318 |
LANG("Sorbian", "sb", 0x042E),
|
|
|
1319 |
LANG("Spanish - Spain (Traditional)", "es_es", 0x040A),
|
|
|
1320 |
LANG("Spanish - Argentina", "es_ar", 0x2C0A),
|
|
|
1321 |
LANG("Spanish - Bolivia", "es_bo", 0x400A),
|
|
|
1322 |
LANG("Spanish - Chile", "es_cl", 0x340A),
|
|
|
1323 |
LANG("Spanish - Colombia", "es_co", 0x240A),
|
|
|
1324 |
LANG("Spanish - Costa Rica", "es_cr", 0x140A),
|
|
|
1325 |
LANG("Spanish - Dominican Republic", "es_do", 0x1C0A),
|
|
|
1326 |
LANG("Spanish - Ecuador", "es_ec", 0x300A),
|
|
|
1327 |
LANG("Spanish - Guatemala", "es_gt", 0x100A),
|
|
|
1328 |
LANG("Spanish - Honduras", "es_hn", 0x480A),
|
|
|
1329 |
LANG("Spanish - Mexico", "es_mx", 0x080A),
|
|
|
1330 |
LANG("Spanish - Nicaragua", "es_ni", 0x4C0A),
|
|
|
1331 |
LANG("Spanish - Panama", "es_pa", 0x180A),
|
|
|
1332 |
LANG("Spanish - Peru", "es_pe", 0x280A),
|
|
|
1333 |
LANG("Spanish - Puerto Rico", "es_pr", 0x500A),
|
|
|
1334 |
LANG("Spanish - Paraguay", "es_py", 0x3C0A),
|
|
|
1335 |
LANG("Spanish - El Salvador", "es_sv", 0x440A),
|
|
|
1336 |
LANG("Spanish - Uruguay", "es_uy", 0x380A),
|
|
|
1337 |
LANG("Spanish - Venezuela", "es_ve", 0x200A),
|
|
|
1338 |
LANG("Southern Sotho", "st", 0x0430),
|
|
|
1339 |
LANG("Swahili", "sw", 0x0441),
|
|
|
1340 |
LANG("Swedish - Sweden", "sv_se", 0x041D),
|
|
|
1341 |
LANG("Swedish - Finland", "sv_fi", 0x081D),
|
|
|
1342 |
LANG("Tamil", "ta", 0x0449),
|
|
|
1343 |
LANG("Tatar", "tt", 0X0444),
|
|
|
1344 |
LANG("Thai", "th", 0x041E),
|
|
|
1345 |
LANG("Turkish", "tr", 0x041F),
|
|
|
1346 |
LANG("Tsonga", "ts", 0x0431),
|
|
|
1347 |
LANG("Ukrainian", "uk", 0x0422),
|
|
|
1348 |
LANG("Urdu", "ur", 0x0420),
|
|
|
1349 |
LANG("Uzbek - Cyrillic", "uz_uz", 0x0843),
|
|
|
1350 |
LANG("Uzbek – Latin", "uz_uz", 0x0443),
|
|
|
1351 |
LANG("Vietnamese", "vi", 0x042A),
|
|
|
1352 |
LANG("Xhosa", "xh", 0x0434),
|
|
|
1353 |
LANG("Yiddish", "yi", 0x043D),
|
|
|
1354 |
LANG("Zulu", "zu", 0x0435),
|
|
|
1355 |
LANG(NULL, NULL, 0x0),
|
|
|
1356 |
};
|
|
|
1357 |
|
|
|
1358 |
uint16_t get_usb_code_for_current_locale(void)
|
|
|
1359 |
{
|
|
|
1360 |
char *locale;
|
|
|
1361 |
char search_string[64];
|
|
|
1362 |
char *ptr;
|
|
|
1363 |
struct lang_map_entry *lang;
|
|
|
1364 |
|
|
|
1365 |
/* Get the current locale. */
|
|
|
1366 |
locale = setlocale(0, NULL);
|
|
|
1367 |
if (!locale)
|
|
|
1368 |
return 0x0;
|
|
|
1369 |
|
|
|
1370 |
/* Make a copy of the current locale string. */
|
|
|
1371 |
strncpy(search_string, locale, sizeof(search_string));
|
|
|
1372 |
search_string[sizeof(search_string)-1] = '\0';
|
|
|
1373 |
|
|
|
1374 |
/* Chop off the encoding part, and make it lower case. */
|
|
|
1375 |
ptr = search_string;
|
|
|
1376 |
while (*ptr) {
|
|
|
1377 |
*ptr = tolower(*ptr);
|
|
|
1378 |
if (*ptr == '.') {
|
|
|
1379 |
*ptr = '\0';
|
|
|
1380 |
break;
|
|
|
1381 |
}
|
|
|
1382 |
ptr++;
|
|
|
1383 |
}
|
|
|
1384 |
|
|
|
1385 |
/* Find the entry which matches the string code of our locale. */
|
|
|
1386 |
lang = lang_map;
|
|
|
1387 |
while (lang->string_code) {
|
|
|
1388 |
if (!strcmp(lang->string_code, search_string)) {
|
|
|
1389 |
return lang->usb_code;
|
|
|
1390 |
}
|
|
|
1391 |
lang++;
|
|
|
1392 |
}
|
|
|
1393 |
|
|
|
1394 |
/* There was no match. Find with just the language only. */
|
|
|
1395 |
/* Chop off the variant. Chop it off at the '_'. */
|
|
|
1396 |
ptr = search_string;
|
|
|
1397 |
while (*ptr) {
|
|
|
1398 |
*ptr = tolower(*ptr);
|
|
|
1399 |
if (*ptr == '_') {
|
|
|
1400 |
*ptr = '\0';
|
|
|
1401 |
break;
|
|
|
1402 |
}
|
|
|
1403 |
ptr++;
|
|
|
1404 |
}
|
|
|
1405 |
|
|
|
1406 |
#if 0 /* TODO: Do we need this? */
|
|
|
1407 |
/* Find the entry which matches the string code of our language. */
|
|
|
1408 |
lang = lang_map;
|
|
|
1409 |
while (lang->string_code) {
|
|
|
1410 |
if (!strcmp(lang->string_code, search_string)) {
|
|
|
1411 |
return lang->usb_code;
|
|
|
1412 |
}
|
|
|
1413 |
lang++;
|
|
|
1414 |
}
|
|
|
1415 |
#endif
|
|
|
1416 |
|
|
|
1417 |
/* Found nothing. */
|
|
|
1418 |
return 0x0;
|
|
|
1419 |
}
|
|
|
1420 |
|
|
|
1421 |
#ifdef __cplusplus
|
|
|
1422 |
}
|
|
|
1423 |
#endif
|