/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.usb;
import android.util.Log;
import java.nio.ByteBuffer;
/**
* A class representing USB request packet.
* This can be used for both reading and writing data to or from a
* {@link android.hardware.usb.UsbDeviceConnection}.
* UsbRequests can be used to transfer data on bulk and interrupt endpoints.
* Requests on bulk endpoints can be sent synchronously via {@link UsbDeviceConnection#bulkTransfer}
* or asynchronously via {@link #queue} and {@link UsbDeviceConnection#requestWait}.
* Requests on interrupt endpoints are only send and received asynchronously.
*
* <p>Requests on endpoint zero are not supported by this class;
* use {@link UsbDeviceConnection#controlTransfer} for endpoint zero requests instead.
*/
public class UsbRequest {
private static final String TAG = "UsbRequest";
// used by the JNI code
private long mNativeContext;
private UsbEndpoint mEndpoint;
// for temporarily saving current buffer across queue and dequeue
private ByteBuffer mBuffer;
private int mLength;
// for client use
private Object mClientData;
public UsbRequest() {
}
/**
* Initializes the request so it can read or write data on the given endpoint.
* Whether the request allows reading or writing depends on the direction of the endpoint.
*
* @param endpoint the endpoint to be used for this request.
* @return true if the request was successfully opened.
*/
public boolean initialize(UsbDeviceConnection connection, UsbEndpoint endpoint) {
mEndpoint = endpoint;
return native_init(connection, endpoint.getAddress(), endpoint.getAttributes(),
endpoint.getMaxPacketSize(), endpoint.getInterval());
}
/**
* Releases all resources related to this request.
*/
public void close() {
mEndpoint = null;
native_close();
}
@Override
protected void finalize() throws Throwable {
try {
if (mEndpoint != null) {
Log.v(TAG, "endpoint still open in finalize(): " + this);
close();
}
} finally {
super.finalize();
}
}
/**
* Returns the endpoint for the request, or null if the request is not opened.
*
* @return the request's endpoint
*/
public UsbEndpoint getEndpoint() {
return mEndpoint;
}
/**
* Returns the client data for the request.
* This can be used in conjunction with {@link #setClientData}
* to associate another object with this request, which can be useful for
* maintaining state between calls to {@link #queue} and
* {@link android.hardware.usb.UsbDeviceConnection#requestWait}
*
* @return the client data for the request
*/
public Object getClientData() {
return mClientData;
}
/**
* Sets the client data for the request.
* This can be used in conjunction with {@link #getClientData}
* to associate another object with this request, which can be useful for
* maintaining state between calls to {@link #queue} and
* {@link android.hardware.usb.UsbDeviceConnection#requestWait}
*
* @param data the client data for the request
*/
public void setClientData(Object data) {
mClientData = data;
}
/**
* Queues the request to send or receive data on its endpoint.
* For OUT endpoints, the given buffer data will be sent on the endpoint.
* For IN endpoints, the endpoint will attempt to read the given number of bytes
* into the specified buffer.
* If the queueing operation is successful, we return true and the result will be
* returned via {@link android.hardware.usb.UsbDeviceConnection#requestWait}
*
* @param buffer the buffer containing the bytes to write, or location to store
* the results of a read
* @param length number of bytes to read or write
* @return true if the queueing operation succeeded
*/
public boolean queue(ByteBuffer buffer, int length) {
boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
boolean result;
if (buffer.isDirect()) {
result = native_queue_direct(buffer, length, out);
} else if (buffer.hasArray()) {
result = native_queue_array(buffer.array(), length, out);
} else {
throw new IllegalArgumentException("buffer is not direct and has no array");
}
if (result) {
// save our buffer for when the request has completed
mBuffer = buffer;
mLength = length;
}
return result;
}
/* package */ void dequeue() {
boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
int bytesRead;
if (mBuffer.isDirect()) {
bytesRead = native_dequeue_direct();
} else {
bytesRead = native_dequeue_array(mBuffer.array(), mLength, out);
}
if (bytesRead >= 0) {
mBuffer.position(Math.min(bytesRead, mLength));
}
mBuffer = null;
mLength = 0;
}
/**
* Cancels a pending queue operation.
*
* @return true if cancelling succeeded
*/
public boolean cancel() {
return native_cancel();
}
private native boolean native_init(UsbDeviceConnection connection, int ep_address,
int ep_attributes, int ep_max_packet_size, int ep_interval);
private native void native_close();
private native boolean native_queue_array(byte[] buffer, int length, boolean out);
private native int native_dequeue_array(byte[] buffer, int length, boolean out);
private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out);
private native int native_dequeue_direct();
private native boolean native_cancel();
}