Reputation: 103
I'm trying to communicate with a USB device that uses interrupt transfer for communications. it is not a polled device, either side may send at any time. All the examples I find seem to be poll-response where you send data first, wait for the send to complete, then wait for the response, process it and then go back to sending data again.
My code is modeled after the following which I found here on stackoverflow (I'm showing the original I based it on because my own code has a lot more going on and is less compact)
boolean retval = request.queue(buffer, 1);
if (mConnection.requestWait() == request) {
// wait for confirmation (request was sent)
UsbRequest inRequest = new UsbRequest();
// URB for the incoming data
inRequest.initialize(mConnection, mEndpointIn);
// the direction is dictated by this initialisation to the incoming endpoint.
if(inRequest.queue(buffer, bufferMaxLength) == true){
mConnection.requestWait();
// wait for this request to be completed
// at this point buffer contains the data received
}
}
The second requestWait() will block until something arrives, so I can't do another TX operation until I receive something! What am I missing?
Upvotes: 1
Views: 4330
Reputation: 711
You said: "The second requestWait() will block until something arrives, so I can't do another TX operation until I receive something"
Having written code of my own also based on the example you show, I think I understand where you are confused: The second requestWait() will return for any USB operation, not just the one that preceeded it. (from the Android API documentation "Note that this may return requests queued on multiple UsbEndpoints")
So if you queue a Send request even while you are waiting, your "receive waitRequest" will return, but for the Send endpoint. You should always check the endpoint of the result of waitRequest, or compare it to the initial request itself. If it matches inRequest, then it's actually the receive operation you were blocking on. If it doesn't match, compare it to your Send request (or in my example code below, I simply assume that it's a send response and ignore it)
You will need to queue send and receive requests from different methods or threads however rather than in the same loop as is implied by the code you supplied.
Here is the code from my own project (be aware that my app is running into heap corruptions, so the code below may not be perfect, but it does allow me to send even while a receive operation is pending)
So here is my receive loop, you'll see the similarities with your code:
while(mUsbDevice != null ) {
if (inRequest.queue(buffer, BUFFER_SIZE) == true) {
// (mUsbConnection.requestWait() is blocking
if (mUsbConnection.requestWait() == inRequest){
// this is an actual receive
// do receive processing here (send to conusmer)
} else{
Log.d(TAG, "mConnection.requestWait() returned for a different request (likely a send operation)");
}
} else {
Log.e(TAG, "failed to queue USB request");
}
buffer.clear();
}
I do the sending form another thread which uses messages to queue incoming send requests:
mHandler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == 1) { // 1 means send a 64 bytes array in msg.obj
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
final byte[] array = (byte[]) msg.obj;
buffer.clear();
buffer.put( array );
UsbRequest outRequest = new UsbRequest();
outRequest.initialize(mUsbConnection, mUsbEndpointOut);
outRequest.queue(buffer, BUFFER_SIZE);
Log.d(L.TAG, "Queueing request:"+outRequest);
// don't do a mConnection.requestWait() here, ReceiveThread is already listening
} else if (msg.what == 2) { // 2 means exit
Log.d(L.TAG, "SenderThread::handleMessage(): terminate");
Looper.myLooper().quit();
} else {
Log.e(L.TAG, "SenderThread::handleMessage(): unknow message type: " + msg.what);
}
}
};
Upvotes: 3