aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans-Christoph Steiner <eighthave@users.sourceforge.net>2006-06-03 13:31:03 +0000
committerHans-Christoph Steiner <eighthave@users.sourceforge.net>2006-06-03 13:31:03 +0000
commit246b7b648787d8aa5747044f4365143ae4b7da46 (patch)
tree626fc9a8ae4c9085fd17e3273af2877310fa9c5b
parentdefbfadc2927d4afd73810b55d2611418f7903d8 (diff)
added Olaf's [hidin] to become part of the Windows Pd-extended distro, with his expressed, written permission, of course
svn path=/trunk/externals/olafmatt/; revision=5162
-rw-r--r--hidin/hidin.c747
-rw-r--r--hidin/hidin.h142
-rw-r--r--hidin/winNT_usb.c752
3 files changed, 1641 insertions, 0 deletions
diff --git a/hidin/hidin.c b/hidin/hidin.c
new file mode 100644
index 0000000..a41c563
--- /dev/null
+++ b/hidin/hidin.c
@@ -0,0 +1,747 @@
+/* hidin.c - read in data from USB HID device */
+/* Copyright 2003 Olaf Matthes, see README for a detailed licence */
+
+/*
+ 'hidin' is used to read in data from any HID (human interface device) connected
+ to the computers USB port(s).
+ However, it does not work with mice or keyboards ('could not open' error)!
+
+ Needs the Windows Driver Development Kit (DDK) to compile!
+
+ */
+
+#define REQUEST_NOTHING 0
+#define REQUEST_READ 1
+#define REQUEST_QUIT 2
+
+#include "m_pd.h"
+#define SETSYM SETSYMBOL
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "pthread.h"
+
+#include "hidin.h"
+
+static char *hidin_version = "hidin v1.0test2, Human Interface Device on USB, (c) 2003-2004 Olaf Matthes";
+
+static t_class *hidin_class;
+
+typedef struct _hidin
+{
+ t_object x_obj;
+ t_outlet *x_outlet; /* outlet for received data */
+ t_outlet *x_devout; /* outlet for list of devices */
+ long x_interval; /* interval in ms to read */
+ long *x_data; /* data read from the interface */
+ long *x_prev_data; /* data we read the time bofore */
+ void *x_qelem; /* qelem for outputing the results */
+ long x_device; /* the HID device number we're connecting to */
+ short x_elements; /* number of elements (i.e. buttons, axes...)
+
+ /* HID specific */
+ t_hid_device *x_hid; /* a struct containing all the needed stuff about the HID device */
+
+ /* tread stuff */
+ long x_requestcode; /* pending request from parent to I/O thread */
+ pthread_mutex_t x_mutex;
+ pthread_cond_t x_requestcondition;
+ pthread_cond_t x_answercondition;
+ pthread_t x_thread;
+} t_hidin;
+
+
+/* Prototypes and short descriptions */
+
+#ifndef PD
+static void *hidin_output(t_hidin *x);
+static void *hidin_doit(void *z);
+static void hidin_tick(t_hidin *x);
+static void hidin_clock_reset(t_hidin *x);
+static void hidin_int(t_hidin *x, long l);
+/* what/when to read */
+static void hidin_interval(t_hidin *x, long l); /* set time interval for reading ports */
+/* direct response to user */
+static void hidin_show(t_hidin *x);
+static void hidin_start(t_hidin *x);
+static void hidin_stop(t_hidin *x);
+static void hidin_bang(t_hidin *x);
+static void hidin_open(t_hidin *x, long l);
+/* helper functions */
+static void hidin_alloc(t_hidin *x, short elem);
+/* standard Max/MSP related stuff */
+static void *hidin_new(t_symbol *s, short argc, t_atom *argv);
+static void hidin_free(t_hidin *x);
+static void hidin_info(t_hidin *x, void *p, void *c);
+static void hidin_assist(t_hidin *x, void *b, long m, long a, char *s);
+void main(void);
+#endif
+
+
+/* ------------------- general support routines --------------------- */
+
+static void thread_post(t_hidin *x, char *p)
+{
+ post("hidin: %s", p);
+}
+
+/* --------------------- data I/O support stuff --------------------- */
+
+/* output values using qelem */
+static void *hidin_output(t_hidin *x)
+{
+ int i;
+ static t_atom list[2]; /* output list format is: '(int)<port> (int)<value>' */
+ long d;
+
+ pthread_mutex_lock(&x->x_mutex);
+ /* check every element for new value */
+ for (i = 0; i < x->x_elements; i++)
+ {
+ d = x->x_data[i];
+ if (d != x->x_prev_data[i])
+ {
+ SETFLOAT(list, (t_float)i+1);
+ SETFLOAT(list+1, (t_float)d);
+ outlet_list(x->x_outlet, NULL, 2, list);
+ x->x_prev_data[i] = d;
+ }
+ }
+ pthread_cond_signal(&x->x_requestcondition); /* request previous state again */
+ pthread_mutex_unlock(&x->x_mutex);
+ return NULL;
+}
+
+static void hidin_parse_input(t_hidin *x)
+{
+ ULONG i, j;
+ PUSAGE pUsage;
+ t_hid_data *inputData;
+
+ unpackReport(x->x_hid->inputReportBuffer, x->x_hid->caps.InputReportByteLength, HidP_Input,
+ x->x_hid->inputData, x->x_hid->inputDataLength, x->x_hid->ppd);
+
+ inputData = x->x_hid->inputData;
+
+ for (j = 0; j < x->x_hid->inputDataLength; j++)
+ {
+ if (inputData->IsButtonData) // state of buttons changed
+ {
+ // post("Usage Page: 0x%x, Usages: ", inputData->UsagePage);
+
+ // set all buttons to zero
+ for (i = 0; i < inputData->ButtonData.MaxUsageLength; i++)
+ x->x_data[i + x->x_hid->caps.NumberInputValueCaps] = 0;
+
+ for (i = 0, pUsage = inputData->ButtonData.Usages;
+ i < inputData->ButtonData.MaxUsageLength;
+ i++, pUsage++)
+ {
+ if (0 == *pUsage)
+ {
+ break; // A usage of zero is a non button.
+ }
+ else
+ {
+ x->x_data[*pUsage + x->x_hid->caps.NumberInputValueCaps - 1] = 1;
+ }
+ }
+ }
+ else // values changed
+ {
+ /* post("Usage Page: 0x%x, Usage: 0x%x, Scaled: %d Value: %d",
+ inputData->UsagePage,
+ inputData->ValueData.Usage,
+ inputData->ValueData.ScaledValue,
+ inputData->ValueData.Value); */
+ // x->x_data[j - x->x_hid->caps.NumberInputButtonCaps] = inputData->ValueData.ScaledValue;
+ x->x_data[j - x->x_hid->caps.NumberInputButtonCaps] = inputData->ValueData.Value;
+ }
+ inputData++;
+ }
+}
+
+/*
+ * this is the actual code that reads from the file handle to the HID device.
+ * it's a second thread to avoid audio dropouts because it might take some time
+ * for the read call to complete.
+ */
+static void *hidin_doit(void *z)
+{
+ t_hidin *x = (t_hidin *)z;
+ int i, interval;
+ long bytes;
+ long ret;
+
+ if (x->x_hid->event == 0)
+ {
+ x->x_hid->event = CreateEvent(NULL, TRUE, FALSE, "");
+ }
+
+ /* prepare overlapped structute */
+ x->x_hid->overlapped.Offset = 0;
+ x->x_hid->overlapped.OffsetHigh = 0;
+ x->x_hid->overlapped.hEvent = x->x_hid->event;
+
+ pthread_mutex_lock(&x->x_mutex);
+ while (1)
+ {
+ if (x->x_requestcode == REQUEST_NOTHING)
+ {
+ pthread_cond_signal(&x->x_answercondition);
+ pthread_cond_wait(&x->x_requestcondition, &x->x_mutex);
+ }
+ else if (x->x_requestcode == REQUEST_READ)
+ {
+ interval = x->x_interval;
+ if (x->x_device != -1)
+ {
+ pthread_mutex_unlock(&x->x_mutex);
+
+ /* read in data from device */
+ bytes = readHidOverlapped(x->x_hid);
+
+ ret = WaitForSingleObject(x->x_hid->event, interval);
+
+ pthread_mutex_lock(&x->x_mutex);
+ if (ret == WAIT_OBJECT_0) /* hey, we got signalled ! => data */
+ {
+ hidin_parse_input(x); /* parse received data */
+ clock_delay(x->x_qelem, 0); /* we don't have qelems on PD ;-( */
+ /* wait for the data output to complete */
+ pthread_cond_wait(&x->x_requestcondition, &x->x_mutex);
+ }
+ else /* if no data, cancel read */
+ {
+ if (!CancelIo(x->x_hid->device) && (x->x_device != -1))
+ thread_post(x, "-- error cancelling read");
+ }
+ if (!ResetEvent(x->x_hid->event))
+ thread_post(x, "-- error resetting event");
+
+ pthread_cond_signal(&x->x_answercondition);
+ }
+ else
+ {
+ /* if device is closed don't try to read and don't alter
+ the request code, stay in current state of operation */
+ pthread_cond_signal(&x->x_answercondition);
+ pthread_cond_wait(&x->x_requestcondition, &x->x_mutex);
+ }
+ }
+ else if (x->x_requestcode == REQUEST_QUIT)
+ {
+ x->x_requestcode = REQUEST_NOTHING;
+ pthread_cond_signal(&x->x_answercondition);
+ break;
+ }
+ else /* error if we get here! */
+ {
+ error("hidin: -- internal error, please report to <olaf.matthes@gmx.de>!");
+ }
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ return (0);
+}
+
+/*
+ * set the update interval time in ms
+ */
+static void hidin_interval(t_hidin *x, t_floatarg l)
+{
+ int n = (int)l;
+
+ pthread_mutex_lock(&x->x_mutex);
+ if (n >= 2)
+ {
+ x->x_interval = n;
+ post("hidin: >> interval: polling every %d msec", n);
+ }
+ else post("hidin: -- interval: wrong parameter value (minimum 2)");
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+/*
+ * print the actual config on the console window
+ * LATER reduce to the absolute minimum, this is just for easier debugging!
+ */
+static void hidin_show(t_hidin *x)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ if (x->x_device == -1)
+ {
+ post("hidin: -- no open HID device");
+ }
+ else
+ {
+ post("hidin: ****** HID device %d ******", x->x_device);
+ post("hidin: ** usage page: %X", x->x_hid->caps.UsagePage);
+ post("hidin: ** input report byte length: %d", x->x_hid->caps.InputReportByteLength);
+ post("hidin: ** output report byte length: %d", x->x_hid->caps.OutputReportByteLength);
+ post("hidin: ** feature report byte length: %d", x->x_hid->caps.FeatureReportByteLength);
+ post("hidin: ** number of link collection nodes: %d", x->x_hid->caps.NumberLinkCollectionNodes);
+ post("hidin: ** number of input button caps: %d", x->x_hid->caps.NumberInputButtonCaps);
+ post("hidin: ** number of inputValue caps: %d", x->x_hid->caps.NumberInputValueCaps);
+ post("hidin: ** number of inputData indices: %d", x->x_hid->caps.NumberInputDataIndices);
+ post("hidin: ** number of output button caps: %d", x->x_hid->caps.NumberOutputButtonCaps);
+ post("hidin: ** number of output value caps: %d", x->x_hid->caps.NumberOutputValueCaps);
+ post("hidin: ** number of output data indices: %d", x->x_hid->caps.NumberOutputDataIndices);
+ post("hidin: ** number of feature button caps: %d", x->x_hid->caps.NumberFeatureButtonCaps);
+ post("hidin: ** number of feature value caps: %d", x->x_hid->caps.NumberFeatureValueCaps);
+ post("hidin: ** number of feature data indices: %d", x->x_hid->caps.NumberFeatureDataIndices);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+/*
+ * start / stop reading
+ */
+static void hidin_int(t_hidin *x, t_floatarg l)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ if (l)
+ {
+ x->x_requestcode = REQUEST_READ;
+ pthread_cond_signal(&x->x_requestcondition);
+ }
+ else
+ {
+ x->x_requestcode = REQUEST_NOTHING;
+ pthread_cond_signal(&x->x_requestcondition);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+ /* start reading */
+static void hidin_start(t_hidin *x)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_requestcode = REQUEST_READ;
+ pthread_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+ /* stop reading */
+static void hidin_stop(t_hidin *x)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_requestcode = REQUEST_NOTHING;
+ pthread_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+ /* get list of devices */
+static void hidin_bang(t_hidin *x)
+{
+ t_atom list[2];
+ int numdev, i;
+
+ pthread_mutex_lock(&x->x_mutex);
+ // check for connected devices
+ numdev = findHidDevices();
+ post("hidin: ** found %d devices on your system", numdev);
+
+ SETFLOAT(list, -1);
+ SETSYM(list+1, gensym("None"));
+ outlet_list(x->x_devout, NULL, 2, list);
+ for (i = 0; i < numdev; i++)
+ {
+ SETFLOAT(list, i + 1);
+ SETSYM(list+1, findDeviceName(i));
+ outlet_list(x->x_devout, NULL, 2, list);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+/*
+ * allocate memory for output data (according to number of elements)
+ */
+static void hidin_alloc(t_hidin *x, short elem)
+{
+ int i;
+
+ if (x->x_device != -1)
+ {
+ // free memory in case we already have some
+ if (x->x_data && x->x_elements > 0)
+ {
+ freebytes(x->x_data, (short)(x->x_elements * sizeof(long)));
+ }
+ if (x->x_prev_data && x->x_elements > 0)
+ {
+ freebytes(x->x_prev_data, (short)(x->x_elements * sizeof(long)));
+ }
+
+ if (elem > 0)
+ {
+ // allocate memory to new size
+ x->x_data = (long *)getbytes((short)(elem * sizeof(long)));
+ x->x_prev_data = (long *)getbytes((short)(elem * sizeof(long)));
+ if (!x->x_data || !x->x_prev_data)
+ {
+ post("hidin: -- out of memory");
+ x->x_elements = 0;
+ return;
+ }
+ // set newly allocated memory to zero
+ for (i = 0; i < elem; i++)
+ {
+ x->x_data[i] = 0;
+ x->x_prev_data[i] = 0;
+ }
+ }
+ x->x_elements = elem;
+ }
+}
+
+/*
+ * init the device data structures
+ */
+static void hidin_initdevice(t_hidin *x)
+{
+ int i;
+ short numElements;
+ short numValues;
+ short numCaps;
+ PHIDP_BUTTON_CAPS buttonCaps;
+ PHIDP_VALUE_CAPS valueCaps;
+ USAGE usage;
+ t_hid_data *data;
+
+ // get device info and show some printout
+ getDeviceInfo(x->x_hid->device);
+
+ // read in device's capabilities and allocate memory accordingly
+ getDeviceCapabilities(x->x_hid);
+
+ // allocate memory for input field
+ x->x_hid->inputReportBuffer = (char*)getbytes((short)(x->x_hid->caps.InputReportByteLength*sizeof(char)));
+
+ // allocate memory for input info
+ x->x_hid->inputButtonCaps = buttonCaps = (PHIDP_BUTTON_CAPS)getbytes((short)(x->x_hid->caps.NumberInputButtonCaps * sizeof(HIDP_BUTTON_CAPS)));
+ x->x_hid->inputValueCaps = valueCaps = (PHIDP_VALUE_CAPS)getbytes((short)(x->x_hid->caps.NumberInputValueCaps * sizeof(HIDP_VALUE_CAPS)));
+
+ //
+ // Have the HidP_X functions fill in the capability structure arrays.
+ //
+
+ numCaps = x->x_hid->caps.NumberInputButtonCaps;
+
+ HidP_GetButtonCaps (HidP_Input,
+ buttonCaps,
+ &numCaps,
+ x->x_hid->ppd);
+
+ numCaps = x->x_hid->caps.NumberInputValueCaps;
+
+ HidP_GetValueCaps (HidP_Input,
+ valueCaps,
+ &numCaps,
+ x->x_hid->ppd);
+ //
+ // Depending on the device, some value caps structures may represent more
+ // than one value. (A range). In the interest of being verbose, over
+ // efficient, we will expand these so that we have one and only one
+ // struct _HID_DATA for each value.
+ //
+ // To do this we need to count up the total number of values are listed
+ // in the value caps structure. For each element in the array we test
+ // for range if it is a range then UsageMax and UsageMin describe the
+ // usages for this range INCLUSIVE.
+ //
+
+ numValues = 0;
+ for (i = 0; i < x->x_hid->caps.NumberInputValueCaps; i++, valueCaps++)
+ {
+ if (valueCaps->IsRange)
+ {
+ numValues += valueCaps->Range.UsageMax - valueCaps->Range.UsageMin + 1;
+ }
+ else
+ {
+ numValues++;
+ }
+ }
+ valueCaps = x->x_hid->inputValueCaps;
+
+ numElements = x->x_hid->caps.NumberInputValueCaps +
+ x->x_hid->caps.NumberInputButtonCaps * x->x_hid->caps.NumberInputDataIndices;
+
+ post("hidin: >> device has %i input elements", numElements);
+ hidin_alloc(x, (short)(numElements)); // allocate memory for all these elements
+
+ x->x_hid->inputDataLength = numValues + x->x_hid->caps.NumberInputButtonCaps;
+
+ x->x_hid->inputData = data = (t_hid_data *)getbytes((short)(x->x_hid->inputDataLength * sizeof(t_hid_data)));
+
+ //
+ // Fill in the button data
+ //
+
+ for (i = 0;
+ i < x->x_hid->caps.NumberInputButtonCaps;
+ i++, data++, buttonCaps++)
+ {
+ data->IsButtonData = TRUE;
+ data->Status = HIDP_STATUS_SUCCESS;
+ data->UsagePage = buttonCaps->UsagePage;
+ if (buttonCaps->IsRange)
+ {
+ data->ButtonData.UsageMin = buttonCaps -> Range.UsageMin;
+ data->ButtonData.UsageMax = buttonCaps -> Range.UsageMax;
+ }
+ else
+ {
+ data -> ButtonData.UsageMin = data -> ButtonData.UsageMax = buttonCaps -> NotRange.Usage;
+ }
+
+ data->ButtonData.MaxUsageLength = HidP_MaxUsageListLength(
+ HidP_Input,
+ buttonCaps->UsagePage,
+ x->x_hid->ppd);
+ data->ButtonData.Usages = (PUSAGE)
+ calloc (data->ButtonData.MaxUsageLength, sizeof (USAGE));
+
+ data->ReportID = buttonCaps -> ReportID;
+ }
+
+ //
+ // Fill in the value data
+ //
+
+ for (i = 0; i < numValues; i++, valueCaps++)
+ {
+ if (valueCaps->IsRange)
+ {
+ for (usage = valueCaps->Range.UsageMin;
+ usage <= valueCaps->Range.UsageMax;
+ usage++)
+ {
+ data->IsButtonData = FALSE;
+ data->Status = HIDP_STATUS_SUCCESS;
+ data->UsagePage = valueCaps->UsagePage;
+ data->ValueData.Usage = usage;
+ data->ReportID = valueCaps->ReportID;
+ data++;
+ }
+ }
+ else
+ {
+ data->IsButtonData = FALSE;
+ data->Status = HIDP_STATUS_SUCCESS;
+ data->UsagePage = valueCaps->UsagePage;
+ data->ValueData.Usage = valueCaps->NotRange.Usage;
+ data->ReportID = valueCaps->ReportID;
+ data++;
+ }
+ }
+
+ // getFeature(x->x_hid);
+}
+
+static void hidin_doopen(t_hidin *x, long deviceID)
+{
+ short elements; // number of elements of HID device
+ long oldrequest;
+
+ pthread_mutex_lock(&x->x_mutex);
+ oldrequest = x->x_requestcode;
+ x->x_requestcode = REQUEST_NOTHING;
+ pthread_cond_signal(&x->x_requestcondition);
+ pthread_cond_wait(&x->x_answercondition, &x->x_mutex);
+
+ // close old device if any
+ if (x->x_device != -1)
+ {
+ CloseHandle(x->x_hid->device);
+
+ // free allocated memory
+ freebytes(x->x_hid->inputData, (short)(x->x_hid->inputDataLength * sizeof(t_hid_data)));
+ freebytes(x->x_hid->inputButtonCaps, (short)(x->x_hid->caps.NumberInputButtonCaps * sizeof(HIDP_BUTTON_CAPS)));
+ freebytes(x->x_hid->inputValueCaps, (short)(x->x_hid->caps.NumberInputValueCaps * sizeof(HIDP_VALUE_CAPS)));
+ freebytes(x->x_hid->inputReportBuffer, (short)(x->x_hid->caps.InputReportByteLength * sizeof(char)));
+ x->x_hid->caps.InputReportByteLength = 0;
+ }
+
+ if (deviceID != -1)
+ {
+ // open new device
+ x->x_hid->device = connectDeviceNumber(deviceID);
+
+ if (x->x_hid->device != INVALID_HANDLE_VALUE)
+ {
+ x->x_device = deviceID;
+ hidin_initdevice(x);
+ }
+ else // open failed...
+ {
+ hidin_alloc(x, 0); // free data memory
+ x->x_device = -1;
+ }
+ }
+ else
+ {
+ hidin_alloc(x, 0); // free data memory
+ post("hidin: << device closed");
+ x->x_device = -1;
+ }
+
+ x->x_requestcode = oldrequest; /* set back to old requestcode */
+ pthread_cond_signal(&x->x_requestcondition); /* go on again */
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+/*
+ * select & open device to use
+ */
+static void hidin_open(t_hidin *x, t_floatarg l)
+{
+ long device = (long)l - 1; /* get new internal device number */
+ hidin_doopen(x, device);
+}
+
+/*
+ * close open device
+ */
+static void hidin_close(t_hidin *x)
+{
+ if (x->x_device != -1)
+ hidin_doopen(x, -1);
+}
+
+/*
+ * the object's new function
+ */
+static void *hidin_new(t_symbol *s, short argc, t_atom *argv)
+{
+ int i, k, n;
+ int deviceID, intv;
+
+ t_hidin *x;
+
+ deviceID = -1; /* default HID device number: none */
+ intv = 10; /* read every 10 ms */
+
+ if (argc >= 1 && argv->a_type == A_FLOAT) /* just one argument (long): device ID */
+ {
+ deviceID = (int)(argv[0].a_w.w_float - 1);
+ if (argc >= 2 && argv[1].a_type == A_FLOAT) /* second argument (long): interval */
+ {
+ intv = (int)argv[1].a_w.w_float;
+ }
+ }
+
+ x = (t_hidin*)pd_new(hidin_class);
+
+ // zero out the struct, to be careful
+ if (x)
+ {
+ for (i = sizeof(t_object); i < sizeof(t_hidin); i++)
+ ((char*)x)[i] = 0;
+ }
+
+ x->x_outlet = outlet_new(&x->x_obj, gensym("list")); // outputs received data
+ x->x_devout = outlet_new(&x->x_obj, gensym("list")); // outputs list of devices
+
+ x->x_qelem = clock_new(x, (t_method)hidin_output);
+
+ pthread_mutex_init(&x->x_mutex, 0);
+ pthread_cond_init(&x->x_requestcondition, 0);
+ pthread_cond_init(&x->x_answercondition, 0);
+
+ x->x_requestcode = REQUEST_NOTHING;
+
+ x->x_interval = intv;
+ x->x_device = -1;
+ x->x_elements = 0;
+ x->x_data = NULL;
+ x->x_prev_data = NULL;
+
+ // allocate memory for the t_hid_device struct
+ x->x_hid = (t_hid_device *)getbytes(sizeof(t_hid_device));
+
+ // create I/O thread
+ pthread_create(&x->x_thread, 0, hidin_doit, x);
+
+ if (deviceID != -1)
+ hidin_doopen(x, deviceID);
+
+ return (x);
+}
+
+static void hidin_free(t_hidin *x)
+{
+ void *threadrtn;
+ t_atom atom;
+
+ // close the HID device
+ hidin_doopen(x, -1);
+
+ // stop IO thread
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_requestcode = REQUEST_QUIT;
+ pthread_cond_signal(&x->x_requestcondition);
+ startpost("hidin: >> stopping HID I/O thread...");
+ SETSYMBOL(&atom, gensym("signalling..."));
+ while (x->x_requestcode != REQUEST_NOTHING)
+ {
+ postatom(1, &atom);
+ pthread_cond_signal(&x->x_requestcondition);
+ pthread_cond_wait(&x->x_answercondition, &x->x_mutex);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ if (pthread_join(x->x_thread, &threadrtn))
+ error("shoutcast_free: join failed");
+ SETSYMBOL(&atom, gensym("done."));
+ postatom(1, &atom);
+ endpost();
+
+ freebytes(x->x_hid, sizeof(t_hid_device));
+
+ clock_free(x->x_qelem);
+
+ pthread_cond_destroy(&x->x_requestcondition);
+ pthread_cond_destroy(&x->x_answercondition);
+ pthread_mutex_destroy(&x->x_mutex);
+}
+
+/*
+ * the object's info function
+ */
+static void hidin_info(t_hidin *x)
+{
+ post(hidin_version);
+ pthread_mutex_lock(&x->x_mutex);
+ if (x->x_device != -1)
+ {
+ post("hidin: ** using device #%d: \"%s\"", x->x_device + 1,
+ findDeviceName(x->x_device)->s_name);
+ post("hidin: ** device has %d values and %d buttons",
+ x->x_hid->caps.NumberInputValueCaps,
+ x->x_hid->caps.NumberInputButtonCaps * x->x_hid->caps.NumberInputDataIndices);
+ // getDeviceInfo(x->x_hid->device);
+ }
+ else post("hidin: -- no open device");
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+void hidin_setup(void)
+{
+ hidin_class = class_new(gensym("hidin"),(t_newmethod)hidin_new, (t_method)hidin_free,
+ sizeof(t_hidin), 0, A_GIMME, 0);
+ class_addbang(hidin_class, (t_method)hidin_bang);
+ class_addfloat(hidin_class, (t_method)hidin_int);
+ class_addmethod(hidin_class, (t_method)hidin_start, gensym("start"), 0);
+ class_addmethod(hidin_class, (t_method)hidin_stop, gensym("stop"), 0);
+ class_addmethod(hidin_class, (t_method)hidin_show, gensym("show"), 0);
+ class_addmethod(hidin_class, (t_method)hidin_info, gensym("print"), 0);
+ class_addmethod(hidin_class, (t_method)hidin_open, gensym("open"), A_FLOAT, 0);
+ class_addmethod(hidin_class, (t_method)hidin_close, gensym("close"), 0);
+ class_addmethod(hidin_class, (t_method)hidin_interval, gensym("interval"), A_FLOAT, 0);
+ class_sethelpsymbol(hidin_class, gensym("help-hidin.pd"));
+ post(hidin_version);
+}
+
diff --git a/hidin/hidin.h b/hidin/hidin.h
new file mode 100644
index 0000000..4425b76
--- /dev/null
+++ b/hidin/hidin.h
@@ -0,0 +1,142 @@
+/*
+
+ hidin.h - headers and declarationd for the 'hidin' external
+
+*/
+
+#ifndef hidin_H
+#define hidin_H
+
+#include <windows.h>
+#include <basetyps.h>
+#include <stdlib.h>
+#include <wtypes.h>
+#include <setupapi.h>
+#include "hidusage.h"
+#include "hidsdi.h"
+#include "setupapi.h"
+
+//
+// A structure to hold the steady state data received from the hid device.
+// Each time a read packet is received we fill in this structure.
+// Each time we wish to write to a hid device we fill in this structure.
+// This structure is here only for convenience. Most real applications will
+// have a more efficient way of moving the hid data to the read, write, and
+// feature routines.
+//
+typedef struct _hid_data
+{
+ BOOLEAN IsButtonData;
+ UCHAR Reserved;
+ USAGE UsagePage; // The usage page for which we are looking.
+ ULONG Status; // The last status returned from the accessor function
+ // when updating this field.
+ ULONG ReportID; // ReportID for this given data structure
+ BOOLEAN IsDataSet; // Variable to track whether a given data structure
+ // has already been added to a report structure
+
+ union {
+ struct {
+ ULONG UsageMin; // Variables to track the usage minimum and max
+ ULONG UsageMax; // If equal, then only a single usage
+ ULONG MaxUsageLength; // Usages buffer length.
+ PUSAGE Usages; // list of usages (buttons ``down'' on the device.
+
+ } ButtonData;
+ struct {
+ USAGE Usage; // The usage describing this value;
+ USHORT Reserved;
+
+ ULONG Value;
+ LONG ScaledValue;
+ } ValueData;
+ };
+} t_hid_data;
+
+typedef struct _hid_device
+{
+ PCHAR devicePath;
+ HANDLE device; // A file handle to the hid device.
+ HANDLE event;
+ OVERLAPPED overlapped;
+
+ BOOL openedForRead;
+ BOOL openedForWrite;
+ BOOL openedOverlapped;
+ BOOL openedExclusive;
+
+ PHIDP_PREPARSED_DATA ppd; // The opaque parser info describing this device
+ HIDP_CAPS caps; // The Capabilities of this hid device.
+ HIDD_ATTRIBUTES attributes;
+ char *inputReportBuffer;
+ t_hid_data *inputData; // array of hid data structures
+ ULONG inputDataLength; // Num elements in this array.
+ PHIDP_BUTTON_CAPS inputButtonCaps;
+ PHIDP_VALUE_CAPS inputValueCaps;
+
+ char *outputReportBuffer;
+ t_hid_data *outputData;
+ ULONG outputDataLength;
+ PHIDP_BUTTON_CAPS outputButtonCaps;
+ PHIDP_VALUE_CAPS outputValueCaps;
+
+ char *featureReportBuffer;
+ t_hid_data *featureData;
+ ULONG featureDataLength;
+ PHIDP_BUTTON_CAPS featureButtonCaps;
+ PHIDP_VALUE_CAPS featureValueCaps;
+} t_hid_device;
+
+
+/*
+ * displays the vendor and product ID and the version
+ * number for the given device handle
+ */
+void getDeviceInfo(HANDLE deviceHandle);
+
+/*
+ * find number of attached HID devices
+ */
+int findHidDevices();
+
+/*
+ * find name of attached HID devices
+ */
+t_symbol *findDeviceName(DWORD deviceID);
+
+/*
+ * connects to the hid device specified through a number
+ * returns a handle to the device (x->x_hid.device)
+ */
+HANDLE connectDeviceNumber(DWORD i);
+
+/* Connects to the USB HID described by the combination of vendor id, product id
+ If the attribute is null, it will connect to first device satisfying the remaining
+ attributes. */
+HANDLE connectDeviceName(DWORD *vendorID, DWORD *productID, DWORD *versionNumber);
+
+/*
+ * get hid device capabilities (and display them)
+ * also instantiates the x->x_hid.caps field and
+ * allocates the memory we'll need to use this device
+ */
+void getDeviceCapabilities(t_hid_device *hid);
+
+
+
+/*
+ * read input data from hid device and display them
+ */
+int readHid(t_hid_device *hidDevice);
+
+int readHidOverlapped(t_hid_device *hidDevice);
+
+BOOLEAN getFeature(t_hid_device *HidDevice);
+
+BOOLEAN unpackReport(PCHAR ReportBuffer, USHORT ReportBufferLength, HIDP_REPORT_TYPE ReportType,
+ t_hid_data *Data, ULONG DataLength, PHIDP_PREPARSED_DATA Ppd);
+
+BOOLEAN packReport(PCHAR ReportBuffer, USHORT ReportBufferLength, HIDP_REPORT_TYPE ReportType,
+ t_hid_data *Data, ULONG DataLength, PHIDP_PREPARSED_DATA Ppd);
+
+#endif // hidin_H \ No newline at end of file
diff --git a/hidin/winNT_usb.c b/hidin/winNT_usb.c
new file mode 100644
index 0000000..0e361b7
--- /dev/null
+++ b/hidin/winNT_usb.c
@@ -0,0 +1,752 @@
+/*
+ hidin USB HID support stuff for Windows 2000 / XP
+
+ Written by Olaf Matthes <olaf.matthes@gmx.de>
+
+ This file contains the implementation for connecting to USB HID
+ (Human Interface Device) devices. It provides several functions
+ to open devices and to query information and capabilities.
+
+*/
+
+#ifdef PD
+#include "m_pd.h" /* we need this because we want to print() to the PD console */
+#include <stdio.h>
+#else
+#include "ext.h" /* we need this because we want to print() to the Max window */
+#endif
+#include "hidin.h"
+
+
+/*
+ * get information for a given device (HANDLE)
+ *
+ */
+
+void getDeviceInfo(HANDLE deviceHandle)
+{
+ HIDD_ATTRIBUTES deviceAttributes;
+ PWCHAR deviceName;
+ ULONG length = 256;
+
+ if(deviceHandle == INVALID_HANDLE_VALUE)
+ {
+ post("hidin: -- couldn't get device info due to an invalid handle");
+ return;
+ }
+
+ if (!HidD_GetAttributes (deviceHandle, &deviceAttributes))
+ {
+ post("hidin: -- failed to get attributes");
+ return;
+ }
+ else
+ {
+ // post("hidin: ** VendorID: 0x%x", deviceAttributes.VendorID);
+ // post("hidin: ** ProductID: 0x%x", deviceAttributes.ProductID);
+ // post("hidin: ** VersionNumber: 0x%x", deviceAttributes.VersionNumber);
+ }
+
+ deviceName = (PWCHAR)getbytes((short)(length));
+ if(!HidD_GetProductString (deviceHandle, deviceName, length))
+ {
+ freebytes(deviceName, (short)(length));
+ return;
+ }
+ else
+ {
+ char name[256];
+ int i = 0;
+ wcstombs(name, deviceName, length);
+ post("hidin: >> opening device: \"%s\"", name);
+ freebytes(deviceName, (short)(length));
+ }
+ return;
+}
+
+/*
+ * find name of attached HID devices
+ */
+t_symbol *findDeviceName(DWORD deviceID)
+{
+ HANDLE deviceHandle;
+ PWCHAR deviceName;
+ ULONG length = 256;
+ char name[256]; // this will be the return value
+ int i = 0;
+
+ deviceHandle = connectDeviceNumber(deviceID);
+
+ if(deviceHandle != INVALID_HANDLE_VALUE)
+ {
+ deviceName = (PWCHAR)getbytes((short)(length));
+ if(!HidD_GetProductString (deviceHandle, deviceName, length))
+ {
+ freebytes(deviceName, (short)(length));
+ sprintf(name, "Unknown (Device #%d)", deviceID + 1);
+ return gensym(name);
+ }
+ else
+ {
+ wcstombs(name, deviceName, length);
+ freebytes(deviceName, (short)(length));
+ }
+
+ CloseHandle(deviceHandle);
+
+ return gensym(name);
+ }
+ return gensym("Unsupported Device");
+}
+
+/*
+ * find number of attached HID devices
+ */
+int findHidDevices()
+{
+ HDEVINFO hardwareDeviceInfo;
+ SP_INTERFACE_DEVICE_DATA deviceInfoData;
+ ULONG i;
+ BOOLEAN done;
+ GUID hidGuid;
+ PSP_INTERFACE_DEVICE_DETAIL_DATA functionClassDeviceData = NULL;
+ ULONG predictedLength = 0;
+ ULONG requiredLength = 0;
+ ULONG NumberDevices = 0;
+
+ HidD_GetHidGuid (&hidGuid);
+
+ //
+ // Open a handle to the plug and play dev node.
+ //
+ hardwareDeviceInfo = SetupDiGetClassDevs ( &hidGuid,
+ NULL, // Define no enumerator (global)
+ NULL, // Define no
+ (DIGCF_PRESENT | // Only Devices present
+ DIGCF_DEVICEINTERFACE)); // Function class devices.
+
+ //
+ // Take a wild guess to start
+ //
+
+ NumberDevices = 4;
+ done = FALSE;
+ deviceInfoData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);
+
+ i=0;
+ while (!done)
+ {
+ NumberDevices *= 2;
+
+ for (; i < NumberDevices; i++)
+ {
+ if (SetupDiEnumDeviceInterfaces (hardwareDeviceInfo,
+ 0, // No care about specific PDOs
+ &hidGuid,
+ i,
+ &deviceInfoData))
+ {
+ //
+ // allocate a function class device data structure to receive the
+ // goods about this particular device.
+ //
+
+ SetupDiGetDeviceInterfaceDetail (
+ hardwareDeviceInfo,
+ &deviceInfoData,
+ NULL, // probing so no output buffer yet
+ 0, // probing so output buffer length of zero
+ &requiredLength,
+ NULL); // not interested in the specific dev-node
+
+
+ predictedLength = requiredLength;
+
+ functionClassDeviceData = malloc (predictedLength);
+ if (functionClassDeviceData)
+ {
+ functionClassDeviceData->cbSize = sizeof (SP_INTERFACE_DEVICE_DETAIL_DATA);
+ }
+ else
+ {
+ SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
+ return 0;
+ }
+
+ //
+ // Retrieve the information from Plug and Play.
+ //
+
+ if (! SetupDiGetDeviceInterfaceDetail (
+ hardwareDeviceInfo,
+ &deviceInfoData,
+ functionClassDeviceData,
+ predictedLength,
+ &requiredLength,
+ NULL))
+ {
+ SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
+ return 0;
+ }
+ //
+ // get name of device
+ //
+ // HidDevices[i] = getbytes(1024 * sizeof(char));
+ }
+ else
+ {
+ if (ERROR_NO_MORE_ITEMS == GetLastError())
+ {
+ done = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
+
+ return (i); // return number of devices
+}
+
+
+/*
+ * connect to Ith USB device (count starting with 0)
+ */
+
+HANDLE connectDeviceNumber(DWORD deviceIndex)
+{
+ GUID hidGUID;
+ HDEVINFO hardwareDeviceInfoSet;
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
+ PSP_INTERFACE_DEVICE_DETAIL_DATA deviceDetail;
+ ULONG requiredSize;
+ HANDLE deviceHandle = INVALID_HANDLE_VALUE;
+ DWORD result;
+
+ //Get the HID GUID value - used as mask to get list of devices
+ HidD_GetHidGuid (&hidGUID);
+
+ //Get a list of devices matching the criteria (hid interface, present)
+ hardwareDeviceInfoSet = SetupDiGetClassDevs (&hidGUID,
+ NULL, // Define no enumerator (global)
+ NULL, // Define no
+ (DIGCF_PRESENT | // Only Devices present
+ DIGCF_DEVICEINTERFACE)); // Function class devices.
+
+ deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+
+ //Go through the list and get the interface data
+ result = SetupDiEnumDeviceInterfaces (hardwareDeviceInfoSet,
+ NULL, //infoData,
+ &hidGUID, //interfaceClassGuid,
+ deviceIndex,
+ &deviceInterfaceData);
+
+ /* Failed to get a device - possibly the index is larger than the number of devices */
+ if (result == FALSE)
+ {
+ SetupDiDestroyDeviceInfoList (hardwareDeviceInfoSet);
+ post("hidin: -- failed to get specified device number");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ //Get the details with null values to get the required size of the buffer
+ SetupDiGetDeviceInterfaceDetail (hardwareDeviceInfoSet,
+ &deviceInterfaceData,
+ NULL, //interfaceDetail,
+ 0, //interfaceDetailSize,
+ &requiredSize,
+ 0); //infoData))
+
+ //Allocate the buffer
+ deviceDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(requiredSize);
+ deviceDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
+
+ //Fill the buffer with the device details
+ if (!SetupDiGetDeviceInterfaceDetail (hardwareDeviceInfoSet,
+ &deviceInterfaceData,
+ deviceDetail,
+ requiredSize,
+ &requiredSize,
+ NULL))
+ {
+ SetupDiDestroyDeviceInfoList (hardwareDeviceInfoSet);
+ free (deviceDetail);
+ post("hidin: -- failed to get device info");
+ return INVALID_HANDLE_VALUE;
+ }
+
+#if 1
+ //Open file on the device (read only)
+ deviceHandle = CreateFile
+ (deviceDetail->DevicePath,
+ GENERIC_READ,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ (LPSECURITY_ATTRIBUTES)NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ NULL);
+#else
+ //Open file on the device (read & write)
+ deviceHandle = CreateFile
+ (deviceDetail->DevicePath,
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ (LPSECURITY_ATTRIBUTES)NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED, // was 0
+ NULL);
+#endif
+
+ if(deviceHandle == INVALID_HANDLE_VALUE)
+ {
+ int err = GetLastError();
+ LPVOID lpMsgBuf;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
+ post("hidin: -- could not get device #%i: %s", deviceIndex + 1, (LPCTSTR)lpMsgBuf);
+ if(err == ERROR_ACCESS_DENIED)
+ post("hidin: -- can not read from mouse and keyboard");
+ LocalFree(lpMsgBuf);
+ }
+
+ SetupDiDestroyDeviceInfoList (hardwareDeviceInfoSet);
+ free (deviceDetail);
+ return deviceHandle;
+}
+
+/*
+ * connect to USB device specified through VendorID, ProductID and VersionNumber
+ *
+ */
+
+HANDLE connectDeviceName(DWORD *vendorID, DWORD *productID, DWORD *versionNumber)
+{
+ HANDLE deviceHandle = INVALID_HANDLE_VALUE;
+ DWORD index = 0;
+ HIDD_ATTRIBUTES deviceAttributes;
+ BOOL matched = FALSE;
+
+ while (!matched && (deviceHandle = connectDeviceNumber(index)) != INVALID_HANDLE_VALUE)
+ {
+ if (!HidD_GetAttributes (deviceHandle, &deviceAttributes))
+ return INVALID_HANDLE_VALUE;
+
+ if ((vendorID == 0 || deviceAttributes.VendorID == *vendorID) &&
+ (productID == 0 || deviceAttributes.ProductID == *productID) &&
+ (versionNumber == 0 || deviceAttributes.VersionNumber == *versionNumber))
+ return deviceHandle; /* matched */
+
+ CloseHandle (deviceHandle); /* not a match - close and try again */
+
+ index++;
+ }
+
+ return INVALID_HANDLE_VALUE;
+}
+
+
+/*
+ * get device capabilities
+ *
+ */
+void getDeviceCapabilities(t_hid_device *hid)
+{
+ // Get the Capabilities structure for the device.
+ PHIDP_PREPARSED_DATA preparsedData;
+ HIDP_CAPS capabilities;
+
+ if(hid->device == INVALID_HANDLE_VALUE)
+ {
+ post("hidin: -- couldn't get device capabilities due to an invalid handle");
+ return;
+ }
+
+ /*
+ API function: HidD_GetPreparsedData
+ Returns: a pointer to a buffer containing the information about the device's capabilities.
+ Requires: A handle returned by CreateFile.
+ There's no need to access the buffer directly,
+ but HidP_GetCaps and other API functions require a pointer to the buffer.
+ */
+
+ HidD_GetPreparsedData(hid->device, &preparsedData);
+
+ /* get the device attributes */
+ // HidD_GetAttributes (hid->device, &hid->attributes);
+
+ /*
+ API function: HidP_GetCaps
+ Learn the device's capabilities.
+ For standard devices such as joysticks, you can find out the specific
+ capabilities of the device.
+ For a custom device, the software will probably know what the device is capable of,
+ and the call only verifies the information.
+ Requires: the pointer to the buffer returned by HidD_GetPreparsedData.
+ Returns: a Capabilities structure containing the information.
+ */
+
+ HidP_GetCaps(preparsedData, &capabilities);
+
+ // Display the capabilities
+
+ hid->caps = capabilities;
+ hid->ppd = preparsedData;
+ // No need for PreparsedData any more, so free the memory it's using.
+ // HidD_FreePreparsedData(preparsedData);
+}
+
+/*
+ * Given a struct _hid_device, obtain a read report and unpack the values
+ * into the InputData array.
+ */
+int readHid(t_hid_device *hidDevice)
+{
+ long value = 0, ret;
+ long bytesRead;
+
+ if(!ReadFile(hidDevice->device,
+ hidDevice->inputReportBuffer,
+ hidDevice->caps.InputReportByteLength,
+ &bytesRead,
+ NULL))
+ {
+ int err = GetLastError();
+ LPVOID lpMsgBuf;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
+ post("hidin: -- could not read from device: %s", (LPCTSTR)lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ return 0;
+ }
+ // we need: bytesRead == hidDevice->caps.InputReportByteLength
+
+ return bytesRead;
+}
+
+/*
+ * Given a struct _hid_device, obtain a read report and unpack the values
+ * into the InputData array.
+ */
+int readHidOverlapped(t_hid_device *hidDevice)
+{
+ long value = 0, ret;
+ long bytesRead;
+
+ if(!ReadFile(hidDevice->device,
+ hidDevice->inputReportBuffer,
+ hidDevice->caps.InputReportByteLength,
+ &bytesRead,
+ (LPOVERLAPPED) &hidDevice->overlapped))
+ {
+ return 0;
+ }
+ return bytesRead;
+}
+
+
+
+BOOLEAN getFeature (t_hid_device *HidDevice)
+/*++
+RoutineDescription:
+ Given a struct _HID_DEVICE, fill in the feature data structures with
+ all features on the device. May issue multiple HidD_GetFeature() calls to
+ deal with multiple report IDs.
+--*/
+{
+ ULONG Index;
+ t_hid_data *pData;
+ BOOLEAN FeatureStatus;
+ BOOLEAN Status;
+
+ /*
+ // As with writing data, the IsDataSet value in all the structures should be
+ // set to FALSE to indicate that the value has yet to have been set
+ */
+
+ pData = HidDevice->featureData;
+
+ for (Index = 0; Index < HidDevice->featureDataLength; Index++, pData++)
+ {
+ pData->IsDataSet = FALSE;
+ }
+
+ /*
+ // Next, each structure in the HID_DATA buffer is filled in with a value
+ // that is retrieved from one or more calls to HidD_GetFeature. The
+ // number of calls is equal to the number of reportIDs on the device
+ */
+
+ Status = TRUE;
+ pData = HidDevice->featureData;
+
+ for (Index = 0; Index < HidDevice->featureDataLength; Index++, pData++)
+ {
+ /*
+ // If a value has yet to have been set for this structure, build a report
+ // buffer with its report ID as the first byte of the buffer and pass
+ // it in the HidD_GetFeature call. Specifying the report ID in the
+ // first specifies which report is actually retrieved from the device.
+ // The rest of the buffer should be zeroed before the call
+ */
+
+ if (!pData->IsDataSet)
+ {
+ memset(HidDevice->featureReportBuffer, 0x00, HidDevice->caps.FeatureReportByteLength);
+
+ HidDevice->featureReportBuffer[0] = (UCHAR) pData -> ReportID;
+
+ FeatureStatus = HidD_GetFeature (HidDevice->device,
+ HidDevice->featureReportBuffer,
+ HidDevice->caps.FeatureReportByteLength);
+
+ /*
+ // If the return value is TRUE, scan through the rest of the HID_DATA
+ // structures and fill whatever values we can from this report
+ */
+
+
+ if (FeatureStatus)
+ {
+ FeatureStatus = unpackReport ( HidDevice->featureReportBuffer,
+ HidDevice->caps.FeatureReportByteLength,
+ HidP_Feature,
+ HidDevice->featureData,
+ HidDevice->featureDataLength,
+ HidDevice->ppd);
+ }
+
+ Status = Status && FeatureStatus;
+ }
+ }
+
+ return (Status);
+}
+
+
+BOOLEAN
+unpackReport (
+ PCHAR ReportBuffer,
+ USHORT ReportBufferLength,
+ HIDP_REPORT_TYPE ReportType,
+ t_hid_data *Data,
+ ULONG DataLength,
+ PHIDP_PREPARSED_DATA Ppd
+)
+/*++
+Routine Description:
+ Given ReportBuffer representing a report from a HID device where the first
+ byte of the buffer is the report ID for the report, extract all the HID_DATA
+ in the Data list from the given report.
+--*/
+{
+ ULONG numUsages; // Number of usages returned from GetUsages.
+ ULONG i;
+ UCHAR reportID;
+ ULONG Index;
+ ULONG nextUsage;
+
+ reportID = ReportBuffer[0];
+
+ for (i = 0; i < DataLength; i++, Data++)
+ {
+ if (reportID == Data->ReportID)
+ {
+ if (Data->IsButtonData)
+ {
+ numUsages = Data->ButtonData.MaxUsageLength;
+
+ Data->Status = HidP_GetUsages (ReportType,
+ Data->UsagePage,
+ 0, // All collections
+ Data->ButtonData.Usages,
+ &numUsages,
+ Ppd,
+ ReportBuffer,
+ ReportBufferLength);
+
+
+ //
+ // Get usages writes the list of usages into the buffer
+ // Data->ButtonData.Usages newUsage is set to the number of usages
+ // written into this array.
+ // A usage cannot not be defined as zero, so we'll mark a zero
+ // following the list of usages to indicate the end of the list of
+ // usages
+ //
+ // NOTE: One anomaly of the GetUsages function is the lack of ability
+ // to distinguish the data for one ButtonCaps from another
+ // if two different caps structures have the same UsagePage
+ // For instance:
+ // Caps1 has UsagePage 07 and UsageRange of 0x00 - 0x167
+ // Caps2 has UsagePage 07 and UsageRange of 0xe0 - 0xe7
+ //
+ // However, calling GetUsages for each of the data structs
+ // will return the same list of usages. It is the
+ // responsibility of the caller to set in the HID_DEVICE
+ // structure which usages actually are valid for the
+ // that structure.
+ //
+
+ /*
+ // Search through the usage list and remove those that
+ // correspond to usages outside the define ranged for this
+ // data structure.
+ */
+
+ for (Index = 0, nextUsage = 0; Index < numUsages; Index++)
+ {
+ if (Data->ButtonData.UsageMin <= Data->ButtonData.Usages[Index] &&
+ Data -> ButtonData.Usages[Index] <= Data->ButtonData.UsageMax)
+ {
+ Data->ButtonData.Usages[nextUsage++] = Data->ButtonData.Usages[Index];
+
+ }
+ }
+
+ if (nextUsage < Data -> ButtonData.MaxUsageLength)
+ {
+ Data->ButtonData.Usages[nextUsage] = 0;
+ }
+ }
+ else
+ {
+ Data->Status = HidP_GetUsageValue (
+ ReportType,
+ Data->UsagePage,
+ 0, // All Collections.
+ Data->ValueData.Usage,
+ &Data->ValueData.Value,
+ Ppd,
+ ReportBuffer,
+ ReportBufferLength);
+
+ Data->Status = HidP_GetScaledUsageValue (
+ ReportType,
+ Data->UsagePage,
+ 0, // All Collections.
+ Data->ValueData.Usage,
+ &Data->ValueData.ScaledValue,
+ Ppd,
+ ReportBuffer,
+ ReportBufferLength);
+ }
+ Data->IsDataSet = TRUE;
+ }
+ }
+ return (TRUE);
+}
+
+
+BOOLEAN
+packReport (
+ PCHAR ReportBuffer,
+ USHORT ReportBufferLength,
+ HIDP_REPORT_TYPE ReportType,
+ t_hid_data *Data,
+ ULONG DataLength,
+ PHIDP_PREPARSED_DATA Ppd
+ )
+/*++
+Routine Description:
+ This routine takes in a list of HID_DATA structures (DATA) and builds
+ in ReportBuffer the given report for all data values in the list that
+ correspond to the report ID of the first item in the list.
+
+ For every data structure in the list that has the same report ID as the first
+ item in the list will be set in the report. Every data item that is
+ set will also have it's IsDataSet field marked with TRUE.
+
+ A return value of FALSE indicates an unexpected error occurred when setting
+ a given data value. The caller should expect that assume that no values
+ within the given data structure were set.
+
+ A return value of TRUE indicates that all data values for the given report
+ ID were set without error.
+--*/
+{
+ ULONG numUsages; // Number of usages to set for a given report.
+ ULONG i;
+ ULONG CurrReportID;
+
+ /*
+ // All report buffers that are initially sent need to be zero'd out
+ */
+
+ memset (ReportBuffer, (UCHAR) 0, ReportBufferLength);
+
+ /*
+ // Go through the data structures and set all the values that correspond to
+ // the CurrReportID which is obtained from the first data structure
+ // in the list
+ */
+
+ CurrReportID = Data -> ReportID;
+
+ for (i = 0; i < DataLength; i++, Data++)
+ {
+ /*
+ // There are two different ways to determine if we set the current data
+ // structure:
+ // 1) Store the report ID were using and only attempt to set those
+ // data structures that correspond to the given report ID. This
+ // example shows this implementation.
+ //
+ // 2) Attempt to set all of the data structures and look for the
+ // returned status value of HIDP_STATUS_INVALID_REPORT_ID. This
+ // error code indicates that the given usage exists but has a
+ // different report ID than the report ID in the current report
+ // buffer
+ */
+
+ if (Data -> ReportID == CurrReportID)
+ {
+ if (Data->IsButtonData)
+ {
+ numUsages = Data->ButtonData.MaxUsageLength;
+ Data->Status = HidP_SetUsages (ReportType,
+ Data->UsagePage,
+ 0, // All collections
+ Data->ButtonData.Usages,
+ &numUsages,
+ Ppd,
+ ReportBuffer,
+ ReportBufferLength);
+ }
+ else
+ {
+ Data->Status = HidP_SetUsageValue (ReportType,
+ Data->UsagePage,
+ 0, // All Collections.
+ Data->ValueData.Usage,
+ Data->ValueData.Value,
+ Ppd,
+ ReportBuffer,
+ ReportBufferLength);
+ }
+
+ if (HIDP_STATUS_SUCCESS != Data->Status)
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ /*
+ // At this point, all data structures that have the same ReportID as the
+ // first one will have been set in the given report. Time to loop
+ // through the structure again and mark all of those data structures as
+ // having been set.
+ */
+
+ for (i = 0; i < DataLength; i++, Data++)
+ {
+ if (CurrReportID == Data -> ReportID)
+ {
+ Data -> IsDataSet = TRUE;
+ }
+ }
+ return (TRUE);
+}
+