We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hello,
I am useing a AT91SAM7 microcontroller with Keil Realview compiler and USB HID. My code is based on the example code of the Atmel SAM7-EK Board.
Does anybody know what to do, when the uController has no data to send and get's a request from the host to send something. When the PC has noting to send I do not get an SAM7-uC USB interrupt for the Interrupt In Endpoint. However for the HID Interrupt Out Endpoint I get interrupts all the time. If I do not send any data when I get the interrupt the communication Device to Host doesn't work properly because if I reply a few times with some data and then stop to reply and then read the data from the host input buffer of the PC using the visual C++ V6 HID USB api it reports some error. If I send the same data all the time on a request it works. I could send some dummy data if I have nothing to send but that fills the PCs HID report input buffer and if it doesn't respond quickly enough data gets lost.
So now what to do?
"When the PC has noting to send I do not get an SAM7-uC USB interrupt for the Interrupt In Endpoint. However for the HID Interrupt Out Endpoint I get interrupts all the time."
Isn't IN and OUT reversed? USB is host centric. Then, IN and OUT is named after the host side.
For vendor specific HID implementation, the HID host sends interrupt OUT transaction to the device just when the host app sends out report using WriteFile.
For interrupt IN transaction, HID PC device driver issues IN transaction periodically by itself. - The device driver issues IN transaction just after the device is enumerated, even when the host app is not running. - The input reports from the device are stored to the IN buffer on the device driver. This buffer is a cyclic one. ( see HidD_GetNumInputBuffers() ) - HID host app reads out input reports from this buffer. ie. the host app doesn't access to the device directly.
For USB, device cannot send any data until the host requests it by IN transaction. Then, the PC device driver issues IN transaction periodically, regardless of the status on the device. This kind of IN transaction is called as "polling-IN" transaction.
To handle this IN transaction, you have two options.
a) Respond to it just when any change occurs on the device b) Always send some data, even if it is the same as the last one.
b) is often applied in many USB examples, because the host app is simpler than a). However, you have to extract change from the data stream.
For a) strategy, On the firmware: Load data to the interrupt IN EP when the data is generated on your firmware. Usually, data loading is done in a timer ISR or one of main loop tasks, in which new data is generated. Not in the endpoint ISR. You may ignore the hardware interrupt from the interrupt IN EP, or may use the interrupt just to know the EP is empty.
On the host app: You have to apply asynchronous (OVERLAPPED) ReadFile(), because synchronous ReadFile() blocks the thread, until the device returns input report. "if I reply a few times with some data and then stop to reply and then read the data from the host input buffer of the PC using the visual C++ V6 HID USB api it reports some error."
Just ignore the error. I suppose your host app handles it properly using OVERLAPPED. And WaitForSingleObject() returns timeout error, when no input report. "I believe I must answer the requests of the host by sending NACKs - but the question is how to do it?"
Just leave the EP alone. When you don't load any data to the interrupt IN EP, the EP is empty, and the EP returns NAK to the next IN transaction from the host.
The hardware interrupt from the IN EP means that the data on the EP has been sent to the host, and the EP is empty now. It's same as UART TX. It NEVER means the arrival of host request to which your firmware has to respond immediately.
Tsuneo
"You have to apply asynchronous (OVERLAPPED) ReadFile(), because synchronous ReadFile() blocks the thread, until the device returns input report."
Is this really a requirement? OVERLAPPED is an efficient way to avoid the blocking, but another way to let an application to continue while waiting for data is to split it into multiple threads. Then it doesn't matter if one thread hangs waiting for data - the rest of the application can do whatever tasks that needs to be done, and when data arrives, the communication thread can read it and update relevant data structures.
"but another way to let an application to continue while waiting for data is to split it into multiple threads."
In principle, you are right. In these reasons, however, the combination of sub-thread and synchronous call is not used.
a) Thread termination While the thread is blocked by the synchronous ReadFile, these calls are not canceled by CancelIo(). Then there is no way to terminate this thread at the end of the application. It means, the application doesn't finish unless any input comes from the device.
CancelIo Function msdn.microsoft.com/.../aa363791.aspx "Cancels all pending input and output (I/O) operations that are issued by the calling thread for the specified file. The function does not cancel I/O operations that other threads issue for a file handle."
b) Error handling Synchronous ReadFile/WriteFile often get stuck on surprised removal of USB device.
Then, OVERLAPPED call is applied in the sub-thread, too. And WaitFor- function is used to make the thread sleep.
Thanks. Yes, I have seen a number of Win application locking up after removing USB devices. This may be the reason why.
My standard code for serial communication is using overlapped transfers, but I haven't written any USB code yet, and wanted to know my available options.
Thank you for your answers so far. I managed to get it working by disableing the interrupt Out Pipes endpoint handler in the uC.
Now I have another problem. I have defined several Report IDs. When I send data on the interrupt Out pipe - and only want to send the amount of bytes - how to do it.
I tried:
if (WriteHandle != INVALID_HANDLE_VALUE) { Result = WriteFile (WriteHandle, OutputReport, REPORTSIZE, //Capabilities.OutputReportByteLength, &BytesWritten, NULL); }
useing REPORTSIZE as the reportsize corresponding to the ReportID which is smaller size than the largest Report ID (max is Capabilities.OutputReportByteLength)
I get a message - the referenced User Buffer is not allowed ... Can't write to device
What have I done wrong?
Hello again,
I think I have found the solution myself. Windows always reads the complete buffer with length Capabilities.???ReportByteLength like Capabilities.InputReportByteLength. The lenth returned by the write function has nothing to do with the number of bytes acutally transferred over USB. It is always the maximum size of that report type ...
"I managed to get it working by disableing the interrupt Out Pipes endpoint handler in the uC."
I think it's IN pipe. You are still mixing up IN and OUT :-) "useing REPORTSIZE as the reportsize corresponding to the ReportID which is smaller size than the largest Report ID (max is Capabilities.OutputReportByteLength)
I get a message - the referenced User Buffer is not allowed ... Can't write to device"
For HID device I/O, use always Capabilities.OutputReportByteLength for nNumberOfBytesToWrite parameter of WriteFile. The OutputReportByteLength holds the greatest length of the output report. Also the buffer handed to WriteFile should have this size. But you don't need to fill the buffer with padding bytes. Just fill it with the size determined by the report ID.
It's Windows HID feature. I don't know the reason why MS have designed the API like this.
They may consider to write multiple reports at a time using an OUT buffer on the device driver, though it is not implemented so far.