add CMake config (#20)
[usb-relay-hid.git] / commandline / hiddata_mswin.c
1 /* hiddata_mswin.c
2 * Win32 variant
3 */
4
5 /* Inspired by hiddata.c|h by Christian Starkjohann
6 * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
7 */
8
9 #if !defined(_WIN32)
10 #error
11 #endif
12
13 #include "hiddata.h"
14 #include <stdio.h>
15 #include <string.h>
16 #include <errno.h>
17
18 #if 0 //ifdef DEBUG
19 #define DEBUG_PRINT(arg) printf arg
20 #else
21 #define DEBUG_PRINT(arg)
22 #endif
23
24 #include "targetver.h"
25 #include <windows.h>
26 #include <setupapi.h>
27 #include <hidsdi.h>
28 #include <hidpi.h>
29
30 #ifdef _MSC_VER
31 #pragma comment(lib, "setupapi")
32 #pragma comment(lib, "hid")
33 #if _MSC_VER < 1900 /* before VS2015 */
34 #define snprintf _snprintf
35 #endif /* VS2015 */
36 #else /* GCC, Mingw... */
37 #endif /*_MSC_VER*/
38
39 /*
40 * Convert UTF-16 null term. string to single byte (ASCII or ISO Latin)
41 * change all weird characters to "?"
42 */
43 static void usbstring_to_ascii(unsigned short *wp, char *cp, int size)
44 {
45 unsigned short *wpend = wp + (size/sizeof(unsigned short));
46 for ( ; wp < wpend; )
47 {
48 unsigned short h = *wp++;
49 *cp++ = (h < 0xFF) ? (char)h : '?';
50 if (h == 0)
51 break;
52 }
53 }
54
55 /*
56 * Read HID string for vendor and device, return as ASCII (or ISO Latin...)
57 */
58 int usbhidGetVendorString(USBDEVHANDLE usbh, char *buffer, int len)
59 {
60 /* HidD_GetManufacturerString returns zero terminated UTF-16 string */
61 /* Error if buffer is too short */
62 if ( !HidD_GetManufacturerString((HANDLE)usbh, (void*)buffer, len ) ) {
63 DEBUG_PRINT(("error obtaining vendor name\n"));
64 return USBHID_ERR_IO_HID;
65 }
66 usbstring_to_ascii((UINT16*)buffer, buffer, len);
67 return 0;
68 }
69
70 int usbhidGetProductString(USBDEVHANDLE usbh, char *buffer, int len)
71 {
72 /* HidD_GetProductString returns zero terminated UTF-16 string */
73 /* Error if buffer is too short */
74 if (!HidD_GetProductString((HANDLE)usbh, (void*)buffer, len ) ) {
75 DEBUG_PRINT(("error obtaining product name\n"));
76 return USBHID_ERR_IO_HID;
77 }
78 usbstring_to_ascii((UINT16*)buffer, buffer, len);
79 return 0;
80 }
81
82 /*
83 * Enumerate HID USB devices.
84 * In Windows this will find also non-USB devices, but assume that
85 * filtering by PID & VID is enough.
86 * Some HID devices (mice, kbd) are locked by Windows and cannot be opened.
87 * If we cannot open a device for R&W, we skip it without error.
88 * Assume our devices are not of types reserved by Windows.
89 */
90 int usbhidEnumDevices(int vendor, int product,
91 void *context,
92 int (*usbhidEnumFunc)(USBDEVHANDLE usbh, void *ctx))
93 {
94 GUID hidGuid; /* GUID for HID class */
95 HDEVINFO deviceInfoList;
96 SP_DEVICE_INTERFACE_DATA deviceInfo;
97 SP_DEVICE_INTERFACE_DETAIL_DATA_W *deviceDetails = NULL;
98 DWORD size;
99 int i;
100 int openFlags = 0;
101 int errorCode = USBHID_ERR_NOTFOUND;
102 HANDLE handle = INVALID_HANDLE_VALUE;
103 HIDD_ATTRIBUTES deviceAttributes;
104 BOOL b;
105
106 HidD_GetHidGuid(&hidGuid);
107 deviceInfoList = SetupDiGetClassDevsW(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
108 if (!deviceInfoList || deviceInfoList == INVALID_HANDLE_VALUE)
109 {
110 return USBHID_ERR_NOTFOUND;
111 }
112
113 deviceInfo.cbSize = sizeof(deviceInfo);
114 for (i=0; ; i++) {
115 if (handle != INVALID_HANDLE_VALUE) {
116 CloseHandle(handle);
117 handle = INVALID_HANDLE_VALUE;
118 }
119 if ( !SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo) )
120 break; /* no more entries */
121 /* First do a dummy call just to determine the actual size required */
122 size = 0;
123 SetupDiGetDeviceInterfaceDetailW(deviceInfoList, &deviceInfo, NULL, 0, &size, NULL);
124 if ( size == 0 )
125 continue;
126 if (deviceDetails != NULL)
127 free(deviceDetails);
128 deviceDetails = malloc(size);
129 if ( !deviceDetails ) {
130 DEBUG_PRINT(("ALLOC ERROR!\n"));
131 continue;
132 }
133 deviceDetails->cbSize = sizeof(*deviceDetails);
134 /* 2nd call */
135 b = SetupDiGetDeviceInterfaceDetailW(deviceInfoList, &deviceInfo, deviceDetails, size, &size, NULL);
136 if ( !b )
137 continue;
138 DEBUG_PRINT(("checking HID path \"%s\"\n", deviceDetails->DevicePath));
139
140 handle = CreateFileW(deviceDetails->DevicePath,
141 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlags, NULL);
142 if (handle == INVALID_HANDLE_VALUE){
143 DEBUG_PRINT(("open USB device failed: gle=%d\n", (int)GetLastError()));
144 /* Opening devices owned by OS or other apps will fail ; just ignore these. */
145 continue;
146 }
147 deviceAttributes.Size = sizeof(deviceAttributes);
148 HidD_GetAttributes(handle, &deviceAttributes);
149 DEBUG_PRINT(("device attributes: vid=%d pid=%d ver=%4.4X\n", deviceAttributes.VendorID, deviceAttributes.ProductID, deviceAttributes.VersionNumber));
150 if (deviceAttributes.VendorID != vendor || deviceAttributes.ProductID != product)
151 continue; /* skip this device */
152
153 errorCode = 0;
154 if ( 0 == usbhidEnumFunc((USBDEVHANDLE)handle, context) )
155 {
156 break; /* stop enumeration */
157 }
158
159 /* Now the handle is owned by the callback It may close it before return or later. */
160 handle = INVALID_HANDLE_VALUE;
161 }
162
163 SetupDiDestroyDeviceInfoList(deviceInfoList);
164 if (deviceDetails != NULL)
165 free(deviceDetails);
166
167 return errorCode;
168 }
169
170
171 void usbhidCloseDevice(USBDEVHANDLE usbh)
172 {
173 CloseHandle((HANDLE)usbh);
174 }
175
176
177 int usbhidSetReport(USBDEVHANDLE usbh, char *buffer, int len)
178 {
179 BOOLEAN rval;
180 rval = HidD_SetFeature((HANDLE)usbh, buffer, len);
181 return rval == 0 ? USBHID_ERR_IO_HID : 0;
182 }
183
184
185 int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len)
186 {
187 BOOLEAN rval = 0;
188 buffer[0] = (char)reportNumber;
189 rval = HidD_GetFeature((HANDLE)usbh, buffer, *len);
190 return rval == 0 ? USBHID_ERR_IO_HID : 0;
191 }
192
193
194 int usbhidStrerror_r( int err, char *buf, int len)
195 {
196 const char *s;
197 switch (err) {
198 case USBHID_ERR_ACCESS: s = "Access to device denied";
199 break;
200 case USBHID_ERR_NOTFOUND: s = "The specified device was not found";
201 break;
202 case USBHID_ERR_IO: s = "Communication error with device";
203 break;
204 case USBHID_ERR_IO_HID: s = "HID I/O error with device";
205 break;
206 default:
207 s = "";
208 }
209
210 return snprintf(buf, len, "%s", s);
211 }