diff --git a/hid_listen/.gitignore b/hid_listen/.gitignore new file mode 100644 index 0000000..65a8870 --- /dev/null +++ b/hid_listen/.gitignore @@ -0,0 +1 @@ +/hid_listen \ No newline at end of file diff --git a/hid_listen/Makefile b/hid_listen/Makefile new file mode 100644 index 0000000..23e8535 --- /dev/null +++ b/hid_listen/Makefile @@ -0,0 +1,68 @@ +PROG = hid_listen + +OS = LINUX +#OS = DARWIN +#OS = WINDOWS + + +ifeq ($(OS), LINUX) +TARGET = $(PROG) +CC = gcc +STRIP = strip +CFLAGS = -O2 -Wall -D$(OS) +LIBS = +else ifeq ($(OS), DARWIN) +TARGET = $(PROG) +CC = gcc +STRIP = strip +SDK = /Developer/SDKs/MacOSX10.5.sdk +CFLAGS = -O2 -Wall -isysroot $(SDK) -D$(OS) -arch ppc -arch i386 +LIBS = -Xlinker -syslibroot -Xlinker $(SDK) -framework IOKit -framework CoreFoundation +else ifeq ($(OS), WINDOWS) +TARGET = $(PROG).exe +CC = i586-mingw32msvc-gcc +STRIP = i586-mingw32msvc-strip +WINDRES = i586-mingw32msvc-windres +CFLAGS = -O2 -Wall -D$(OS) +LIBS = -lhid -lsetupapi +KEY_SPC = ~/bin/cert/mykey.spc +KEY_PVK = ~/bin/cert/mykey.pvk +KEY_TS = http://timestamp.comodoca.com/authenticode +endif + + +MAKEFLAGS = --jobs=2 +OBJS = hid_listen.o rawhid.o + +all: $(TARGET) + +$(PROG): $(OBJS) + gcc -o $(PROG) $(OBJS) $(LIBS) + $(STRIP) $(PROG) + +$(PROG).app: $(PROG) Info.plist + mkdir -p $(PROG).app/Contents/MacOS + mkdir -p $(PROG).app/Contents/Resources/English.lproj + cp Info.plist $(PROG).app/Contents/ + echo -n 'APPL????' > $(PROG).app/Contents/PkgInfo + cp $(PROG) $(PROG).app/Contents/MacOS/$(PROG) + cp icons/$(PROG).icns $(PROG).app/Contents/Resources/$(PROG).icns + touch $(PROG).app + +$(PROG).dmg: $(PROG).app + hdiutil create -ov -srcfolder $(PROG).app $(PROG).dmg + +$(PROG).exe: $(OBJS) + $(CC) $(OBJS) -o $(PROG).exe $(LIBS) + $(STRIP) $(PROG).exe + -signcode -spc $(KEY_SPC) -v $(KEY_PVK) -t $(KEY_TS) $(PROG).exe + +resource.o: resource.rs icons/$(PROG).ico + $(WINDRES) -o resource.o resource.rs + + + +clean: + rm -f *.o $(PROG) $(PROG).exe $(PROG).exe.bak $(PROG).dmg + rm -rf $(PROG).app + diff --git a/hid_listen/hid_listen.c b/hid_listen/hid_listen.c new file mode 100644 index 0000000..de10b63 --- /dev/null +++ b/hid_listen/hid_listen.c @@ -0,0 +1,88 @@ +/* HID Listen, http://www.pjrc.com/teensy/hid_listen.html + * Listens (and prints) all communication received from a USB HID device, + * which is useful for view debug messages from the Teensy USB Board. + * Copyright 2008, PJRC.COM, LLC + * + * You may redistribute this program and/or modify it under the terms + * of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ + */ + +#include "rawhid.h" +#include +#include +#include +#include +#include + +static void delay_ms(unsigned int msec); + +int main(void) { + char buf[64], *in, *out; + rawhid_t *hid; + int num, count; + + printf("Waiting for device:"); + fflush(stdout); + while (1) { + hid = rawhid_open_only1(0, 0, 0xFF31, 0x0074); + if (hid == NULL) { + printf("."); + fflush(stdout); + delay_ms(1000); + continue; + } + printf("\nListening:\n"); + while (1) { + num = rawhid_read(hid, buf, sizeof(buf), 200); + if (num < 0) + break; + if (num == 0) + continue; + in = out = buf; + for (count = 0; count < num; count++) { + if (*in) { + *out++ = *in; + } + in++; + } + count = out - buf; + // printf("read %d bytes, %d actual\n", num, count); + if (count) { + struct timeval tv; + time_t nowtime; + struct tm *nowtm; + char tmbuf[64]; + + gettimeofday(&tv, NULL); + nowtime = tv.tv_sec; + nowtm = localtime(&nowtime); + strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm); + printf("%s.%06ld ", tmbuf, tv.tv_usec); + + num = fwrite(buf, 1, count, stdout); + fflush(stdout); + } + } + rawhid_close(hid); + printf("\nDevice disconnected.\nWaiting for new device:"); + } + return 0; +} + +#if (defined(WIN32) || defined(WINDOWS) || defined(__WINDOWS__)) +#include +static void delay_ms(unsigned int msec) { Sleep(msec); } +#else +#include +static void delay_ms(unsigned int msec) { usleep(msec * 1000); } +#endif diff --git a/hid_listen/rawhid.c b/hid_listen/rawhid.c new file mode 100644 index 0000000..f6ab1da --- /dev/null +++ b/hid_listen/rawhid.c @@ -0,0 +1,720 @@ +/* Raw HID I/O Routines + * Copyright 2008, PJRC.COM, LLC + * paul@pjrc.com + * + * You may redistribute this program and/or modify it under the terms + * of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ + */ + + +// This code will someday be turned into "librawhid", an easy-to-use +// and truly cross platform library for accessing HID reports. But +// there are many complexities not properly handled by this simple +// code that would be expected from a high quality library. In +// particular, how report IDs are handled is not uniform on the 3 +// platforms. The mac code uses a single buffer which assumes no +// other functions can cause the "run loop" to process HID callbacks. +// The linux version doesn't extract usage and usage page from the +// report descriptor and just hardcodes a signature for the Teensy +// USB debug example. Lacking from all platforms are functions to +// manage multiple devices and robust detection of device removal +// and attachment. There are probably lots of other issues... this +// code has really only been used in 2 projects. If you use it, +// please report bugs to paul@pjrc.com + + +#include +#include +#include +#include +#include "rawhid.h" + +#ifdef OPERATING_SYSTEM +#undef OPERATING_SYSTEM +#endif + + +/*************************************************************************/ +/** **/ +/** Linux **/ +/** **/ +/*************************************************************************/ + +#if defined(LINUX) || defined(__LINUX__) || #system(linux) +#define OPERATING_SYSTEM linux +#include +#include +#include +#include +#include +#include +#include + + +struct rawhid_struct { + int fd; + int name; + int isok; +}; + + +rawhid_t * rawhid_open_only1(int vid, int pid, int usage_page, int usage) +{ + struct rawhid_struct *hid; + struct stat devstat; + struct hidraw_devinfo info; + struct hidraw_report_descriptor *desc; + char buf[512]; + // TODO; inputs are ignored and hardcoded into this signature.... + const unsigned char signature[]={0x06,0x31,0xFF,0x09,0x74}; + int r, i, fd=-1, len, found=0; + + //printf("Searching for device using hidraw....\n"); + for (i=0; i 0) close(fd); + snprintf(buf, sizeof(buf), "/dev/hidraw%d", i); + r = stat(buf, &devstat); + if (r < 0) continue; + //printf("device: %s\n", buf); + fd = open(buf, O_RDWR); + if (fd < 0) continue; + //printf(" opened\n"); + r = ioctl(fd, HIDIOCGRAWINFO, &info); + if (r < 0) continue; + //printf(" vid=%04X, pid=%04X\n", info.vendor & 0xFFFF, info.product & 0xFFFF); + r = ioctl(fd, HIDIOCGRDESCSIZE, &len); + if (r < 0 || len < 1) continue; + //printf(" len=%u\n", len); + desc = (struct hidraw_report_descriptor *)buf; + if (len > sizeof(buf)-sizeof(int)) len = sizeof(buf)-sizeof(int); + desc->size = len; + r = ioctl(fd, HIDIOCGRDESC, desc); + if (r < 0) continue; + if (len >= sizeof(signature) && + memcmp(desc->value, signature, sizeof(signature)) == 0) { + //printf(" Match\n"); + found = 1; + break; + } + } + if (!found) { + if (fd > 0) close(fd); + return NULL; + } + hid = (struct rawhid_struct *)malloc(sizeof(struct rawhid_struct)); + if (!hid) { + close(fd); + return NULL; + } + hid->fd = fd; + return hid; +} + +int rawhid_status(rawhid_t *hid) +{ + // TODO: how to check if device is still online? + return -1; +} + +int rawhid_read(rawhid_t *h, void *buf, int bufsize, int timeout_ms) +{ + struct rawhid_struct *hid; + int num; + + hid = (struct rawhid_struct *)h; + if (!hid || hid->fd < 0) return -1; + + while (1) { + num = read(hid->fd, buf, bufsize); + if (num < 0) { + if (errno == EINTR || errno == EAGAIN) continue; + if (errno == EIO) { + return -1; + printf("I/O Error\n"); + } + printf("read error, r=%d, errno=%d\n", num, errno); + return -1; + } + //printf("read %d bytes\n", num); + return num; + } +} + +void rawhid_close(rawhid_t *h) +{ + struct rawhid_struct *hid; + + hid = (struct rawhid_struct *)h; + if (!hid || hid->fd < 0) return; + close(hid->fd); + hid->fd = -1; +} + +#if 0 +struct rawhid_list_struct { + int count; + struct rawhid_list_entry { + int name; + int desc_length; + const char *desc; + } dev[HIDRAW_MAX_DEVICES]; +}; + +rawhid_list_t * rawhid_list_open(int vid, int pid, int usage_page, int usage) +{ + struct rawhid_list_struct *list; + struct stat devstat; + struct hidraw_devinfo info; + struct hidraw_report_descriptor *desc; + char buf[512], *p; + const unsigned char signature[]={0x06,0x31,0xFF,0x09,0x74}; + int r, i, fd=-1, len; + + list = (struct rawhid_list_struct *)malloc(sizeof(struct rawhid_list_struct)); + if (!list) return NULL; + list->count = 0; + + printf("Searching for device using hidraw....\n"); + for (i=0; i 0) close(fd); + snprintf(buf, sizeof(buf), "/dev/hidraw%d", i); + r = stat(buf, &devstat); + if (r < 0) continue; + printf("device: %s\n", buf); + fd = open(buf, O_RDWR); + if (fd < 0) continue; + printf(" opened\n"); + r = ioctl(fd, HIDIOCGRAWINFO, &info); + if (r < 0) continue; + printf(" vid=%04X, pid=%04X\n", info.vendor & 0xFFFF, info.product & 0xFFFF); + r = ioctl(fd, HIDIOCGRDESCSIZE, &len); + if (r < 0 || len < 1) continue; + printf(" len=%u\n", len); + desc = (struct hidraw_report_descriptor *)buf; + if (len > sizeof(buf)-sizeof(int)) len = sizeof(buf)-sizeof(int); + desc->size = len; + r = ioctl(fd, HIDIOCGRDESC, desc); + if (r < 0) continue; + if (len < sizeof(signature)) continue; + // TODO: actual report parsing would be nice! + if (memcmp(desc->value, signature, sizeof(signature)) != 0) continue; + p = (char *)malloc(len); + if (!p) continue; + printf(" Match\n"); + memcpy(p, desc->value, len); + list->dev[list->count].desc = p; + list->dev[list->count].desc_length = len; + list->dev[list->count].name = i; + list->count++; + } + if (fd > 0) close(fd); + return list; +} + +int rawhid_list_count(rawhid_list_t *list) +{ + if (!list) return 0; + return ((struct rawhid_list_struct *)list)->count; +} + +void rawhid_list_close(rawhid_list_t *list) +{ + if (list) free(list); +} +#endif + + +#endif // linux + + +/*************************************************************************/ +/** **/ +/** Mac OS X 10.5 **/ +/** **/ +/*************************************************************************/ + +#if (defined(DARWIN) || defined(__DARWIN__)) && !defined(OPERATING_SYSTEM) +#define OPERATING_SYSTEM darwin +#include +#include +#include +#include + +// http://developer.apple.com/technotes/tn2007/tn2187.html + + +struct rawhid_struct { + IOHIDDeviceRef ref; + int disconnected; + uint8_t *buffer; + int buffer_used; + int buffer_report_id; +}; + + +static void unplug_callback(void *hid, IOReturn ret, void *ref) +{ + // This callback can only be called when the "run loop" (managed by macos) + // is run. If the GUI is running it when idle, this will get called + // automatically. If not, the run loop needs to be run explicitly + // before checking the result of this function. + //printf("HID/macos: unplugged callback!\n"); + ((struct rawhid_struct *)hid)->disconnected = 1; +} + + +static void input_callback(void *context, IOReturn result, void *sender, + IOHIDReportType type, uint32_t reportID, uint8_t *report, + CFIndex reportLength) +{ + struct rawhid_struct *hid; + + //printf("input callback\n"); + if (!context) return; + hid = (struct rawhid_struct *)context; + hid->buffer_used = reportLength; + hid->buffer_report_id = reportID; + //printf("id = %d, reportLength = %d\n", reportID, (int)reportLength); + //if (hid->ref == sender) printf("ref matches :-)\n"); + //if (report == hid->buffer) printf("buffer matches :)\n"); +} + + +rawhid_t * rawhid_open_only1(int vid, int pid, int usage_page, int usage) +{ + IOHIDManagerRef hid_manager; + CFMutableDictionaryRef dict; + IOReturn ret; + CFSetRef device_set; + IOHIDDeviceRef device_list[256]; + uint8_t *buf; + struct rawhid_struct *hid; + int num_devices; + + // get access to the HID Manager + hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) { + printf("HID/macos: unable to access HID manager"); + return NULL; + } + // configure it to look for our type of device + dict = IOServiceMatching(kIOHIDDeviceKey); + if (dict == NULL) { + printf("HID/macos: unable to create iokit dictionary"); + return NULL; + } + if (vid > 0) { + CFDictionarySetValue(dict, CFSTR(kIOHIDVendorIDKey), + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vid)); + } + if (pid > 0) { + CFDictionarySetValue(dict, CFSTR(kIOHIDProductIDKey), + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid)); + } + if (usage_page > 0) { + CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsagePageKey), + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage_page)); + } + if (usage > 0) { + CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsageKey), + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage)); + } + IOHIDManagerSetDeviceMatching(hid_manager, dict); + + // now open the HID manager + ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone); + if (ret != kIOReturnSuccess) { + printf("HID/macos: Unable to open HID manager (IOHIDManagerOpen failed)"); + return NULL; + } + // get a list of devices that match our requirements + device_set = IOHIDManagerCopyDevices(hid_manager); + if (device_set == NULL) { + //printf("HID/macos: no devices found\n"); + return NULL; + } + num_devices = (int)CFSetGetCount(device_set); + //printf("number of devices found = %d\n", num_devices); + if (num_devices < 1) { + CFRelease(device_set); + printf("HID/macos: no devices found, even though HID manager returned a set\n"); + return NULL; + } + if (num_devices > 256) { + CFRelease(device_set); + printf("HID/macos: too many devices, we get confused if more than 256!\n"); + return NULL; + } + CFSetGetValues(device_set, (const void **)&device_list); + CFRelease(device_set); + // open the first device in the list + ret = IOHIDDeviceOpen(device_list[0], kIOHIDOptionsTypeNone); + if (ret != kIOReturnSuccess) { + printf("HID/macos: error opening device\n"); + return NULL; + } + // return this device + hid = (struct rawhid_struct *)malloc(sizeof(struct rawhid_struct)); + buf = (uint8_t *)malloc(0x1000); + if (hid == NULL || buf == NULL) { + IOHIDDeviceRegisterRemovalCallback(device_list[0], NULL, NULL); + IOHIDDeviceClose(device_list[0], kIOHIDOptionsTypeNone); + printf("HID/macos: Unable to allocate memory\n"); + return NULL; + } + hid->ref = device_list[0]; + hid->disconnected = 0; + hid->buffer = buf; + hid->buffer_used = 0; + + // register a callback to receive input + IOHIDDeviceRegisterInputReportCallback(hid->ref, hid->buffer, 0x1000, + input_callback, hid); + + + // register a callback to find out when it's unplugged + IOHIDDeviceScheduleWithRunLoop(hid->ref, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDDeviceRegisterRemovalCallback(hid->ref, unplug_callback, hid); + return hid; +} + +int rawhid_status(rawhid_t *hid) +{ + if (!hid) return -1; + // if a GUI is causing the run loop run, this will likely mess it up. Just + // comment it out and if the callback still gets called without this, then + // there's no need to run the run loop here! + while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ; + if (((struct rawhid_struct *)hid)->disconnected) { + //printf("HID/macos: status: disconnected\n"); + return -1; + } + //printf("HID/macos: status: ok\n"); + return 0; +} + +void rawhid_close(rawhid_t *hid) +{ + IOHIDDeviceRef ref; + + if (!hid) return; + ref = ((struct rawhid_struct *)hid)->ref; + IOHIDDeviceRegisterRemovalCallback(ref, NULL, NULL); + IOHIDDeviceClose(ref, kIOHIDOptionsTypeNone); + free(hid); +} + +int rawhid_read(rawhid_t *h, void *buf, int bufsize, int timeout_ms) +{ + struct rawhid_struct *hid; + int r, len; + + //printf("begin read\n"); + hid = (struct rawhid_struct *)h; + if (!hid || hid->disconnected) return -1; + while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) { + if (hid->buffer_used) { + len = hid->buffer_used; + if (len > bufsize) len = bufsize; + memcpy(buf, hid->buffer, len); + hid->buffer_used = 0; + return len; + } + if (hid->disconnected) { + return -1; + } + } + r = CFRunLoopRunInMode(kCFRunLoopDefaultMode, (double)timeout_ms / 1000.0, true); + if (r == kCFRunLoopRunTimedOut) { + //printf("read timeout\n"); + return 0; + } + if (hid->buffer_used) { + len = hid->buffer_used; + if (len > bufsize) len = bufsize; + memcpy(buf, hid->buffer, len); + hid->buffer_used = 0; + return len; + } + if (hid->disconnected) return -1; + return 0; + //num = bufsize; + //ret = IOHIDDeviceGetReport(ref, kIOHIDReportTypeInput, 0, buf, &num); + //if (!ret) return -1; + //return num; +} + +int rawhid_write(rawhid_t *hid, const void *buf, int len, int timeout_ms) +{ + IOReturn ret; + + if (((struct rawhid_struct *)hid)->disconnected) return -1; + ret = IOHIDDeviceSetReport(((struct rawhid_struct *)hid)->ref, + kIOHIDReportTypeOutput, 0, buf, len); + if (ret != kIOReturnSuccess) return -1; + return 0; +} + + +#endif // Darwin - Mac OS X + + +/*************************************************************************/ +/** **/ +/** Windows 2000/XP/Vista **/ +/** **/ +/*************************************************************************/ + +#if (defined(WIN32) || defined(WINDOWS) || defined(__WINDOWS__)) && !defined(OPERATING_SYSTEM) +#define OPERATING_SYSTEM windows +#include +#include +#include +#include + +// http://msdn.microsoft.com/en-us/library/ms790932.aspx + +struct rawhid_struct { + HANDLE handle; +}; + + +rawhid_t * rawhid_open_only1(int vid, int pid, int usage_page, int usage) +{ + GUID guid; + HDEVINFO info; + DWORD index=0, required_size; + SP_DEVICE_INTERFACE_DATA iface; + SP_DEVICE_INTERFACE_DETAIL_DATA *details; + HIDD_ATTRIBUTES attrib; + PHIDP_PREPARSED_DATA hid_data; + HIDP_CAPS capabilities; + struct rawhid_struct *hid; + HANDLE h; + BOOL ret; + + + HidD_GetHidGuid(&guid); + info = SetupDiGetClassDevs(&guid, NULL, NULL, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (info == INVALID_HANDLE_VALUE) { + printf("HID/win32: SetupDiGetClassDevs failed"); + return NULL; + } + for (index=0; ;index++) { + iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface); + if (!ret) { + // end of list + SetupDiDestroyDeviceInfoList(info); + return NULL; + } + SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &required_size, NULL); + details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(required_size); + if (details == NULL) continue; + memset(details, 0, required_size); + details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details, + required_size, NULL, NULL); + if (!ret) { + free(details); + continue; + } + h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + free(details); + if (h == INVALID_HANDLE_VALUE) continue; + attrib.Size = sizeof(HIDD_ATTRIBUTES); + ret = HidD_GetAttributes(h, &attrib); + if (!ret) { + CloseHandle(h); + continue; + } + //printf("HID/win32: USB Device:\n"); + //printf("HID/win32: vid = 0x%04X\n", (int)(attrib.VendorID)); + //printf("HID/win32: pid = 0x%04X\n", (int)(attrib.ProductID)); + if (vid > 0 && vid != (int)(attrib.VendorID)) { + CloseHandle(h); + continue; + } + if (pid > 0 && pid != (int)(attrib.ProductID)) { + CloseHandle(h); + continue; + } + if (!HidD_GetPreparsedData(h, &hid_data)) { + printf("HID/win32: HidD_GetPreparsedData failed\n"); + CloseHandle(h); + continue; + } + if (!HidP_GetCaps(hid_data, &capabilities)) { + printf("HID/win32: HidP_GetCaps failed\n"); + HidD_FreePreparsedData(hid_data); + CloseHandle(h); + continue; + } + //printf("HID/win32: usage_page = 0x%04X\n", (int)(capabilities.UsagePage)); + //printf("HID/win32: usage = 0x%04X\n", (int)(capabilities.Usage)); + if (usage_page > 0 && usage_page != (int)(capabilities.UsagePage)) { + HidD_FreePreparsedData(hid_data); + CloseHandle(h); + continue; + } + if (usage > 0 && usage != (int)(capabilities.Usage)) { + HidD_FreePreparsedData(hid_data); + CloseHandle(h); + continue; + } + HidD_FreePreparsedData(hid_data); + hid = (struct rawhid_struct *)malloc(sizeof(struct rawhid_struct)); + if (!hid) { + CloseHandle(h); + printf("HID/win32: Unable to get %d bytes", sizeof(struct rawhid_struct)); + continue; + } + hid->handle = h; + return hid; + } +} + + +int rawhid_status(rawhid_t *hid) +{ + PHIDP_PREPARSED_DATA hid_data; + + if (!hid) return -1; + if (!HidD_GetPreparsedData(((struct rawhid_struct *)hid)->handle, &hid_data)) { + printf("HID/win32: HidD_GetPreparsedData failed, device assumed disconnected\n"); + return -1; + } + printf("HID/win32: HidD_GetPreparsedData ok, device still online :-)\n"); + HidD_FreePreparsedData(hid_data); + return 0; +} + +void rawhid_close(rawhid_t *hid) +{ + if (!hid) return; + CloseHandle(((struct rawhid_struct *)hid)->handle); + free(hid); +} + +int rawhid_read(rawhid_t *h, void *buf, int bufsize, int timeout_ms) +{ + DWORD num=0, result; + BOOL ret; + OVERLAPPED ov; + struct rawhid_struct *hid; + int r; + + hid = (struct rawhid_struct *)h; + if (!hid) return -1; + + memset(&ov, 0, sizeof(OVERLAPPED)); + ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ov.hEvent == NULL) return -1; + + ret = ReadFile(hid->handle, buf, bufsize, &num, &ov); + if (ret) { + //printf("HID/win32: read success (immediate)\n"); + r = num; + } else { + if (GetLastError() == ERROR_IO_PENDING) { + result = WaitForSingleObject(ov.hEvent, timeout_ms); + if (result == WAIT_OBJECT_0) { + if (GetOverlappedResult(hid->handle, &ov, &num, FALSE)) { + //printf("HID/win32: read success (delayed)\n"); + r = num; + } else { + //printf("HID/win32: read failure (delayed)\n"); + r = -1; + } + } else { + //printf("HID/win32: read timeout, %lx\n", result); + CancelIo(hid->handle); + r = 0; + } + } else { + //printf("HID/win32: read error (immediate)\n"); + r = -1; + } + } + CloseHandle(ov.hEvent); + return r; +} + + +int rawhid_write(rawhid_t *h, const void *buf, int len, int timeout_ms) +{ + DWORD num=0; + BOOL ret; + OVERLAPPED ov; + struct rawhid_struct *hid; + int r; + + hid = (struct rawhid_struct *)h; + if (!hid) return -1; + + memset(&ov, 0, sizeof(OVERLAPPED)); + ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ov.hEvent == NULL) return -1; + + // first byte is report ID, must be zero if report IDs not used + ret = WriteFile(hid->handle, buf, len, &num, &ov); + if (ret) { + if (num == len) { + //printf("HID/win32: write success (immediate)\n"); + r = 0; + } else { + //printf("HID/win32: partial write (immediate)\n"); + r = -1; + } + } else { + if (GetLastError() == ERROR_IO_PENDING) { + if (GetOverlappedResult(hid->handle, &ov, &num, TRUE)) { + if (num == len) { + //printf("HID/win32: write success (delayed)\n"); + r = 0; + } else { + //printf("HID/win32: partial write (delayed)\n"); + r = -1; + } + } else { + //printf("HID/win32: write error (delayed)\n"); + r = -1; + } + } else { + //printf("HID/win32: write error (immediate)\n"); + r = -1; + } + } + CloseHandle(ov.hEvent); + return r; +} + + +#endif // windows + + + +#ifndef OPERATING_SYSTEM +#error Unknown operating system +#endif + + + + + + + + diff --git a/hid_listen/rawhid.h b/hid_listen/rawhid.h new file mode 100644 index 0000000..d7d0a81 --- /dev/null +++ b/hid_listen/rawhid.h @@ -0,0 +1,23 @@ +#ifndef rawhid_included_h__ +#define rawhid_included_h__ + +// Raw HID, Basic API +typedef void rawhid_t; +rawhid_t * rawhid_open_only1(int vid, int pid, int usage_page, int usage); +int rawhid_status(rawhid_t *hid); +int rawhid_read(rawhid_t *h, void *buf, int bufsize, int timeout_ms); +int rawhid_write(rawhid_t *hid, const void *buf, int len, int timeout_ms); +void rawhid_close(rawhid_t *h); + + +// Raw HID, Multiple Device API +typedef void rawhid_list_t; +rawhid_list_t * rawhid_list_open(int vid, int pid, int usage_page, int usage); +int rawhid_list_count(rawhid_list_t *list); +void rawhid_list_close(rawhid_list_t *list); +int rawhid_list_indexof(rawhid_list_t *list, rawhid_t *hid); +void rawhid_list_remove(rawhid_list_t *list, rawhid_t *hid); +rawhid_t * rawhid_open(rawhid_list_t *list, int index); + + +#endif