1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.hardware.usb; 18 19 import android.annotation.Nullable; 20 import android.util.Log; 21 22 import com.android.internal.util.Preconditions; 23 24 import dalvik.system.CloseGuard; 25 26 import java.nio.BufferOverflowException; 27 import java.nio.ByteBuffer; 28 29 /** 30 * A class representing USB request packet. 31 * This can be used for both reading and writing data to or from a 32 * {@link android.hardware.usb.UsbDeviceConnection}. 33 * UsbRequests can be used to transfer data on bulk and interrupt endpoints. 34 * Requests on bulk endpoints can be sent synchronously via {@link UsbDeviceConnection#bulkTransfer} 35 * or asynchronously via {@link #queue} and {@link UsbDeviceConnection#requestWait}. 36 * Requests on interrupt endpoints are only send and received asynchronously. 37 * 38 * <p>Requests on endpoint zero are not supported by this class; 39 * use {@link UsbDeviceConnection#controlTransfer} for endpoint zero requests instead. 40 */ 41 public class UsbRequest { 42 43 private static final String TAG = "UsbRequest"; 44 45 // From drivers/usb/core/devio.c 46 private static final int MAX_USBFS_BUFFER_SIZE = 16384; 47 48 // used by the JNI code 49 private long mNativeContext; 50 51 private UsbEndpoint mEndpoint; 52 53 /** The buffer that is currently being read / written */ 54 private ByteBuffer mBuffer; 55 56 /** The amount of data to read / write when using {@link #queue} */ 57 private int mLength; 58 59 // for client use 60 private Object mClientData; 61 62 // Prevent the connection from being finalized 63 private UsbDeviceConnection mConnection; 64 65 /** 66 * Whether this buffer was {@link #queue(ByteBuffer) queued using the new behavior} or 67 * {@link #queue(ByteBuffer, int) queued using the deprecated behavior}. 68 */ 69 private boolean mIsUsingNewQueue; 70 71 /** Temporary buffer than might be used while buffer is enqueued */ 72 private ByteBuffer mTempBuffer; 73 74 private final CloseGuard mCloseGuard = CloseGuard.get(); 75 76 /** 77 * Lock for queue, enqueue and dequeue, so a queue operation can be finished by a dequeue 78 * operation on a different thread. 79 */ 80 private final Object mLock = new Object(); 81 UsbRequest()82 public UsbRequest() { 83 } 84 85 /** 86 * Initializes the request so it can read or write data on the given endpoint. 87 * Whether the request allows reading or writing depends on the direction of the endpoint. 88 * 89 * @param endpoint the endpoint to be used for this request. 90 * @return true if the request was successfully opened. 91 */ initialize(UsbDeviceConnection connection, UsbEndpoint endpoint)92 public boolean initialize(UsbDeviceConnection connection, UsbEndpoint endpoint) { 93 mEndpoint = endpoint; 94 mConnection = Preconditions.checkNotNull(connection, "connection"); 95 96 boolean wasInitialized = native_init(connection, endpoint.getAddress(), 97 endpoint.getAttributes(), endpoint.getMaxPacketSize(), endpoint.getInterval()); 98 99 if (wasInitialized) { 100 mCloseGuard.open("close"); 101 } 102 103 return wasInitialized; 104 } 105 106 /** 107 * Releases all resources related to this request. 108 */ close()109 public void close() { 110 if (mNativeContext != 0) { 111 mEndpoint = null; 112 mConnection = null; 113 native_close(); 114 mCloseGuard.close(); 115 } 116 } 117 118 @Override finalize()119 protected void finalize() throws Throwable { 120 try { 121 mCloseGuard.warnIfOpen(); 122 close(); 123 } finally { 124 super.finalize(); 125 } 126 } 127 128 /** 129 * Returns the endpoint for the request, or null if the request is not opened. 130 * 131 * @return the request's endpoint 132 */ getEndpoint()133 public UsbEndpoint getEndpoint() { 134 return mEndpoint; 135 } 136 137 /** 138 * Returns the client data for the request. 139 * This can be used in conjunction with {@link #setClientData} 140 * to associate another object with this request, which can be useful for 141 * maintaining state between calls to {@link #queue} and 142 * {@link android.hardware.usb.UsbDeviceConnection#requestWait} 143 * 144 * @return the client data for the request 145 */ getClientData()146 public Object getClientData() { 147 return mClientData; 148 } 149 150 /** 151 * Sets the client data for the request. 152 * This can be used in conjunction with {@link #getClientData} 153 * to associate another object with this request, which can be useful for 154 * maintaining state between calls to {@link #queue} and 155 * {@link android.hardware.usb.UsbDeviceConnection#requestWait} 156 * 157 * @param data the client data for the request 158 */ setClientData(Object data)159 public void setClientData(Object data) { 160 mClientData = data; 161 } 162 163 /** 164 * Queues the request to send or receive data on its endpoint. 165 * <p>For OUT endpoints, the given buffer data will be sent on the endpoint. For IN endpoints, 166 * the endpoint will attempt to read the given number of bytes into the specified buffer. If the 167 * queueing operation is successful, return true. The result will be returned via 168 * {@link UsbDeviceConnection#requestWait}</p> 169 * 170 * @param buffer the buffer containing the bytes to write, or location to store the results of a 171 * read. Position and array offset will be ignored and assumed to be 0. Limit and 172 * capacity will be ignored. Once the request 173 * {@link UsbDeviceConnection#requestWait() is processed} the position will be set 174 * to the number of bytes read/written. 175 * @param length number of bytes to read or write. 176 * 177 * @return true if the queueing operation succeeded 178 * 179 * @deprecated Use {@link #queue(ByteBuffer)} instead. 180 */ 181 @Deprecated queue(ByteBuffer buffer, int length)182 public boolean queue(ByteBuffer buffer, int length) { 183 boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); 184 boolean result; 185 186 synchronized (mLock) { 187 // save our buffer for when the request has completed 188 mBuffer = buffer; 189 mLength = length; 190 191 // Note: On a buffer slice we lost the capacity information about the underlying buffer, 192 // hence we cannot check if the access would be a data leak/memory corruption. 193 194 if (buffer.isDirect()) { 195 result = native_queue_direct(buffer, length, out); 196 } else if (buffer.hasArray()) { 197 result = native_queue_array(buffer.array(), length, out); 198 } else { 199 throw new IllegalArgumentException("buffer is not direct and has no array"); 200 } 201 if (!result) { 202 mBuffer = null; 203 mLength = 0; 204 } 205 } 206 207 return result; 208 } 209 210 /** 211 * Queues the request to send or receive data on its endpoint. 212 * 213 * <p>For OUT endpoints, the remaining bytes of the buffer will be sent on the endpoint. For IN 214 * endpoints, the endpoint will attempt to fill the remaining bytes of the buffer. If the 215 * queueing operation is successful, return true. The result will be returned via 216 * {@link UsbDeviceConnection#requestWait}</p> 217 * 218 * @param buffer the buffer containing the bytes to send, or the buffer to fill. The state 219 * of the buffer is undefined until the request is returned by 220 * {@link UsbDeviceConnection#requestWait}. If the request failed the buffer 221 * will be unchanged; if the request succeeded the position of the buffer is 222 * incremented by the number of bytes sent/received. 223 * 224 * @return true if the queueing operation succeeded 225 */ queue(@ullable ByteBuffer buffer)226 public boolean queue(@Nullable ByteBuffer buffer) { 227 // Request need to be initialized 228 Preconditions.checkState(mNativeContext != 0, "request is not initialized"); 229 230 // Request can not be currently queued 231 Preconditions.checkState(!mIsUsingNewQueue, "this request is currently queued"); 232 233 boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); 234 boolean wasQueued; 235 236 synchronized (mLock) { 237 mBuffer = buffer; 238 239 if (buffer == null) { 240 // Null buffers enqueue empty USB requests which is supported 241 mIsUsingNewQueue = true; 242 wasQueued = native_queue(null, 0, 0); 243 } else { 244 // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once 245 Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, 246 "number of remaining bytes"); 247 248 // Can not receive into read-only buffers. 249 Preconditions.checkArgument(!(buffer.isReadOnly() && !isSend), "buffer can not be " 250 + "read-only when receiving data"); 251 252 if (!buffer.isDirect()) { 253 mTempBuffer = ByteBuffer.allocateDirect(mBuffer.remaining()); 254 255 if (isSend) { 256 // Copy buffer into temporary buffer 257 mBuffer.mark(); 258 mTempBuffer.put(mBuffer); 259 mTempBuffer.flip(); 260 mBuffer.reset(); 261 } 262 263 // Send/Receive into the temp buffer instead 264 buffer = mTempBuffer; 265 } 266 267 mIsUsingNewQueue = true; 268 wasQueued = native_queue(buffer, buffer.position(), buffer.remaining()); 269 } 270 } 271 272 if (!wasQueued) { 273 mIsUsingNewQueue = false; 274 mTempBuffer = null; 275 mBuffer = null; 276 } 277 278 return wasQueued; 279 } 280 dequeue(boolean useBufferOverflowInsteadOfIllegalArg)281 /* package */ void dequeue(boolean useBufferOverflowInsteadOfIllegalArg) { 282 boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); 283 int bytesTransferred; 284 285 synchronized (mLock) { 286 if (mIsUsingNewQueue) { 287 bytesTransferred = native_dequeue_direct(); 288 mIsUsingNewQueue = false; 289 290 if (mBuffer == null) { 291 // Nothing to do 292 } else if (mTempBuffer == null) { 293 mBuffer.position(mBuffer.position() + bytesTransferred); 294 } else { 295 mTempBuffer.limit(bytesTransferred); 296 297 // The user might have modified mBuffer which might make put/position fail. 298 // Changing the buffer while a request is in flight is not supported. Still, 299 // make sure to free mTempBuffer correctly. 300 try { 301 if (isSend) { 302 mBuffer.position(mBuffer.position() + bytesTransferred); 303 } else { 304 // Copy temp buffer back into original buffer 305 mBuffer.put(mTempBuffer); 306 } 307 } finally { 308 mTempBuffer = null; 309 } 310 } 311 } else { 312 if (mBuffer.isDirect()) { 313 bytesTransferred = native_dequeue_direct(); 314 } else { 315 bytesTransferred = native_dequeue_array(mBuffer.array(), mLength, isSend); 316 } 317 if (bytesTransferred >= 0) { 318 int bytesToStore = Math.min(bytesTransferred, mLength); 319 try { 320 mBuffer.position(bytesToStore); 321 } catch (IllegalArgumentException e) { 322 if (useBufferOverflowInsteadOfIllegalArg) { 323 Log.e(TAG, "Buffer " + mBuffer + " does not have enough space to read " 324 + bytesToStore + " bytes", e); 325 throw new BufferOverflowException(); 326 } else { 327 throw e; 328 } 329 } 330 } 331 } 332 333 mBuffer = null; 334 mLength = 0; 335 } 336 } 337 338 /** 339 * Cancels a pending queue operation. 340 * 341 * @return true if cancelling succeeded 342 */ cancel()343 public boolean cancel() { 344 return native_cancel(); 345 } 346 native_init(UsbDeviceConnection connection, int ep_address, int ep_attributes, int ep_max_packet_size, int ep_interval)347 private native boolean native_init(UsbDeviceConnection connection, int ep_address, 348 int ep_attributes, int ep_max_packet_size, int ep_interval); native_close()349 private native void native_close(); native_queue(ByteBuffer buffer, int offset, int length)350 private native boolean native_queue(ByteBuffer buffer, int offset, int length); native_queue_array(byte[] buffer, int length, boolean out)351 private native boolean native_queue_array(byte[] buffer, int length, boolean out); native_dequeue_array(byte[] buffer, int length, boolean out)352 private native int native_dequeue_array(byte[] buffer, int length, boolean out); native_queue_direct(ByteBuffer buffer, int length, boolean out)353 private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out); native_dequeue_direct()354 private native int native_dequeue_direct(); native_cancel()355 private native boolean native_cancel(); 356 } 357