Author Topic: SendOutputReportAsync not sending data to ble hid device  (Read 13182 times)

sd

  • Member
  • ***
  • Posts: 17
SendOutputReportAsync not sending data to ble hid device
« on: January 16, 2019, 09:08:12 pm »
Hi Jan,

I'm here again asking for your help! I'm trying to develop host application on Windows 10 machine for my Bluetooth Low Energy HID Device. I am referring to https://docs.microsoft.com/en-us/uwp/api/windows.devices.humaninterfacedevice.hiddevice. Everything works upto the enumeration part. But SendOutputReportAsync() isnt actually sending data to my ble hid device. [https://docs.microsoft.com/en-us/uwp/api/windows.devices.humaninterfacedevice.hidoutputreport] Please guide me on how do I proceed ahead or some direction on how to write host application from scratch. Am I looking into the wrong place? I can confirm that the firmware works because I tested it with nrfConnect software.

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #1 on: January 17, 2019, 08:18:42 pm »
Use whatever debugging tools you have to isolate the problem.

In the host application, use single-stepping, watch variables, error codes, etc. to monitor what is happening.

A protocol analyzer will show if the host is sending Output reports on the bus.

The host may use interrupt or control transfers to send Output reports depending on whether the HID has an interrupt OUT endpoint.



sd

  • Member
  • ***
  • Posts: 17
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #2 on: January 18, 2019, 04:54:31 pm »
Sure. Will generic_hid_cs_62 code work with BLE HID device? I can see declarations & definitions nicely explained in the book. Code compiles error-free and also the generated GUI finds the BLE HID device, but how do I make it send/receive reports?

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #3 on: January 19, 2019, 11:13:44 am »
On the host side:

If using WriteFile or Filestreams for Output reports, be sure the buffer matches the size of a defined Output report + 1. The first byte should contain the report ID. Use zero if no report IDs defined.

If using ReadFile or Filestreams for Input reports, be sure the buffer is at least as large as the largest defined Input report + 1. The first received byte will contain the report ID, zero if no report IDs defined.

On the device side:

Be sure the IN endpoint is configured to send data on receiving an IN token packet.

Be sure the OUT endpoint is configured to receive data on receiving an OUT token packet.

How to do this varies with the chip architecture.


sd

  • Member
  • ***
  • Posts: 17
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #4 on: January 20, 2019, 05:27:46 pm »
Thank you for this important tip!! So I changed report id = 1 in RequestToSendOutputReport() and got the output as shown in attachment. But I do not see this data on my BLE HID device. What do you think could be the problem? Also in the comments for functions used for input report, it is mentioned that it assumes report id =0. So where do I change the report id used for input report?

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #5 on: January 20, 2019, 09:53:42 pm »
The report ID is the first byte in the buffer.

Be sure the OUT endpoint is configured to receive data on receiving an OUT token packet.


sd

  • Member
  • ***
  • Posts: 17
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #6 on: January 21, 2019, 01:28:13 pm »
Hi Jan,

I'm able to receive data from BLE HID device on Windows machine using nrfConnect & SimpleHIDWrite as shown the attached images. But how different is generic_hid_cs_v62 code? Do you think I can make any changes in this code instead of firmware?

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #7 on: January 21, 2019, 09:42:33 pm »
With a hardware-based protocol analyzer, you can view any differences on the bus between the working and nonworking code.

Otherwise, use any debugging tools you have to verify that the complete data is going out on the bus and the device storing the received data.


sd

  • Member
  • ***
  • Posts: 17
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #8 on: January 26, 2019, 05:17:06 pm »
I tried debugging and inserting some debug statements but its not really helping. Can you give me a direction to write an API for BLE HID device from scratch?

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #9 on: January 27, 2019, 06:01:58 pm »
In addition to my code, my HID page has links to HID host code from other sources:

http://www.janaxelson.com/hidpage.htm

sd

  • Member
  • ***
  • Posts: 17
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #10 on: January 30, 2019, 08:56:03 pm »
I have 3rd and 5th editions of your book. I have pasted the code which I'm trying to use with my BLE HID device. The pasted vendor ID & product ID are the original ones, practically its different. The commented part gives errors on Windows 10. The handle to BLE device is not getting fetched.

Code: [Select]
#include <iostream>
#include <windows.h>
#include <hidclass.h>
#include <hidsdi.h>
#include <setupapi.h>
#include <devguid.h>
#include <regstr.h>

#pragma comment(lib,"hid")

int main()
{
GUID HidGuid;
HidD_GetHidGuid(&HidGuid);

HANDLE DeviceInfoSet;
DeviceInfoSet = SetupDiGetClassDevs(&HidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

BOOLEAN Result;
SP_DEVICE_INTERFACE_DATA MyDeviceInterfaceData;
MyDeviceInterfaceData.cbSize = sizeof(MyDeviceInterfaceData);
DWORD MemberIndex = 0;
Result = SetupDiEnumDeviceInterfaces(DeviceInfoSet, 0, &HidGuid, MemberIndex, &MyDeviceInterfaceData);

PSP_DEVICE_INTERFACE_DETAIL_DATA DetailData;
ULONG Length;
Result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, &MyDeviceInterfaceData, NULL, 0, &Length, NULL);

DetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(Length);
DetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
Result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, &MyDeviceInterfaceData, DetailData, Length, &Length, NULL);

SetupDiDestroyDeviceInfoList(DeviceInfoSet);

HANDLE DeviceHandle;
DeviceHandle = CreateFile(DetailData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, NULL);

CloseHandle(DeviceHandle);

/*HDEVNOTIFY DeviceNotificationHandle;
DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface;
DevBroadcastDeviceInterface.dbcc_size = sizeof(DevBroadcastDeviceInterface);
DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
DevBroadcastDeviceInterface.dbcc_classguid = HidGuid;
DeviceNotificationHandle = RegisterDeviceNotification(m_hWnd, &DevBroadcastDeviceInterface, DEVICE_NOTIFY_WINDOW_HANDLE);

BOOL CUsbhidiocDlg::OnDeviceChange(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case DBT_DEVICEARRIVAL:
// Find out if the device path name matches wParam. If yes, perform any tasks required on device attachment.
return TRUE;
case DBT_DEVICEREMOVECOMPLETE:
// Find out if the device path name matches wParam. If yes, perform any tasks required on device removal.
return TRUE;
default:
return TRUE;
}
}

PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
{
PDEV_BROADCAST_DEVICEINTERFACE lpdbi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
CString DeviceNameString;
DeviceNameString = lpdbi->dbcc_name;
if((DeviceNameString.CompareNoCase(DetailData > DevicePath)) == 0)
{
// The names match.
}
else
{
// It’s a different device.
}
}

UnregisterDeviceNotification(DeviceNotificationHandle);*/

HIDD_ATTRIBUTES Attributes;
Attributes.Size = sizeof(Attributes);
Result = HidD_GetAttributes(DeviceHandle, &Attributes);
const unsigned int VendorID = 0x0925;
const unsigned int ProductID = 0x1234;
if (Attributes.VendorID == VendorID)
{
if (Attributes.ProductID == ProductID)
{
std::cout << "The Vendor ID and Product ID match.";
}
else
{
std::cout << "The Product ID doesn't match.";
CloseHandle(DeviceHandle); // Close the handle.
}
}
else
{
std::cout << "The Vendor ID doesn't match.";
CloseHandle(DeviceHandle); // Close the handle.
}
}

I'm also getting an error --->>>> Exception thrown at 0x1F60E78A in api1.exe: 0xC0000008: An invalid handle was specified.
« Last Edit: January 30, 2019, 09:08:05 pm by sd »

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #11 on: January 31, 2019, 09:50:10 am »
It looks like you are closing the handle immediately after requesting it:

Code: [Select]
DeviceHandle = CreateFile(DetailData->DevicePath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, NULL);

CloseHandle(DeviceHandle);

sd

  • Member
  • ***
  • Posts: 17
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #12 on: January 31, 2019, 10:13:07 am »
I tried commenting that but does not work.

Also if I comment the CloseHandle here, I do not get exception error.

Code: [Select]
std::cout << "The Vendor ID doesn't match.";
CloseHandle(DeviceHandle); // Close the handle.

But I am not able to detect my device by polling against vendorId & productId.

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #13 on: January 31, 2019, 10:53:54 am »
Earlier you said you were detecting the device with my example code? So I suggest comparing your code with that.
« Last Edit: January 31, 2019, 11:20:08 am by Jan Axelson »

sd

  • Member
  • ***
  • Posts: 17
Re: SendOutputReportAsync not sending data to ble hid device
« Reply #14 on: January 31, 2019, 07:35:00 pm »
Hey Jan,

I could easily understand and play with your Visual C++ code snippets in 3rd edition. But I cannot compile your visual C++ code put up on the website. Also I cannot think of making any changes to the Visual C# code, so I decided to rewrite everything starting from basics, that way I can get a clear understanding and find where the problem actually lies instead of working in loops and hitting a dead end. I made some progress. Here is my simple code -

Code: [Select]
#include <iostream>
#include <windows.h>
#include <hidclass.h>
#include <hidsdi.h>
#include <setupapi.h>
#include <devguid.h>
#include <regstr.h>

#pragma comment(lib,"hid")

int main()
{
GUID HidGuid;
HidD_GetHidGuid(&HidGuid);

//HANDLE DeviceInfoSet;
HDEVINFO DeviceInfoSet;
DeviceInfoSet = SetupDiGetClassDevs(&HidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

/**/
if(DeviceInfoSet== INVALID_HANDLE_VALUE)
return NULL;

SP_DEVICE_INTERFACE_DATA MyDeviceInterfaceData;
MyDeviceInterfaceData.cbSize = sizeof(MyDeviceInterfaceData);
SP_DEVINFO_DATA dd;
dd.cbSize = sizeof(SP_DEVINFO_DATA);
HANDLE DeviceHandle=NULL;

for (DWORD MemberIndex = 0; SetupDiEnumDeviceInterfaces(DeviceInfoSet, 0, &HidGuid, MemberIndex, &MyDeviceInterfaceData); MemberIndex++)
{
SP_DEVICE_INTERFACE_DETAIL_DATA DetailData;
DetailData.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

DWORD Length = 0;
if (!SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, &MyDeviceInterfaceData, NULL, 0, &Length, NULL))
{
int err = GetLastError();
if (err == ERROR_NO_MORE_ITEMS)
break;

PSP_DEVICE_INTERFACE_DETAIL_DATA pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)GlobalAlloc(GPTR, Length);
pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

if (!SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, &MyDeviceInterfaceData, pInterfaceDetailData, Length, &Length, &dd))
break;


DeviceHandle = CreateFile(pInterfaceDetailData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, NULL);

HIDD_ATTRIBUTES Attributes;
Attributes.Size = sizeof(Attributes);
HidD_GetAttributes(DeviceHandle, &Attributes);
const unsigned int VendorID = 0x1F38;
const unsigned int ProductID = 0x0100;
if (Attributes.VendorID == VendorID)
{
if (Attributes.ProductID == ProductID)
{
std::cout << "The Vendor ID and Product ID match.";
std::cout << "\nDeviceHandle: " << DeviceHandle;
std::cout << "\nVendor ID: " << Attributes.VendorID;
std::cout << "\nProduct ID: " << Attributes.ProductID;
std::cout << "\nVersionNumber: " << Attributes.VersionNumber;
std::cout << "\nSize: " << Attributes.Size;

PHIDP_PREPARSED_DATA PreparsedData;
HidD_GetPreparsedData(DeviceHandle, &PreparsedData);

HIDP_CAPS Capabilities;
HidP_GetCaps(PreparsedData, &Capabilities);
std::cout << "\nUsage: " << Capabilities.Usage;
std::cout << "\nUsagePage: " << Capabilities.UsagePage;
std::cout << "\nInputReportByteLength: " << Capabilities.InputReportByteLength;
std::cout << "\nOutputReportByteLength: " << Capabilities.OutputReportByteLength;
std::cout << "\nNumberLinkCollectionNodes: " << Capabilities.NumberLinkCollectionNodes;
std::cout << "\nNumberInputButtonCaps: " << Capabilities.NumberInputButtonCaps;
std::cout << "\nNumberInputValueCaps: " << Capabilities.NumberInputValueCaps;
std::cout << "\nNumberInputDataIndices: " << Capabilities.NumberInputDataIndices;
std::cout << "\nNumberOutputButtonCaps: " << Capabilities.NumberOutputButtonCaps;
std::cout << "\nNumberOutputValueCaps: " << Capabilities.NumberOutputValueCaps;
std::cout << "\nNumberOutputDataIndices: " << Capabilities.NumberOutputDataIndices;

CHAR InputReportBuffer[3];
DWORD BytesRead;
DWORD Result;
HANDLE hEventObject;
OVERLAPPED HIDOverlapped;
hEventObject = CreateEvent((LPSECURITY_ATTRIBUTES)NULL, FALSE, TRUE, NULL);
HIDOverlapped.hEvent = hEventObject;
HIDOverlapped.Offset = 0;
HIDOverlapped.OffsetHigh = 0;
// Set the first byte in the buffer to the Report ID.
InputReportBuffer[0] = 0;
HANDLE ReadHandle = CreateFile(pInterfaceDetailData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); //NULL, NULL);
if (ReadHandle == INVALID_HANDLE_VALUE)
std::cout << "\ncould not open";
else
std::cout << "\nopened";
Result = ReadFile(ReadHandle, InputReportBuffer, Capabilities.InputReportByteLength, &BytesRead, (LPOVERLAPPED)&HIDOverlapped); //NULL);
Result = WaitForSingleObject(hEventObject, 3000);
switch (Result)
{
case WAIT_OBJECT_0:
{
std::cout << "\nBytesRead: " << BytesRead;
std::cout << "\nInputReportBuffer: " << InputReportBuffer;
// Success;
// Use the report data;
break;
}
case WAIT_TIMEOUT:
{
std::cout << "\nBytesRead: " << BytesRead;
std::cout << "\nInputReportBuffer: " << InputReportBuffer;
// Timeout error;
//Cancel the read operation.
CancelIo(ReadHandle);
break;
}
default:
{
std::cout << "\nBytesRead: " << BytesRead;
std::cout << "\nInputReportBuffer: " << InputReportBuffer;
// Undefined error;
//Cancel the read operation.
CancelIo(ReadHandle);
break;
}
}
}
}
GlobalFree(pInterfaceDetailData);
}
}
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
}

I am able to obtain handle to my device but not able to read any data. Upto CreateFile() everything works fine. Also how can I get information about endpoint/s? Is there any HID function for that?

I tried using
1.
Code: [Select]
Result = HidD_GetInputReport(ReadHandle, InputReportBuffer, Capabilities.InputReportByteLength);
if (Result)
std::cout << "\nsuccess";
else
std::cout << "\nfail";

2.
Code: [Select]
Result = ReadFile(ReadHandle, InputReportBuffer, Capabilities.InputReportByteLength, &BytesRead, NULL); //(LPOVERLAPPED)&HIDOverlapped);
if (Result)
{
std::cout << "\nBytesRead: " << BytesRead;
std::cout << "\nInputReportBuffer[1]" << InputReportBuffer[1];
std::cout << "\nInputReportBuffer[2]" << InputReportBuffer[2];
std::cout << "\nInputReportBuffer[3]" << InputReportBuffer[3];
}
else
std::cout << "\nfail1";

Both print out 'fail'. I also tried changing InputReportBuffer[0] to 0 and 1, just in case.
« Last Edit: February 01, 2019, 12:00:00 am by sd »