Removed Windows specific wchar_t; added project url in usage().
[usb-relay-hid.git] / commandline / hiddata.c
1 /* Name: hiddata.c
2 * Author: Christian Starkjohann
3 * Creation Date: 2008-04-11
4 * Tabsize: 4
5 * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
6 * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
7 */
8
9 #include "hiddata.h"
10 #include <stdio.h>
11 #include <string.h>
12
13 #if 0 //ifdef DEBUG
14 #define DEBUG_PRINT(arg) printf arg
15 #else
16 #define DEBUG_PRINT(arg)
17 #endif
18
19 #define A_MAX_USB_STRING_LEN 126
20
21
22 /* ######################################################################## */
23 #if defined(_WIN32)
24 /* ######################################################################## */
25
26 #include "targetver.h"
27 #include <windows.h>
28 #include <setupapi.h>
29 #include <hidsdi.h>
30 #include <hidpi.h>
31
32 #ifdef _MSC_VER
33 #pragma comment(lib, "setupapi")
34 #pragma comment(lib, "hid")
35 #endif /*_MSC_VER*/
36
37 /*
38 * Convert UTF-16 null term. string to single byte (ASCII or ISO Latin)
39 * change all weird characters to "?"
40 */
41 static void usbstring_to_ascii(unsigned short *wp, char *cp, int size)
42 {
43 unsigned short *wpend = wp + (size/sizeof(unsigned short));
44 for( ; wp < wpend; )
45 {
46 unsigned short h = *wp++;
47 *cp++ = (h < 0xFF) ? (char)h : '?';
48 if (h == 0)
49 break;
50 }
51 }
52
53 /*
54 * Read HID string for vendor and device, return as ASCII (or ISO Latin...)
55 */
56 int usbhidGetVendorString(USBDEVHANDLE usbh, char *buffer, int len)
57 {
58 /* HidD_GetManufacturerString returns zero terminated UTF-16 string */
59 /* Error if buffer is too short */
60 if ( !HidD_GetManufacturerString((HANDLE)usbh, (void*)buffer, len ) ) {
61 DEBUG_PRINT(("error obtaining vendor name\n"));
62 return USBOPEN_ERR_IO;
63 }
64 usbstring_to_ascii((UINT16*)buffer, buffer, len);
65 return 0;
66 }
67
68 int usbhidGetProductString(USBDEVHANDLE usbh, char *buffer, int len)
69 {
70 /* HidD_GetProductString returns zero terminated UTF-16 string */
71 /* Error if buffer is too short */
72 if (!HidD_GetProductString((HANDLE)usbh, (void*)buffer, len ) ) {
73 DEBUG_PRINT(("error obtaining product name\n"));
74 return USBOPEN_ERR_IO;
75 }
76 usbstring_to_ascii((UINT16*)buffer, buffer, len);
77 return 0;
78 }
79
80 /*
81 * Enumerate HID USB devices.
82 * In Windows this will find also non-USB devices, but assume that
83 * filtering by PID & VID is enough.
84 * Some HID devices (mice, kbd) are locked by Windows and cannot be opened.
85 * If we cannot open a device for R&W, we skip it without error.
86 * Assume our devices are not of types reserved by Windows.
87 */
88 int usbhidEnumDevices(int vendor, int product,
89 void *context,
90 int (*usbhidEnumFunc)(USBDEVHANDLE usbh, void *ctx))
91 {
92 GUID hidGuid; /* GUID for HID class */
93 HDEVINFO deviceInfoList;
94 SP_DEVICE_INTERFACE_DATA deviceInfo;
95 SP_DEVICE_INTERFACE_DETAIL_DATA_W *deviceDetails = NULL;
96 DWORD size;
97 int i, openFlag = 0; /* may be FILE_FLAG_OVERLAPPED */
98 int errorCode = USBOPEN_ERR_NOTFOUND;
99 HANDLE handle = INVALID_HANDLE_VALUE;
100 HIDD_ATTRIBUTES deviceAttributes;
101
102 HidD_GetHidGuid(&hidGuid);
103 deviceInfoList = SetupDiGetClassDevsW(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
104 if (!deviceInfoList || deviceInfoList == INVALID_HANDLE_VALUE)
105 {
106 return USBOPEN_ERR_NOTFOUND;
107 }
108
109 deviceInfo.cbSize = sizeof(deviceInfo);
110 for (i=0; ; i++) {
111 if(handle != INVALID_HANDLE_VALUE){
112 CloseHandle(handle);
113 handle = INVALID_HANDLE_VALUE;
114 }
115 if( !SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo) )
116 break; /* no more entries */
117 /* first do a dummy call just to determine the actual size required */
118 SetupDiGetDeviceInterfaceDetailW(deviceInfoList, &deviceInfo, NULL, 0, &size, NULL);
119 if(deviceDetails != NULL)
120 free(deviceDetails);
121 deviceDetails = malloc(size);
122 deviceDetails->cbSize = sizeof(*deviceDetails);
123 /* this call is for real: */
124 SetupDiGetDeviceInterfaceDetailW(deviceInfoList, &deviceInfo, deviceDetails, size, &size, NULL);
125 DEBUG_PRINT(("checking HID path \"%s\"\n", deviceDetails->DevicePath));
126
127 handle = CreateFileW(deviceDetails->DevicePath,
128 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
129 if(handle == INVALID_HANDLE_VALUE){
130 DEBUG_PRINT(("open USB device failed: gle=%d\n", (int)GetLastError()));
131 /* errorCode = USBOPEN_ERR_ACCESS; opening will always fail for mouse -- ignore */
132 continue;
133 }
134 deviceAttributes.Size = sizeof(deviceAttributes);
135 HidD_GetAttributes(handle, &deviceAttributes);
136 DEBUG_PRINT(("device attributes: vid=%d pid=%d ver=%4.4X\n", deviceAttributes.VendorID, deviceAttributes.ProductID, deviceAttributes.VersionNumber));
137 if(deviceAttributes.VendorID != vendor || deviceAttributes.ProductID != product)
138 continue; /* skip this device */
139
140 errorCode = 0;
141 if ( 0 == usbhidEnumFunc((USBDEVHANDLE)handle, context) )
142 {
143 break; /* stop enumeration */
144 }
145
146 /* Now the handle is owned by the callback */
147 handle = INVALID_HANDLE_VALUE;
148 }
149
150 SetupDiDestroyDeviceInfoList(deviceInfoList);
151 if(deviceDetails != NULL)
152 free(deviceDetails);
153
154 return errorCode;
155 }
156
157
158
159 void usbhidCloseDevice(USBDEVHANDLE usbh)
160 {
161 CloseHandle((HANDLE)usbh);
162 }
163
164
165
166 int usbhidSetReport(USBDEVHANDLE usbh, char *buffer, int len)
167 {
168 BOOLEAN rval;
169 rval = HidD_SetFeature((HANDLE)usbh, buffer, len);
170 return rval == 0 ? USBOPEN_ERR_IO : 0;
171 }
172
173
174 int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len)
175 {
176 BOOLEAN rval = 0;
177 buffer[0] = reportNumber;
178 rval = HidD_GetFeature((HANDLE)usbh, buffer, *len);
179 return rval == 0 ? USBOPEN_ERR_IO : 0;
180 }
181
182
183
184 /* ######################################################################## */
185 #else /* defined WIN32 #################################################### */
186 /* ######################################################################## */
187
188 #include <usb.h>
189
190 #define usbDevice usb_dev_handle /* use libusb's device structure */
191
192 /* ------------------------------------------------------------------------- */
193
194 #define USBRQ_HID_GET_REPORT 0x01
195 #define USBRQ_HID_SET_REPORT 0x09
196
197 #define USB_HID_REPORT_TYPE_FEATURE 3
198
199
200 static int usesReportIDs = 0; /* 1 => 1st byte of get/set report buffer is report ID */
201
202 /* ------------------------------------------------------------------------- */
203
204 static int usbhidGetStringAscii(usb_dev_handle *dev, int index, char *buf, int buflen)
205 {
206 char buffer[256];
207 int rval, i;
208
209 if((rval = usb_get_string_simple(dev, index, buf, buflen)) >= 0) /* use libusb version if it works */
210 return rval;
211 if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, 0x0409, buffer, sizeof(buffer), 5000)) < 0)
212 return rval;
213 if(buffer[1] != USB_DT_STRING){
214 *buf = 0;
215 return 0;
216 }
217 if((unsigned char)buffer[0] < rval)
218 rval = (unsigned char)buffer[0];
219 rval /= 2;
220 /* lossy conversion to ISO Latin1: */
221 for(i=1;i<rval;i++){
222 if(i > buflen) /* destination buffer overflow */
223 break;
224 buf[i-1] = buffer[2 * i];
225 if(buffer[2 * i + 1] != 0) /* outside of ISO Latin1 range */
226 buf[i-1] = '?';
227 }
228 buf[i-1] = 0;
229 return i-1;
230 }
231
232 int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int product, char *productName, int _usesReportIDs)
233 {
234 struct usb_bus *bus;
235 struct usb_device *dev;
236 usb_dev_handle *handle = NULL;
237 int errorCode = USBOPEN_ERR_NOTFOUND;
238 static int didUsbInit = 0;
239
240 if(!didUsbInit){
241 usb_init();
242 didUsbInit = 1;
243 }
244 usb_find_busses();
245 usb_find_devices();
246 for(bus=usb_get_busses(); bus; bus=bus->next){
247 for(dev=bus->devices; dev; dev=dev->next){
248 if(dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product){
249 char string[256];
250 int len;
251 handle = usb_open(dev); /* we need to open the device in order to query strings */
252 if(!handle){
253 errorCode = USBOPEN_ERR_ACCESS;
254 fprintf(stderr, "Warning: cannot open USB device: %s\n", usb_strerror());
255 continue;
256 }
257 if(vendorName == NULL && productName == NULL){ /* name does not matter */
258 break;
259 }
260 /* now check whether the names match: */
261 len = usbhidGetStringAscii(handle, dev->descriptor.iManufacturer, string, sizeof(string));
262 if(len < 0){
263 errorCode = USBOPEN_ERR_IO;
264 fprintf(stderr, "Warning: cannot query manufacturer for device: %s\n", usb_strerror());
265 }else{
266 errorCode = USBOPEN_ERR_NOTFOUND;
267 /* fprintf(stderr, "seen device from vendor ->%s<-\n", string); */
268 if(strcmp(string, vendorName) == 0){
269 len = usbhidGetStringAscii(handle, dev->descriptor.iProduct, string, sizeof(string));
270 if(len < 0){
271 errorCode = USBOPEN_ERR_IO;
272 fprintf(stderr, "Warning: cannot query product for device: %s\n", usb_strerror());
273 }else{
274 errorCode = USBOPEN_ERR_NOTFOUND;
275 /* fprintf(stderr, "seen product ->%s<-\n", string); */
276 if(strcmp(string, productName) == 0)
277 break;
278 }
279 }
280 }
281 usb_close(handle);
282 handle = NULL;
283 }
284 }
285 if(handle)
286 break;
287 }
288 if(handle != NULL){
289 errorCode = 0;
290 *device = (void *)handle;
291 usesReportIDs = _usesReportIDs;
292 }
293 return errorCode;
294 }
295
296 /* ------------------------------------------------------------------------- */
297
298 void usbhidCloseDevice(usbDevice_t *device)
299 {
300 if(device != NULL)
301 usb_close((void *)device);
302 }
303
304 /* ------------------------------------------------------------------------- */
305
306 int usbhidSetReport(usbDevice_t *device, char *buffer, int len)
307 {
308 int bytesSent, reportId = buffer[0];
309
310 if(!usesReportIDs){
311 buffer++; /* skip dummy report ID */
312 len--;
313 }
314 bytesSent = usb_control_msg((void *)device, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, USBRQ_HID_SET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | (reportId & 0xff), 0, buffer, len, 5000);
315 if(bytesSent != len){
316 if(bytesSent < 0)
317 fprintf(stderr, "Error sending message: %s\n", usb_strerror());
318 return USBOPEN_ERR_IO;
319 }
320 return 0;
321 }
322
323 /* ------------------------------------------------------------------------- */
324
325 int usbhidGetReport(usbDevice_t *device, int reportNumber, char *buffer, int *len)
326 {
327 int bytesReceived, maxLen = *len;
328
329 if(!usesReportIDs){
330 buffer++; /* make room for dummy report ID */
331 maxLen--;
332 }
333 bytesReceived = usb_control_msg((void *)device, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_ENDPOINT_IN, USBRQ_HID_GET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | reportNumber, 0, buffer, maxLen, 5000);
334 if(bytesReceived < 0){
335 fprintf(stderr, "Error sending message: %s\n", usb_strerror());
336 return USBOPEN_ERR_IO;
337 }
338 *len = bytesReceived;
339 if(!usesReportIDs){
340 buffer[-1] = reportNumber; /* add dummy report ID */
341 (*len)++;
342 }
343 return 0;
344 }
345
346 /* ######################################################################## */
347 #endif /* WIN32 */
348 /* ######################################################################## */