xref: /netbsd-src/external/bsd/libfido2/dist/src/hid_win.c (revision 2d40c4512a84c0d064ec30a492c5e2a14d230bc3)
1ba9bdd8bSchristos /*
2*2d40c451Schristos  * Copyright (c) 2019-2022 Yubico AB. All rights reserved.
3ba9bdd8bSchristos  * Use of this source code is governed by a BSD-style
4ba9bdd8bSchristos  * license that can be found in the LICENSE file.
5*2d40c451Schristos  * SPDX-License-Identifier: BSD-2-Clause
6ba9bdd8bSchristos  */
7ba9bdd8bSchristos 
8ba9bdd8bSchristos #include <sys/types.h>
9ba9bdd8bSchristos 
10ba9bdd8bSchristos #include <fcntl.h>
11ba9bdd8bSchristos #ifdef HAVE_UNISTD_H
12ba9bdd8bSchristos #include <unistd.h>
13ba9bdd8bSchristos #endif
14ba9bdd8bSchristos #include <windows.h>
15ba9bdd8bSchristos #include <setupapi.h>
16ba9bdd8bSchristos #include <initguid.h>
171fc1e710Schristos #include <devpkey.h>
181fc1e710Schristos #include <devpropdef.h>
19ba9bdd8bSchristos #include <hidclass.h>
20ba9bdd8bSchristos #include <hidsdi.h>
2195dbdf32Schristos #include <wchar.h>
22ba9bdd8bSchristos 
23ba9bdd8bSchristos #include "fido.h"
24ba9bdd8bSchristos 
251fc1e710Schristos #if defined(__MINGW32__) &&  __MINGW64_VERSION_MAJOR < 6
261fc1e710Schristos WINSETUPAPI WINBOOL WINAPI SetupDiGetDevicePropertyW(HDEVINFO,
271fc1e710Schristos     PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE,
281fc1e710Schristos     DWORD, PDWORD, DWORD);
291fc1e710Schristos #endif
301fc1e710Schristos 
31*2d40c451Schristos #if defined(__MINGW32__) &&  __MINGW64_VERSION_MAJOR < 8
321fc1e710Schristos DEFINE_DEVPROPKEY(DEVPKEY_Device_Parent, 0x4340a6c5, 0x93fa, 0x4706, 0x97,
331fc1e710Schristos     0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8);
341fc1e710Schristos #endif
351fc1e710Schristos 
361fc1e710Schristos struct hid_win {
371fc1e710Schristos 	HANDLE		dev;
381fc1e710Schristos 	OVERLAPPED	overlap;
391fc1e710Schristos 	int		report_pending;
401fc1e710Schristos 	size_t		report_in_len;
411fc1e710Schristos 	size_t		report_out_len;
421fc1e710Schristos 	unsigned char	report[1 + CTAP_MAX_REPORT_LEN];
431fc1e710Schristos };
44ba9bdd8bSchristos 
45ba9bdd8bSchristos static bool
is_fido(HANDLE dev)46ba9bdd8bSchristos is_fido(HANDLE dev)
47ba9bdd8bSchristos {
48ba9bdd8bSchristos 	PHIDP_PREPARSED_DATA	data = NULL;
49ba9bdd8bSchristos 	HIDP_CAPS		caps;
501fc1e710Schristos 	int			fido = 0;
51ba9bdd8bSchristos 
52ba9bdd8bSchristos 	if (HidD_GetPreparsedData(dev, &data) == false) {
53ba9bdd8bSchristos 		fido_log_debug("%s: HidD_GetPreparsedData", __func__);
54ba9bdd8bSchristos 		goto fail;
55ba9bdd8bSchristos 	}
56ba9bdd8bSchristos 
57ba9bdd8bSchristos 	if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
58ba9bdd8bSchristos 		fido_log_debug("%s: HidP_GetCaps", __func__);
59ba9bdd8bSchristos 		goto fail;
60ba9bdd8bSchristos 	}
61ba9bdd8bSchristos 
621fc1e710Schristos 	fido = (uint16_t)caps.UsagePage == 0xf1d0;
63ba9bdd8bSchristos fail:
64ba9bdd8bSchristos 	if (data != NULL)
65ba9bdd8bSchristos 		HidD_FreePreparsedData(data);
66ba9bdd8bSchristos 
671fc1e710Schristos 	return (fido);
681fc1e710Schristos }
691fc1e710Schristos 
701fc1e710Schristos static int
get_report_len(HANDLE dev,int dir,size_t * report_len)711fc1e710Schristos get_report_len(HANDLE dev, int dir, size_t *report_len)
721fc1e710Schristos {
731fc1e710Schristos 	PHIDP_PREPARSED_DATA	data = NULL;
741fc1e710Schristos 	HIDP_CAPS		caps;
751fc1e710Schristos 	USHORT			v;
761fc1e710Schristos 	int			ok = -1;
771fc1e710Schristos 
781fc1e710Schristos 	if (HidD_GetPreparsedData(dev, &data) == false) {
791fc1e710Schristos 		fido_log_debug("%s: HidD_GetPreparsedData/%d", __func__, dir);
801fc1e710Schristos 		goto fail;
811fc1e710Schristos 	}
821fc1e710Schristos 
831fc1e710Schristos 	if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
841fc1e710Schristos 		fido_log_debug("%s: HidP_GetCaps/%d", __func__, dir);
851fc1e710Schristos 		goto fail;
861fc1e710Schristos 	}
871fc1e710Schristos 
881fc1e710Schristos 	if (dir == 0)
891fc1e710Schristos 		v = caps.InputReportByteLength;
901fc1e710Schristos 	else
911fc1e710Schristos 		v = caps.OutputReportByteLength;
921fc1e710Schristos 
931fc1e710Schristos 	if ((*report_len = (size_t)v) == 0) {
941fc1e710Schristos 		fido_log_debug("%s: report_len == 0", __func__);
951fc1e710Schristos 		goto fail;
961fc1e710Schristos 	}
971fc1e710Schristos 
981fc1e710Schristos 	ok = 0;
991fc1e710Schristos fail:
1001fc1e710Schristos 	if (data != NULL)
1011fc1e710Schristos 		HidD_FreePreparsedData(data);
1021fc1e710Schristos 
1031fc1e710Schristos 	return (ok);
104ba9bdd8bSchristos }
105ba9bdd8bSchristos 
106ba9bdd8bSchristos static int
get_id(HANDLE dev,int16_t * vendor_id,int16_t * product_id)107*2d40c451Schristos get_id(HANDLE dev, int16_t *vendor_id, int16_t *product_id)
108ba9bdd8bSchristos {
109ba9bdd8bSchristos 	HIDD_ATTRIBUTES attr;
110ba9bdd8bSchristos 
111ba9bdd8bSchristos 	attr.Size = sizeof(attr);
112ba9bdd8bSchristos 
11395dbdf32Schristos 	if (HidD_GetAttributes(dev, &attr) == false) {
114ba9bdd8bSchristos 		fido_log_debug("%s: HidD_GetAttributes", __func__);
115ba9bdd8bSchristos 		return (-1);
116ba9bdd8bSchristos 	}
117ba9bdd8bSchristos 
1181fc1e710Schristos 	*vendor_id = (int16_t)attr.VendorID;
1191fc1e710Schristos 	*product_id = (int16_t)attr.ProductID;
120ba9bdd8bSchristos 
121ba9bdd8bSchristos 	return (0);
122ba9bdd8bSchristos }
123ba9bdd8bSchristos 
124ba9bdd8bSchristos static int
get_manufacturer(HANDLE dev,char ** manufacturer)125*2d40c451Schristos get_manufacturer(HANDLE dev, char **manufacturer)
126ba9bdd8bSchristos {
127ba9bdd8bSchristos 	wchar_t	buf[512];
128ba9bdd8bSchristos 	int	utf8_len;
129ba9bdd8bSchristos 	int	ok = -1;
130ba9bdd8bSchristos 
131ba9bdd8bSchristos 	*manufacturer = NULL;
132ba9bdd8bSchristos 
133ba9bdd8bSchristos 	if (HidD_GetManufacturerString(dev, &buf, sizeof(buf)) == false) {
134ba9bdd8bSchristos 		fido_log_debug("%s: HidD_GetManufacturerString", __func__);
135ba9bdd8bSchristos 		goto fail;
136ba9bdd8bSchristos 	}
137ba9bdd8bSchristos 
138ba9bdd8bSchristos 	if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
139ba9bdd8bSchristos 	    -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
140ba9bdd8bSchristos 		fido_log_debug("%s: WideCharToMultiByte", __func__);
141ba9bdd8bSchristos 		goto fail;
142ba9bdd8bSchristos 	}
143ba9bdd8bSchristos 
1441fc1e710Schristos 	if ((*manufacturer = malloc((size_t)utf8_len)) == NULL) {
145ba9bdd8bSchristos 		fido_log_debug("%s: malloc", __func__);
146ba9bdd8bSchristos 		goto fail;
147ba9bdd8bSchristos 	}
148ba9bdd8bSchristos 
149ba9bdd8bSchristos 	if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
150ba9bdd8bSchristos 	    *manufacturer, utf8_len, NULL, NULL) != utf8_len) {
151ba9bdd8bSchristos 		fido_log_debug("%s: WideCharToMultiByte", __func__);
152ba9bdd8bSchristos 		goto fail;
153ba9bdd8bSchristos 	}
154ba9bdd8bSchristos 
155*2d40c451Schristos 	ok = 0;
156*2d40c451Schristos fail:
157*2d40c451Schristos 	if (ok < 0) {
158*2d40c451Schristos 		free(*manufacturer);
159*2d40c451Schristos 		*manufacturer = NULL;
160*2d40c451Schristos 	}
161*2d40c451Schristos 
162*2d40c451Schristos 	return (ok);
163*2d40c451Schristos }
164*2d40c451Schristos 
165*2d40c451Schristos static int
get_product(HANDLE dev,char ** product)166*2d40c451Schristos get_product(HANDLE dev, char **product)
167*2d40c451Schristos {
168*2d40c451Schristos 	wchar_t	buf[512];
169*2d40c451Schristos 	int	utf8_len;
170*2d40c451Schristos 	int	ok = -1;
171*2d40c451Schristos 
172*2d40c451Schristos 	*product = NULL;
173*2d40c451Schristos 
174ba9bdd8bSchristos 	if (HidD_GetProductString(dev, &buf, sizeof(buf)) == false) {
175ba9bdd8bSchristos 		fido_log_debug("%s: HidD_GetProductString", __func__);
176ba9bdd8bSchristos 		goto fail;
177ba9bdd8bSchristos 	}
178ba9bdd8bSchristos 
179ba9bdd8bSchristos 	if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
180ba9bdd8bSchristos 	    -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
181ba9bdd8bSchristos 		fido_log_debug("%s: WideCharToMultiByte", __func__);
182ba9bdd8bSchristos 		goto fail;
183ba9bdd8bSchristos 	}
184ba9bdd8bSchristos 
1851fc1e710Schristos 	if ((*product = malloc((size_t)utf8_len)) == NULL) {
186ba9bdd8bSchristos 		fido_log_debug("%s: malloc", __func__);
187ba9bdd8bSchristos 		goto fail;
188ba9bdd8bSchristos 	}
189ba9bdd8bSchristos 
190ba9bdd8bSchristos 	if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
191ba9bdd8bSchristos 	    *product, utf8_len, NULL, NULL) != utf8_len) {
192ba9bdd8bSchristos 		fido_log_debug("%s: WideCharToMultiByte", __func__);
193ba9bdd8bSchristos 		goto fail;
194ba9bdd8bSchristos 	}
195ba9bdd8bSchristos 
196ba9bdd8bSchristos 	ok = 0;
197ba9bdd8bSchristos fail:
198ba9bdd8bSchristos 	if (ok < 0) {
199ba9bdd8bSchristos 		free(*product);
200ba9bdd8bSchristos 		*product = NULL;
201ba9bdd8bSchristos 	}
202ba9bdd8bSchristos 
203ba9bdd8bSchristos 	return (ok);
204ba9bdd8bSchristos }
205ba9bdd8bSchristos 
2061fc1e710Schristos static char *
get_path(HDEVINFO devinfo,SP_DEVICE_INTERFACE_DATA * ifdata)2071fc1e710Schristos get_path(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *ifdata)
2081fc1e710Schristos {
2091fc1e710Schristos 	SP_DEVICE_INTERFACE_DETAIL_DATA_A	*ifdetail = NULL;
2101fc1e710Schristos 	char					*path = NULL;
2111fc1e710Schristos 	DWORD					 len = 0;
2121fc1e710Schristos 
2131fc1e710Schristos 	/*
2141fc1e710Schristos 	 * "Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail
2151fc1e710Schristos 	 * with a NULL DeviceInterfaceDetailData pointer, a
2161fc1e710Schristos 	 * DeviceInterfaceDetailDataSize of zero, and a valid RequiredSize
2171fc1e710Schristos 	 * variable. In response to such a call, this function returns the
2181fc1e710Schristos 	 * required buffer size at RequiredSize and fails with GetLastError
2191fc1e710Schristos 	 * returning ERROR_INSUFFICIENT_BUFFER."
2201fc1e710Schristos 	 */
2211fc1e710Schristos 	if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, NULL, 0, &len,
2221fc1e710Schristos 	    NULL) != false || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
2231fc1e710Schristos 		fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1",
2241fc1e710Schristos 		    __func__);
2251fc1e710Schristos 		goto fail;
2261fc1e710Schristos 	}
2271fc1e710Schristos 
2281fc1e710Schristos 	if ((ifdetail = malloc(len)) == NULL) {
2291fc1e710Schristos 		fido_log_debug("%s: malloc", __func__);
2301fc1e710Schristos 		goto fail;
2311fc1e710Schristos 	}
2321fc1e710Schristos 
2331fc1e710Schristos 	ifdetail->cbSize = sizeof(*ifdetail);
2341fc1e710Schristos 
2351fc1e710Schristos 	if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, ifdetail, len,
2361fc1e710Schristos 	    NULL, NULL) == false) {
2371fc1e710Schristos 		fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2",
2381fc1e710Schristos 		    __func__);
2391fc1e710Schristos 		goto fail;
2401fc1e710Schristos 	}
2411fc1e710Schristos 
2421fc1e710Schristos 	if ((path = strdup(ifdetail->DevicePath)) == NULL) {
2431fc1e710Schristos 		fido_log_debug("%s: strdup", __func__);
2441fc1e710Schristos 		goto fail;
2451fc1e710Schristos 	}
2461fc1e710Schristos 
2471fc1e710Schristos fail:
2481fc1e710Schristos 	free(ifdetail);
2491fc1e710Schristos 
2501fc1e710Schristos 	return (path);
2511fc1e710Schristos }
2521fc1e710Schristos 
2531fc1e710Schristos #ifndef FIDO_HID_ANY
2541fc1e710Schristos static bool
hid_ok(HDEVINFO devinfo,DWORD idx)2551fc1e710Schristos hid_ok(HDEVINFO devinfo, DWORD idx)
2561fc1e710Schristos {
2571fc1e710Schristos 	SP_DEVINFO_DATA	 devinfo_data;
2581fc1e710Schristos 	wchar_t		*parent = NULL;
2591fc1e710Schristos 	DWORD		 parent_type = DEVPROP_TYPE_STRING;
2601fc1e710Schristos 	DWORD		 len = 0;
2611fc1e710Schristos 	bool		 ok = false;
2621fc1e710Schristos 
2631fc1e710Schristos 	memset(&devinfo_data, 0, sizeof(devinfo_data));
2641fc1e710Schristos 	devinfo_data.cbSize = sizeof(devinfo_data);
2651fc1e710Schristos 
2661fc1e710Schristos 	if (SetupDiEnumDeviceInfo(devinfo, idx, &devinfo_data) == false) {
2671fc1e710Schristos 		fido_log_debug("%s: SetupDiEnumDeviceInfo", __func__);
2681fc1e710Schristos 		goto fail;
2691fc1e710Schristos 	}
2701fc1e710Schristos 
2711fc1e710Schristos 	if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
2721fc1e710Schristos 	    &DEVPKEY_Device_Parent, &parent_type, NULL, 0, &len, 0) != false ||
2731fc1e710Schristos 	    GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
2741fc1e710Schristos 		fido_log_debug("%s: SetupDiGetDevicePropertyW 1", __func__);
2751fc1e710Schristos 		goto fail;
2761fc1e710Schristos 	}
2771fc1e710Schristos 
2781fc1e710Schristos 	if ((parent = malloc(len)) == NULL) {
2791fc1e710Schristos 		fido_log_debug("%s: malloc", __func__);
2801fc1e710Schristos 		goto fail;
2811fc1e710Schristos 	}
2821fc1e710Schristos 
2831fc1e710Schristos 	if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
2841fc1e710Schristos 	    &DEVPKEY_Device_Parent, &parent_type, (PBYTE)parent, len, NULL,
2851fc1e710Schristos 	    0) == false) {
2861fc1e710Schristos 		fido_log_debug("%s: SetupDiGetDevicePropertyW 2", __func__);
2871fc1e710Schristos 		goto fail;
2881fc1e710Schristos 	}
2891fc1e710Schristos 
2901fc1e710Schristos 	ok = wcsncmp(parent, L"USB\\", 4) == 0;
2911fc1e710Schristos fail:
2921fc1e710Schristos 	free(parent);
2931fc1e710Schristos 
2941fc1e710Schristos 	return (ok);
2951fc1e710Schristos }
2961fc1e710Schristos #endif
2971fc1e710Schristos 
298ba9bdd8bSchristos static int
copy_info(fido_dev_info_t * di,HDEVINFO devinfo,DWORD idx,SP_DEVICE_INTERFACE_DATA * ifdata)2991fc1e710Schristos copy_info(fido_dev_info_t *di, HDEVINFO devinfo, DWORD idx,
3001fc1e710Schristos     SP_DEVICE_INTERFACE_DATA *ifdata)
301ba9bdd8bSchristos {
302ba9bdd8bSchristos 	HANDLE	dev = INVALID_HANDLE_VALUE;
303ba9bdd8bSchristos 	int	ok = -1;
304ba9bdd8bSchristos 
305ba9bdd8bSchristos 	memset(di, 0, sizeof(*di));
306ba9bdd8bSchristos 
3071fc1e710Schristos 	if ((di->path = get_path(devinfo, ifdata)) == NULL) {
3081fc1e710Schristos 		fido_log_debug("%s: get_path", __func__);
309ba9bdd8bSchristos 		goto fail;
3101fc1e710Schristos 	}
3111fc1e710Schristos 
3121fc1e710Schristos 	fido_log_debug("%s: path=%s", __func__, di->path);
3131fc1e710Schristos 
3141fc1e710Schristos #ifndef FIDO_HID_ANY
3151fc1e710Schristos 	if (hid_ok(devinfo, idx) == false) {
3161fc1e710Schristos 		fido_log_debug("%s: hid_ok", __func__);
3171fc1e710Schristos 		goto fail;
3181fc1e710Schristos 	}
3191fc1e710Schristos #endif
3201fc1e710Schristos 
3211fc1e710Schristos 	dev = CreateFileA(di->path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
3221fc1e710Schristos 	    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
3231fc1e710Schristos 	if (dev == INVALID_HANDLE_VALUE) {
3241fc1e710Schristos 		fido_log_debug("%s: CreateFileA", __func__);
3251fc1e710Schristos 		goto fail;
3261fc1e710Schristos 	}
3271fc1e710Schristos 
3281fc1e710Schristos 	if (is_fido(dev) == false) {
3291fc1e710Schristos 		fido_log_debug("%s: is_fido", __func__);
3301fc1e710Schristos 		goto fail;
3311fc1e710Schristos 	}
332ba9bdd8bSchristos 
333*2d40c451Schristos 	if (get_id(dev, &di->vendor_id, &di->product_id) < 0) {
334*2d40c451Schristos 		fido_log_debug("%s: get_id", __func__);
335*2d40c451Schristos 		goto fail;
336*2d40c451Schristos 	}
337*2d40c451Schristos 
338*2d40c451Schristos 	if (get_manufacturer(dev, &di->manufacturer) < 0) {
339*2d40c451Schristos 		fido_log_debug("%s: get_manufacturer", __func__);
340*2d40c451Schristos 		di->manufacturer = strdup("");
341*2d40c451Schristos 	}
342*2d40c451Schristos 
343*2d40c451Schristos 	if (get_product(dev, &di->product) < 0) {
344*2d40c451Schristos 		fido_log_debug("%s: get_product", __func__);
345*2d40c451Schristos 		di->product = strdup("");
346*2d40c451Schristos 	}
347*2d40c451Schristos 
348*2d40c451Schristos 	if (di->manufacturer == NULL || di->product == NULL) {
349*2d40c451Schristos 		fido_log_debug("%s: manufacturer/product", __func__);
350ba9bdd8bSchristos 		goto fail;
3511fc1e710Schristos 	}
352ba9bdd8bSchristos 
353ba9bdd8bSchristos 	ok = 0;
354ba9bdd8bSchristos fail:
355ba9bdd8bSchristos 	if (dev != INVALID_HANDLE_VALUE)
356ba9bdd8bSchristos 		CloseHandle(dev);
357ba9bdd8bSchristos 
358ba9bdd8bSchristos 	if (ok < 0) {
359ba9bdd8bSchristos 		free(di->path);
360ba9bdd8bSchristos 		free(di->manufacturer);
361ba9bdd8bSchristos 		free(di->product);
362ba9bdd8bSchristos 		explicit_bzero(di, sizeof(*di));
363ba9bdd8bSchristos 	}
364ba9bdd8bSchristos 
365ba9bdd8bSchristos 	return (ok);
366ba9bdd8bSchristos }
367ba9bdd8bSchristos 
368ba9bdd8bSchristos int
fido_hid_manifest(fido_dev_info_t * devlist,size_t ilen,size_t * olen)369ba9bdd8bSchristos fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
370ba9bdd8bSchristos {
371ba9bdd8bSchristos 	GUID				hid_guid = GUID_DEVINTERFACE_HID;
372ba9bdd8bSchristos 	HDEVINFO			devinfo = INVALID_HANDLE_VALUE;
373ba9bdd8bSchristos 	SP_DEVICE_INTERFACE_DATA	ifdata;
3741fc1e710Schristos 	DWORD				idx;
375ba9bdd8bSchristos 	int				r = FIDO_ERR_INTERNAL;
376ba9bdd8bSchristos 
377ba9bdd8bSchristos 	*olen = 0;
378ba9bdd8bSchristos 
379ba9bdd8bSchristos 	if (ilen == 0)
380ba9bdd8bSchristos 		return (FIDO_OK); /* nothing to do */
381ba9bdd8bSchristos 	if (devlist == NULL)
382ba9bdd8bSchristos 		return (FIDO_ERR_INVALID_ARGUMENT);
383ba9bdd8bSchristos 
3841fc1e710Schristos 	if ((devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL,
3851fc1e710Schristos 	    DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)) == INVALID_HANDLE_VALUE) {
386ba9bdd8bSchristos 		fido_log_debug("%s: SetupDiGetClassDevsA", __func__);
387ba9bdd8bSchristos 		goto fail;
388ba9bdd8bSchristos 	}
389ba9bdd8bSchristos 
390ba9bdd8bSchristos 	ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
391ba9bdd8bSchristos 
3921fc1e710Schristos 	for (idx = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid,
3931fc1e710Schristos 	    idx, &ifdata) == true; idx++) {
3941fc1e710Schristos 		if (copy_info(&devlist[*olen], devinfo, idx, &ifdata) == 0) {
395ba9bdd8bSchristos 			devlist[*olen].io = (fido_dev_io_t) {
396ba9bdd8bSchristos 				fido_hid_open,
397ba9bdd8bSchristos 				fido_hid_close,
398ba9bdd8bSchristos 				fido_hid_read,
399ba9bdd8bSchristos 				fido_hid_write,
400ba9bdd8bSchristos 			};
401ba9bdd8bSchristos 			if (++(*olen) == ilen)
402ba9bdd8bSchristos 				break;
403ba9bdd8bSchristos 		}
404ba9bdd8bSchristos 	}
405ba9bdd8bSchristos 
406ba9bdd8bSchristos 	r = FIDO_OK;
407ba9bdd8bSchristos fail:
408ba9bdd8bSchristos 	if (devinfo != INVALID_HANDLE_VALUE)
409ba9bdd8bSchristos 		SetupDiDestroyDeviceInfoList(devinfo);
410ba9bdd8bSchristos 
411ba9bdd8bSchristos 	return (r);
412ba9bdd8bSchristos }
413ba9bdd8bSchristos 
414ba9bdd8bSchristos void *
fido_hid_open(const char * path)415ba9bdd8bSchristos fido_hid_open(const char *path)
416ba9bdd8bSchristos {
4171fc1e710Schristos 	struct hid_win *ctx;
418ba9bdd8bSchristos 
4191fc1e710Schristos 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
420ba9bdd8bSchristos 		return (NULL);
421ba9bdd8bSchristos 
4221fc1e710Schristos 	ctx->dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
4231fc1e710Schristos 	    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
4241fc1e710Schristos 	    FILE_FLAG_OVERLAPPED, NULL);
4251fc1e710Schristos 
4261fc1e710Schristos 	if (ctx->dev == INVALID_HANDLE_VALUE) {
4271fc1e710Schristos 		free(ctx);
4281fc1e710Schristos 		return (NULL);
4291fc1e710Schristos 	}
4301fc1e710Schristos 
4311fc1e710Schristos 	if ((ctx->overlap.hEvent = CreateEventA(NULL, FALSE, FALSE,
4321fc1e710Schristos 	    NULL)) == NULL) {
4331fc1e710Schristos 		fido_log_debug("%s: CreateEventA", __func__);
4341fc1e710Schristos 		fido_hid_close(ctx);
4351fc1e710Schristos 		return (NULL);
4361fc1e710Schristos 	}
4371fc1e710Schristos 
4381fc1e710Schristos 	if (get_report_len(ctx->dev, 0, &ctx->report_in_len) < 0 ||
4391fc1e710Schristos 	    get_report_len(ctx->dev, 1, &ctx->report_out_len) < 0) {
4401fc1e710Schristos 		fido_log_debug("%s: get_report_len", __func__);
4411fc1e710Schristos 		fido_hid_close(ctx);
4421fc1e710Schristos 		return (NULL);
4431fc1e710Schristos 	}
4441fc1e710Schristos 
4451fc1e710Schristos 	return (ctx);
446ba9bdd8bSchristos }
447ba9bdd8bSchristos 
448ba9bdd8bSchristos void
fido_hid_close(void * handle)449ba9bdd8bSchristos fido_hid_close(void *handle)
450ba9bdd8bSchristos {
4511fc1e710Schristos 	struct hid_win *ctx = handle;
4521fc1e710Schristos 
4531fc1e710Schristos 	if (ctx->overlap.hEvent != NULL) {
4541fc1e710Schristos 		if (ctx->report_pending) {
4551fc1e710Schristos 			fido_log_debug("%s: report_pending", __func__);
45695dbdf32Schristos 			if (CancelIoEx(ctx->dev, &ctx->overlap) == 0)
45795dbdf32Schristos 				fido_log_debug("%s CancelIoEx: 0x%lx",
458*2d40c451Schristos 				    __func__, (u_long)GetLastError());
4591fc1e710Schristos 		}
4601fc1e710Schristos 		CloseHandle(ctx->overlap.hEvent);
4611fc1e710Schristos 	}
4621fc1e710Schristos 
4631fc1e710Schristos 	explicit_bzero(ctx->report, sizeof(ctx->report));
4641fc1e710Schristos 	CloseHandle(ctx->dev);
4651fc1e710Schristos 	free(ctx);
466ba9bdd8bSchristos }
467ba9bdd8bSchristos 
468ba9bdd8bSchristos int
fido_hid_set_sigmask(void * handle,const fido_sigset_t * sigmask)46995dbdf32Schristos fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
47095dbdf32Schristos {
47195dbdf32Schristos 	(void)handle;
47295dbdf32Schristos 	(void)sigmask;
47395dbdf32Schristos 
47495dbdf32Schristos 	return (FIDO_ERR_INTERNAL);
47595dbdf32Schristos }
47695dbdf32Schristos 
47795dbdf32Schristos int
fido_hid_read(void * handle,unsigned char * buf,size_t len,int ms)478ba9bdd8bSchristos fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
479ba9bdd8bSchristos {
4801fc1e710Schristos 	struct hid_win	*ctx = handle;
481ba9bdd8bSchristos 	DWORD		 n;
482ba9bdd8bSchristos 
4831fc1e710Schristos 	if (len != ctx->report_in_len - 1 || len > sizeof(ctx->report) - 1) {
4841fc1e710Schristos 		fido_log_debug("%s: len %zu", __func__, len);
485ba9bdd8bSchristos 		return (-1);
486ba9bdd8bSchristos 	}
487ba9bdd8bSchristos 
4881fc1e710Schristos 	if (ctx->report_pending == 0) {
4891fc1e710Schristos 		memset(&ctx->report, 0, sizeof(ctx->report));
4901fc1e710Schristos 		ResetEvent(ctx->overlap.hEvent);
4911fc1e710Schristos 		if (ReadFile(ctx->dev, ctx->report, (DWORD)(len + 1), &n,
4921fc1e710Schristos 		    &ctx->overlap) == 0 && GetLastError() != ERROR_IO_PENDING) {
4931fc1e710Schristos 			CancelIo(ctx->dev);
494ba9bdd8bSchristos 			fido_log_debug("%s: ReadFile", __func__);
4951fc1e710Schristos 			return (-1);
4961fc1e710Schristos 		}
4971fc1e710Schristos 		ctx->report_pending = 1;
498ba9bdd8bSchristos 	}
499ba9bdd8bSchristos 
5001fc1e710Schristos 	if (ms > -1 && WaitForSingleObject(ctx->overlap.hEvent,
5011fc1e710Schristos 	    (DWORD)ms) != WAIT_OBJECT_0)
5021fc1e710Schristos 		return (0);
503ba9bdd8bSchristos 
5041fc1e710Schristos 	ctx->report_pending = 0;
505ba9bdd8bSchristos 
5061fc1e710Schristos 	if (GetOverlappedResult(ctx->dev, &ctx->overlap, &n, TRUE) == 0) {
5071fc1e710Schristos 		fido_log_debug("%s: GetOverlappedResult", __func__);
5081fc1e710Schristos 		return (-1);
5091fc1e710Schristos 	}
5101fc1e710Schristos 
5111fc1e710Schristos 	if (n != len + 1) {
5121fc1e710Schristos 		fido_log_debug("%s: expected %zu, got %zu", __func__,
5131fc1e710Schristos 		    len + 1, (size_t)n);
5141fc1e710Schristos 		return (-1);
5151fc1e710Schristos 	}
5161fc1e710Schristos 
5171fc1e710Schristos 	memcpy(buf, ctx->report + 1, len);
5181fc1e710Schristos 	explicit_bzero(ctx->report, sizeof(ctx->report));
5191fc1e710Schristos 
5201fc1e710Schristos 	return ((int)len);
521ba9bdd8bSchristos }
522ba9bdd8bSchristos 
523ba9bdd8bSchristos int
fido_hid_write(void * handle,const unsigned char * buf,size_t len)524ba9bdd8bSchristos fido_hid_write(void *handle, const unsigned char *buf, size_t len)
525ba9bdd8bSchristos {
5261fc1e710Schristos 	struct hid_win	*ctx = handle;
5271fc1e710Schristos 	OVERLAPPED	 overlap;
528ba9bdd8bSchristos 	DWORD		 n;
529ba9bdd8bSchristos 
5301fc1e710Schristos 	memset(&overlap, 0, sizeof(overlap));
5311fc1e710Schristos 
5321fc1e710Schristos 	if (len != ctx->report_out_len) {
5331fc1e710Schristos 		fido_log_debug("%s: len %zu", __func__, len);
534ba9bdd8bSchristos 		return (-1);
535ba9bdd8bSchristos 	}
536ba9bdd8bSchristos 
5371fc1e710Schristos 	if (WriteFile(ctx->dev, buf, (DWORD)len, NULL, &overlap) == 0 &&
5381fc1e710Schristos 	    GetLastError() != ERROR_IO_PENDING) {
539ba9bdd8bSchristos 		fido_log_debug("%s: WriteFile", __func__);
540ba9bdd8bSchristos 		return (-1);
541ba9bdd8bSchristos 	}
542ba9bdd8bSchristos 
5431fc1e710Schristos 	if (GetOverlappedResult(ctx->dev, &overlap, &n, TRUE) == 0) {
5441fc1e710Schristos 		fido_log_debug("%s: GetOverlappedResult", __func__);
5451fc1e710Schristos 		return (-1);
5461fc1e710Schristos 	}
5471fc1e710Schristos 
5481fc1e710Schristos 	if (n != len) {
5491fc1e710Schristos 		fido_log_debug("%s: expected %zu, got %zu", __func__, len,
5501fc1e710Schristos 		    (size_t)n);
5511fc1e710Schristos 		return (-1);
5521fc1e710Schristos 	}
5531fc1e710Schristos 
5541fc1e710Schristos 	return ((int)len);
5551fc1e710Schristos }
5561fc1e710Schristos 
5571fc1e710Schristos size_t
fido_hid_report_in_len(void * handle)5581fc1e710Schristos fido_hid_report_in_len(void *handle)
5591fc1e710Schristos {
5601fc1e710Schristos 	struct hid_win *ctx = handle;
5611fc1e710Schristos 
5621fc1e710Schristos 	return (ctx->report_in_len - 1);
5631fc1e710Schristos }
5641fc1e710Schristos 
5651fc1e710Schristos size_t
fido_hid_report_out_len(void * handle)5661fc1e710Schristos fido_hid_report_out_len(void *handle)
5671fc1e710Schristos {
5681fc1e710Schristos 	struct hid_win *ctx = handle;
5691fc1e710Schristos 
5701fc1e710Schristos 	return (ctx->report_out_len - 1);
571ba9bdd8bSchristos }
572