From 246b7b648787d8aa5747044f4365143ae4b7da46 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sat, 3 Jun 2006 13:31:03 +0000 Subject: 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 --- hidin/hidin.c | 747 +++++++++++++++++++++++++++++++++++++++++++++++++++++ hidin/hidin.h | 142 +++++++++++ hidin/winNT_usb.c | 752 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1641 insertions(+) create mode 100644 hidin/hidin.c create mode 100644 hidin/hidin.h create mode 100644 hidin/winNT_usb.c 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 +#include +#include +#include +#include +#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) (int)' */ + 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 !"); + } + } + 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 +#include +#include +#include +#include +#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 + + 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 +#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); +} + -- cgit v1.2.1