This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DLPC350: DLPC350 USB connection question

Part Number: DLPC350

Hello, E2E support.

I have question about the USB connection for DLPC350 device.

Development Platform: Visual Studio 2010
OS: Windows 10

The software will crash sometime if program run to function of hid_close(), maybe the g_DeviceHandle is NULL or invalid value.
The function is normal in Windows 7.
Is the problem of priority thread in windows 10?
How can I solved it and prevention the software crash again?
Thank you!

int USB_Close()
{
    if(g_DeviceHandle!=NULL)
    {
        hid_close(g_DeviceHandle);
        USBConnected = false;
        g_DeviceHandle = NULL;
    }
    return 0;
}

void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
{
	if (!dev)
		return;
		
    CancelIo(dev->device_handle);
	CloseHandle(dev->ol.hEvent);
	CloseHandle(dev->device_handle);
	LocalFree(dev->last_error_str);
	free(dev->read_buf);
	free(dev);
}

usb.cpp

/*******************************************************
 HIDAPI - Multi-Platform library for
 communication with HID devices.

 Alan Ott
 Signal 11 Software

 8/22/2009

 Copyright 2009, All Rights Reserved.
 
 At the discretion of the user of this library,
 this software may be licensed under the terms of the
 GNU Public License v3, a BSD-Style license, or the
 original HIDAPI license as outlined in the LICENSE.txt,
 LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
 files located at the root of the source distribution.
 These files may also be found in the public source
 code repository located at:
        http://github.com/signal11/hidapi .
********************************************************/

#include <windows.h>

#ifndef _NTDEF_
typedef LONG NTSTATUS;
#endif

#ifdef __MINGW32__
#include <ntdef.h>
#include <winbase.h>
#endif

#ifdef __CYGWIN__
#include <ntdef.h>
#define _wcsdup wcsdup
#endif

/*#define HIDAPI_USE_DDK*/

#ifdef __cplusplus
extern "C" {
#endif
	#include <setupapi.h>
	#include <winioctl.h>
	#ifdef HIDAPI_USE_DDK
		#include <hidsdi.h>
	#endif

	/* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */
	#define HID_OUT_CTL_CODE(id)  \
		CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
	#define IOCTL_HID_GET_FEATURE                   HID_OUT_CTL_CODE(100)

#ifdef __cplusplus
} /* extern "C" */
#endif

#include <stdio.h>
#include <stdlib.h>


#include "hidapi.h"

#ifdef _MSC_VER
	/* Thanks Microsoft, but I know how to use strncpy(). */
	#pragma warning(disable:4996)
#endif

#ifdef __cplusplus
extern "C" {
#endif

#ifndef HIDAPI_USE_DDK
	/* Since we're not building with the DDK, and the HID header
	   files aren't part of the SDK, we have to define all this
	   stuff here. In lookup_functions(), the function pointers
	   defined below are set. */
	typedef struct _HIDD_ATTRIBUTES{
		ULONG Size;
		USHORT VendorID;
		USHORT ProductID;
		USHORT VersionNumber;
	} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;

	typedef USHORT USAGE;
	typedef struct _HIDP_CAPS {
		USAGE Usage;
		USAGE UsagePage;
		USHORT InputReportByteLength;
		USHORT OutputReportByteLength;
		USHORT FeatureReportByteLength;
		USHORT Reserved[17];
		USHORT fields_not_used_by_hidapi[10];
	} HIDP_CAPS, *PHIDP_CAPS;
	typedef void* PHIDP_PREPARSED_DATA;
	#define HIDP_STATUS_SUCCESS 0x110000

	typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
	typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
	typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
	typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
	typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
	typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
	typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
	typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
	typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
	typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps);

	static HidD_GetAttributes_ HidD_GetAttributes;
	static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;
	static HidD_GetManufacturerString_ HidD_GetManufacturerString;
	static HidD_GetProductString_ HidD_GetProductString;
	static HidD_SetFeature_ HidD_SetFeature;
	static HidD_GetFeature_ HidD_GetFeature;
	static HidD_GetIndexedString_ HidD_GetIndexedString;
	static HidD_GetPreparsedData_ HidD_GetPreparsedData;
	static HidD_FreePreparsedData_ HidD_FreePreparsedData;
	static HidP_GetCaps_ HidP_GetCaps;

	static HMODULE lib_handle = NULL;
	static BOOLEAN initialized = FALSE;
#endif /* HIDAPI_USE_DDK */

struct hid_device_ {
		HANDLE device_handle;
		BOOL blocking;
		USHORT output_report_length;
		size_t input_report_length;
		void *last_error_str;
		DWORD last_error_num;
		BOOL read_pending;
		char *read_buf;
		OVERLAPPED ol;
};

static hid_device *new_hid_device()
{
	hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
	dev->device_handle = INVALID_HANDLE_VALUE;
	dev->blocking = TRUE;
	dev->output_report_length = 0;
	dev->input_report_length = 0;
	dev->last_error_str = NULL;
	dev->last_error_num = 0;
	dev->read_pending = FALSE;
	dev->read_buf = NULL;
	memset(&dev->ol, 0, sizeof(dev->ol));
	dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL);

	return dev;
}


static void register_error(hid_device *device, const char *op)
{
	WCHAR *ptr, *msg;

	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPVOID)&msg, 0/*sz*/,
		NULL);
	
	/* Get rid of the CR and LF that FormatMessage() sticks at the
	   end of the message. Thanks Microsoft! */
	ptr = msg;
	while (*ptr) {
		if (*ptr == '\r') {
			*ptr = 0x0000;
			break;
		}
		ptr++;
	}

	/* Store the message off in the Device entry so that
	   the hid_error() function can pick it up. */
	LocalFree(device->last_error_str);
	device->last_error_str = msg;
}

#ifndef HIDAPI_USE_DDK
static int lookup_functions()
{
	lib_handle = LoadLibraryA("hid.dll");
	if (lib_handle) {
#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
		RESOLVE(HidD_GetAttributes);
		RESOLVE(HidD_GetSerialNumberString);
		RESOLVE(HidD_GetManufacturerString);
		RESOLVE(HidD_GetProductString);
		RESOLVE(HidD_SetFeature);
		RESOLVE(HidD_GetFeature);
		RESOLVE(HidD_GetIndexedString);
		RESOLVE(HidD_GetPreparsedData);
		RESOLVE(HidD_FreePreparsedData);
		RESOLVE(HidP_GetCaps);
#undef RESOLVE
	}
	else
		return -1;

	return 0;
}
#endif

static HANDLE open_device(const char *path, BOOL enumerate)
{
	HANDLE handle;
	DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
	DWORD share_mode = (enumerate)?
	                      FILE_SHARE_READ|FILE_SHARE_WRITE:
	                      FILE_SHARE_READ;

	handle = CreateFileA(path,
		desired_access,
		share_mode,
		NULL,
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
		0);

	return handle;
}

int HID_API_EXPORT hid_init(void)
{
#ifndef HIDAPI_USE_DDK
	if (!initialized) {
		if (lookup_functions() < 0) {
			hid_exit();
			return -1;
		}
		initialized = TRUE;
	}
#endif
	return 0;
}

int HID_API_EXPORT hid_exit(void)
{
#ifndef HIDAPI_USE_DDK
	if (lib_handle)
		FreeLibrary(lib_handle);
	lib_handle = NULL;
	initialized = FALSE;
#endif
	return 0;
}

struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
{
	BOOL res;
	struct hid_device_info *root = NULL; /* return object */
	struct hid_device_info *cur_dev = NULL;

	/* Windows objects for interacting with the driver. */
	GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
	SP_DEVINFO_DATA devinfo_data;
	SP_DEVICE_INTERFACE_DATA device_interface_data;
	SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
	HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
	int device_index = 0;
	int i;

	if (hid_init() < 0)
		return NULL;

	/* Initialize the Windows objects. */
	memset(&devinfo_data, 0x0, sizeof(devinfo_data));
	devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
	device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

	/* Get information for all the devices belonging to the HID class. */
	device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
	
	/* Iterate over each device in the HID class, looking for the right one. */
	
	for (;;) {
		HANDLE write_handle = INVALID_HANDLE_VALUE;
		DWORD required_size = 0;
		HIDD_ATTRIBUTES attrib;

		res = SetupDiEnumDeviceInterfaces(device_info_set,
			NULL,
			&InterfaceClassGuid,
			device_index,
			&device_interface_data);
		
		if (!res) {
			/* A return of FALSE from this function means that
			   there are no more devices. */
			break;
		}

		/* Call with 0-sized detail size, and let the function
		   tell us how long the detail struct needs to be. The
		   size is put in &required_size. */
		res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
			&device_interface_data,
			NULL,
			0,
			&required_size,
			NULL);

		/* Allocate a long enough structure for device_interface_detail_data. */
		device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size);
		device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);

		/* Get the detailed data for this device. The detail data gives us
		   the device path for this device, which is then passed into
		   CreateFile() to get a handle to the device. */
		res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
			&device_interface_data,
			device_interface_detail_data,
			required_size,
			NULL,
			NULL);

		if (!res) {
			/* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail");
			   Continue to the next device. */
			goto cont;
		}

		/* Make sure this device is of Setup Class "HIDClass" and has a
		   driver bound to it. */
		for (i = 0; ; i++) {
			char driver_name[256];

			/* Populate devinfo_data. This function will return failure
			   when there are no more interfaces left. */
			res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
			if (!res)
				goto cont;

			res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
			               SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
			if (!res)
				goto cont;

			if (strcmp(driver_name, "HIDClass") == 0) {
				/* See if there's a driver bound. */
				res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
				           SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
				if (res)
					break;
			}
		}

		//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);

		/* Open a handle to the device */
		write_handle = open_device(device_interface_detail_data->DevicePath, TRUE);

		/* Check validity of write_handle. */
		if (write_handle == INVALID_HANDLE_VALUE) {
			/* Unable to open the device. */
			//register_error(dev, "CreateFile");
			goto cont_close;
		}		


		/* Get the Vendor ID and Product ID for this device. */
		attrib.Size = sizeof(HIDD_ATTRIBUTES);
		HidD_GetAttributes(write_handle, &attrib);
		//wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);

		/* Check the VID/PID to see if we should add this
		   device to the enumeration list. */
		if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
		    (product_id == 0x0 || attrib.ProductID == product_id)) {

			#define WSTR_LEN 512
			const char *str;
			struct hid_device_info *tmp;
			PHIDP_PREPARSED_DATA pp_data = NULL;
			HIDP_CAPS caps;
			BOOLEAN res;
			NTSTATUS nt_res;
			wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
			size_t len;

			/* VID/PID match. Create the record. */
			tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
			if (cur_dev) {
				cur_dev->next = tmp;
			}
			else {
				root = tmp;
			}
			cur_dev = tmp;

			/* Get the Usage Page and Usage for this device. */
			res = HidD_GetPreparsedData(write_handle, &pp_data);
			if (res) {
				nt_res = HidP_GetCaps(pp_data, &caps);
				if (nt_res == HIDP_STATUS_SUCCESS) {
					cur_dev->usage_page = caps.UsagePage;
					cur_dev->usage = caps.Usage;
				}

				HidD_FreePreparsedData(pp_data);
			}
			
			/* Fill out the record */
			cur_dev->next = NULL;
			str = device_interface_detail_data->DevicePath;
			if (str) {
				len = strlen(str);
				cur_dev->path = (char*) calloc(len+1, sizeof(char));
				strncpy(cur_dev->path, str, len+1);
				cur_dev->path[len] = '\0';
			}
			else
				cur_dev->path = NULL;

			/* Serial Number */
			res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr));
			wstr[WSTR_LEN-1] = 0x0000;
			if (res) {
				cur_dev->serial_number = _wcsdup(wstr);
			}

			/* Manufacturer String */
			res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr));
			wstr[WSTR_LEN-1] = 0x0000;
			if (res) {
				cur_dev->manufacturer_string = _wcsdup(wstr);
			}

			/* Product String */
			res = HidD_GetProductString(write_handle, wstr, sizeof(wstr));
			wstr[WSTR_LEN-1] = 0x0000;
			if (res) {
				cur_dev->product_string = _wcsdup(wstr);
			}

			/* VID/PID */
			cur_dev->vendor_id = attrib.VendorID;
			cur_dev->product_id = attrib.ProductID;

			/* Release Number */
			cur_dev->release_number = attrib.VersionNumber;

			/* Interface Number. It can sometimes be parsed out of the path
			   on Windows if a device has multiple interfaces. See
			   http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
			   search for "Hardware IDs for HID Devices" at MSDN. If it's not
			   in the path, it's set to -1. */
			cur_dev->interface_number = -1;
			if (cur_dev->path) {
				char *interface_component = strstr(cur_dev->path, "&mi_");
				if (interface_component) {
					char *hex_str = interface_component + 4;
					char *endptr = NULL;
					cur_dev->interface_number = strtol(hex_str, &endptr, 16);
					if (endptr == hex_str) {
						/* The parsing failed. Set interface_number to -1. */
						cur_dev->interface_number = -1;
					}
				}
			}
		}

cont_close:
		CloseHandle(write_handle);
cont:
		/* We no longer need the detail data. It can be freed */
		free(device_interface_detail_data);

		device_index++;

	}

	/* Close the device information handle. */
	SetupDiDestroyDeviceInfoList(device_info_set);

	return root;

}

void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
{
	/* TODO: Merge this with the Linux version. This function is platform-independent. */
	struct hid_device_info *d = devs;
	while (d) {
		struct hid_device_info *next = d->next;
		free(d->path);
		free(d->serial_number);
		free(d->manufacturer_string);
		free(d->product_string);
		free(d);
		d = next;
	}
}


HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
{
	/* TODO: Merge this functions with the Linux version. This function should be platform independent. */
	struct hid_device_info *devs, *cur_dev;
	const char *path_to_open = NULL;
	hid_device *handle = NULL;
	
	devs = hid_enumerate(vendor_id, product_id);
	cur_dev = devs;
	while (cur_dev) {
		if (cur_dev->vendor_id == vendor_id &&
		    cur_dev->product_id == product_id) {
			if (serial_number) {
				if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
					path_to_open = cur_dev->path;
					break;
				}
			}
			else {
				path_to_open = cur_dev->path;
				break;
			}
		}
		cur_dev = cur_dev->next;
	}

	if (path_to_open) {
		/* Open the device */
		handle = hid_open_path(path_to_open);
	}

	hid_free_enumeration(devs);
	
	return handle;
}

HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
{
	hid_device *dev;
	HIDP_CAPS caps;
	PHIDP_PREPARSED_DATA pp_data = NULL;
	BOOLEAN res;
	NTSTATUS nt_res;

	if (hid_init() < 0) {
		return NULL;
	}

	dev = new_hid_device();

	/* Open a handle to the device */
	dev->device_handle = open_device(path, FALSE);

	/* Check validity of write_handle. */
	if (dev->device_handle == INVALID_HANDLE_VALUE) {
		/* Unable to open the device. */
		register_error(dev, "CreateFile");
		goto err;
	}

	/* Get the Input Report length for the device. */
	res = HidD_GetPreparsedData(dev->device_handle, &pp_data);
	if (!res) {
		register_error(dev, "HidD_GetPreparsedData");
		goto err;
	}
	nt_res = HidP_GetCaps(pp_data, &caps);
	if (nt_res != HIDP_STATUS_SUCCESS) {
		register_error(dev, "HidP_GetCaps");	
		goto err_pp_data;
	}
	dev->output_report_length = caps.OutputReportByteLength;
	dev->input_report_length = caps.InputReportByteLength;
	HidD_FreePreparsedData(pp_data);

	dev->read_buf = (char*) malloc(dev->input_report_length);

	return dev;

err_pp_data:
		HidD_FreePreparsedData(pp_data);
err:	
		CloseHandle(dev->device_handle);
		free(dev);
		return NULL;
}

int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
{
	DWORD bytes_written;
	BOOL res;

	OVERLAPPED ol;
	unsigned char *buf;
	memset(&ol, 0, sizeof(ol));

	/* Make sure the right number of bytes are passed to WriteFile. Windows
	   expects the number of bytes which are in the _longest_ report (plus
	   one for the report number) bytes even if the data is a report
	   which is shorter than that. Windows gives us this value in
	   caps.OutputReportByteLength. If a user passes in fewer bytes than this,
	   create a temporary buffer which is the proper size. */
	if (length >= dev->output_report_length) {
		/* The user passed the right number of bytes. Use the buffer as-is. */
		buf = (unsigned char *) data;
	} else {
		/* Create a temporary buffer and copy the user's data
		   into it, padding the rest with zeros. */
		buf = (unsigned char *) malloc(dev->output_report_length);
		memcpy(buf, data, length);
		memset(buf + length, 0, dev->output_report_length - length);
		length = dev->output_report_length;
	}

	res = WriteFile(dev->device_handle, buf, length, NULL, &ol);
	
	if (!res) {
		if (GetLastError() != ERROR_IO_PENDING) {
			/* WriteFile() failed. Return error. */
			register_error(dev, "WriteFile");
			bytes_written = -1;
			goto end_of_function;
		}
	}

	/* Wait here until the write is done. This makes
	   hid_write() synchronous. */
	res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/);
	if (!res) {
		/* The Write operation failed. */
		register_error(dev, "WriteFile");
		bytes_written = -1;
		goto end_of_function;
	}

end_of_function:
	if (buf != data)
		free(buf);

	return bytes_written;
}


int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
{
	DWORD bytes_read = 0;
	BOOL res;

	/* Copy the handle for convenience. */
	HANDLE ev = dev->ol.hEvent;

	if (!dev->read_pending) {
		/* Start an Overlapped I/O read. */
		dev->read_pending = TRUE;
		memset(dev->read_buf, 0, dev->input_report_length);
		ResetEvent(ev);
		res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol);
		
		if (!res) {
			if (GetLastError() != ERROR_IO_PENDING) {
				/* ReadFile() has failed.
				   Clean up and return error. */
				CancelIo(dev->device_handle);
				dev->read_pending = FALSE;
				goto end_of_function;
			}
		}
	}

	if (milliseconds >= 0) {
		/* See if there is any data yet. */
		res = WaitForSingleObject(ev, milliseconds);
		if (res != WAIT_OBJECT_0) {
			/* There was no data this time. Return zero bytes available,
			   but leave the Overlapped I/O running. */
			return 0;
		}
	}

	/* Either WaitForSingleObject() told us that ReadFile has completed, or
	   we are in non-blocking mode. Get the number of bytes read. The actual
	   data has been copied to the data[] array which was passed to ReadFile(). */
	res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
	
	/* Set pending back to false, even if GetOverlappedResult() returned error. */
	dev->read_pending = FALSE;

	if (res && bytes_read > 0) {
		if (dev->read_buf[0] == 0x0) {
			/* If report numbers aren't being used, but Windows sticks a report
			   number (0x0) on the beginning of the report anyway. To make this
			   work like the other platforms, and to make it work more like the
			   HID spec, we'll skip over this byte. */
			size_t copy_len;
			bytes_read--;
			copy_len = length > bytes_read ? bytes_read : length;
			memcpy(data, dev->read_buf+1, copy_len);
		}
		else {
			/* Copy the whole buffer, report number and all. */
			size_t copy_len = length > bytes_read ? bytes_read : length;
			memcpy(data, dev->read_buf, copy_len);
		}
	}
	
end_of_function:
	if (!res) {
		register_error(dev, "GetOverlappedResult");
		return -1;
	}
	
	return bytes_read;
}

int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
{
	return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
}

int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
{
	dev->blocking = !nonblock;
	return 0; /* Success */
}

int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
{
	BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length);
	if (!res) {
		register_error(dev, "HidD_SetFeature");
		return -1;
	}

	return length;
}


int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
{
	BOOL res;
#if 0
	res = HidD_GetFeature(dev->device_handle, data, length);
	if (!res) {
		register_error(dev, "HidD_GetFeature");
		return -1;
	}
	return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */
#else
	DWORD bytes_returned;

	OVERLAPPED ol;
	memset(&ol, 0, sizeof(ol));

	res = DeviceIoControl(dev->device_handle,
		IOCTL_HID_GET_FEATURE,
		data, length,
		data, length,
		&bytes_returned, &ol);

	if (!res) {
		if (GetLastError() != ERROR_IO_PENDING) {
			/* DeviceIoControl() failed. Return error. */
			register_error(dev, "Send Feature Report DeviceIoControl");
			return -1;
		}
	}

	/* Wait here until the write is done. This makes
	   hid_get_feature_report() synchronous. */
	res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);
	if (!res) {
		/* The operation failed. */
		register_error(dev, "Send Feature Report GetOverLappedResult");
		return -1;
	}
	return bytes_returned;
#endif
}

void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
{
	if (!dev)
		return;
	CancelIo(dev->device_handle);
	CloseHandle(dev->ol.hEvent);
	CloseHandle(dev->device_handle);
	LocalFree(dev->last_error_str);
	free(dev->read_buf);
	free(dev);
}

int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
	BOOL res;

	res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
	if (!res) {
		register_error(dev, "HidD_GetManufacturerString");
		return -1;
	}

	return 0;
}

int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
	BOOL res;

	res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
	if (!res) {
		register_error(dev, "HidD_GetProductString");
		return -1;
	}

	return 0;
}

int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
	BOOL res;

	res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
	if (!res) {
		register_error(dev, "HidD_GetSerialNumberString");
		return -1;
	}

	return 0;
}

int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
{
	BOOL res;

	res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * maxlen);
	if (!res) {
		register_error(dev, "HidD_GetIndexedString");
		return -1;
	}

	return 0;
}


HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev)
{
	return (wchar_t*)dev->last_error_str;
}


/*#define PICPGM*/
/*#define S11*/
#define P32
#ifdef S11 
  unsigned short VendorID = 0xa0a0;
	unsigned short ProductID = 0x0001;
#endif

#ifdef P32
  unsigned short VendorID = 0x04d8;
	unsigned short ProductID = 0x3f;
#endif


#ifdef PICPGM
  unsigned short VendorID = 0x04d8;
  unsigned short ProductID = 0x0033;
#endif


#if 0
int __cdecl main(int argc, char* argv[])
{
	int res;
	unsigned char buf[65];

	UNREFERENCED_PARAMETER(argc);
	UNREFERENCED_PARAMETER(argv);

	/* Set up the command buffer. */
	memset(buf,0x00,sizeof(buf));
	buf[0] = 0;
	buf[1] = 0x81;
	

	/* Open the device. */
	int handle = open(VendorID, ProductID, L"12345");
	if (handle < 0)
		printf("unable to open device\n");


	/* Toggle LED (cmd 0x80) */
	buf[1] = 0x80;
	res = write(handle, buf, 65);
	if (res < 0)
		printf("Unable to write()\n");

	/* Request state (cmd 0x81) */
	buf[1] = 0x81;
	write(handle, buf, 65);
	if (res < 0)
		printf("Unable to write() (2)\n");

	/* Read requested state */
	read(handle, buf, 65);
	if (res < 0)
		printf("Unable to read()\n");

	/* Print out the returned buffer. */
	for (int i = 0; i < 4; i++)
		printf("buf[%d]: %d\n", i, buf[i]);

	return 0;
}
#endif

#ifdef __cplusplus
} /* extern "C" */
#endif

Best Regards,

Frank

  • Hello Frank,

    Welcome to DLP forum and thank you for your interest in DLP technology. 

    I will consult my colleagues and get back to you on this issue.

    regards,

    Vivek

  • Hello Vivek,

    Have any update information for this issue? thanks!

    Best Regards,

    Frank

  • Hi Frank,

    We are still looking into this and will respond when we have an update soon.

    Thanks

    Chris

  • Hi Frank,

    Could you please provide more info like which Win32 function caused the crash? 

    regards,

    Vivek

  • Hi Vivek,

    Below is function of hid_close().

    void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
    {
    	if (!dev)
    		return;
    	CancelIo(dev->device_handle);
    	CloseHandle(dev->ol.hEvent);
    	CloseHandle(dev->device_handle);    //Crash here
    	LocalFree(dev->last_error_str);
    	free(dev->read_buf);
    	free(dev);
    }

    CloseHandle() is Win32 function like below.

    WINBASEAPI
    BOOL
    WINAPI
    CloseHandle(
        __in HANDLE hObject
        );

    WinBase.h

    I using 4 devices of  DLPC350 and than will repeat close and open USB devices.In this case will crash in function CloseHandle() sometime. Hope this info can help you to clarity, thanks.

    Best Regards,

    Frank

  • Hi Frank, 

    Thank you. I will get back in couple of days.

    regards,

    Vivek

  • Hi Vivek,

    Have any good news for this issue? Thanks.

    Best Regards,

    Frank

  • Frank,

    I've spent a little time looking at this, but still haven't come to a conclusion. My biggest concern right now is that hid_close(...) checks if hid_device pointer is not NULL, but doesn't validate that the members of `dev` are valid. For example, `dev->device_handle` and `dev->ol.hEvent` are passed into CancelIo() and CloseHandle() without validating that they're not NULL and not INVALID_HANDLE_VALUE. `dev-read_buf` and `dev->last_error_str` are also nullable, so I'd add checks for those as well.

    Can you add those checks and let me know if you still encounter the crashes?

    Regards,

    Andrew

  • Hi Andrew,

    Added checks for dev->device_handle、dev->ol.hEvent、dev->last_error_str and dev->read_buf, but program still crash sometime.

    void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
    {
        if (!dev)
            return;

        if (dev->device_handle != INVALID_HANDLE_VALUE)
        {
            CancelIo(dev->device_handle);
            Sleep(1000);
            CloseHandle(dev->device_handle);
        }

        if(dev->ol.hEvent != NULL)
            CloseHandle(dev->ol.hEvent);

        if(dev->last_error_str != NULL)
            LocalFree(dev->last_error_str);

        if(dev->read_buf != NULL)
            free(dev->read_buf);

        free(dev);
    }

    The windows will save dump files if program crash, The dump files data info as attached. 

    PROCESS_NAME:  TRAOI.exe
    
    ERROR_CODE: (NTSTATUS) 0xc0000374 -    n w l   C
    
    EXCEPTION_CODE_STR:  c0000374
    
    EXCEPTION_PARAMETER1:  00007ffd751827f0
    
    ADDITIONAL_DEBUG_TEXT:  Followup set based on attribute [Heap_Error_Type] from Frame:[0] on thread:[PSEUDO_THREAD] ; Followup set based on attribute [Is_ChosenCrashFollowupThread] from Frame:[0] on thread:[PSEUDO_THREAD]
    
    FAULTING_THREAD:  ffffffff
    
    STACK_TEXT:  
    00000000`00000000 00000000`00000000 hidapi!unknown_function+0x0
    
    
    SYMBOL_NAME:  hidapi!unknown_function
    
    MODULE_NAME: hidapi
    
    IMAGE_NAME:  hidapi.dll
    
    STACK_COMMAND:  !heap ; ** Pseudo Context ** ManagedPseudo ** Value: 16bbdd33c90 ** ; kb
    
    FAILURE_BUCKET_ID:  HEAP_CORRUPTION_ACTIONABLE_BlockNotBusy_DOUBLE_FREE_c0000374_hidapi.dll!unknown_function
    
    OS_VERSION:  10.0.17763.1
    
    BUILDLAB_STR:  rs5_release
    
    OSPLATFORM_TYPE:  x64
    
    OSNAME:  Windows 10
    
    FAILURE_ID_HASH:  {f80cf53b-d6fc-b832-6628-fe756af5190a}
    
    Followup:     MachineOwner
    ---------
    
    0:000> lmvm hidapi
    Browse full module list
    start             end                 module name
    00007ffd`664f0000 00007ffd`664f8000   hidapi   T (no symbols)           
        Loaded symbol image file: hidapi.dll
        Image path: c:\AOI\hidapi.dll
        Image name: hidapi.dll
        Browse all global symbols  functions  data
        Timestamp:        Wed Aug 18 20:00:39 2021 (611CF667)
        CheckSum:         00008C94
        ImageSize:        00008000
        Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
        Information from resource tables:
    
    **************************************************************
    *                                                            *
    *                  HEAP ERROR DETECTED                       *
    *                                                            *
    **************************************************************
    
    Details:
    
    Heap address:  00000000014a0000
    Error address: 000000001fe99ca0
    Error type: HEAP_FAILURE_BLOCK_NOT_BUSY
    Details:    The caller performed an operation (such as a free
                or a size check) that is illegal on a free block.
    Follow-up:  Check the error's stack trace to find the culprit.
    
    
    Stack trace:
    Stack trace at 0x00007ffd75182848
        00007ffd750bef41: ntdll!RtlpLogHeapFailure+0x45
        00007ffd750ce520: ntdll!RtlFreeHeap+0x966e0
        00007ffd710f82bf: KERNELBASE!LocalFree+0x2f
        00007ffd664f1071: hidapi+0x1071
        00007ffd664f1b50: hidapi+0x1b50
        00007ffd5a0b1842: DLPControl+0x11842
        00007ffd5a0a44fb: DLPControl+0x44fb
        00007ffd5a0afac4: DLPControl+0xfac4
        00007ffd5a0b00d8: DLPControl+0x100d8
        00007ffd54e377f4: LightDll+0x77f4
        00007ffd54e3bcd0: LightDll+0xbcd0
        00007ffd54e57aee: LightDll+0x27aee
        00007ff6a9cf5413: TRAOI+0xab5413
        00007ff6a9cf5f6c: TRAOI+0xab5f6c
        00007ff6aa2526c8: TRAOI+0x10126c8
        00007ff6aa25360c: TRAOI+0x101360c
    
    

    IMAGE_NAME:  hidapi.dll

    OSNAME:  Windows 10

    OS_VERSION:  10.0.17763.1

    I found the stack trace crash on LogHeapFailure, maybe that is illegal on a free block.

    Stack trace at 0x00007ffd75182848
        00007ffd750bef41: ntdll!RtlpLogHeapFailure+0x45
        00007ffd750ce520: ntdll!RtlFreeHeap+0x966e0
        00007ffd710f82bf: KERNELBASE!LocalFree+0x2f
        00007ffd664f1071: hidapi+0x1071
        00007ffd664f1b50: hidapi+0x1b50

    I provide files of hidapi.h、hid.c、usb.h and usb. cpp for you reference. 

    hidapi.h

    /*******************************************************
     HIDAPI - Multi-Platform library for
     communication with HID devices.
    
     Alan Ott
     Signal 11 Software
    
     8/22/2009
    
     Copyright 2009, All Rights Reserved.
     
     At the discretion of the user of this library,
     this software may be licensed under the terms of the
     GNU Public License v3, a BSD-Style license, or the
     original HIDAPI license as outlined in the LICENSE.txt,
     LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
     files located at the root of the source distribution.
     These files may also be found in the public source
     code repository located at:
            http://github.com/signal11/hidapi .
    ********************************************************/
    
    #include <windows.h>
    
    #ifndef _NTDEF_
    typedef LONG NTSTATUS;
    #endif
    
    #ifdef __MINGW32__
    #include <ntdef.h>
    #include <winbase.h>
    #endif
    
    #ifdef __CYGWIN__
    #include <ntdef.h>
    #define _wcsdup wcsdup
    #endif
    
    /*#define HIDAPI_USE_DDK*/
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    	#include <setupapi.h>
    	#include <winioctl.h>
    	#ifdef HIDAPI_USE_DDK
    		#include <hidsdi.h>
    	#endif
    
    	/* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */
    	#define HID_OUT_CTL_CODE(id)  \
    		CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
    	#define IOCTL_HID_GET_FEATURE                   HID_OUT_CTL_CODE(100)
    
    #ifdef __cplusplus
    } /* extern "C" */
    #endif
    
    #include <stdio.h>
    #include <stdlib.h>
    
    
    #include "hidapi.h"
    
    #ifdef _MSC_VER
    	/* Thanks Microsoft, but I know how to use strncpy(). */
    	#pragma warning(disable:4996)
    #endif
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #ifndef HIDAPI_USE_DDK
    	/* Since we're not building with the DDK, and the HID header
    	   files aren't part of the SDK, we have to define all this
    	   stuff here. In lookup_functions(), the function pointers
    	   defined below are set. */
    	typedef struct _HIDD_ATTRIBUTES{
    		ULONG Size;
    		USHORT VendorID;
    		USHORT ProductID;
    		USHORT VersionNumber;
    	} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
    
    	typedef USHORT USAGE;
    	typedef struct _HIDP_CAPS {
    		USAGE Usage;
    		USAGE UsagePage;
    		USHORT InputReportByteLength;
    		USHORT OutputReportByteLength;
    		USHORT FeatureReportByteLength;
    		USHORT Reserved[17];
    		USHORT fields_not_used_by_hidapi[10];
    	} HIDP_CAPS, *PHIDP_CAPS;
    	typedef void* PHIDP_PREPARSED_DATA;
    	#define HIDP_STATUS_SUCCESS 0x110000
    
    	typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
    	typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
    	typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
    	typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
    	typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
    	typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
    	typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
    	typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
    	typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
    	typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps);
    
    	static HidD_GetAttributes_ HidD_GetAttributes;
    	static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;
    	static HidD_GetManufacturerString_ HidD_GetManufacturerString;
    	static HidD_GetProductString_ HidD_GetProductString;
    	static HidD_SetFeature_ HidD_SetFeature;
    	static HidD_GetFeature_ HidD_GetFeature;
    	static HidD_GetIndexedString_ HidD_GetIndexedString;
    	static HidD_GetPreparsedData_ HidD_GetPreparsedData;
    	static HidD_FreePreparsedData_ HidD_FreePreparsedData;
    	static HidP_GetCaps_ HidP_GetCaps;
    
    	static HMODULE lib_handle = NULL;
    	static BOOLEAN initialized = FALSE;
    #endif /* HIDAPI_USE_DDK */
    
    struct hid_device_ {
    		HANDLE device_handle;
    		BOOL blocking;
    		USHORT output_report_length;
    		size_t input_report_length;
    		void *last_error_str;
    		DWORD last_error_num;
    		BOOL read_pending;
    		char *read_buf;
    		OVERLAPPED ol;
    };
    
    static hid_device *new_hid_device()
    {
    	hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
    	dev->device_handle = INVALID_HANDLE_VALUE;
    	dev->blocking = TRUE;
    	dev->output_report_length = 0;
    	dev->input_report_length = 0;
    	dev->last_error_str = NULL;
    	dev->last_error_num = 0;
    	dev->read_pending = FALSE;
    	dev->read_buf = NULL;
    	memset(&dev->ol, 0, sizeof(dev->ol));
    	dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL);
    
    	return dev;
    }
    
    
    static void register_error(hid_device *device, const char *op)
    {
    	WCHAR *ptr, *msg;
    
    	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
    		FORMAT_MESSAGE_FROM_SYSTEM |
    		FORMAT_MESSAGE_IGNORE_INSERTS,
    		NULL,
    		GetLastError(),
    		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    		(LPVOID)&msg, 0/*sz*/,
    		NULL);
    	
    	/* Get rid of the CR and LF that FormatMessage() sticks at the
    	   end of the message. Thanks Microsoft! */
    	ptr = msg;
    	while (*ptr) {
    		if (*ptr == '\r') {
    			*ptr = 0x0000;
    			break;
    		}
    		ptr++;
    	}
    
    	/* Store the message off in the Device entry so that
    	   the hid_error() function can pick it up. */
    	LocalFree(device->last_error_str);
    	device->last_error_str = msg;
    }
    
    #ifndef HIDAPI_USE_DDK
    static int lookup_functions()
    {
    	lib_handle = LoadLibraryA("hid.dll");
    	if (lib_handle) {
    #define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
    		RESOLVE(HidD_GetAttributes);
    		RESOLVE(HidD_GetSerialNumberString);
    		RESOLVE(HidD_GetManufacturerString);
    		RESOLVE(HidD_GetProductString);
    		RESOLVE(HidD_SetFeature);
    		RESOLVE(HidD_GetFeature);
    		RESOLVE(HidD_GetIndexedString);
    		RESOLVE(HidD_GetPreparsedData);
    		RESOLVE(HidD_FreePreparsedData);
    		RESOLVE(HidP_GetCaps);
    #undef RESOLVE
    	}
    	else
    		return -1;
    
    	return 0;
    }
    #endif
    
    static HANDLE open_device(const char *path, BOOL enumerate)
    {
    	HANDLE handle;
    	DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
    	DWORD share_mode = (enumerate)?
    	                      FILE_SHARE_READ|FILE_SHARE_WRITE:
    	                      FILE_SHARE_READ;
    
    	handle = CreateFileA(path,
    		desired_access,
    		share_mode,
    		NULL,
    		OPEN_EXISTING,
    		FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
    		0);
    
    	return handle;
    }
    
    int HID_API_EXPORT hid_init(void)
    {
    #ifndef HIDAPI_USE_DDK
    	if (!initialized) {
    		if (lookup_functions() < 0) {
    			hid_exit();
    			return -1;
    		}
    		initialized = TRUE;
    	}
    #endif
    	return 0;
    }
    
    int HID_API_EXPORT hid_exit(void)
    {
    #ifndef HIDAPI_USE_DDK
    	if (lib_handle)
    		FreeLibrary(lib_handle);
    	lib_handle = NULL;
    	initialized = FALSE;
    #endif
    	return 0;
    }
    
    struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
    {
    	BOOL res;
    	struct hid_device_info *root = NULL; /* return object */
    	struct hid_device_info *cur_dev = NULL;
    
    	/* Windows objects for interacting with the driver. */
    	GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
    	SP_DEVINFO_DATA devinfo_data;
    	SP_DEVICE_INTERFACE_DATA device_interface_data;
    	SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
    	HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
    	int device_index = 0;
    	int i;
    
    	if (hid_init() < 0)
    		return NULL;
    
    	/* Initialize the Windows objects. */
    	memset(&devinfo_data, 0x0, sizeof(devinfo_data));
    	devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
    	device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
    
    	/* Get information for all the devices belonging to the HID class. */
    	device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    	
    	/* Iterate over each device in the HID class, looking for the right one. */
    	
    	for (;;) {
    		HANDLE write_handle = INVALID_HANDLE_VALUE;
    		DWORD required_size = 0;
    		HIDD_ATTRIBUTES attrib;
    
    		res = SetupDiEnumDeviceInterfaces(device_info_set,
    			NULL,
    			&InterfaceClassGuid,
    			device_index,
    			&device_interface_data);
    		
    		if (!res) {
    			/* A return of FALSE from this function means that
    			   there are no more devices. */
    			break;
    		}
    
    		/* Call with 0-sized detail size, and let the function
    		   tell us how long the detail struct needs to be. The
    		   size is put in &required_size. */
    		res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
    			&device_interface_data,
    			NULL,
    			0,
    			&required_size,
    			NULL);
    
    		/* Allocate a long enough structure for device_interface_detail_data. */
    		device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size);
    		device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
    
    		/* Get the detailed data for this device. The detail data gives us
    		   the device path for this device, which is then passed into
    		   CreateFile() to get a handle to the device. */
    		res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
    			&device_interface_data,
    			device_interface_detail_data,
    			required_size,
    			NULL,
    			NULL);
    
    		if (!res) {
    			/* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail");
    			   Continue to the next device. */
    			goto cont;
    		}
    
    		/* Make sure this device is of Setup Class "HIDClass" and has a
    		   driver bound to it. */
    		for (i = 0; ; i++) {
    			char driver_name[256];
    
    			/* Populate devinfo_data. This function will return failure
    			   when there are no more interfaces left. */
    			res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
    			if (!res)
    				goto cont;
    
    			res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
    			               SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
    			if (!res)
    				goto cont;
    
    			if (strcmp(driver_name, "HIDClass") == 0) {
    				/* See if there's a driver bound. */
    				res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
    				           SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
    				if (res)
    					break;
    			}
    		}
    
    		//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
    
    		/* Open a handle to the device */
    		write_handle = open_device(device_interface_detail_data->DevicePath, TRUE);
    
    		/* Check validity of write_handle. */
    		if (write_handle == INVALID_HANDLE_VALUE) {
    			/* Unable to open the device. */
    			//register_error(dev, "CreateFile");
    			goto cont_close;
    		}		
    
    
    		/* Get the Vendor ID and Product ID for this device. */
    		attrib.Size = sizeof(HIDD_ATTRIBUTES);
    		HidD_GetAttributes(write_handle, &attrib);
    		//wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);
    
    		/* Check the VID/PID to see if we should add this
    		   device to the enumeration list. */
    		if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
    		    (product_id == 0x0 || attrib.ProductID == product_id)) {
    
    			#define WSTR_LEN 512
    			const char *str;
    			struct hid_device_info *tmp;
    			PHIDP_PREPARSED_DATA pp_data = NULL;
    			HIDP_CAPS caps;
    			BOOLEAN res;
    			NTSTATUS nt_res;
    			wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
    			size_t len;
    
    			/* VID/PID match. Create the record. */
    			tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
    			if (cur_dev) {
    				cur_dev->next = tmp;
    			}
    			else {
    				root = tmp;
    			}
    			cur_dev = tmp;
    
    			/* Get the Usage Page and Usage for this device. */
    			res = HidD_GetPreparsedData(write_handle, &pp_data);
    			if (res) {
    				nt_res = HidP_GetCaps(pp_data, &caps);
    				if (nt_res == HIDP_STATUS_SUCCESS) {
    					cur_dev->usage_page = caps.UsagePage;
    					cur_dev->usage = caps.Usage;
    				}
    
    				HidD_FreePreparsedData(pp_data);
    			}
    			
    			/* Fill out the record */
    			cur_dev->next = NULL;
    			str = device_interface_detail_data->DevicePath;
    			if (str) {
    				len = strlen(str);
    				cur_dev->path = (char*) calloc(len+1, sizeof(char));
    				strncpy(cur_dev->path, str, len+1);
    				cur_dev->path[len] = '\0';
    			}
    			else
    				cur_dev->path = NULL;
    
    			/* Serial Number */
    			res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr));
    			wstr[WSTR_LEN-1] = 0x0000;
    			if (res) {
    				cur_dev->serial_number = _wcsdup(wstr);
    			}
    
    			/* Manufacturer String */
    			res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr));
    			wstr[WSTR_LEN-1] = 0x0000;
    			if (res) {
    				cur_dev->manufacturer_string = _wcsdup(wstr);
    			}
    
    			/* Product String */
    			res = HidD_GetProductString(write_handle, wstr, sizeof(wstr));
    			wstr[WSTR_LEN-1] = 0x0000;
    			if (res) {
    				cur_dev->product_string = _wcsdup(wstr);
    			}
    
    			/* VID/PID */
    			cur_dev->vendor_id = attrib.VendorID;
    			cur_dev->product_id = attrib.ProductID;
    
    			/* Release Number */
    			cur_dev->release_number = attrib.VersionNumber;
    
    			/* Interface Number. It can sometimes be parsed out of the path
    			   on Windows if a device has multiple interfaces. See
    			   http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
    			   search for "Hardware IDs for HID Devices" at MSDN. If it's not
    			   in the path, it's set to -1. */
    			cur_dev->interface_number = -1;
    			if (cur_dev->path) {
    				char *interface_component = strstr(cur_dev->path, "&mi_");
    				if (interface_component) {
    					char *hex_str = interface_component + 4;
    					char *endptr = NULL;
    					cur_dev->interface_number = strtol(hex_str, &endptr, 16);
    					if (endptr == hex_str) {
    						/* The parsing failed. Set interface_number to -1. */
    						cur_dev->interface_number = -1;
    					}
    				}
    			}
    		}
    
    cont_close:
    		CloseHandle(write_handle);
    cont:
    		/* We no longer need the detail data. It can be freed */
    		free(device_interface_detail_data);
    
    		device_index++;
    
    	}
    
    	/* Close the device information handle. */
    	SetupDiDestroyDeviceInfoList(device_info_set);
    
    	return root;
    
    }
    
    void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
    {
    	/* TODO: Merge this with the Linux version. This function is platform-independent. */
    	struct hid_device_info *d = devs;
    	while (d) {
    		struct hid_device_info *next = d->next;
    		free(d->path);
    		free(d->serial_number);
    		free(d->manufacturer_string);
    		free(d->product_string);
    		free(d);
    		d = next;
    	}
    }
    
    
    HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
    {
    	/* TODO: Merge this functions with the Linux version. This function should be platform independent. */
    	struct hid_device_info *devs, *cur_dev;
    	const char *path_to_open = NULL;
    	hid_device *handle = NULL;
    	
    	devs = hid_enumerate(vendor_id, product_id);
    	cur_dev = devs;
    	while (cur_dev) {
    		if (cur_dev->vendor_id == vendor_id &&
    		    cur_dev->product_id == product_id) {
    			if (serial_number) {
    				if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
    					path_to_open = cur_dev->path;
    					break;
    				}
    			}
    			else {
    				path_to_open = cur_dev->path;
    				break;
    			}
    		}
    		cur_dev = cur_dev->next;
    	}
    
    	if (path_to_open) {
    		/* Open the device */
    		handle = hid_open_path(path_to_open);
    	}
    
    	hid_free_enumeration(devs);
    	
    	return handle;
    }
    
    HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
    {
    	hid_device *dev;
    	HIDP_CAPS caps;
    	PHIDP_PREPARSED_DATA pp_data = NULL;
    	BOOLEAN res;
    	NTSTATUS nt_res;
    
    	if (hid_init() < 0) {
    		return NULL;
    	}
    
    	dev = new_hid_device();
    
    	/* Open a handle to the device */
    	dev->device_handle = open_device(path, FALSE);
    
    	/* Check validity of write_handle. */
    	if (dev->device_handle == INVALID_HANDLE_VALUE) {
    		/* Unable to open the device. */
    		register_error(dev, "CreateFile");
    		goto err;
    	}
    
    	/* Get the Input Report length for the device. */
    	res = HidD_GetPreparsedData(dev->device_handle, &pp_data);
    	if (!res) {
    		register_error(dev, "HidD_GetPreparsedData");
    		goto err;
    	}
    	nt_res = HidP_GetCaps(pp_data, &caps);
    	if (nt_res != HIDP_STATUS_SUCCESS) {
    		register_error(dev, "HidP_GetCaps");	
    		goto err_pp_data;
    	}
    	dev->output_report_length = caps.OutputReportByteLength;
    	dev->input_report_length = caps.InputReportByteLength;
    	HidD_FreePreparsedData(pp_data);
    
    	dev->read_buf = (char*) malloc(dev->input_report_length);
    
    	return dev;
    
    err_pp_data:
    		HidD_FreePreparsedData(pp_data);
    err:	
    		CloseHandle(dev->device_handle);
    		free(dev);
    		return NULL;
    }
    
    int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
    {
    	DWORD bytes_written;
    	BOOL res;
    
    	OVERLAPPED ol;
    	unsigned char *buf;
    	memset(&ol, 0, sizeof(ol));
    
    	/* Make sure the right number of bytes are passed to WriteFile. Windows
    	   expects the number of bytes which are in the _longest_ report (plus
    	   one for the report number) bytes even if the data is a report
    	   which is shorter than that. Windows gives us this value in
    	   caps.OutputReportByteLength. If a user passes in fewer bytes than this,
    	   create a temporary buffer which is the proper size. */
    	if (length >= dev->output_report_length) {
    		/* The user passed the right number of bytes. Use the buffer as-is. */
    		buf = (unsigned char *) data;
    	} else {
    		/* Create a temporary buffer and copy the user's data
    		   into it, padding the rest with zeros. */
    		buf = (unsigned char *) malloc(dev->output_report_length);
    		memcpy(buf, data, length);
    		memset(buf + length, 0, dev->output_report_length - length);
    		length = dev->output_report_length;
    	}
    
    	res = WriteFile(dev->device_handle, buf, (DWORD)length, NULL, &ol);
    	
    	if (!res) {
    		if (GetLastError() != ERROR_IO_PENDING) {
    			/* WriteFile() failed. Return error. */
    			register_error(dev, "WriteFile");
    			bytes_written = -1;
    			goto end_of_function;
    		}
    	}
    
    	/* Wait here until the write is done. This makes
    	   hid_write() synchronous. */
    	res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/);
    	if (!res) {
    		/* The Write operation failed. */
    		register_error(dev, "WriteFile");
    		bytes_written = -1;
    		goto end_of_function;
    	}
    
    end_of_function:
    	if (buf != data)
    		free(buf);
    
    	return bytes_written;
    }
    
    
    int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
    {
    	DWORD bytes_read = 0;
    	BOOL res;
    
    	/* Copy the handle for convenience. */
    	HANDLE ev = dev->ol.hEvent;
    
    	if (!dev->read_pending) {
    		/* Start an Overlapped I/O read. */
    		dev->read_pending = TRUE;
    		memset(dev->read_buf, 0, dev->input_report_length);
    		ResetEvent(ev);
    		res = ReadFile(dev->device_handle, dev->read_buf, (DWORD)(dev->input_report_length), &bytes_read, &dev->ol);
    		
    		if (!res) {
    			if (GetLastError() != ERROR_IO_PENDING) {
    				/* ReadFile() has failed.
    				   Clean up and return error. */
    				CancelIo(dev->device_handle);
    				dev->read_pending = FALSE;
    				goto end_of_function;
    			}
    		}
    	}
    
    	if (milliseconds >= 0) {
    		/* See if there is any data yet. */
    		res = WaitForSingleObject(ev, milliseconds);
    		if (res != WAIT_OBJECT_0) {
    			/* There was no data this time. Return zero bytes available,
    			   but leave the Overlapped I/O running. */
    			return 0;
    		}
    	}
    
    	/* Either WaitForSingleObject() told us that ReadFile has completed, or
    	   we are in non-blocking mode. Get the number of bytes read. The actual
    	   data has been copied to the data[] array which was passed to ReadFile(). */
    	res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
    	
    	/* Set pending back to false, even if GetOverlappedResult() returned error. */
    	dev->read_pending = FALSE;
    
    	if (res && bytes_read > 0) {
    		if (dev->read_buf[0] == 0x0) {
    			/* If report numbers aren't being used, but Windows sticks a report
    			   number (0x0) on the beginning of the report anyway. To make this
    			   work like the other platforms, and to make it work more like the
    			   HID spec, we'll skip over this byte. */
    			size_t copy_len;
    			bytes_read--;
    			copy_len = length > bytes_read ? bytes_read : length;
    			memcpy(data, dev->read_buf+1, copy_len);
    		}
    		else {
    			/* Copy the whole buffer, report number and all. */
    			size_t copy_len = length > bytes_read ? bytes_read : length;
    			memcpy(data, dev->read_buf, copy_len);
    		}
    	}
    	
    end_of_function:
    	if (!res) {
    		register_error(dev, "GetOverlappedResult");
    		return -1;
    	}
    	
    	return bytes_read;
    }
    
    int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
    {
    	return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
    }
    
    int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
    {
    	dev->blocking = !nonblock;
    	return 0; /* Success */
    }
    
    int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
    {
    	BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, (ULONG)length);
    	if (!res) {
    		register_error(dev, "HidD_SetFeature");
    		return -1;
    	}
    
    	return (int)length;
    }
    
    
    int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
    {
    	BOOL res;
    #if 0
    	res = HidD_GetFeature(dev->device_handle, data, length);
    	if (!res) {
    		register_error(dev, "HidD_GetFeature");
    		return -1;
    	}
    	return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */
    #else
    	DWORD bytes_returned;
    
    	OVERLAPPED ol;
    	memset(&ol, 0, sizeof(ol));
    
    	res = DeviceIoControl(dev->device_handle,
    		IOCTL_HID_GET_FEATURE,
    		data, (DWORD)length,
    		data, (DWORD)length,
    		&bytes_returned, &ol);
    
    	if (!res) {
    		if (GetLastError() != ERROR_IO_PENDING) {
    			/* DeviceIoControl() failed. Return error. */
    			register_error(dev, "Send Feature Report DeviceIoControl");
    			return -1;
    		}
    	}
    
    	/* Wait here until the write is done. This makes
    	   hid_get_feature_report() synchronous. */
    	res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);
    	if (!res) {
    		/* The operation failed. */
    		register_error(dev, "Send Feature Report GetOverLappedResult");
    		return -1;
    	}
    	return bytes_returned;
    #endif
    }
    
    void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
    {//[Frank Yang @ 20210811] Device Handle�����ѼƤ���NULL�b����close
    	if (!dev)
    		return;
    
    	if (dev->device_handle != INVALID_HANDLE_VALUE)
    	{
    		CancelIo(dev->device_handle);
    		Sleep(1000);
    		CloseHandle(dev->device_handle);
    	}
    
    	if(dev->ol.hEvent != NULL)
    		CloseHandle(dev->ol.hEvent);
    
    	if(dev->last_error_str != NULL)
    		LocalFree(dev->last_error_str);
    
    	if(dev->read_buf != NULL)
    		free(dev->read_buf);
    
    	free(dev);
    }
    
    int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
    {
    	BOOL res;
    
    	res = HidD_GetManufacturerString(dev->device_handle, string, (ULONG)(sizeof(wchar_t) * maxlen));
    	if (!res) {
    		register_error(dev, "HidD_GetManufacturerString");
    		return -1;
    	}
    
    	return 0;
    }
    
    int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
    {
    	BOOL res;
    
    	res = HidD_GetProductString(dev->device_handle, string, (ULONG)(sizeof(wchar_t) * maxlen));
    	if (!res) {
    		register_error(dev, "HidD_GetProductString");
    		return -1;
    	}
    
    	return 0;
    }
    
    int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
    {
    	BOOL res;
    
    	res = HidD_GetSerialNumberString(dev->device_handle, string, (ULONG)(sizeof(wchar_t) * maxlen));
    	if (!res) {
    		register_error(dev, "HidD_GetSerialNumberString");
    		return -1;
    	}
    
    	return 0;
    }
    
    int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
    {
    	BOOL res;
    
    	res = HidD_GetIndexedString(dev->device_handle, string_index, string, (ULONG)(sizeof(wchar_t) * maxlen));
    	if (!res) {
    		register_error(dev, "HidD_GetIndexedString");
    		return -1;
    	}
    
    	return 0;
    }
    
    
    HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev)
    {
    	return (wchar_t*)dev->last_error_str;
    }
    
    
    /*#define PICPGM*/
    /*#define S11*/
    #define P32
    #ifdef S11 
      unsigned short VendorID = 0xa0a0;
    	unsigned short ProductID = 0x0001;
    #endif
    
    #ifdef P32
      unsigned short VendorID = 0x04d8;
    	unsigned short ProductID = 0x3f;
    #endif
    
    
    #ifdef PICPGM
      unsigned short VendorID = 0x04d8;
      unsigned short ProductID = 0x0033;
    #endif
    
    
    #if 0
    int __cdecl main(int argc, char* argv[])
    {
    	int res;
    	unsigned char buf[65];
    
    	UNREFERENCED_PARAMETER(argc);
    	UNREFERENCED_PARAMETER(argv);
    
    	/* Set up the command buffer. */
    	memset(buf,0x00,sizeof(buf));
    	buf[0] = 0;
    	buf[1] = 0x81;
    	
    
    	/* Open the device. */
    	int handle = open(VendorID, ProductID, L"12345");
    	if (handle < 0)
    		printf("unable to open device\n");
    
    
    	/* Toggle LED (cmd 0x80) */
    	buf[1] = 0x80;
    	res = write(handle, buf, 65);
    	if (res < 0)
    		printf("Unable to write()\n");
    
    	/* Request state (cmd 0x81) */
    	buf[1] = 0x81;
    	write(handle, buf, 65);
    	if (res < 0)
    		printf("Unable to write() (2)\n");
    
    	/* Read requested state */
    	read(handle, buf, 65);
    	if (res < 0)
    		printf("Unable to read()\n");
    
    	/* Print out the returned buffer. */
    	for (int i = 0; i < 4; i++)
    		printf("buf[%d]: %d\n", i, buf[i]);
    
    	return 0;
    }
    #endif
    
    #ifdef __cplusplus
    } /* extern "C" */
    #endif
    
    usb.h8015.usb.cpp

    Please help again, thanks.

    Best Regards,

    Frank

  • Hi Frank,

    Sorry for the delay we are still looking into this.

    Regards,

    Arthur

  • Frank,

    Thanks for the information! Per the stack trace, I looked for places where `hidapi.c` calls `LocalFree`. It looks like it's only used in the `register_error` function. That function calls `LocalFree(device->last_error_str)` without checking that `device->last_error_string` is not NULL. Can you try checking if `device->last_error_string` exists before freeing it and let me know the results?

    Thanks!

  • Hello, E2E support.

    Good news, Software crush issue already be fixed it, but I found another questions.

    The DLP devices will get wrong GPIO number some time.
    We defined MAX_GPIO = 8 and using 4 DLP devices,
    Question 1:  Is the one pin equal to one DLP device ?
    Question 2:  We set number are 1、2、16 and 32 for GPIO, but device will got 1、2、16 and 64, one device will connected fail.
    How can I fixed it? Please help again, thanks.

    Function code as bellow

    #define MAX_GPIO 8
    ...
    ...
    ...
    bool DLPControl::GetGPIONum(int* num)
    {
    	const int Pin[MAX_GPIO] = {13, 14, 15, 20, 21, 24, 25, 27};
    
    	bool enAltFunc, altFunc1, dirOutput, outTypeOpenDrain, state;
    
    	*num = 0;
    	for(int i =0; i<MAX_GPIO; i++)
    	{
    		if(DLPC350_GetGPIOConfig(Pin[i], &enAltFunc, &altFunc1, &dirOutput, &outTypeOpenDrain, &state)!=0)
    			return false;
    		if(state)
    			(*num) += 1<<i;
    	}
    	return true;
    }
    
    int DLPC350_GetGPIOConfig(unsigned int pinNum, bool *pEnAltFunc, bool *pAltFunc1, bool *pDirOutput, bool *pOutTypeOpenDrain, bool *pState)
    /**
     * (I2C: 0x44)
     * (USB: CMD2: 0x1A, CMD3: 0x38)
     *
     * This API reads back the GPIO configuration on a specific set of DLPC350 pins. The
     * command reads back their direction, output buffer type, and  state.
     *
     * @param   pinNum - I - GPIO selection. See Table 2-38 in the programmer's guide for description of available pins
     *
     * @param   *pEnAltFunc - O - 0=disable alternative function; enable GPIO; 1=enable alternative function; disable GPIO
     *
     * @param   *pAltFunc1 - O - must be set to false
     *
     * @param   *pDirOutput - O - 0=input; 1=output
     *
     * @param   *pOutTypeOpenDrain - O - 0=Standard buffer (drives high or low); 1=open drain buffer (drives low only)
     *
     * @param   *pState - O - 0=LOW; 1=HIGH
     *
     * @return  0 = PASS    <BR>
     *          -1 = FAIL  <BR>
     *
     */
    {
        hidMessageStruct msg;
    
        DLPC350_PrepReadCmdWithParam(GPIO_CONFIG, (unsigned char)pinNum);
    
        if(DLPC350_Read() > 0)
        {
            memcpy(&msg, g_InputBuffer, 65);
            *pEnAltFunc = ((msg.text.data[1] & BIT7) == BIT7);
            *pAltFunc1 = ((msg.text.data[1] & BIT6) == BIT6);
            *pDirOutput = ((msg.text.data[1] & BIT5) == BIT5);
            *pOutTypeOpenDrain = ((msg.text.data[1] & BIT4) == BIT4);
            if(*pDirOutput)
                *pState = ((msg.text.data[1] & BIT3) == BIT3);
            else
                *pState = ((msg.text.data[1] & BIT2) == BIT2);
            return 0;
        }
        return -1;
    }