diff options
Diffstat (limited to 'src/hid_win.c')
-rw-r--r-- | src/hid_win.c | 398 |
1 files changed, 299 insertions, 99 deletions
diff --git a/src/hid_win.c b/src/hid_win.c index f970589..018b4d9 100644 --- a/src/hid_win.c +++ b/src/hid_win.c | |||
@@ -14,19 +14,39 @@ | |||
14 | #include <windows.h> | 14 | #include <windows.h> |
15 | #include <setupapi.h> | 15 | #include <setupapi.h> |
16 | #include <initguid.h> | 16 | #include <initguid.h> |
17 | #include <devpkey.h> | ||
18 | #include <devpropdef.h> | ||
17 | #include <hidclass.h> | 19 | #include <hidclass.h> |
18 | #include <hidsdi.h> | 20 | #include <hidsdi.h> |
19 | 21 | ||
20 | #include "fido.h" | 22 | #include "fido.h" |
21 | 23 | ||
22 | #define REPORT_LEN 65 | 24 | #if defined(__MINGW32__) && __MINGW64_VERSION_MAJOR < 6 |
25 | WINSETUPAPI WINBOOL WINAPI SetupDiGetDevicePropertyW(HDEVINFO, | ||
26 | PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE, | ||
27 | DWORD, PDWORD, DWORD); | ||
28 | #endif | ||
29 | |||
30 | #if defined(__MINGW32__) | ||
31 | DEFINE_DEVPROPKEY(DEVPKEY_Device_Parent, 0x4340a6c5, 0x93fa, 0x4706, 0x97, | ||
32 | 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8); | ||
33 | #endif | ||
34 | |||
35 | struct hid_win { | ||
36 | HANDLE dev; | ||
37 | OVERLAPPED overlap; | ||
38 | int report_pending; | ||
39 | size_t report_in_len; | ||
40 | size_t report_out_len; | ||
41 | unsigned char report[1 + CTAP_MAX_REPORT_LEN]; | ||
42 | }; | ||
23 | 43 | ||
24 | static bool | 44 | static bool |
25 | is_fido(HANDLE dev) | 45 | is_fido(HANDLE dev) |
26 | { | 46 | { |
27 | PHIDP_PREPARSED_DATA data = NULL; | 47 | PHIDP_PREPARSED_DATA data = NULL; |
28 | HIDP_CAPS caps; | 48 | HIDP_CAPS caps; |
29 | uint16_t usage_page = 0; | 49 | int fido = 0; |
30 | 50 | ||
31 | if (HidD_GetPreparsedData(dev, &data) == false) { | 51 | if (HidD_GetPreparsedData(dev, &data) == false) { |
32 | fido_log_debug("%s: HidD_GetPreparsedData", __func__); | 52 | fido_log_debug("%s: HidD_GetPreparsedData", __func__); |
@@ -38,18 +58,48 @@ is_fido(HANDLE dev) | |||
38 | goto fail; | 58 | goto fail; |
39 | } | 59 | } |
40 | 60 | ||
41 | if (caps.OutputReportByteLength != REPORT_LEN || | 61 | fido = (uint16_t)caps.UsagePage == 0xf1d0; |
42 | caps.InputReportByteLength != REPORT_LEN) { | 62 | fail: |
43 | fido_log_debug("%s: unsupported report len", __func__); | 63 | if (data != NULL) |
64 | HidD_FreePreparsedData(data); | ||
65 | |||
66 | return (fido); | ||
67 | } | ||
68 | |||
69 | static int | ||
70 | get_report_len(HANDLE dev, int dir, size_t *report_len) | ||
71 | { | ||
72 | PHIDP_PREPARSED_DATA data = NULL; | ||
73 | HIDP_CAPS caps; | ||
74 | USHORT v; | ||
75 | int ok = -1; | ||
76 | |||
77 | if (HidD_GetPreparsedData(dev, &data) == false) { | ||
78 | fido_log_debug("%s: HidD_GetPreparsedData/%d", __func__, dir); | ||
44 | goto fail; | 79 | goto fail; |
45 | } | 80 | } |
46 | 81 | ||
47 | usage_page = caps.UsagePage; | 82 | if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) { |
83 | fido_log_debug("%s: HidP_GetCaps/%d", __func__, dir); | ||
84 | goto fail; | ||
85 | } | ||
86 | |||
87 | if (dir == 0) | ||
88 | v = caps.InputReportByteLength; | ||
89 | else | ||
90 | v = caps.OutputReportByteLength; | ||
91 | |||
92 | if ((*report_len = (size_t)v) == 0) { | ||
93 | fido_log_debug("%s: report_len == 0", __func__); | ||
94 | goto fail; | ||
95 | } | ||
96 | |||
97 | ok = 0; | ||
48 | fail: | 98 | fail: |
49 | if (data != NULL) | 99 | if (data != NULL) |
50 | HidD_FreePreparsedData(data); | 100 | HidD_FreePreparsedData(data); |
51 | 101 | ||
52 | return (usage_page == 0xf1d0); | 102 | return (ok); |
53 | } | 103 | } |
54 | 104 | ||
55 | static int | 105 | static int |
@@ -59,13 +109,14 @@ get_int(HANDLE dev, int16_t *vendor_id, int16_t *product_id) | |||
59 | 109 | ||
60 | attr.Size = sizeof(attr); | 110 | attr.Size = sizeof(attr); |
61 | 111 | ||
62 | if (HidD_GetAttributes(dev, &attr) == false) { | 112 | if (HidD_GetAttributes(dev, &attr) == false || |
113 | attr.VendorID > INT16_MAX || attr.ProductID > INT16_MAX) { | ||
63 | fido_log_debug("%s: HidD_GetAttributes", __func__); | 114 | fido_log_debug("%s: HidD_GetAttributes", __func__); |
64 | return (-1); | 115 | return (-1); |
65 | } | 116 | } |
66 | 117 | ||
67 | *vendor_id = attr.VendorID; | 118 | *vendor_id = (int16_t)attr.VendorID; |
68 | *product_id = attr.ProductID; | 119 | *product_id = (int16_t)attr.ProductID; |
69 | 120 | ||
70 | return (0); | 121 | return (0); |
71 | } | 122 | } |
@@ -91,7 +142,7 @@ get_str(HANDLE dev, char **manufacturer, char **product) | |||
91 | goto fail; | 142 | goto fail; |
92 | } | 143 | } |
93 | 144 | ||
94 | if ((*manufacturer = malloc(utf8_len)) == NULL) { | 145 | if ((*manufacturer = malloc((size_t)utf8_len)) == NULL) { |
95 | fido_log_debug("%s: malloc", __func__); | 146 | fido_log_debug("%s: malloc", __func__); |
96 | goto fail; | 147 | goto fail; |
97 | } | 148 | } |
@@ -113,7 +164,7 @@ get_str(HANDLE dev, char **manufacturer, char **product) | |||
113 | goto fail; | 164 | goto fail; |
114 | } | 165 | } |
115 | 166 | ||
116 | if ((*product = malloc(utf8_len)) == NULL) { | 167 | if ((*product = malloc((size_t)utf8_len)) == NULL) { |
117 | fido_log_debug("%s: malloc", __func__); | 168 | fido_log_debug("%s: malloc", __func__); |
118 | goto fail; | 169 | goto fail; |
119 | } | 170 | } |
@@ -136,25 +187,138 @@ fail: | |||
136 | return (ok); | 187 | return (ok); |
137 | } | 188 | } |
138 | 189 | ||
190 | static char * | ||
191 | get_path(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *ifdata) | ||
192 | { | ||
193 | SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL; | ||
194 | char *path = NULL; | ||
195 | DWORD len = 0; | ||
196 | |||
197 | /* | ||
198 | * "Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail | ||
199 | * with a NULL DeviceInterfaceDetailData pointer, a | ||
200 | * DeviceInterfaceDetailDataSize of zero, and a valid RequiredSize | ||
201 | * variable. In response to such a call, this function returns the | ||
202 | * required buffer size at RequiredSize and fails with GetLastError | ||
203 | * returning ERROR_INSUFFICIENT_BUFFER." | ||
204 | */ | ||
205 | if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, NULL, 0, &len, | ||
206 | NULL) != false || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { | ||
207 | fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1", | ||
208 | __func__); | ||
209 | goto fail; | ||
210 | } | ||
211 | |||
212 | if ((ifdetail = malloc(len)) == NULL) { | ||
213 | fido_log_debug("%s: malloc", __func__); | ||
214 | goto fail; | ||
215 | } | ||
216 | |||
217 | ifdetail->cbSize = sizeof(*ifdetail); | ||
218 | |||
219 | if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, ifdetail, len, | ||
220 | NULL, NULL) == false) { | ||
221 | fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2", | ||
222 | __func__); | ||
223 | goto fail; | ||
224 | } | ||
225 | |||
226 | if ((path = strdup(ifdetail->DevicePath)) == NULL) { | ||
227 | fido_log_debug("%s: strdup", __func__); | ||
228 | goto fail; | ||
229 | } | ||
230 | |||
231 | fail: | ||
232 | free(ifdetail); | ||
233 | |||
234 | return (path); | ||
235 | } | ||
236 | |||
237 | #ifndef FIDO_HID_ANY | ||
238 | static bool | ||
239 | hid_ok(HDEVINFO devinfo, DWORD idx) | ||
240 | { | ||
241 | SP_DEVINFO_DATA devinfo_data; | ||
242 | wchar_t *parent = NULL; | ||
243 | DWORD parent_type = DEVPROP_TYPE_STRING; | ||
244 | DWORD len = 0; | ||
245 | bool ok = false; | ||
246 | |||
247 | memset(&devinfo_data, 0, sizeof(devinfo_data)); | ||
248 | devinfo_data.cbSize = sizeof(devinfo_data); | ||
249 | |||
250 | if (SetupDiEnumDeviceInfo(devinfo, idx, &devinfo_data) == false) { | ||
251 | fido_log_debug("%s: SetupDiEnumDeviceInfo", __func__); | ||
252 | goto fail; | ||
253 | } | ||
254 | |||
255 | if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data, | ||
256 | &DEVPKEY_Device_Parent, &parent_type, NULL, 0, &len, 0) != false || | ||
257 | GetLastError() != ERROR_INSUFFICIENT_BUFFER) { | ||
258 | fido_log_debug("%s: SetupDiGetDevicePropertyW 1", __func__); | ||
259 | goto fail; | ||
260 | } | ||
261 | |||
262 | if ((parent = malloc(len)) == NULL) { | ||
263 | fido_log_debug("%s: malloc", __func__); | ||
264 | goto fail; | ||
265 | } | ||
266 | |||
267 | if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data, | ||
268 | &DEVPKEY_Device_Parent, &parent_type, (PBYTE)parent, len, NULL, | ||
269 | 0) == false) { | ||
270 | fido_log_debug("%s: SetupDiGetDevicePropertyW 2", __func__); | ||
271 | goto fail; | ||
272 | } | ||
273 | |||
274 | ok = wcsncmp(parent, L"USB\\", 4) == 0; | ||
275 | fail: | ||
276 | free(parent); | ||
277 | |||
278 | return (ok); | ||
279 | } | ||
280 | #endif | ||
281 | |||
139 | static int | 282 | static int |
140 | copy_info(fido_dev_info_t *di, const char *path) | 283 | copy_info(fido_dev_info_t *di, HDEVINFO devinfo, DWORD idx, |
284 | SP_DEVICE_INTERFACE_DATA *ifdata) | ||
141 | { | 285 | { |
142 | HANDLE dev = INVALID_HANDLE_VALUE; | 286 | HANDLE dev = INVALID_HANDLE_VALUE; |
143 | int ok = -1; | 287 | int ok = -1; |
144 | 288 | ||
145 | memset(di, 0, sizeof(*di)); | 289 | memset(di, 0, sizeof(*di)); |
146 | 290 | ||
147 | dev = CreateFileA(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | 291 | if ((di->path = get_path(devinfo, ifdata)) == NULL) { |
148 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | 292 | fido_log_debug("%s: get_path", __func__); |
149 | if (dev == INVALID_HANDLE_VALUE || is_fido(dev) == 0) | ||
150 | goto fail; | 293 | goto fail; |
294 | } | ||
151 | 295 | ||
152 | if (get_int(dev, &di->vendor_id, &di->product_id) < 0 || | 296 | fido_log_debug("%s: path=%s", __func__, di->path); |
153 | get_str(dev, &di->manufacturer, &di->product) < 0) | 297 | |
298 | #ifndef FIDO_HID_ANY | ||
299 | if (hid_ok(devinfo, idx) == false) { | ||
300 | fido_log_debug("%s: hid_ok", __func__); | ||
154 | goto fail; | 301 | goto fail; |
302 | } | ||
303 | #endif | ||
155 | 304 | ||
156 | if ((di->path = strdup(path)) == NULL) | 305 | dev = CreateFileA(di->path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, |
306 | NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | ||
307 | if (dev == INVALID_HANDLE_VALUE) { | ||
308 | fido_log_debug("%s: CreateFileA", __func__); | ||
157 | goto fail; | 309 | goto fail; |
310 | } | ||
311 | |||
312 | if (is_fido(dev) == false) { | ||
313 | fido_log_debug("%s: is_fido", __func__); | ||
314 | goto fail; | ||
315 | } | ||
316 | |||
317 | if (get_int(dev, &di->vendor_id, &di->product_id) < 0 || | ||
318 | get_str(dev, &di->manufacturer, &di->product) < 0) { | ||
319 | fido_log_debug("%s: get_int/get_str", __func__); | ||
320 | goto fail; | ||
321 | } | ||
158 | 322 | ||
159 | ok = 0; | 323 | ok = 0; |
160 | fail: | 324 | fail: |
@@ -174,66 +338,30 @@ fail: | |||
174 | int | 338 | int |
175 | fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) | 339 | fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) |
176 | { | 340 | { |
177 | GUID hid_guid = GUID_DEVINTERFACE_HID; | 341 | GUID hid_guid = GUID_DEVINTERFACE_HID; |
178 | HDEVINFO devinfo = INVALID_HANDLE_VALUE; | 342 | HDEVINFO devinfo = INVALID_HANDLE_VALUE; |
179 | SP_DEVICE_INTERFACE_DATA ifdata; | 343 | SP_DEVICE_INTERFACE_DATA ifdata; |
180 | SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL; | 344 | DWORD idx; |
181 | DWORD len = 0; | 345 | int r = FIDO_ERR_INTERNAL; |
182 | DWORD idx = 0; | ||
183 | int r = FIDO_ERR_INTERNAL; | ||
184 | 346 | ||
185 | *olen = 0; | 347 | *olen = 0; |
186 | 348 | ||
187 | if (ilen == 0) | 349 | if (ilen == 0) |
188 | return (FIDO_OK); /* nothing to do */ | 350 | return (FIDO_OK); /* nothing to do */ |
189 | |||
190 | if (devlist == NULL) | 351 | if (devlist == NULL) |
191 | return (FIDO_ERR_INVALID_ARGUMENT); | 352 | return (FIDO_ERR_INVALID_ARGUMENT); |
192 | 353 | ||
193 | devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL, | 354 | if ((devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL, |
194 | DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); | 355 | DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)) == INVALID_HANDLE_VALUE) { |
195 | if (devinfo == INVALID_HANDLE_VALUE) { | ||
196 | fido_log_debug("%s: SetupDiGetClassDevsA", __func__); | 356 | fido_log_debug("%s: SetupDiGetClassDevsA", __func__); |
197 | goto fail; | 357 | goto fail; |
198 | } | 358 | } |
199 | 359 | ||
200 | ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | 360 | ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); |
201 | 361 | ||
202 | while (SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid, idx++, | 362 | for (idx = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid, |
203 | &ifdata) == true) { | 363 | idx, &ifdata) == true; idx++) { |
204 | /* | 364 | if (copy_info(&devlist[*olen], devinfo, idx, &ifdata) == 0) { |
205 | * "Get the required buffer size. Call | ||
206 | * SetupDiGetDeviceInterfaceDetail with a NULL | ||
207 | * DeviceInterfaceDetailData pointer, a | ||
208 | * DeviceInterfaceDetailDataSize of zero, and a valid | ||
209 | * RequiredSize variable. In response to such a call, this | ||
210 | * function returns the required buffer size at RequiredSize | ||
211 | * and fails with GetLastError returning | ||
212 | * ERROR_INSUFFICIENT_BUFFER." | ||
213 | */ | ||
214 | if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, NULL, 0, | ||
215 | &len, NULL) != false || | ||
216 | GetLastError() != ERROR_INSUFFICIENT_BUFFER) { | ||
217 | fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1", | ||
218 | __func__); | ||
219 | goto fail; | ||
220 | } | ||
221 | |||
222 | if ((ifdetail = malloc(len)) == NULL) { | ||
223 | fido_log_debug("%s: malloc", __func__); | ||
224 | goto fail; | ||
225 | } | ||
226 | |||
227 | ifdetail->cbSize = sizeof(*ifdetail); | ||
228 | |||
229 | if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, ifdetail, | ||
230 | len, NULL, NULL) == false) { | ||
231 | fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2", | ||
232 | __func__); | ||
233 | goto fail; | ||
234 | } | ||
235 | |||
236 | if (copy_info(&devlist[*olen], ifdetail->DevicePath) == 0) { | ||
237 | devlist[*olen].io = (fido_dev_io_t) { | 365 | devlist[*olen].io = (fido_dev_io_t) { |
238 | fido_hid_open, | 366 | fido_hid_open, |
239 | fido_hid_close, | 367 | fido_hid_close, |
@@ -243,9 +371,6 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) | |||
243 | if (++(*olen) == ilen) | 371 | if (++(*olen) == ilen) |
244 | break; | 372 | break; |
245 | } | 373 | } |
246 | |||
247 | free(ifdetail); | ||
248 | ifdetail = NULL; | ||
249 | } | 374 | } |
250 | 375 | ||
251 | r = FIDO_OK; | 376 | r = FIDO_OK; |
@@ -253,78 +378,153 @@ fail: | |||
253 | if (devinfo != INVALID_HANDLE_VALUE) | 378 | if (devinfo != INVALID_HANDLE_VALUE) |
254 | SetupDiDestroyDeviceInfoList(devinfo); | 379 | SetupDiDestroyDeviceInfoList(devinfo); |
255 | 380 | ||
256 | free(ifdetail); | ||
257 | |||
258 | return (r); | 381 | return (r); |
259 | } | 382 | } |
260 | 383 | ||
261 | void * | 384 | void * |
262 | fido_hid_open(const char *path) | 385 | fido_hid_open(const char *path) |
263 | { | 386 | { |
264 | HANDLE dev; | 387 | struct hid_win *ctx; |
388 | |||
389 | if ((ctx = calloc(1, sizeof(*ctx))) == NULL) | ||
390 | return (NULL); | ||
265 | 391 | ||
266 | dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, | 392 | ctx->dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, |
267 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | 393 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, |
268 | FILE_ATTRIBUTE_NORMAL, NULL); | 394 | FILE_FLAG_OVERLAPPED, NULL); |
395 | |||
396 | if (ctx->dev == INVALID_HANDLE_VALUE) { | ||
397 | free(ctx); | ||
398 | return (NULL); | ||
399 | } | ||
269 | 400 | ||
270 | if (dev == INVALID_HANDLE_VALUE) | 401 | if ((ctx->overlap.hEvent = CreateEventA(NULL, FALSE, FALSE, |
402 | NULL)) == NULL) { | ||
403 | fido_log_debug("%s: CreateEventA", __func__); | ||
404 | fido_hid_close(ctx); | ||
271 | return (NULL); | 405 | return (NULL); |
406 | } | ||
272 | 407 | ||
273 | return (dev); | 408 | if (get_report_len(ctx->dev, 0, &ctx->report_in_len) < 0 || |
409 | get_report_len(ctx->dev, 1, &ctx->report_out_len) < 0) { | ||
410 | fido_log_debug("%s: get_report_len", __func__); | ||
411 | fido_hid_close(ctx); | ||
412 | return (NULL); | ||
413 | } | ||
414 | |||
415 | return (ctx); | ||
274 | } | 416 | } |
275 | 417 | ||
276 | void | 418 | void |
277 | fido_hid_close(void *handle) | 419 | fido_hid_close(void *handle) |
278 | { | 420 | { |
279 | CloseHandle(handle); | 421 | struct hid_win *ctx = handle; |
422 | |||
423 | if (ctx->overlap.hEvent != NULL) { | ||
424 | if (ctx->report_pending) { | ||
425 | fido_log_debug("%s: report_pending", __func__); | ||
426 | CancelIo(ctx->dev); | ||
427 | } | ||
428 | CloseHandle(ctx->overlap.hEvent); | ||
429 | } | ||
430 | |||
431 | explicit_bzero(ctx->report, sizeof(ctx->report)); | ||
432 | CloseHandle(ctx->dev); | ||
433 | free(ctx); | ||
280 | } | 434 | } |
281 | 435 | ||
282 | int | 436 | int |
283 | fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) | 437 | fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) |
284 | { | 438 | { |
285 | DWORD n; | 439 | struct hid_win *ctx = handle; |
286 | int r = -1; | 440 | DWORD n; |
287 | uint8_t report[REPORT_LEN]; | ||
288 | 441 | ||
289 | (void)ms; /* XXX */ | 442 | if (len != ctx->report_in_len - 1 || len > sizeof(ctx->report) - 1) { |
443 | fido_log_debug("%s: len %zu", __func__, len); | ||
444 | return (-1); | ||
445 | } | ||
446 | |||
447 | if (ctx->report_pending == 0) { | ||
448 | memset(&ctx->report, 0, sizeof(ctx->report)); | ||
449 | ResetEvent(ctx->overlap.hEvent); | ||
450 | if (ReadFile(ctx->dev, ctx->report, (DWORD)(len + 1), &n, | ||
451 | &ctx->overlap) == 0 && GetLastError() != ERROR_IO_PENDING) { | ||
452 | CancelIo(ctx->dev); | ||
453 | fido_log_debug("%s: ReadFile", __func__); | ||
454 | return (-1); | ||
455 | } | ||
456 | ctx->report_pending = 1; | ||
457 | } | ||
290 | 458 | ||
291 | memset(report, 0, sizeof(report)); | 459 | if (ms > -1 && WaitForSingleObject(ctx->overlap.hEvent, |
460 | (DWORD)ms) != WAIT_OBJECT_0) | ||
461 | return (0); | ||
292 | 462 | ||
293 | if (len != sizeof(report) - 1) { | 463 | ctx->report_pending = 0; |
294 | fido_log_debug("%s: invalid len", __func__); | 464 | |
465 | if (GetOverlappedResult(ctx->dev, &ctx->overlap, &n, TRUE) == 0) { | ||
466 | fido_log_debug("%s: GetOverlappedResult", __func__); | ||
295 | return (-1); | 467 | return (-1); |
296 | } | 468 | } |
297 | 469 | ||
298 | if (ReadFile(handle, report, sizeof(report), &n, NULL) == false || | 470 | if (n != len + 1) { |
299 | n != sizeof(report)) { | 471 | fido_log_debug("%s: expected %zu, got %zu", __func__, |
300 | fido_log_debug("%s: ReadFile", __func__); | 472 | len + 1, (size_t)n); |
301 | goto fail; | 473 | return (-1); |
302 | } | 474 | } |
303 | 475 | ||
304 | r = sizeof(report) - 1; | 476 | memcpy(buf, ctx->report + 1, len); |
305 | memcpy(buf, report + 1, len); | 477 | explicit_bzero(ctx->report, sizeof(ctx->report)); |
306 | |||
307 | fail: | ||
308 | explicit_bzero(report, sizeof(report)); | ||
309 | 478 | ||
310 | return (r); | 479 | return ((int)len); |
311 | } | 480 | } |
312 | 481 | ||
313 | int | 482 | int |
314 | fido_hid_write(void *handle, const unsigned char *buf, size_t len) | 483 | fido_hid_write(void *handle, const unsigned char *buf, size_t len) |
315 | { | 484 | { |
316 | DWORD n; | 485 | struct hid_win *ctx = handle; |
486 | OVERLAPPED overlap; | ||
487 | DWORD n; | ||
488 | |||
489 | memset(&overlap, 0, sizeof(overlap)); | ||
317 | 490 | ||
318 | if (len != REPORT_LEN) { | 491 | if (len != ctx->report_out_len) { |
319 | fido_log_debug("%s: invalid len", __func__); | 492 | fido_log_debug("%s: len %zu", __func__, len); |
320 | return (-1); | 493 | return (-1); |
321 | } | 494 | } |
322 | 495 | ||
323 | if (WriteFile(handle, buf, (DWORD)len, &n, NULL) == false || | 496 | if (WriteFile(ctx->dev, buf, (DWORD)len, NULL, &overlap) == 0 && |
324 | n != REPORT_LEN) { | 497 | GetLastError() != ERROR_IO_PENDING) { |
325 | fido_log_debug("%s: WriteFile", __func__); | 498 | fido_log_debug("%s: WriteFile", __func__); |
326 | return (-1); | 499 | return (-1); |
327 | } | 500 | } |
328 | 501 | ||
329 | return (REPORT_LEN); | 502 | if (GetOverlappedResult(ctx->dev, &overlap, &n, TRUE) == 0) { |
503 | fido_log_debug("%s: GetOverlappedResult", __func__); | ||
504 | return (-1); | ||
505 | } | ||
506 | |||
507 | if (n != len) { | ||
508 | fido_log_debug("%s: expected %zu, got %zu", __func__, len, | ||
509 | (size_t)n); | ||
510 | return (-1); | ||
511 | } | ||
512 | |||
513 | return ((int)len); | ||
514 | } | ||
515 | |||
516 | size_t | ||
517 | fido_hid_report_in_len(void *handle) | ||
518 | { | ||
519 | struct hid_win *ctx = handle; | ||
520 | |||
521 | return (ctx->report_in_len - 1); | ||
522 | } | ||
523 | |||
524 | size_t | ||
525 | fido_hid_report_out_len(void *handle) | ||
526 | { | ||
527 | struct hid_win *ctx = handle; | ||
528 | |||
529 | return (ctx->report_out_len - 1); | ||
330 | } | 530 | } |