384 lines
14 KiB
C
384 lines
14 KiB
C
/* Name: hiddata.c
|
|
* Author: Christian Starkjohann
|
|
* Creation Date: 2008-04-11
|
|
* Tabsize: 4
|
|
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
|
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include "hiddata.h"
|
|
|
|
/* ######################################################################## */
|
|
#if defined(WIN32) /* ##################################################### */
|
|
/* ######################################################################## */
|
|
|
|
#include <windows.h>
|
|
#include <setupapi.h>
|
|
#include "hidsdi.h"
|
|
#include <ddk/hidpi.h>
|
|
|
|
#ifdef DEBUG
|
|
#define DEBUG_PRINT(arg) printf arg
|
|
#else
|
|
#define DEBUG_PRINT(arg)
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
static void convertUniToAscii(char *buffer)
|
|
{
|
|
unsigned short *uni = (void *)buffer;
|
|
char *ascii = buffer;
|
|
|
|
while(*uni != 0){
|
|
if(*uni >= 256){
|
|
*ascii++ = '?';
|
|
}else{
|
|
*ascii++ = *uni++;
|
|
}
|
|
}
|
|
*ascii++ = 0;
|
|
}
|
|
|
|
int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int product, char *productName, int usesReportIDs)
|
|
{
|
|
GUID hidGuid; /* GUID for HID driver */
|
|
HDEVINFO deviceInfoList;
|
|
SP_DEVICE_INTERFACE_DATA deviceInfo;
|
|
SP_DEVICE_INTERFACE_DETAIL_DATA *deviceDetails = NULL;
|
|
DWORD size;
|
|
int i, openFlag = 0; /* may be FILE_FLAG_OVERLAPPED */
|
|
int errorCode = USBOPEN_ERR_NOTFOUND;
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
HIDD_ATTRIBUTES deviceAttributes;
|
|
|
|
HidD_GetHidGuid(&hidGuid);
|
|
deviceInfoList = SetupDiGetClassDevs(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
|
|
deviceInfo.cbSize = sizeof(deviceInfo);
|
|
for(i=0;;i++){
|
|
if(handle != INVALID_HANDLE_VALUE){
|
|
CloseHandle(handle);
|
|
handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
if(!SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo))
|
|
break; /* no more entries */
|
|
/* first do a dummy call just to determine the actual size required */
|
|
SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, NULL, 0, &size, NULL);
|
|
if(deviceDetails != NULL)
|
|
free(deviceDetails);
|
|
deviceDetails = malloc(size);
|
|
deviceDetails->cbSize = sizeof(*deviceDetails);
|
|
/* this call is for real: */
|
|
SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, deviceDetails, size, &size, NULL);
|
|
DEBUG_PRINT(("checking HID path \"%s\"\n", deviceDetails->DevicePath));
|
|
#if 0
|
|
/* If we want to access a mouse our keyboard, we can only use feature
|
|
* requests as the device is locked by Windows. It must be opened
|
|
* with ACCESS_TYPE_NONE.
|
|
*/
|
|
handle = CreateFile(deviceDetails->DevicePath, ACCESS_TYPE_NONE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
|
|
#endif
|
|
/* attempt opening for R/W -- we don't care about devices which can't be accessed */
|
|
handle = CreateFile(deviceDetails->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
|
|
if(handle == INVALID_HANDLE_VALUE){
|
|
DEBUG_PRINT(("opening failed: %d\n", (int)GetLastError()));
|
|
/* errorCode = USBOPEN_ERR_ACCESS; opening will always fail for mouse -- ignore */
|
|
continue;
|
|
}
|
|
deviceAttributes.Size = sizeof(deviceAttributes);
|
|
HidD_GetAttributes(handle, &deviceAttributes);
|
|
DEBUG_PRINT(("device attributes: vid=%d pid=%d\n", deviceAttributes.VendorID, deviceAttributes.ProductID));
|
|
if(deviceAttributes.VendorID != vendor || deviceAttributes.ProductID != product)
|
|
continue; /* ignore this device */
|
|
errorCode = USBOPEN_ERR_NOTFOUND;
|
|
if(vendorName != NULL && productName != NULL){
|
|
char buffer[512];
|
|
if(!HidD_GetManufacturerString(handle, buffer, sizeof(buffer))){
|
|
DEBUG_PRINT(("error obtaining vendor name\n"));
|
|
errorCode = USBOPEN_ERR_IO;
|
|
continue;
|
|
}
|
|
convertUniToAscii(buffer);
|
|
DEBUG_PRINT(("vendorName = \"%s\"\n", buffer));
|
|
if(strcmp(vendorName, buffer) != 0)
|
|
continue;
|
|
if(!HidD_GetProductString(handle, buffer, sizeof(buffer))){
|
|
DEBUG_PRINT(("error obtaining product name\n"));
|
|
errorCode = USBOPEN_ERR_IO;
|
|
continue;
|
|
}
|
|
convertUniToAscii(buffer);
|
|
DEBUG_PRINT(("productName = \"%s\"\n", buffer));
|
|
if(strcmp(productName, buffer) != 0)
|
|
continue;
|
|
}
|
|
break; /* we have found the device we are looking for! */
|
|
}
|
|
SetupDiDestroyDeviceInfoList(deviceInfoList);
|
|
if(deviceDetails != NULL)
|
|
free(deviceDetails);
|
|
if(handle != INVALID_HANDLE_VALUE){
|
|
*device = (usbDevice_t *)handle;
|
|
errorCode = 0;
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
void usbhidCloseDevice(usbDevice_t *device)
|
|
{
|
|
CloseHandle((HANDLE)device);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
int usbhidSetReport(usbDevice_t *device, char *buffer, int len)
|
|
{
|
|
BOOLEAN rval;
|
|
|
|
rval = HidD_SetFeature((HANDLE)device, buffer, len);
|
|
return rval == 0 ? USBOPEN_ERR_IO : 0;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
int usbhidGetReport(usbDevice_t *device, int reportNumber, char *buffer, int *len)
|
|
{
|
|
BOOLEAN rval = 0;
|
|
|
|
buffer[0] = reportNumber;
|
|
rval = HidD_GetFeature((HANDLE)device, buffer, *len);
|
|
return rval == 0 ? USBOPEN_ERR_IO : 0;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
/* ######################################################################## */
|
|
#else /* defined WIN32 #################################################### */
|
|
/* ######################################################################## */
|
|
|
|
#include <string.h>
|
|
#include <usb.h>
|
|
|
|
#define usbDevice usb_dev_handle /* use libusb's device structure */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
#define USBRQ_HID_GET_REPORT 0x01
|
|
#define USBRQ_HID_SET_REPORT 0x09
|
|
|
|
#define USB_HID_REPORT_TYPE_FEATURE 3
|
|
|
|
|
|
static int usesReportIDs;
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static int usbhidGetStringAscii(usb_dev_handle *dev, int index, char *buf, int buflen)
|
|
{
|
|
char buffer[256];
|
|
int rval, i;
|
|
|
|
if((rval = usb_get_string_simple(dev, index, buf, buflen)) >= 0) /* use libusb version if it works */
|
|
return rval;
|
|
if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, 0x0409, buffer, sizeof(buffer), 5000)) < 0)
|
|
return rval;
|
|
if(buffer[1] != USB_DT_STRING){
|
|
*buf = 0;
|
|
return 0;
|
|
}
|
|
if((unsigned char)buffer[0] < rval)
|
|
rval = (unsigned char)buffer[0];
|
|
rval /= 2;
|
|
/* lossy conversion to ISO Latin1: */
|
|
for(i=1;i<rval;i++){
|
|
if(i > buflen) /* destination buffer overflow */
|
|
break;
|
|
buf[i-1] = buffer[2 * i];
|
|
if(buffer[2 * i + 1] != 0) /* outside of ISO Latin1 range */
|
|
buf[i-1] = '?';
|
|
}
|
|
buf[i-1] = 0;
|
|
return i-1;
|
|
}
|
|
|
|
int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int product, char *productName, int _usesReportIDs)
|
|
{
|
|
char drivername[2];
|
|
int detachrc;
|
|
struct usb_bus *bus;
|
|
struct usb_device *dev;
|
|
usb_dev_handle *handle = NULL;
|
|
int errorCode = USBOPEN_ERR_NOTFOUND;
|
|
static int didUsbInit = 0;
|
|
|
|
if(!didUsbInit){
|
|
usb_init();
|
|
didUsbInit = 1;
|
|
}
|
|
usb_find_busses();
|
|
usb_find_devices();
|
|
for(bus=usb_get_busses(); bus; bus=bus->next){
|
|
for(dev=bus->devices; dev; dev=dev->next){
|
|
if(dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product){
|
|
char string[256];
|
|
int len;
|
|
handle = usb_open(dev); /* we need to open the device in order to query strings */
|
|
if(!handle){
|
|
errorCode = USBOPEN_ERR_ACCESS;
|
|
fprintf(stderr, "Warning: cannot open USB device: %s\n", usb_strerror());
|
|
continue;
|
|
}
|
|
|
|
|
|
// Likely no need to capture the return code. It will return an error if no driver is
|
|
// attached, which is A-OK. Nothing will be detached in that case.
|
|
usb_get_driver_np(handle, 0, drivername, sizeof(drivername));
|
|
// If we have an empty then nothing is attached, otherwise will be something like "dummy".
|
|
if(strlen(drivername) > 0){
|
|
// Not doing this causes several issues writing control messages to the interface.
|
|
detachrc = usb_detach_kernel_driver_np(handle, 0);
|
|
if (detachrc != 0) {
|
|
errorCode = USBOPEN_ERR_IO;
|
|
fprintf(stderr, "Warning: cannot detach kernel driver from interface 0: %s\n", usb_strerror());
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if(vendorName == NULL && productName == NULL){ /* name does not matter */
|
|
break;
|
|
}
|
|
/* now check whether the names match: */
|
|
len = usbhidGetStringAscii(handle, dev->descriptor.iManufacturer, string, sizeof(string));
|
|
if(len < 0){
|
|
errorCode = USBOPEN_ERR_IO;
|
|
fprintf(stderr, "Warning: cannot query manufacturer for device: %s\n", usb_strerror());
|
|
}else{
|
|
errorCode = USBOPEN_ERR_NOTFOUND;
|
|
/* fprintf(stderr, "seen device from vendor ->%s<-\n", string); */
|
|
if(strcmp(string, vendorName) == 0){
|
|
len = usbhidGetStringAscii(handle, dev->descriptor.iProduct, string, sizeof(string));
|
|
if(len < 0){
|
|
errorCode = USBOPEN_ERR_IO;
|
|
fprintf(stderr, "Warning: cannot query product for device: %s\n", usb_strerror());
|
|
}else{
|
|
errorCode = USBOPEN_ERR_NOTFOUND;
|
|
/* fprintf(stderr, "seen product ->%s<-\n", string); */
|
|
if(strcmp(string, productName) == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
usb_close(handle);
|
|
handle = NULL;
|
|
}
|
|
}
|
|
if(handle)
|
|
break;
|
|
}
|
|
if(handle != NULL){
|
|
errorCode = 0;
|
|
*device = (void *)handle;
|
|
usesReportIDs = _usesReportIDs;
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
void usbhidCloseDevice(usbDevice_t *device)
|
|
{
|
|
if(device != NULL)
|
|
usb_close((void *)device);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
int usbhidSetReport(usbDevice_t *device, char *buffer, int len)
|
|
{
|
|
int bytesSent, reportId = buffer[0];
|
|
int claimrc, releaserc;
|
|
|
|
if(!usesReportIDs){
|
|
buffer++; /* skip dummy report ID */
|
|
len--;
|
|
}
|
|
|
|
claimrc = usb_claim_interface((void *)device, 0);
|
|
if(claimrc != 0){
|
|
fprintf(stderr, "Error claiming interface 0 before sending a control message: %s\n", usb_strerror());
|
|
return USBOPEN_ERR_IO;
|
|
}
|
|
|
|
// original hiddata.h
|
|
// 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);
|
|
// modification by todbot, matches roughly what hiddata does
|
|
// change by tod to use USB_RECIP_INTERFACE instead of USB_RECIP_DEVICE
|
|
bytesSent = usb_control_msg((void *)device, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_ENDPOINT_OUT, USBRQ_HID_SET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | (reportId & 0xff), 0, buffer, len, 5000);
|
|
if(bytesSent != len){
|
|
if(bytesSent < 0)
|
|
{
|
|
fprintf(stderr, "Error sending message: %s\n", usb_strerror());
|
|
}
|
|
|
|
usb_release_interface((void *)device, 0);
|
|
return USBOPEN_ERR_IO;
|
|
}
|
|
|
|
releaserc = usb_release_interface((void *)device, 0);
|
|
if(releaserc != 0){
|
|
fprintf(stderr, "Error releasing interface 0 after sending a control message: %s\n", usb_strerror());
|
|
return USBOPEN_ERR_IO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
int usbhidGetReport(usbDevice_t *device, int reportNumber, char *buffer, int *len)
|
|
{
|
|
int bytesReceived, maxLen = *len;
|
|
int claimrc, releaserc;
|
|
|
|
if(!usesReportIDs){
|
|
buffer++; /* make room for dummy report ID */
|
|
maxLen--;
|
|
}
|
|
|
|
claimrc = usb_claim_interface((void *)device, 0);
|
|
if(claimrc != 0){
|
|
fprintf(stderr, "Error claiming interface 0 before sending a control message: %s\n", usb_strerror());
|
|
return USBOPEN_ERR_IO;
|
|
}
|
|
|
|
// original hiddata.h
|
|
//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);
|
|
// change by tod to use USB_RECIP_INTERFACE instead of USB_RECIP_DEVICE
|
|
bytesReceived = usb_control_msg((void *)device, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_ENDPOINT_IN, USBRQ_HID_GET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | reportNumber, 0, buffer, maxLen, 5000);
|
|
if(bytesReceived < 0){
|
|
fprintf(stderr, "Error sending message: %s\n", usb_strerror());
|
|
usb_release_interface((void *)device, 0);
|
|
return USBOPEN_ERR_IO;
|
|
}
|
|
*len = bytesReceived;
|
|
if(!usesReportIDs){
|
|
buffer[-1] = reportNumber; /* add dummy report ID */
|
|
(*len)++;
|
|
}
|
|
|
|
releaserc = usb_release_interface((void *)device, 0);
|
|
if(releaserc != 0){
|
|
fprintf(stderr, "Error releasing interface 0 after sending a control message: %s\n", usb_strerror());
|
|
return USBOPEN_ERR_IO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ######################################################################## */
|
|
#endif /* defined WIN32 ################################################### */
|
|
/* ######################################################################## */
|