Author Topic: HID - Sending OUT Report while IN Report is Waiting  (Read 1369 times)

Victor M

  • Member
  • ***
  • Posts: 11
HID - Sending OUT Report while IN Report is Waiting
« on: August 30, 2018, 12:58:02 pm »
I'm trying to learn how to use USB for general two way communications with a generic HID device.

For the device I have selected the Atmel ATxmega256A3BU on a demo board which includes an LCD, buttons, and LEDs.

For the host application I'm using Jan's 'generic_hid_cs_60' (Visual C#).
I have modified the code for PC and device to support two basic features --

⦁   When a button on the windows form is clicked, an OUT report is sent to endpoint 2 with 64 bytes of character data (obtained from a new text box on the form). When received, the text is displayed on the device's LCD.

⦁   When a button on the device is pressed, a different text string is written to the device endpoint 1 buffer. Then when the 'Get Input Report' (by interrupt) button on the form is clicked, the received text string is written to a listbox (new listbox, not LstResults) on the form.

Both of these features are working, but I don't want to have to click a button on the form in order to get the message to display in the listbox.

In an attempt to fix this, I made changes to the application code - 1) changed the readTimeout value from 5000 to -1 (no timeout). 2) After receiving the IN report, I call the method to initiate another IN report. This way the host is always sending IN tokens, and when the device writes to the IN buffer, the data is transferred immediately. This makes the second feature work as intended.

Of course this creates a different problem, the OUT report (first feature) can never occur because it is blocked by the _transferInProgress flag.

Question: is there a way to make both these features work as intended? For instance, can the Get IN Report be canceled so the Get OUT Report can be sent?

I did see the following on the HID FAQ page, and it might relate to my problem, but I don't really understand the answer. Could someone clarify? Is there example code?

" How can I find out if a report is available without hanging my application?
Use overlapped ReadFile with a short timeout or use ReadFileEx to signal when a report is available. In .NET, you can do asynchronous ReadFile calls with a delegate and BeginInvoke and EndInvoke methods. "

Any help will be greatly appreciated.

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 2746
    • Lakeview Research
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #1 on: August 30, 2018, 01:17:55 pm »
The host driver continuously requests data from the interrupt IN endpoint regardless of whether applications use it. You are just requesting the data from the buffer more frequently.

If the IN data isn't a response to OUT data,  you don't need to wait for an OUT transfer to complete before requesting IN data.

Before initiating another read, check for a write request. Or use delegates, etc. See my more recent example code.

Let me know if you still have questions. I don't have the code in front of me right now.

Victor M

  • Member
  • ***
  • Posts: 11
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #2 on: August 31, 2018, 12:07:13 am »
Hi Jan, thanks for the reply.
So even before the application calls RequestToGetInputReport(), the host USB software stack has retrieved the report from the HID? That's very interesting.

To state the problem more clearly, the only way I know of to read the IN report (in a timely manner) is to call RequestToGetInputReport() every time it returns data. But in the meantime if I need to send an OUT report, it gets blocked or the software hangs.

You mentioned "more recent example code" but I don't see any. I thought I was using the most recent. It is V6.2 dated 11/12/13. If there is other code, could you give me the link?
I have used delegates in other c# windows form code, but I don't understand exactly how it can help in this case. Could you be more explicit?

I see the FileStream object _deviceData being created in the FindTheHid() method. Does this FileStream support concurrent read and write? In other words, when the read method is called (asynchronously) should I then be able to call the write method before read completes? It doesn't seem to work. I noticed there is a flag, _transferInProgress to prevent this. It must be there for a reason. If I ignore the flag and call RequestToSendOutputReport() it causes the program to hang.

As I study this more I will definitely have more questions.
Thanks,
Victor

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 2746
    • Lakeview Research
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #3 on: August 31, 2018, 04:10:20 pm »
>So even before the application calls RequestToGetInputReport(), the host USB software stack has retrieved the report from the HID?

yes, if the endpoint has sent one. The driver uses a ring buffer.

V6.2 is the latest.

These might help

https://docs.microsoft.com/en-us/dotnet/standard/io/asynchronous-file-i-o

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/using-async-for-file-access


Victor M

  • Member
  • ***
  • Posts: 11
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #4 on: August 31, 2018, 08:12:27 pm »
Quote
yes, if the endpoint has sent one.

Okay, but to test my understanding, the device cannot initiate a transaction, it can only respond to an 'IN token' from the host, right? If the device has no data in it's buffer it replies with NAK. So there must be a periodic polling by the host.

Thanks for the references. I will study those.

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 2746
    • Lakeview Research
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #5 on: August 31, 2018, 10:16:49 pm »
yes

Victor M

  • Member
  • ***
  • Posts: 11
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #6 on: September 04, 2018, 10:29:44 pm »
Jan, I believe I understand the implications of asynchronous calls, but with respect to that I notice in your example code the FileStream is opened with the async parameter set to false.

Code: [Select]
_deviceData = new FileStream(_hidHandle, FileAccess.Read | FileAccess.Write, _myHid.Capabilities.InputReportByteLength, false);
If I change it to true, the following exception happens - Additional information: Handle does not support asynchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened synchronously (that is, it was not opened for overlapped I/O).

The FileStream opened on _hidHandle does not support overlapped read/write. It seems full duplex operation may not be possible with HID using FileStream, at least not the way it's done in the example. However that does not prevent read and write methods from being called asynchronously, as indeed they are in the example.

In spite of the foregoing I was able to get it to work (somewhat) as follows:

The timeout for reading the IN report was set very short - 5ms. OnReadTimeout() closes communications (FileStream and Handle are closed). The timeout event however does not cause code execution to resume in RequestToGetInputReport(). Execution resumes only when an IN report has been received. When that happens, RequestToGetInputReport() is again called, causing a new FileStream to be opened.

While waiting for an IN report (none being received), an OUT report may be sent. A call to RequestToSendOutputReport() will also cause a new FileStream to be opened.

The thing worth noting is that the FileStream is being opened and closed continually as each IN reprot is received. I think this is not very desirable. Also there is another side effect - whenever an OUT report is sent, and then an IN report is received, there is a duplicate of the IN report. What's happening (I think) is the IN report causes execution to resume in RequestToGetInputReport(), and the report gets printed. Then RequestToGetInputReport() is called again (but with the newly instantiated FileStream), and the same report is again received. So I have to ignore the second report.

Question: Is there any way to establish an I/O connection between the application and USB host that is asynchronous overlapped so that data can be read and written without having to continually open and close the connection?

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 2746
    • Lakeview Research
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #7 on: September 05, 2018, 03:12:23 pm »
I added the _transferInProgress variable because the example sends data, then waits for a response. If the Input and Output reports are independent, you can delete the instances of:

if (_transferInProgress)


Victor M

  • Member
  • ***
  • Posts: 11
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #8 on: September 05, 2018, 07:44:39 pm »
Jan,

If there is a pending call to get an IN report, and I try to send an OUT report (ignoring _transferInProgress) the program hangs. There is no exception thrown. In the code below (Hid.cs) I put a breakpoint at

Task t = fileStreamDeviceData.WriteAsync(outputReportBuffer, 0, outputReportBuffer.Length, cts.Token);

and the breakpoint is hit, but if I put the breakpoint at

await t;

the program just hangs, it never gets to await t;

Code: [Select]
internal async Task<Boolean> SendOutputReportViaInterruptTransfer
(FileStream fileStreamDeviceData, SafeFileHandle hidHandle, Byte[] outputReportBuffer, CancellationTokenSource cts)
{
try
{
var success = false;

// Begin writing the Output report.

Task t = fileStreamDeviceData.WriteAsync(outputReportBuffer, 0, outputReportBuffer.Length, cts.Token);

await t;

// Gets to here only if the write operation completed before a timeout.

Debug.Print("Asynchronous write completed");

// The operation has one of these completion states:

switch (t.Status)
{
case TaskStatus.RanToCompletion:
success = true;
Debug.Print("Output report written to device");
break;
case TaskStatus.Canceled:
Debug.Print("Task canceled");
break;
case TaskStatus.Faulted:
Debug.Print("Unhandled exception");
break;
}

return success;
}
catch (Exception ex)
{
DisplayException(ModuleName, ex);
throw;
}
}

Victor M

  • Member
  • ***
  • Posts: 11
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #9 on: September 05, 2018, 08:20:46 pm »
Okay, just to clarify - when there is a pending request for an IN report, and an OUT report is requested, the OUT report will be blocked until the IN report has completed, then the OUT report will complete.

Is there a way to cancel the IN report request, so the OUT report can be sent without waiting?

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 2746
    • Lakeview Research
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #10 on: September 06, 2018, 11:50:18 am »
See the ReadTimeout variable for canceling a request if no data is available.
« Last Edit: September 06, 2018, 01:08:38 pm by Jan Axelson »

Victor M

  • Member
  • ***
  • Posts: 11
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #11 on: September 06, 2018, 11:01:48 pm »
Jan, Yes, I'm using the readTimeout variable --

Code: [Select]
cts.CancelAfter(readTimeout); where timeout is say 5 seconds. But a timeout does not cancel the ReadAsync() task, it only calls the function specified by --

Code: [Select]
Action onReadTimeoutAction = OnReadTimeout;
cts.Token.Register(onReadTimeoutAction);

The task only completes when an IN report is received. In the meantime, an OUT report is blocked from being sent. I have been trying to find a way to make the task terminate on a cancellation request, but so far no luck.

I found one article that suggested if the awaited task (ReadAsync) included
Code: [Select]
t.ThrowIfCancellationRequested(); ('t' is the cancellation token) it would cause the task to terminate when a cancellation was requested.
« Last Edit: September 06, 2018, 11:13:07 pm by Victor M »

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 2746
    • Lakeview Research
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #12 on: September 07, 2018, 03:26:06 pm »
This looks worth a try

t.ThrowIfCancellationRequested();

Victor M

  • Member
  • ***
  • Posts: 11
Re: HID - Sending OUT Report while IN Report is Waiting
« Reply #13 on: September 08, 2018, 11:13:44 am »
Jan,

I meant to note that using t.ThrowIfCancellationRequested(); is only hypothetical in this case because it would have to go in ReadAsync() which is a method of the FileStream class in System.IO namespace. So it's not something that can be changed, I think that is correct -- when it comes to coding, I'm more of a monkey-see-monkey-do kind of guy. I do try to understand as much as I can though, and learn as I go.

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 2746
    • Lakeview Research