2 * Author: Christian Starkjohann
3 * Creation Date: 2008-04-11
5 * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
6 * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
15 #define DEBUG_PRINT(arg) printf arg
17 #define DEBUG_PRINT(arg)
20 #define A_MAX_USB_STRING_LEN 126
23 /* ######################################################################## */
25 /* ######################################################################## */
27 #include "targetver.h"
34 #pragma comment(lib, "setupapi")
35 #pragma comment(lib, "hid")
39 * Convert UTF-16 null term. string to single byte (ASCII or ISO Latin)
40 * change all weird characters to "?"
42 static void usbstring_to_ascii(unsigned short *wp
, char *cp
, int size
)
44 unsigned short *wpend
= wp
+ (size
/sizeof(unsigned short));
47 unsigned short h
= *wp
++;
48 *cp
++ = (h
< 0xFF) ? (char)h
: '?';
55 * Read HID string for vendor and device, return as ASCII (or ISO Latin...)
57 int usbhidGetVendorString(USBDEVHANDLE usbh
, char *buffer
, int len
)
59 /* HidD_GetManufacturerString returns zero terminated UTF-16 string */
60 /* Error if buffer is too short */
61 if ( !HidD_GetManufacturerString((HANDLE
)usbh
, (void*)buffer
, len
) ) {
62 DEBUG_PRINT(("error obtaining vendor name\n"));
63 return USBOPEN_ERR_IO
;
65 usbstring_to_ascii((UINT16
*)buffer
, buffer
, len
);
69 int usbhidGetProductString(USBDEVHANDLE usbh
, char *buffer
, int len
)
71 /* HidD_GetProductString returns zero terminated UTF-16 string */
72 /* Error if buffer is too short */
73 if (!HidD_GetProductString((HANDLE
)usbh
, (void*)buffer
, len
) ) {
74 DEBUG_PRINT(("error obtaining product name\n"));
75 return USBOPEN_ERR_IO
;
77 usbstring_to_ascii((UINT16
*)buffer
, buffer
, len
);
82 * Enumerate HID USB devices.
83 * In Windows this will find also non-USB devices, but assume that
84 * filtering by PID & VID is enough.
85 * Some HID devices (mice, kbd) are locked by Windows and cannot be opened.
86 * If we cannot open a device for R&W, we skip it without error.
87 * Assume our devices are not of types reserved by Windows.
89 int usbhidEnumDevices(int vendor
, int product
,
91 int (*usbhidEnumFunc
)(USBDEVHANDLE usbh
, void *ctx
))
93 GUID hidGuid
; /* GUID for HID class */
94 HDEVINFO deviceInfoList
;
95 SP_DEVICE_INTERFACE_DATA deviceInfo
;
96 SP_DEVICE_INTERFACE_DETAIL_DATA_W
*deviceDetails
= NULL
;
98 int i
, openFlag
= 0; /* may be FILE_FLAG_OVERLAPPED */
99 int errorCode
= USBOPEN_ERR_NOTFOUND
;
100 HANDLE handle
= INVALID_HANDLE_VALUE
;
101 HIDD_ATTRIBUTES deviceAttributes
;
103 HidD_GetHidGuid(&hidGuid
);
104 deviceInfoList
= SetupDiGetClassDevsW(&hidGuid
, NULL
, NULL
, DIGCF_PRESENT
| DIGCF_INTERFACEDEVICE
);
105 if (!deviceInfoList
|| deviceInfoList
== INVALID_HANDLE_VALUE
)
107 return USBOPEN_ERR_NOTFOUND
;
110 deviceInfo
.cbSize
= sizeof(deviceInfo
);
112 if(handle
!= INVALID_HANDLE_VALUE
){
114 handle
= INVALID_HANDLE_VALUE
;
116 if( !SetupDiEnumDeviceInterfaces(deviceInfoList
, 0, &hidGuid
, i
, &deviceInfo
) )
117 break; /* no more entries */
118 /* first do a dummy call just to determine the actual size required */
119 SetupDiGetDeviceInterfaceDetailW(deviceInfoList
, &deviceInfo
, NULL
, 0, &size
, NULL
);
120 if(deviceDetails
!= NULL
)
122 deviceDetails
= malloc(size
);
123 deviceDetails
->cbSize
= sizeof(*deviceDetails
);
124 /* this call is for real: */
125 SetupDiGetDeviceInterfaceDetailW(deviceInfoList
, &deviceInfo
, deviceDetails
, size
, &size
, NULL
);
126 DEBUG_PRINT(("checking HID path \"%s\"\n", deviceDetails
->DevicePath
));
128 handle
= CreateFileW(deviceDetails
->DevicePath
,
129 GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, openFlag
, NULL
);
130 if(handle
== INVALID_HANDLE_VALUE
){
131 DEBUG_PRINT(("open USB device failed: gle=%d\n", (int)GetLastError()));
132 /* errorCode = USBOPEN_ERR_ACCESS; opening will always fail for mouse -- ignore */
135 deviceAttributes
.Size
= sizeof(deviceAttributes
);
136 HidD_GetAttributes(handle
, &deviceAttributes
);
137 DEBUG_PRINT(("device attributes: vid=%d pid=%d ver=%4.4X\n", deviceAttributes
.VendorID
, deviceAttributes
.ProductID
, deviceAttributes
.VersionNumber
));
138 if(deviceAttributes
.VendorID
!= vendor
|| deviceAttributes
.ProductID
!= product
)
139 continue; /* skip this device */
142 if ( 0 == usbhidEnumFunc((USBDEVHANDLE
)handle
, context
) )
144 break; /* stop enumeration */
147 /* Now the handle is owned by the callback */
148 handle
= INVALID_HANDLE_VALUE
;
151 SetupDiDestroyDeviceInfoList(deviceInfoList
);
152 if(deviceDetails
!= NULL
)
159 void usbhidCloseDevice(USBDEVHANDLE usbh
)
161 CloseHandle((HANDLE
)usbh
);
165 int usbhidSetReport(USBDEVHANDLE usbh
, char *buffer
, int len
)
168 rval
= HidD_SetFeature((HANDLE
)usbh
, buffer
, len
);
169 return rval
== 0 ? USBOPEN_ERR_IO
: 0;
173 int usbhidGetReport(USBDEVHANDLE usbh
, int reportNumber
, char *buffer
, int *len
)
176 buffer
[0] = reportNumber
;
177 rval
= HidD_GetFeature((HANDLE
)usbh
, buffer
, *len
);
178 return rval
== 0 ? USBOPEN_ERR_IO
: 0;
183 /* ######################################################################## */
184 #else /* defined WIN32 #################################################### */
185 /* ######################################################################## */
187 // Using the old simple version of libusb (before 1.0)
190 // USBDEVHANDLE is same as struct usb_device *
191 // Open handles are stored in struct usb_device (HACK! revise for new libusb)
193 static inline struct usb_device
* usbDevStruct(USBDEVHANDLE usbh
) {
194 return (struct usb_device
*)usbh
;
197 static inline usb_dev_handle
* usbDevHandle(USBDEVHANDLE usbh
) {
198 return (usb_dev_handle
*)(usbDevStruct(usbh
)->children
);
201 // TO DO: for devices that prepend report ID, implement this somehow
202 static inline int usesReportIDs(USBDEVHANDLE usbh
) {
203 /* return 1 if 1st byte of get/set report buffer is report ID */
207 /* ------------------------------------------------------------------------- */
209 #define USBRQ_HID_GET_REPORT 0x01
210 #define USBRQ_HID_SET_REPORT 0x09
212 #define USB_HID_REPORT_TYPE_FEATURE 3
214 #define A_REPORT_REQUEST_TIMEOUT 5000 /* in milliseconds */
216 /* ------------------------------------------------------------------------- */
218 int usbhidEnumDevices(int vendor
, int product
,
220 int (*usbhidEnumFunc
)(USBDEVHANDLE usbh
, void *ctx
))
224 struct usb_device
*dev
;
225 usb_dev_handle
*handle
= NULL
;
226 int errorCode
= USBOPEN_ERR_NOTFOUND
;
227 static int didUsbInit
= 0;
235 for (bus
=usb_get_busses(); bus
; bus
=bus
->next
) {
236 for (dev
=bus
->devices
; dev
; dev
=dev
->next
) {
237 if (dev
->descriptor
.idVendor
== vendor
&& dev
->descriptor
.idProduct
== product
) {
238 handle
= usb_open(dev
); /* we need to open the device in order to query strings */
240 errorCode
= USBOPEN_ERR_ACCESS
;
241 fprintf(stderr
, "Warning: cannot open USB device: %s\n", usb_strerror());
247 // printf("Probing: [%s] nc=%u %p\n", dev->filename, dev->num_children, dev->children);
248 // Assume our devices are leaf, so we can use dev->children to store the handle
249 if ( dev
->children
) {
250 fprintf(stderr
, "ERROR: assertion failed for usb dev %p\n", dev
);
256 dev
->children
= (void*)handle
;
257 if ( 0 == usbhidEnumFunc((void*)dev
, context
) )
259 break; /* stop enumeration */
262 /* Now the handle is owned by the callback */
271 /* ------------------------------------------------------------------------- */
273 void usbhidCloseDevice(USBDEVHANDLE usbh
)
276 usb_close(usbDevHandle(usbh
));
277 usbDevStruct(usbh
)->children
= NULL
;
281 /* ------------------------------------------------------------------------- */
283 static int usbhidGetStringAscii(struct usb_dev_handle
*dev
, int index
, char *buf
, int buflen
)
286 if((rval
= usb_get_string_simple(dev
, index
, buf
, buflen
)) >= 0) /* use libusb version if it works */
289 fprintf(stderr
, "usbhid: Access denied to USB device. Run as root or adjust device permissions.\n");
291 fprintf(stderr
, "usbhid: %s error %s\n", __FUNCTION__
, usb_strerror());
295 int usbhidGetVendorString(USBDEVHANDLE usbh
, char *buffer
, int len
)
297 int len2
= usbhidGetStringAscii(usbDevHandle(usbh
), usbDevStruct(usbh
)->descriptor
.iManufacturer
, buffer
, len
);
299 fprintf(stderr
, "Warning: cannot query vendor for device\n");
300 return USBOPEN_ERR_IO
;
305 int usbhidGetProductString(USBDEVHANDLE usbh
, char *buffer
, int len
)
307 int len2
= usbhidGetStringAscii(usbDevHandle(usbh
), usbDevStruct(usbh
)->descriptor
.iProduct
, buffer
, len
);
309 fprintf(stderr
, "Warning: cannot query product for device\n");
310 return USBOPEN_ERR_IO
;
318 int usbhidSetReport(USBDEVHANDLE usbh
, char *buffer
, int len
)
320 int bytesSent
, reportId
= buffer
[0];
322 if ( !usesReportIDs(usbh
) ) {
323 buffer
++; /* skip dummy report ID */
326 bytesSent
= usb_control_msg(usbDevHandle(usbh
),
327 USB_TYPE_CLASS
| USB_RECIP_DEVICE
| USB_ENDPOINT_OUT
, USBRQ_HID_SET_REPORT
, USB_HID_REPORT_TYPE_FEATURE
<< 8 | (reportId
& 0xff),
328 0, buffer
, len
, A_REPORT_REQUEST_TIMEOUT
);
329 if (bytesSent
!= len
) {
331 fprintf(stderr
, "Error sending message: %s\n", usb_strerror());
332 return USBOPEN_ERR_IO
;
337 /* ------------------------------------------------------------------------- */
339 int usbhidGetReport(USBDEVHANDLE usbh
, int reportNumber
, char *buffer
, int *len
)
341 int bytesReceived
, maxLen
= *len
;
343 if (!usesReportIDs(usbh
)) {
344 buffer
++; /* make room for dummy report ID */
347 bytesReceived
= usb_control_msg(usbDevHandle(usbh
),
348 USB_TYPE_CLASS
| USB_RECIP_DEVICE
| USB_ENDPOINT_IN
, USBRQ_HID_GET_REPORT
, USB_HID_REPORT_TYPE_FEATURE
<< 8 | reportNumber
,
349 0, buffer
, maxLen
, A_REPORT_REQUEST_TIMEOUT
);
350 if(bytesReceived
< 0){
351 fprintf(stderr
, "Error sending message: %s\n", usb_strerror());
352 return USBOPEN_ERR_IO
;
354 *len
= bytesReceived
;
355 if (!usesReportIDs(usbh
)) {
356 buffer
[-1] = reportNumber
; /* add dummy report ID */
363 void usbhidSetUsesReportId(USBDEVHANDLE usbh
)
365 //TODO Implement if some devices prepend report IDs
368 /* ######################################################################## */
370 /* ######################################################################## */