#ifdef _WINDOWS /* * Microsoft Windows DDK HID support for Pd/Max [hidio] object * * Copyright (c) 2006 Olaf Matthes. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * */ #include #include #include #include /* * Please note that this file needs the Microsoft Driver Developent Kit (DDK) * to be installed in order to compile! */ #ifdef _MSC_VER #include #else #include #endif /* _MSC_VER */ #include "hidio.h" //#define DEBUG(x) #define DEBUG(x) x #define debug_print(d, p) post(p) typedef struct _hid_device_ { HANDLE fh; /* file handle to the hid device */ OVERLAPPED overlapped; 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; unsigned long inputDataLength; // Num elements in this array. PHIDP_BUTTON_CAPS inputButtonCaps; PHIDP_VALUE_CAPS inputValueCaps; char *outputReportBuffer; unsigned long outputDataLength; PHIDP_BUTTON_CAPS outputButtonCaps; PHIDP_VALUE_CAPS outputValueCaps; char *featureReportBuffer; unsigned long featureDataLength; PHIDP_BUTTON_CAPS featureButtonCaps; PHIDP_VALUE_CAPS featureValueCaps; } t_hid_device; /*============================================================================== * GLOBAL VARS *======================================================================== */ extern t_int hidio_instance_count; // in hidio.h /* store device pointers so I don't have to query them all the time */ // t_hid_devinfo device_pointer[MAX_DEVICES]; /*============================================================================== * FUNCTION PROTOTYPES *============================================================================== */ /*============================================================================== * Event TYPE/CODE CONVERSION FUNCTIONS *============================================================================== */ /* ============================================================================== */ /* WINDOWS DDK HID SPECIFIC REALLY LOW-LEVEL STUFF */ /* ============================================================================== */ /* * connect to Ith USB device (count starting with 0) */ static 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); error("%s: failed to get specified device number", CLASS_NAME); 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); error("%s: failed to get device info", CLASS_NAME); return INVALID_HANDLE_VALUE; } /* 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, NULL); if (deviceHandle == INVALID_HANDLE_VALUE) { int err = GetLastError(); LPVOID lpMsgBuf; if (err == ERROR_ACCESS_DENIED) { /* error("%s: can not read from mouse and keyboard", CLASS_NAME); */ return INVALID_HANDLE_VALUE; } FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); error("%s: could not get device #%i: %s (%d)", CLASS_NAME, deviceIndex + 1, (LPCTSTR)lpMsgBuf, err); LocalFree(lpMsgBuf); } SetupDiDestroyDeviceInfoList (hardwareDeviceInfoSet); free (deviceDetail); return deviceHandle; } static long _hidio_read(t_hid_device *self) { unsigned long ret, err = 0; long bytes = 0; if (!ReadFile(self->fh, self->inputReportBuffer, self->caps.InputReportByteLength, &bytes, (LPOVERLAPPED)&self->overlapped)) { if (ERROR_IO_PENDING != (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); error("%s: read: %s", CLASS_NAME, (LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return -1; } } ret = WaitForSingleObject(self->overlapped.hEvent, 0); if (ret == WAIT_OBJECT_0) /* hey, we got signalled ! => data */ { return bytes; } else /* if no data, cancel read */ { if (!CancelIo(self->fh) && (self->fh != INVALID_HANDLE_VALUE)) return -1; /* CancelIo() failed */ } if (!ResetEvent(self->overlapped.hEvent)) return -1; /* ResetEvent() failed */ return bytes; } /* count devices by looking into the registry */ short _hid_count_devices(void) { short i, gNumDevices = 0; long ret; HKEY hKey; long DeviceNameLen, KeyNameLen; char KeyName[MAXPDSTRING]; char DeviceName[MAXPDSTRING]; /* Search in Windows Registry for enumerated HID devices */ if ((ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\hidusb\\Enum", 0, KEY_QUERY_VALUE, &hKey)) != ERROR_SUCCESS) { error("hidio: failed to get list of HID devices from registry"); return EXIT_FAILURE; } for (i = 0; i < MAX_DEVICES + 3; i++) /* there are three entries that are no devices */ { DeviceNameLen = 80; KeyNameLen = 100; ret = RegEnumValue(hKey, i, KeyName, &KeyNameLen, NULL, NULL, DeviceName, &DeviceNameLen); if (ret == ERROR_SUCCESS) { if (!strncmp(KeyName, "Count", 5)) { /* this is the number of devices as HEX DWORD */ continue; } else if (!strncmp(DeviceName, "USB\\Vid", 7)) { /* we found a device, DeviceName contains the path */ // post("device #%d: %s = %s", gNumDevices, KeyName, DeviceName); gNumDevices++; continue; } } else if (ret == ERROR_NO_MORE_ITEMS) /* no more entries in registry */ { break; } else /* any other error while looking into registry */ { char errbuf[MAXPDSTRING]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ret, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errbuf, MAXPDSTRING, NULL); error("hidio: $s", errbuf); break; } } RegCloseKey(hKey); return gNumDevices; /* return number of devices */ } /* get device path for a HID specified by number */ static short _hid_get_device_path(short device_number, char **path, short length) { GUID guid; HDEVINFO DeviceInfo; SP_DEVICE_INTERFACE_DATA DeviceInterface; PSP_INTERFACE_DEVICE_DETAIL_DATA DeviceDetail; unsigned long iSize; HidD_GetHidGuid(&guid); DeviceInfo = SetupDiGetClassDevs(&guid, NULL, NULL, (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); DeviceInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); if (!SetupDiEnumDeviceInterfaces(DeviceInfo, NULL, &guid, device_number, &DeviceInterface)) { SetupDiDestroyDeviceInfoList(DeviceInfo); return EXIT_FAILURE; } SetupDiGetDeviceInterfaceDetail( DeviceInfo, &DeviceInterface, NULL, 0, &iSize, 0 ); DeviceDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(iSize); DeviceDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA); if (SetupDiGetDeviceInterfaceDetail(DeviceInfo, &DeviceInterface, DeviceDetail, iSize, &iSize, NULL)) { if (!*path && !length) /* we got no memory passed in, allocate some */ { /* WARNING: caller has to free this memory!! */ *path = (char *)getbytes((short)(strlen(DeviceDetail->DevicePath) * sizeof(char))); } /* copy path */ strcpy(*path, DeviceDetail->DevicePath); } free(DeviceDetail); SetupDiDestroyDeviceInfoList(DeviceInfo); return 0; } /* get capabilities (usage page & usage ID) of an already opened device */ static short _hid_get_capabilities(HANDLE fd, HIDP_CAPS *capabilities) { PHIDP_PREPARSED_DATA preparsedData; if (fd == INVALID_HANDLE_VALUE) { error("hidio: couldn't get device capabilities due to an invalid handle"); return EXIT_FAILURE; } /* returns and allocates a buffer with device info */ HidD_GetPreparsedData(fd, &preparsedData); /* get capabilities of device from above buffer */ HidP_GetCaps(preparsedData, capabilities); /* no need for PreparsedData any more, so free the memory it's using */ HidD_FreePreparsedData(preparsedData); return 0; } /* ============================================================================== */ /* WINDOWS DDK HID SPECIFIC SUPPORT FUNCTIONS */ /* ============================================================================== */ short get_device_number_by_id(unsigned short vendor_id, unsigned short product_id) { return EXIT_FAILURE; } short get_device_number_from_usage(short device_number, unsigned short usage_page, unsigned short usage) { HANDLE fd = INVALID_HANDLE_VALUE; HIDP_CAPS capabilities; char path[MAX_PATH]; char *pp = (char *)path; short ret, i; short device_count = _hid_count_devices(); for (i = device_number; i < device_count; i++) { /* get path for specified device number */ ret = _hid_get_device_path(i, &pp, MAX_PATH); if (ret == -1) { return EXIT_FAILURE; } else { /* open file on the device (read & write, no overlapp) */ fd = CreateFile(path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, NULL); if (fd == INVALID_HANDLE_VALUE) { return EXIT_FAILURE; } /* get the capabilities */ _hid_get_capabilities(fd, &capabilities); /* check whether they match with what we want */ if (capabilities.UsagePage == usage_page && capabilities.Usage == usage) { CloseHandle(fd); return i; } CloseHandle(fd); } } return EXIT_FAILURE; } /* * This function is needed to translate the USB HID relative flag into the * [hidio]/linux style events */ static void convert_axis_to_symbols(t_hid_element *new_element, int array_index) { if (new_element->relative) { new_element->type = ps_relative; new_element->name = relative_symbols[array_index]; } else { new_element->type = ps_absolute; new_element->name = absolute_symbols[array_index]; } } static void get_usage_symbols(unsigned short usage_page, unsigned short usage, t_hid_element *new_element) { char buffer[MAXPDSTRING]; switch (usage_page) { case HID_USAGE_PAGE_GENERIC: switch (usage) { case HID_USAGE_GENERIC_X: convert_axis_to_symbols(new_element, 0); break; case HID_USAGE_GENERIC_Y: convert_axis_to_symbols(new_element, 1); break; case HID_USAGE_GENERIC_Z: convert_axis_to_symbols(new_element, 2); break; case HID_USAGE_GENERIC_RX: convert_axis_to_symbols(new_element, 3); break; case HID_USAGE_GENERIC_RY: convert_axis_to_symbols(new_element, 4); break; case HID_USAGE_GENERIC_RZ: convert_axis_to_symbols(new_element, 5); break; case HID_USAGE_GENERIC_SLIDER: convert_axis_to_symbols(new_element, 6); break; case HID_USAGE_GENERIC_DIAL: convert_axis_to_symbols(new_element, 7); break; case HID_USAGE_GENERIC_WHEEL: convert_axis_to_symbols(new_element, 8); break; case HID_USAGE_GENERIC_HATSWITCH: // TODO: this is still a mystery how to handle, due to USB HID vs. Linux input.h new_element->type = ps_absolute; new_element->name = absolute_symbols[9]; /* hatswitch */ break; default: new_element->type = gensym("DESKTOP"); snprintf(buffer, sizeof(buffer), "DESKTOP%ld", usage); new_element->name = gensym(buffer); } break; case HID_USAGE_PAGE_SIMULATION: switch (usage) { case HID_USAGE_SIMULATION_RUDDER: new_element->type = ps_absolute; new_element->name = absolute_symbols[5]; /* rz */ break; case HID_USAGE_SIMULATION_THROTTLE: new_element->type = ps_absolute; new_element->name = absolute_symbols[6]; /* slider */ break; default: new_element->type = gensym("SIMULATION"); snprintf(buffer, sizeof(buffer), "SIMULATION%ld", usage); new_element->name = gensym(buffer); } break; case HID_USAGE_PAGE_KEYBOARD: new_element->type = ps_key; if( (usage > -1) && (usage < KEY_ARRAY_MAX) ) new_element->name = key_symbols[usage]; else /* PowerBook ADB keyboard reports 0xffffffff */ new_element->name = key_symbols[0]; break; case HID_USAGE_PAGE_BUTTON: new_element->type = ps_button; new_element->name = button_symbols[usage]; break; case HID_USAGE_PAGE_LED: new_element->type = ps_led; new_element->name = led_symbols[usage]; break; case HID_USAGE_PAGE_DIGITIZER: /* no sure whether this is the right for PID in OS X */ new_element->type = ps_pid; new_element->name = pid_symbols[usage]; break; default: /* the rest are "vendor defined" so no translation table is possible */ snprintf(buffer, sizeof(buffer), "0x%04x", (unsigned int) usage_page); new_element->type = gensym(buffer); snprintf(buffer, sizeof(buffer), "0x%04x", (unsigned int) usage); new_element->name = gensym(buffer); } } static void hidio_build_element_list(t_hidio *x) { t_hid_device *self = (t_hid_device *)x->x_hid_device; t_hid_element *new_element = NULL; unsigned short numButtons = 0; unsigned short numValues = 0; unsigned short numelem; /* total number of elements */ unsigned short numCaps; unsigned long i; PHIDP_BUTTON_CAPS buttonCaps; PHIDP_VALUE_CAPS valueCaps; USAGE usage; element_count[x->x_device_number] = 0; if (self->fh != INVALID_HANDLE_VALUE) { /* now get device capabilities */ HidD_GetPreparsedData(self->fh, &self->ppd); HidP_GetCaps(self->ppd, &self->caps); /* allocate memory for input and output reports */ /* these buffers get used for sending and receiving */ self->inputReportBuffer = (char *)getbytes((short)(self->caps.InputReportByteLength * sizeof(char))); self->outputReportBuffer = (char *)getbytes((short)(self->caps.OutputReportByteLength * sizeof(char))); self->featureReportBuffer = (char *)getbytes((short)(self->caps.FeatureReportByteLength * sizeof(char))); /* allocate memory for input info */ self->inputButtonCaps = buttonCaps = (PHIDP_BUTTON_CAPS)getbytes((short)(self->caps.NumberInputButtonCaps * sizeof(HIDP_BUTTON_CAPS))); self->inputValueCaps = valueCaps = (PHIDP_VALUE_CAPS)getbytes((short)(self->caps.NumberInputValueCaps * sizeof(HIDP_VALUE_CAPS))); /* get capapbilities for buttons and values from device */ numCaps = self->caps.NumberInputButtonCaps; HidP_GetButtonCaps (HidP_Input, buttonCaps, &numCaps, self->ppd); numCaps = self->caps.NumberInputValueCaps; HidP_GetValueCaps (HidP_Input, valueCaps, &numCaps, self->ppd); /* number of elements is number of values (axes) plus number of buttons */ #if 1 for (i = 0; i < self->caps.NumberInputValueCaps; i++, valueCaps++) { if (valueCaps->IsRange) numValues += valueCaps->Range.UsageMax - valueCaps->Range.UsageMin + 1; else numValues++; } for (i = 0; i < self->caps.NumberInputButtonCaps; i++, buttonCaps++) { if (buttonCaps->IsRange) numButtons += buttonCaps->Range.UsageMax - buttonCaps->Range.UsageMin + 1; else numButtons++; } numelem = numValues + numButtons; valueCaps = self->inputValueCaps; buttonCaps = self->inputButtonCaps; #else /* maybe this could be done as well? works with my gamepad at least */ numelem = HidP_MaxDataListLength(HidP_Input, self->ppd); + HidP_MaxUsageListLength(HidP_Input, 0, self->ppd); #endif /* now look through the reported capabilities of the devcie and fill in the elements struct */ for (i = 0; i < self->caps.NumberInputButtonCaps; i++, buttonCaps++) { if (buttonCaps->IsRange) { for (usage = buttonCaps->Range.UsageMin; usage <= buttonCaps->Range.UsageMax; usage++) { new_element = getbytes(sizeof(t_hid_element)); new_element->usage_page = buttonCaps->UsagePage; new_element->usage_id = usage; new_element->relative = !buttonCaps->IsAbsolute; /* buttons always are absolute, no? */ new_element->min = 0; new_element->max = 1; new_element->instance = 0; get_usage_symbols(new_element->usage_page, new_element->usage_id, new_element); #ifdef PD SETSYMBOL(new_element->output_message, new_element->name); SETFLOAT(new_element->output_message + 1, new_element->instance); #else /* Max */ atom_setsym(new_element->output_message, new_element->name); atom_setlong(new_element->output_message + 1, (long)new_element->instance); #endif /* PD */ element[x->x_device_number][element_count[x->x_device_number]] = new_element; ++element_count[x->x_device_number]; } } else { new_element = getbytes(sizeof(t_hid_element)); new_element->usage_page = buttonCaps->UsagePage; new_element->usage_id = buttonCaps->NotRange.Usage; new_element->relative = !buttonCaps->IsAbsolute; /* buttons always are absolute, no? */ new_element->min = 0; new_element->max = 1; new_element->instance = 0; get_usage_symbols(new_element->usage_page, new_element->usage_id, new_element); #ifdef PD SETSYMBOL(new_element->output_message, new_element->name); SETFLOAT(new_element->output_message + 1, new_element->instance); #else /* Max */ atom_setsym(new_element->output_message, new_element->name); atom_setlong(new_element->output_message + 1, (long)new_element->instance); #endif /* PD */ element[x->x_device_number][element_count[x->x_device_number]] = new_element; ++element_count[x->x_device_number]; } } /* get value data */ for (i = 0; i < numValues; i++, valueCaps++) { if (valueCaps->IsRange) { for (usage = valueCaps->Range.UsageMin; usage <= valueCaps->Range.UsageMax; usage++) { new_element = getbytes(sizeof(t_hid_element)); new_element->usage_page = valueCaps->UsagePage; new_element->usage_id = usage; new_element->relative = !valueCaps->IsAbsolute; new_element->min = valueCaps->LogicalMin; new_element->max = valueCaps->LogicalMax; new_element->instance = 0; get_usage_symbols(new_element->usage_page, new_element->usage_id, new_element); #ifdef PD SETSYMBOL(new_element->output_message, new_element->name); SETFLOAT(new_element->output_message + 1, new_element->instance); #else /* Max */ atom_setsym(new_element->output_message, new_element->name); atom_setlong(new_element->output_message + 1, (long)new_element->instance); #endif /* PD */ element[x->x_device_number][element_count[x->x_device_number]] = new_element; ++element_count[x->x_device_number]; } } else { new_element = getbytes(sizeof(t_hid_element)); new_element->usage_page = valueCaps->UsagePage; new_element->usage_id = valueCaps->NotRange.Usage; new_element->relative = !valueCaps->IsAbsolute; new_element->min = valueCaps->LogicalMin; new_element->max = valueCaps->LogicalMax; new_element->instance = 0; get_usage_symbols(new_element->usage_page, new_element->usage_id, new_element); #ifdef PD SETSYMBOL(new_element->output_message, new_element->name); SETFLOAT(new_element->output_message + 1, new_element->instance); #else /* Max */ atom_setsym(new_element->output_message, new_element->name); atom_setlong(new_element->output_message + 1, (long)new_element->instance); #endif /* PD */ element[x->x_device_number][element_count[x->x_device_number]] = new_element; ++element_count[x->x_device_number]; } } } } t_int hidio_print_element_list(t_hidio *x) { t_hid_device *self = (t_hid_device *)x->x_hid_device; t_hid_element *current_element; int i; debug_print(LOG_DEBUG,"hidio_print_element_list"); post("[hidio] found %d elements:", element_count[x->x_device_number]); post("\n TYPE\tCODE\t#\tEVENT NAME"); post("-----------------------------------------------------------"); for (i = 0; i < element_count[x->x_device_number]; i++) { current_element = element[x->x_device_number][i]; post(" %s\t%s\t%d", current_element->type->s_name, current_element->name->s_name,(int) current_element->instance); } post(""); return EXIT_SUCCESS; } t_int hidio_print_device_list(t_hidio *x) { t_hid_device *self = (t_hid_device *)x->x_hid_device; struct _GUID GUID; SP_INTERFACE_DEVICE_DATA DeviceInterfaceData; struct { DWORD cbSize; char DevicePath[MAX_PATH]; } FunctionClassDeviceData; HIDD_ATTRIBUTES HIDAttributes; SECURITY_ATTRIBUTES SecurityAttributes; int i; HANDLE PnPHandle, HIDHandle; ULONG BytesReturned; int Success, ManufacturerName, ProductName; PWCHAR widestring[MAXPDSTRING]; char ManufacturerBuffer[MAXPDSTRING]; char ProductBuffer[MAXPDSTRING]; const char NotSupplied[] = "NULL"; DWORD lastError = 0; /* Initialize the GUID array and setup the security attributes for Win2000 */ HidD_GetHidGuid(&GUID); SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); SecurityAttributes.lpSecurityDescriptor = NULL; SecurityAttributes.bInheritHandle = FALSE; /* Get a handle for the Plug and Play node and request currently active devices */ PnPHandle = SetupDiGetClassDevs(&GUID, NULL, NULL, DIGCF_PRESENT|DIGCF_INTERFACEDEVICE); if ((int)PnPHandle == -1) { error("[hidio] ERROR: Could not attach to PnP node"); return (t_int) GetLastError(); } post(""); /* Lets look for a maximum of 32 Devices */ for (i = 0; i < MAX_DEVICES; i++) { /* Initialize our data */ DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData); /* Is there a device at this table entry */ Success = SetupDiEnumDeviceInterfaces(PnPHandle, NULL, &GUID, i, &DeviceInterfaceData); if (Success) { /* There is a device here, get it's name */ FunctionClassDeviceData.cbSize = 5; Success = SetupDiGetDeviceInterfaceDetail(PnPHandle, &DeviceInterfaceData, (PSP_INTERFACE_DEVICE_DETAIL_DATA)&FunctionClassDeviceData, 256, &BytesReturned, NULL); if (!Success) { error("[hidio] ERROR: Could not find the system name for device %d",i); return GetLastError(); } /* Can now open this device */ HIDHandle = CreateFile(FunctionClassDeviceData.DevicePath, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, &SecurityAttributes, OPEN_EXISTING, 0, NULL); lastError = GetLastError(); if (HIDHandle == INVALID_HANDLE_VALUE) { error("[hidio] ERROR: Could not open HID #%d, Errorcode = %d", i, (int)lastError); return lastError; } /* Get the information about this HID */ Success = HidD_GetAttributes(HIDHandle, &HIDAttributes); if (!Success) { error("[hidio] ERROR: Could not get HID attributes"); return GetLastError(); } ManufacturerName = HidD_GetManufacturerString(HIDHandle, widestring, MAXPDSTRING); wcstombs(ManufacturerBuffer, (const unsigned short *)widestring, MAXPDSTRING); ProductName = HidD_GetProductString(HIDHandle, widestring, MAXPDSTRING); wcstombs(ProductBuffer, (const unsigned short *)widestring, MAXPDSTRING); /* And display it! */ post("__________________________________________________"); post("Device %d: '%s' '%s' version %d", i, ManufacturerName ? ManufacturerBuffer : NotSupplied, ProductName ? ProductBuffer : NotSupplied, HIDAttributes.VersionNumber); post(" vendorID: 0x%04x productID: 0x%04x", HIDAttributes.VendorID, HIDAttributes.ProductID); CloseHandle(HIDHandle); } // if (SetupDiEnumDeviceInterfaces . . } // for (i = 0; i < 32; i++) SetupDiDestroyDeviceInfoList(PnPHandle); post(""); return EXIT_SUCCESS; } void hidio_output_device_name(t_hidio *x, char *manufacturer, char *product) { t_hid_device *self = (t_hid_device *)x->x_hid_device; char *device_name; t_symbol *device_name_symbol; device_name = malloc( strlen(manufacturer) + 1 + strlen(product) + 1 ); // device_name = malloc( 7 + strlen(manufacturer) + 1 + strlen(product) + 1 ); // strcpy( device_name, "append " ); strcat( device_name, manufacturer ); strcat ( device_name, " "); strcat( device_name, product ); // outlet_anything( x->x_status_outlet, gensym( device_name ),0,NULL ); #ifdef PD outlet_symbol( x->x_status_outlet, gensym( device_name ) ); #else outlet_anything( x->x_status_outlet, gensym( device_name ),0,NULL ); #endif } /* ------------------------------------------------------------------------------ */ /* FORCE FEEDBACK FUNCTIONS */ /* ------------------------------------------------------------------------------ */ /* cross-platform force feedback functions */ t_int hidio_ff_autocenter( t_hidio *x, t_float value ) { t_hid_device *self = (t_hid_device *)x->x_hid_device; return EXIT_SUCCESS; } t_int hidio_ff_gain( t_hidio *x, t_float value ) { t_hid_device *self = (t_hid_device *)x->x_hid_device; return EXIT_SUCCESS; } t_int hidio_ff_motors( t_hidio *x, t_float value ) { t_hid_device *self = (t_hid_device *)x->x_hid_device; return EXIT_SUCCESS; } t_int hidio_ff_continue( t_hidio *x ) { t_hid_device *self = (t_hid_device *)x->x_hid_device; return EXIT_SUCCESS; } t_int hidio_ff_pause( t_hidio *x ) { t_hid_device *self = (t_hid_device *)x->x_hid_device; return EXIT_SUCCESS; } t_int hidio_ff_reset( t_hidio *x ) { t_hid_device *self = (t_hid_device *)x->x_hid_device; return EXIT_SUCCESS; } t_int hidio_ff_stopall( t_hidio *x ) { t_hid_device *self = (t_hid_device *)x->x_hid_device; return EXIT_SUCCESS; } // these are just for testing... t_int hidio_ff_fftest ( t_hidio *x, t_float value) { t_hid_device *self = (t_hid_device *)x->x_hid_device; return EXIT_SUCCESS; } void hidio_ff_print( t_hidio *x ) { t_hid_device *self = (t_hid_device *)x->x_hid_device; } /* ============================================================================== */ /* Pd [hidio] FUNCTIONS */ /* ============================================================================== */ void hidio_platform_specific_info(t_hidio *x) { t_hid_device *self = (t_hid_device *)x->x_hid_device; //debug_print(LOG_DEBUG,"hidio_platform_specific_info"); } void hidio_get_events(t_hidio *x) { t_hid_device *self = (t_hid_device *)x->x_hid_device; t_hid_element *current_element = NULL; long bytesRead; debug_print(LOG_DEBUG,"hidio_get_events"); while (_hidio_read(self) > 0) { unsigned long i; unsigned long size, length; unsigned short *usages; for (i = 0; i < element_count[x->x_device_number]; i++) { current_element = element[x->x_device_number][i]; /* first try getting value data */ if (HIDP_STATUS_SUCCESS == HidP_GetUsageValue(HidP_Input, current_element[i].usage_page, 0, current_element[i].usage_id, ¤t_element[i].value, self->ppd, self->inputReportBuffer, self->caps.InputReportByteLength)) { continue; } /* now try getting scaled value data */ if (HIDP_STATUS_SUCCESS == HidP_GetScaledUsageValue(HidP_Input, current_element[i].usage_page, 0, current_element[i].usage_id, ¤t_element[i].value, self->ppd, self->inputReportBuffer, self->caps.InputReportByteLength)) { continue; } /* ask Windows how many usages we might expect at max. */ length = size = HidP_MaxUsageListLength(HidP_Input, current_element[i].usage_page, self->ppd); if (size) { /* uh, can't I alloc this memory in advance? but we might have several button usages on different usage pages, so I'd have to figure out the largest one */ usages = (unsigned short *)getbytes((short)(size * sizeof(unsigned short))); /* finally try getting button data */ if (HIDP_STATUS_SUCCESS == HidP_GetUsages(HidP_Input, current_element[i].usage_page, 0, usages, &length, self->ppd, self->inputReportBuffer, self->caps.InputReportByteLength)) { unsigned long j; current_element[i].value = 0; for (j = 0; j < length, usages[j] != 0; j++) { if (current_element[i].usage_id == usages[j]) { current_element[i].value = 1; break; } } } freebytes(usages, (short)(size * sizeof(unsigned short))); } } } } t_int hidio_open_device(t_hidio *x, short device_number) { t_hid_device *self = (t_hid_device *)x->x_hid_device; if (device_number >= 0) { // open new device self->fh = connectDeviceNumber(device_number); if (self->fh != INVALID_HANDLE_VALUE) { hidio_build_element_list(x); /* prepare overlapped structure */ self->overlapped.Offset = 0; self->overlapped.OffsetHigh = 0; self->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, ""); return EXIT_SUCCESS; } else return EXIT_FAILURE; } return EXIT_SUCCESS; } t_int hidio_close_device(t_hidio *x) { t_hid_device *self = (t_hid_device *)x->x_hid_device; debug_print(LOG_DEBUG, "hidio_close_device"); if (x->x_device_number > -1) { if (self->fh != INVALID_HANDLE_VALUE) { unsigned long i; CloseHandle(self->fh); self->fh = INVALID_HANDLE_VALUE; /* free element list */ for (i = 0; i < element_count[x->x_device_number]; i++); freebytes(element[x->x_device_number][i], sizeof(t_hid_element)); element_count[x->x_device_number] = 0; /* free allocated memory */ if (self->inputButtonCaps) freebytes(self->inputButtonCaps, (short)(self->caps.NumberInputButtonCaps * sizeof(HIDP_BUTTON_CAPS))); if (self->inputValueCaps) freebytes(self->inputValueCaps, (short)(self->caps.NumberInputValueCaps * sizeof(HIDP_VALUE_CAPS))); /* free report buffers */ if (self->inputReportBuffer) freebytes(self->inputReportBuffer, (short)(self->caps.InputReportByteLength * sizeof(char))); if (self->outputReportBuffer) freebytes(self->outputReportBuffer, (short)(self->caps.OutputReportByteLength * sizeof(char))); if (self->featureReportBuffer) freebytes(self->featureReportBuffer, (short)(self->caps.FeatureReportByteLength * sizeof(char))); /* free preparsed data */ if (self->ppd) HidD_FreePreparsedData(self->ppd); } } return EXIT_SUCCESS; } void hidio_build_device_list(void) { debug_print(LOG_DEBUG,"hidio_build_device_list"); } void hidio_print(t_hidio *x) { t_hid_device *self = (t_hid_device *)x->x_hid_device; hidio_print_device_list(x); if (x->x_device_open) { hidio_print_element_list(x); hidio_ff_print( x ); } } void hidio_platform_specific_free(t_hidio *x) { t_hid_device *self = (t_hid_device *)x->x_hid_device; debug_print(LOG_DEBUG,"hidio_platform_specific_free"); if (self) freebytes(self, sizeof(t_hid_device)); } void *hidio_platform_specific_new(t_hidio *x) { t_hid_device *self; debug_print(LOG_DEBUG,"hidio_platform_specific_new"); /* alloc memory for our instance */ self = (t_hid_device *)getbytes(sizeof(t_hid_device)); self->fh = INVALID_HANDLE_VALUE; return (void *)self; /* return void pointer to our data struct */ } #endif /* _WINDOWS */