From 8ddbd3faab208bf46f907f86dba319f3153613de Mon Sep 17 00:00:00 2001 From: Bernhard Tittelbach Date: Sat, 19 May 2012 20:48:29 +0000 Subject: moved hid70dongle usbhid pc interface git-svn-id: https://svn.spreadspace.org/mur.sat@443 7de4ea59-55d0-425e-a1af-a3118ea81d4c --- tools/atmega324u_usbhid/Makefile | 65 ++++ tools/atmega324u_usbhid/hid.h | 6 + tools/atmega324u_usbhid/hid_LINUX.c | 381 +++++++++++++++++++++++ tools/atmega324u_usbhid/hid_MACOSX.c | 412 +++++++++++++++++++++++++ tools/atmega324u_usbhid/hid_WINDOWS.c | 328 ++++++++++++++++++++ tools/atmega324u_usbhid/rawhid_testinterface.c | 75 +++++ tools/atmega324u_usbhid/reset.c | 22 ++ 7 files changed, 1289 insertions(+) create mode 100644 tools/atmega324u_usbhid/Makefile create mode 100644 tools/atmega324u_usbhid/hid.h create mode 100644 tools/atmega324u_usbhid/hid_LINUX.c create mode 100644 tools/atmega324u_usbhid/hid_MACOSX.c create mode 100644 tools/atmega324u_usbhid/hid_WINDOWS.c create mode 100644 tools/atmega324u_usbhid/rawhid_testinterface.c create mode 100644 tools/atmega324u_usbhid/reset.c (limited to 'tools') diff --git a/tools/atmega324u_usbhid/Makefile b/tools/atmega324u_usbhid/Makefile new file mode 100644 index 0000000..f1db097 --- /dev/null +++ b/tools/atmega324u_usbhid/Makefile @@ -0,0 +1,65 @@ + +OS = LINUX +#OS = MACOSX +#OS = WINDOWS + +PROG = rawhid_testinterface + +# To set up Ubuntu Linux to cross compile for Windows: +# +# apt-get install mingw32 mingw32-binutils mingw32-runtime +# +# Just edit the variable above for WINDOWS, then use "make" to build rawhid.exe + +ifeq ($(OS), LINUX) +TARGET = $(PROG) +CC = gcc +STRIP = strip +CFLAGS = -Wall -O2 -DOS_$(OS) +LIBS = -lusb +else ifeq ($(OS), MACOSX) +TARGET = $(PROG).dmg +SDK = /Developer/SDKs/MacOSX10.5.sdk +ARCH = -mmacosx-version-min=10.5 -arch ppc -arch i386 +CC = gcc +STRIP = strip +CFLAGS = -Wall -O2 -DOS_$(OS) -isysroot $(SDK) $(ARCH) +LIBS = $(ARCH) -Wl,-syslibroot,$(SDK) -framework IOKit -framework CoreFoundation +else ifeq ($(OS), WINDOWS) +TARGET = $(PROG).exe +CC = i586-mingw32msvc-gcc +STRIP = i586-mingw32msvc-strip +CFLAGS = -Wall -O2 -DOS_$(OS) +LIBS = -lhid -lsetupapi +endif + +OBJS = $(PROG).o hid.o + +all: $(TARGET) reset + +$(PROG): $(OBJS) + $(CC) -o $(PROG) $(OBJS) $(LIBS) + $(STRIP) $(PROG) + +$(PROG).exe: $(PROG) + cp $(PROG) $(PROG).exe + +$(PROG).dmg: $(PROG) + mkdir tmp + cp $(PROG) tmp + hdiutil create -ov -volname "Raw HID Test" -srcfolder tmp $(PROG).dmg + +hid.o: hid_$(OS).c hid.h + $(CC) $(CFLAGS) -c -o $@ $< + +reset: reset.o hid.o + $(CC) -o $@ $+ $(LIBS) + $(STRIP) $@ + +reset.o: reset.c + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + rm -f *.o $(PROG) $(PROG).exe $(PROG).dmg + rm -rf tmp reset + diff --git a/tools/atmega324u_usbhid/hid.h b/tools/atmega324u_usbhid/hid.h new file mode 100644 index 0000000..4d83703 --- /dev/null +++ b/tools/atmega324u_usbhid/hid.h @@ -0,0 +1,6 @@ + +int rawhid_open(int max, int vid, int pid, int usage_page, int usage); +int rawhid_recv(int num, void *buf, int len, int timeout); +int rawhid_send(int num, void *buf, int len, int timeout); +void rawhid_close(int num); + diff --git a/tools/atmega324u_usbhid/hid_LINUX.c b/tools/atmega324u_usbhid/hid_LINUX.c new file mode 100644 index 0000000..8e98113 --- /dev/null +++ b/tools/atmega324u_usbhid/hid_LINUX.c @@ -0,0 +1,381 @@ +/* Simple Raw HID functions for Linux - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ + +#include +#include +#include +#include + +#include "hid.h" + + +// On Linux there are several options to access HID devices. +// +// libusb 0.1 - the only way that works well on all distributions +// libusb 1.0 - someday will become standard on most distributions +// hidraw driver - relatively new, not supported on many distributions (yet) +// hiddev driver - old, ubuntu, fedora, others dropping support +// usbfs - low level usb API: http://www.kernel.org/doc/htmldocs/usb.html#usbfs +// +// This code uses libusb 0.1, which is well supported on all linux distributions +// and very stable and widely used by many programs. libusb 0.1 only provides a +// simple synchronous interface, basically the same as this code needs. However, +// if you want non-blocking I/O, libusb 0.1 simply does not provide that. There +// is also no kernel-level buffering, so performance is poor. +// +// UPDATE: As of November 2011, hidraw support seems to be working well in all +// major linux distributions. Embedded and "small" distros, used on ARM boards, +// routers and embedded hardware stil seem to omit the hidraw driver. +// +// The hidraw driver is a great solution. However, it has only been supported +// by relatively recent (in 2009) kernels. Here is a quick survey of the status +// of hidraw support in various distributions as of Sept 2009: +// +// Fedora 11: works, kernel 2.6.29.4 +// Mandiva 2009.1: works, kernel 2.6.29.1 +// Ubuntu 9.10-alpha6: works, kernel 2.6.31 +// Ubuntu 9.04: sysfs attrs chain broken (hidraw root only), 2.6.28 kernel +// openSUSE 11.1: sysfs attrs chain broken (hidraw root only), 2.6.27.7 kernel +// Debian Live, Lenny 5.0.2: sysfs attrs chain broken (hidraw root only), 2.6.26 +// SimplyMEPIS 8.0.10: sysfs attrs chain broken (hidraw root only), 2.6.27 +// Mint 7: sysfs attrs chain broken (hidraw root only), 2.6.28 kernel +// Gentoo 2008.0-r1: sysfs attrs chain broken (hidraw root only), 2.6.24 kernel +// Centos 5: no hidraw or hiddev devices, 2.6.18 kernel +// Slitaz 2.0: no hidraw devices (has hiddev), 2.6.25.5 kernel +// Puppy 4.3: no hidraw devices (has hiddev), 2.6.30.5 kernel +// Damn Small 4.4.10: (would not boot) +// Gentoo 10.0-test20090926: (would not boot) +// PCLinuxOS 2009.2: (would not boot) +// Slackware: (no live cd available? www.slackware-live.org dead) + + + +#define printf(...) // comment this out for lots of info + + +// a list of all opened HID devices, so the caller can +// simply refer to them by number +typedef struct hid_struct hid_t; +static hid_t *first_hid = NULL; +static hid_t *last_hid = NULL; +struct hid_struct { + usb_dev_handle *usb; + int open; + int iface; + int ep_in; + int ep_out; + struct hid_struct *prev; + struct hid_struct *next; +}; + + +// private functions, not intended to be used from outside this file +static void add_hid(hid_t *h); +static hid_t * get_hid(int num); +static void free_all_hid(void); +static void hid_close(hid_t *hid); +static int hid_parse_item(uint32_t *val, uint8_t **data, const uint8_t *end); + + +// rawhid_recv - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int rawhid_recv(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + int r; + + hid = get_hid(num); + if (!hid || !hid->open) return -1; + r = usb_interrupt_read(hid->usb, hid->ep_in, buf, len, timeout); + if (r >= 0) return r; + if (r == -110) return 0; // timeout + return -1; +} + +// rawhid_send - send a packet +// Inputs: +// num = device to transmit to (zero based) +// buf = buffer containing packet to send +// len = number of bytes to transmit +// timeout = time to wait, in milliseconds +// Output: +// number of bytes sent, or -1 on error +// +int rawhid_send(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return -1; + if (hid->ep_out) { + return usb_interrupt_write(hid->usb, hid->ep_out, buf, len, timeout); + } else { + return usb_control_msg(hid->usb, 0x21, 9, 0, hid->iface, buf, len, timeout); + } +} + +// rawhid_open - open 1 or more devices +// +// Inputs: +// max = maximum number of devices to open +// vid = Vendor ID, or -1 if any +// pid = Product ID, or -1 if any +// usage_page = top level usage page, or -1 if any +// usage = top level usage number, or -1 if any +// Output: +// actual number of devices opened +// +int rawhid_open(int max, int vid, int pid, int usage_page, int usage) +{ + struct usb_bus *bus; + struct usb_device *dev; + struct usb_interface *iface; + struct usb_interface_descriptor *desc; + struct usb_endpoint_descriptor *ep; + usb_dev_handle *u; + uint8_t buf[1024], *p; + int i, n, len, tag, ep_in, ep_out, count=0, claimed; + uint32_t val=0, parsed_usage, parsed_usage_page; + hid_t *hid; + + if (first_hid) free_all_hid(); + printf("rawhid_open, max=%d\n", max); + if (max < 1) return 0; + usb_init(); + usb_find_busses(); + usb_find_devices(); + for (bus = usb_get_busses(); bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + if (vid > 0 && dev->descriptor.idVendor != vid) continue; + if (pid > 0 && dev->descriptor.idProduct != pid) continue; + if (!dev->config) continue; + if (dev->config->bNumInterfaces < 1) continue; + printf("device: vid=%04X, pic=%04X, with %d iface\n", + dev->descriptor.idVendor, + dev->descriptor.idProduct, + dev->config->bNumInterfaces); + iface = dev->config->interface; + u = NULL; + claimed = 0; + for (i=0; iconfig->bNumInterfaces && iface; i++, iface++) { + desc = iface->altsetting; + if (!desc) continue; + printf(" type %d, %d, %d\n", desc->bInterfaceClass, + desc->bInterfaceSubClass, desc->bInterfaceProtocol); + if (desc->bInterfaceClass != 3) continue; + if (desc->bInterfaceSubClass != 0) continue; + if (desc->bInterfaceProtocol != 0) continue; + ep = desc->endpoint; + ep_in = ep_out = 0; + for (n = 0; n < desc->bNumEndpoints; n++, ep++) { + if (ep->bEndpointAddress & 0x80) { + if (!ep_in) ep_in = ep->bEndpointAddress & 0x7F; + printf(" IN endpoint %d\n", ep_in); + } else { + if (!ep_out) ep_out = ep->bEndpointAddress; + printf(" OUT endpoint %d\n", ep_out); + } + } + if (!ep_in) continue; + if (!u) { + u = usb_open(dev); + if (!u) { + printf(" unable to open device\n"); + break; + } + } + printf(" hid interface (generic)\n"); + if (usb_get_driver_np(u, i, (char *)buf, sizeof(buf)) >= 0) { + printf(" in use by driver \"%s\"\n", buf); + if (usb_detach_kernel_driver_np(u, i) < 0) { + printf(" unable to detach from kernel\n"); + continue; + } + } + if (usb_claim_interface(u, i) < 0) { + printf(" unable claim interface %d\n", i); + continue; + } + len = usb_control_msg(u, 0x81, 6, 0x2200, i, (char *)buf, sizeof(buf), 250); + printf(" descriptor, len=%d\n", len); + if (len < 2) { + usb_release_interface(u, i); + continue; + } + p = buf; + parsed_usage_page = parsed_usage = 0; + while ((tag = hid_parse_item(&val, &p, buf + len)) >= 0) { + printf(" tag: %X, val %X\n", tag, val); + if (tag == 4) parsed_usage_page = val; + if (tag == 8) parsed_usage = val; + if (parsed_usage_page && parsed_usage) break; + } + if ((!parsed_usage_page) || (!parsed_usage) || + (usage_page > 0 && parsed_usage_page != usage_page) || + (usage > 0 && parsed_usage != usage)) { + usb_release_interface(u, i); + continue; + } + hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); + if (!hid) { + usb_release_interface(u, i); + continue; + } + hid->usb = u; + hid->iface = i; + hid->ep_in = ep_in; + hid->ep_out = ep_out; + hid->open = 1; + add_hid(hid); + claimed++; + count++; + if (count >= max) return count; + } + if (u && !claimed) usb_close(u); + } + } + return count; +} + + +// rawhid_close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void rawhid_close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + hid_close(hid); +} + +// Chuck Robey wrote a real HID report parser +// (chuckr@telenix.org) chuckr@chuckr.org +// http://people.freebsd.org/~chuckr/code/python/uhidParser-0.2.tbz +// this tiny thing only needs to extract the top-level usage page +// and usage, and even then is may not be truly correct, but it does +// work with the Teensy Raw HID example. +static int hid_parse_item(uint32_t *val, uint8_t **data, const uint8_t *end) +{ + const uint8_t *p = *data; + uint8_t tag; + int table[4] = {0, 1, 2, 4}; + int len; + + if (p >= end) return -1; + if (p[0] == 0xFE) { + // long item, HID 1.11, 6.2.2.3, page 27 + if (p + 5 >= end || p + p[1] >= end) return -1; + tag = p[2]; + *val = 0; + len = p[1] + 5; + } else { + // short item, HID 1.11, 6.2.2.2, page 26 + tag = p[0] & 0xFC; + len = table[p[0] & 0x03]; + if (p + len + 1 >= end) return -1; + switch (p[0] & 0x03) { + case 3: *val = p[1] | (p[2] << 8) | (p[3] << 16) | (p[4] << 24); break; + case 2: *val = p[1] | (p[2] << 8); break; + case 1: *val = p[1]; break; + case 0: *val = 0; break; + } + } + *data += len + 1; + return tag; +} + + +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + for (p = first_hid; p; p = p->next) { + hid_close(p); + } + p = first_hid; + while (p) { + q = p; + p = p->next; + free(q); + } + first_hid = last_hid = NULL; +} + + +static void hid_close(hid_t *hid) +{ + hid_t *p; + int others=0; + + usb_release_interface(hid->usb, hid->iface); + for (p = first_hid; p; p = p->next) { + if (p->open && p->usb == hid->usb) others++; + } + if (!others) usb_close(hid->usb); + hid->usb = NULL; +} + + + diff --git a/tools/atmega324u_usbhid/hid_MACOSX.c b/tools/atmega324u_usbhid/hid_MACOSX.c new file mode 100644 index 0000000..c5d5d1b --- /dev/null +++ b/tools/atmega324u_usbhid/hid_MACOSX.c @@ -0,0 +1,412 @@ +/* Simple Raw HID functions for Linux - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ + +#include +#include +#include +#include +#include +#include + +#include "hid.h" + +#define BUFFER_SIZE 64 + +#define printf(...) // comment this out to get lots of info printed + + +// a list of all opened HID devices, so the caller can +// simply refer to them by number +typedef struct hid_struct hid_t; +typedef struct buffer_struct buffer_t; +static hid_t *first_hid = NULL; +static hid_t *last_hid = NULL; +struct hid_struct { + IOHIDDeviceRef ref; + int open; + uint8_t buffer[BUFFER_SIZE]; + buffer_t *first_buffer; + buffer_t *last_buffer; + struct hid_struct *prev; + struct hid_struct *next; +}; +struct buffer_struct { + struct buffer_struct *next; + uint32_t len; + uint8_t buf[BUFFER_SIZE]; +}; + +// private functions, not intended to be used from outside this file +static void add_hid(hid_t *); +static hid_t * get_hid(int); +static void free_all_hid(void); +static void hid_close(hid_t *); +static void attach_callback(void *, IOReturn, void *, IOHIDDeviceRef); +static void detach_callback(void *, IOReturn, void *hid_mgr, IOHIDDeviceRef dev); +static void timeout_callback(CFRunLoopTimerRef, void *); +static void input_callback(void *, IOReturn, void *, IOHIDReportType, + uint32_t, uint8_t *, CFIndex); + + + +// rawhid_recv - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int rawhid_recv(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + buffer_t *b; + CFRunLoopTimerRef timer=NULL; + CFRunLoopTimerContext context; + int ret=0, timeout_occurred=0; + + if (len < 1) return 0; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + if ((b = hid->first_buffer) != NULL) { + if (len > b->len) len = b->len; + memcpy(buf, b->buf, len); + hid->first_buffer = b->next; + free(b); + return len; + } + memset(&context, 0, sizeof(context)); + context.info = &timeout_occurred; + timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + + (double)timeout / 1000.0, 0, 0, 0, timeout_callback, &context); + CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); + while (1) { + CFRunLoopRun(); + if ((b = hid->first_buffer) != NULL) { + if (len > b->len) len = b->len; + memcpy(buf, b->buf, len); + hid->first_buffer = b->next; + free(b); + ret = len; + break; + } + if (!hid->open) { + printf("rawhid_recv, device not open\n"); + ret = -1; + break; + } + if (timeout_occurred) break; + } + CFRunLoopTimerInvalidate(timer); + CFRelease(timer); + return ret; +} + +static void input_callback(void *context, IOReturn ret, void *sender, + IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len) +{ + buffer_t *n; + hid_t *hid; + + printf("input_callback\n"); + if (ret != kIOReturnSuccess || len < 1) return; + hid = context; + if (!hid || hid->ref != sender) return; + n = (buffer_t *)malloc(sizeof(buffer_t)); + if (!n) return; + if (len > BUFFER_SIZE) len = BUFFER_SIZE; + memcpy(n->buf, data, len); + n->len = len; + n->next = NULL; + if (!hid->first_buffer || !hid->last_buffer) { + hid->first_buffer = hid->last_buffer = n; + } else { + hid->last_buffer->next = n; + hid->last_buffer = n; + } + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +static void timeout_callback(CFRunLoopTimerRef timer, void *info) +{ + printf("timeout_callback\n"); + *(int *)info = 1; + CFRunLoopStop(CFRunLoopGetCurrent()); +} + + +void output_callback(void *context, IOReturn ret, void *sender, + IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len) +{ + printf("output_callback, r=%d\n", ret); + if (ret == kIOReturnSuccess) { + *(int *)context = len; + } else { + // timeout if not success? + *(int *)context = 0; + } + CFRunLoopStop(CFRunLoopGetCurrent()); +} + + +// rawhid_send - send a packet +// Inputs: +// num = device to transmit to (zero based) +// buf = buffer containing packet to send +// len = number of bytes to transmit +// timeout = time to wait, in milliseconds +// Output: +// number of bytes sent, or -1 on error +// +int rawhid_send(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + int result=-100; + + hid = get_hid(num); + if (!hid || !hid->open) return -1; +#if 1 + #warning "Send timeout not implemented on MACOSX" + IOReturn ret = IOHIDDeviceSetReport(hid->ref, kIOHIDReportTypeOutput, 0, buf, len); + result = (ret == kIOReturnSuccess) ? len : -1; +#endif +#if 0 + // No matter what I tried this never actually sends an output + // report and output_callback never gets called. Why?? + // Did I miss something? This is exactly the same params as + // the sync call that works. Is it an Apple bug? + // (submitted to Apple on 22-sep-2009, problem ID 7245050) + // + IOHIDDeviceScheduleWithRunLoop(hid->ref, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + // should already be scheduled with run loop by attach_callback, + // sadly this doesn't make any difference either way + + // could this be related? + // http://lists.apple.com/archives/usb/2008/Aug/msg00021.html + // + IOHIDDeviceSetReportWithCallback(hid->ref, kIOHIDReportTypeOutput, + 0, buf, len, (double)timeout / 1000.0, output_callback, &result); + while (1) { + printf("enter run loop (send)\n"); + CFRunLoopRun(); + printf("leave run loop (send)\n"); + if (result > -100) break; + if (!hid->open) { + result = -1; + break; + } + } +#endif + return result; +} + + +// rawhid_open - open 1 or more devices +// +// Inputs: +// max = maximum number of devices to open +// vid = Vendor ID, or -1 if any +// pid = Product ID, or -1 if any +// usage_page = top level usage page, or -1 if any +// usage = top level usage number, or -1 if any +// Output: +// actual number of devices opened +// +int rawhid_open(int max, int vid, int pid, int usage_page, int usage) +{ + static IOHIDManagerRef hid_manager=NULL; + CFMutableDictionaryRef dict; + CFNumberRef num; + IOReturn ret; + hid_t *p; + int count=0; + + if (first_hid) free_all_hid(); + printf("rawhid_open, max=%d\n", max); + if (max < 1) return 0; + // Start the HID Manager + // http://developer.apple.com/technotes/tn2007/tn2187.html + if (!hid_manager) { + hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) { + if (hid_manager) CFRelease(hid_manager); + return 0; + } + } + if (vid > 0 || pid > 0 || usage_page > 0 || usage > 0) { + // Tell the HID Manager what type of devices we want + dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dict) return 0; + if (vid > 0) { + num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vid); + CFDictionarySetValue(dict, CFSTR(kIOHIDVendorIDKey), num); + CFRelease(num); + } + if (pid > 0) { + num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid); + CFDictionarySetValue(dict, CFSTR(kIOHIDProductIDKey), num); + CFRelease(num); + } + if (usage_page > 0) { + num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage_page); + CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsagePageKey), num); + CFRelease(num); + } + if (usage > 0) { + num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); + CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsageKey), num); + CFRelease(num); + } + IOHIDManagerSetDeviceMatching(hid_manager, dict); + CFRelease(dict); + } else { + IOHIDManagerSetDeviceMatching(hid_manager, NULL); + } + // set up a callbacks for device attach & detach + IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), + kCFRunLoopDefaultMode); + IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, NULL); + IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL); + ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone); + if (ret != kIOReturnSuccess) { + IOHIDManagerUnscheduleFromRunLoop(hid_manager, + CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFRelease(hid_manager); + return 0; + } + printf("run loop\n"); + // let it do the callback for all devices + while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ; + // count up how many were added by the callback + for (p = first_hid; p; p = p->next) count++; + return count; +} + + +// rawhid_close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void rawhid_close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + hid_close(hid); + hid->open = 0; +} + + +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + for (p = first_hid; p; p = p->next) { + hid_close(p); + } + p = first_hid; + while (p) { + q = p; + p = p->next; + free(q); + } + first_hid = last_hid = NULL; +} + + +static void hid_close(hid_t *hid) +{ + if (!hid || !hid->open || !hid->ref) return; + IOHIDDeviceUnscheduleFromRunLoop(hid->ref, CFRunLoopGetCurrent( ), kCFRunLoopDefaultMode); + IOHIDDeviceClose(hid->ref, kIOHIDOptionsTypeNone); + hid->ref = NULL; +} + +static void detach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev) +{ + hid_t *p; + + printf("detach callback\n"); + for (p = first_hid; p; p = p->next) { + if (p->ref == dev) { + p->open = 0; + CFRunLoopStop(CFRunLoopGetCurrent()); + return; + } + } +} + + +static void attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev) +{ + struct hid_struct *h; + + printf("attach callback\n"); + if (IOHIDDeviceOpen(dev, kIOHIDOptionsTypeNone) != kIOReturnSuccess) return; + h = (hid_t *)malloc(sizeof(hid_t)); + if (!h) return; + memset(h, 0, sizeof(hid_t)); + IOHIDDeviceScheduleWithRunLoop(dev, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDDeviceRegisterInputReportCallback(dev, h->buffer, sizeof(h->buffer), + input_callback, h); + h->ref = dev; + h->open = 1; + add_hid(h); +} + + diff --git a/tools/atmega324u_usbhid/hid_WINDOWS.c b/tools/atmega324u_usbhid/hid_WINDOWS.c new file mode 100644 index 0000000..0718773 --- /dev/null +++ b/tools/atmega324u_usbhid/hid_WINDOWS.c @@ -0,0 +1,328 @@ +/* Simple Raw HID functions for Windows - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "hid.h" + + +// a list of all opened HID devices, so the caller can +// simply refer to them by number +typedef struct hid_struct hid_t; +static hid_t *first_hid = NULL; +static hid_t *last_hid = NULL; +struct hid_struct { + HANDLE handle; + int open; + struct hid_struct *prev; + struct hid_struct *next; +}; +static HANDLE rx_event=NULL; +static HANDLE tx_event=NULL; +static CRITICAL_SECTION rx_mutex; +static CRITICAL_SECTION tx_mutex; + + +// private functions, not intended to be used from outside this file +static void add_hid(hid_t *h); +static hid_t * get_hid(int num); +static void free_all_hid(void); +static void hid_close(hid_t *hid); +void print_win32_err(void); + + + + +// rawhid_recv - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int rawhid_recv(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD n, r; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&rx_mutex); + ResetEvent(&rx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = rx_event; + if (!ReadFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(rx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&rx_mutex); + if (n <= 0) return -1; + n--; + if (n > len) n = len; + memcpy(buf, tmpbuf + 1, n); + return n; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&rx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&rx_mutex); + return -1; +} + +// rawhid_send - send a packet +// Inputs: +// num = device to transmit to (zero based) +// buf = buffer containing packet to send +// len = number of bytes to transmit +// timeout = time to wait, in milliseconds +// Output: +// number of bytes sent, or -1 on error +// +int rawhid_send(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD n, r; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&tx_mutex); + ResetEvent(&tx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = tx_event; + tmpbuf[0] = 0; + memcpy(tmpbuf + 1, buf, len); + if (!WriteFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(tx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&tx_mutex); + if (n <= 0) return -1; + return n - 1; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&tx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&tx_mutex); + return -1; +} + +// rawhid_open - open 1 or more devices +// +// Inputs: +// max = maximum number of devices to open +// vid = Vendor ID, or -1 if any +// pid = Product ID, or -1 if any +// usage_page = top level usage page, or -1 if any +// usage = top level usage number, or -1 if any +// Output: +// actual number of devices opened +// +int rawhid_open(int max, int vid, int pid, int usage_page, int usage) +{ + GUID guid; + HDEVINFO info; + DWORD index=0, reqd_size; + SP_DEVICE_INTERFACE_DATA iface; + SP_DEVICE_INTERFACE_DETAIL_DATA *details; + HIDD_ATTRIBUTES attrib; + PHIDP_PREPARSED_DATA hid_data; + HIDP_CAPS capabilities; + HANDLE h; + BOOL ret; + hid_t *hid; + int count=0; + + if (first_hid) free_all_hid(); + if (max < 1) return 0; + if (!rx_event) { + rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + InitializeCriticalSection(&rx_mutex); + InitializeCriticalSection(&tx_mutex); + } + HidD_GetHidGuid(&guid); + info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (info == INVALID_HANDLE_VALUE) return 0; + for (index=0; 1 ;index++) { + iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface); + if (!ret) return count; + SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &reqd_size, NULL); + details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(reqd_size); + if (details == NULL) continue; + + memset(details, 0, reqd_size); + details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details, + reqd_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); + //printf("vid: %4x\n", attrib.VendorID); + if (!ret || (vid > 0 && attrib.VendorID != vid) || + (pid > 0 && attrib.ProductID != pid) || + !HidD_GetPreparsedData(h, &hid_data)) { + CloseHandle(h); + continue; + } + if (!HidP_GetCaps(hid_data, &capabilities) || + (usage_page > 0 && capabilities.UsagePage != usage_page) || + (usage > 0 && capabilities.Usage != usage)) { + HidD_FreePreparsedData(hid_data); + CloseHandle(h); + continue; + } + HidD_FreePreparsedData(hid_data); + hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); + if (!hid) { + CloseHandle(h); + continue; + } + hid->handle = h; + hid->open = 1; + add_hid(hid); + count++; + if (count >= max) return count; + } + return count; +} + + +// rawhid_close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void rawhid_close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + hid_close(hid); +} + + + +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + for (p = first_hid; p; p = p->next) { + hid_close(p); + } + p = first_hid; + while (p) { + q = p; + p = p->next; + free(q); + } + first_hid = last_hid = NULL; +} + + +static void hid_close(hid_t *hid) +{ + CloseHandle(hid->handle); + hid->handle = NULL; +} + + +void print_win32_err(void) +{ + char buf[256]; + DWORD err; + + err = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, + 0, buf, sizeof(buf), NULL); + printf("err %ld: %s\n", err, buf); +} + + + + + diff --git a/tools/atmega324u_usbhid/rawhid_testinterface.c b/tools/atmega324u_usbhid/rawhid_testinterface.c new file mode 100644 index 0000000..386fa9e --- /dev/null +++ b/tools/atmega324u_usbhid/rawhid_testinterface.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#if defined(OS_LINUX) || defined(OS_MACOSX) +#include +#include +#elif defined(OS_WINDOWS) +#include +#endif + +#include "hid.h" + + +void sendstr(char * tosend) +{ + rawhid_send(0, tosend, strlen(tosend),1000); +} + +int mtime_diff(struct timeval high,struct timeval low) +{ + int result=1000*(high.tv_sec-low.tv_sec); + result+=high.tv_usec/1000-low.tv_usec/1000; + return result; +} + +#define BUF_LEN 64 + +int main (int argc, char *argv[]) +{ + int r, num; + char buf[BUF_LEN]; + // C-based example is 16C0:0480:FFAB:0200 + unsigned int attemtps = 100; + while(attemtps--) + { + r = rawhid_open(1, 0x16C0, 0x0481, 0xFFAB, 0x0200); + if (r > 0) + break; + else + sleep(1.0); + } + if (r <= 0) + { + printf("no rawhid device found\n"); + return -1; + } + printf("found rawhid device\n\n"); + //~ printf("Clearing Buffer\n"); + //~ sendstr("c"); // clear the buffer + //~ struct timeval start_time,stop_time; + //~ gettimeofday(&start_time,NULL); + //~ gettimeofday(&stop_time,NULL); + //~ while (mtime_diff(stop_time,start_time)<1000) { + // check if any Raw HID packet has arrived + + while (1) + { + num = rawhid_recv(0, buf, BUF_LEN, 250); + if (num < 0) + { + printf("\nerror reading, device went offline\n"); + rawhid_close(0); + return 0; + } + if (num > 0) + { + buf[num]='\0'; + printf("%s\n",buf); + fflush(0); + } + } +} diff --git a/tools/atmega324u_usbhid/reset.c b/tools/atmega324u_usbhid/reset.c new file mode 100644 index 0000000..7955b1b --- /dev/null +++ b/tools/atmega324u_usbhid/reset.c @@ -0,0 +1,22 @@ +#include +#include + +#include "hid.h" + + +void sendstr(char * tosend) +{ + rawhid_send(0, tosend, strlen(tosend),1000); +} + +int main (int argc, char *argv[]) +{ + // C-based example is 16C0:0480:FFAB:0200 + int r = rawhid_open(1, 0x16C0, 0x0481, 0xFFAB, 0x0200); + if (r <= 0) { + printf("no rawhid device found\n"); + return -1; + } + sendstr("r"); // clear the buffer + return 0; +} -- cgit v1.2.3