14
14
import android .util .Log ;
15
15
16
16
import com .hoho .android .usbserial .util .MonotonicClock ;
17
+ import com .hoho .android .usbserial .util .UsbUtils ;
17
18
18
19
import java .io .IOException ;
19
20
import java .nio .ByteBuffer ;
20
21
import java .util .EnumSet ;
22
+ import java .util .LinkedList ;
23
+ import java .util .Objects ;
21
24
22
25
/**
23
26
* A base class shared by several driver implementations.
@@ -38,8 +41,12 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
38
41
protected UsbDeviceConnection mConnection ;
39
42
protected UsbEndpoint mReadEndpoint ;
40
43
protected UsbEndpoint mWriteEndpoint ;
41
- protected UsbRequest mUsbRequest ;
44
+ protected UsbRequest mReadRequest ;
45
+ protected LinkedList <UsbRequest > mReadQueueRequests ;
46
+ private int mReadQueueBufferCount ;
47
+ private int mReadQueueBufferSize ;
42
48
protected FlowControl mFlowControl = FlowControl .NONE ;
49
+ protected UsbUtils .Supplier <UsbRequest > mUsbRequestSupplier = UsbRequest ::new ; // override for testing
43
50
44
51
/**
45
52
* Internal write buffer.
@@ -110,6 +117,50 @@ public final void setWriteBufferSize(int bufferSize) {
110
117
}
111
118
}
112
119
120
+ /**
121
+ * for applications doing permanent read() with timeout=0, multiple buffers can be
122
+ * used to copy next data from Linux kernel, while the current data is processed.
123
+ * @param bufferCount number of buffers to use for readQueue
124
+ * disabled with 0
125
+ * @param bufferSize size of each buffer
126
+ */
127
+ public void setReadQueue (int bufferCount , int bufferSize ) {
128
+ if (bufferCount < 0 ) {
129
+ throw new IllegalArgumentException ("Invalid bufferCount" );
130
+ }
131
+ if (bufferCount > 0 && bufferSize <= 0 ) {
132
+ throw new IllegalArgumentException ("Invalid bufferSize" );
133
+ }
134
+ if (isOpen ()) {
135
+ if (bufferCount < mReadQueueBufferCount ) {
136
+ throw new IllegalStateException ("Cannot reduce bufferCount when port is open" );
137
+ }
138
+ if (mReadQueueBufferCount != 0 && bufferSize != mReadQueueBufferSize ) {
139
+ throw new IllegalStateException ("Cannot change bufferSize when port is open" );
140
+ }
141
+ if (bufferCount > 0 ) {
142
+ if (mReadQueueRequests == null ) {
143
+ mReadQueueRequests = new LinkedList <>();
144
+ }
145
+ for (int i = mReadQueueRequests .size (); i < bufferCount ; i ++) {
146
+ ByteBuffer buffer = ByteBuffer .allocate (bufferSize );
147
+ UsbRequest request = mUsbRequestSupplier .get ();
148
+ request .initialize (mConnection , mReadEndpoint );
149
+ request .setClientData (buffer );
150
+ request .queue (buffer , bufferSize );
151
+ mReadQueueRequests .add (request );
152
+ }
153
+ }
154
+ }
155
+ mReadQueueBufferCount = bufferCount ;
156
+ mReadQueueBufferSize = bufferSize ;
157
+ }
158
+
159
+ public int getReadQueueBufferCount () { return mReadQueueBufferCount ; }
160
+ public int getReadQueueBufferSize () { return mReadQueueBufferSize ; }
161
+
162
+ private boolean useReadQueue () { return mReadQueueBufferCount != 0 ; }
163
+
113
164
@ Override
114
165
public void open (UsbDeviceConnection connection ) throws IOException {
115
166
if (mConnection != null ) {
@@ -125,8 +176,9 @@ public void open(UsbDeviceConnection connection) throws IOException {
125
176
if (mReadEndpoint == null || mWriteEndpoint == null ) {
126
177
throw new IOException ("Could not get read & write endpoints" );
127
178
}
128
- mUsbRequest = new UsbRequest ();
129
- mUsbRequest .initialize (mConnection , mReadEndpoint );
179
+ mReadRequest = mUsbRequestSupplier .get ();
180
+ mReadRequest .initialize (mConnection , mReadEndpoint );
181
+ setReadQueue (mReadQueueBufferCount , mReadQueueBufferSize ); // fill mReadQueueRequests
130
182
ok = true ;
131
183
} finally {
132
184
if (!ok ) {
@@ -144,11 +196,19 @@ public void close() throws IOException {
144
196
if (mConnection == null ) {
145
197
throw new IOException ("Already closed" );
146
198
}
147
- UsbRequest usbRequest = mUsbRequest ;
148
- mUsbRequest = null ;
199
+ UsbRequest readRequest = mReadRequest ;
200
+ mReadRequest = null ;
149
201
try {
150
- usbRequest .cancel ();
202
+ readRequest .cancel ();
151
203
} catch (Exception ignored ) {}
204
+ if (mReadQueueRequests != null ) {
205
+ for (UsbRequest readQueueRequest : mReadQueueRequests ) {
206
+ try {
207
+ readQueueRequest .cancel ();
208
+ } catch (Exception ignored ) {}
209
+ }
210
+ mReadQueueRequests = null ;
211
+ }
152
212
try {
153
213
closeInt ();
154
214
} catch (Exception ignored ) {}
@@ -168,7 +228,7 @@ protected void testConnection(boolean full) throws IOException {
168
228
}
169
229
170
230
protected void testConnection (boolean full , String msg ) throws IOException {
171
- if (mUsbRequest == null ) {
231
+ if (mReadRequest == null ) {
172
232
throw new IOException ("Connection closed" );
173
233
}
174
234
if (!full ) {
@@ -199,6 +259,9 @@ protected int read(final byte[] dest, int length, final int timeout, boolean tes
199
259
length = Math .min (length , dest .length );
200
260
final int nread ;
201
261
if (timeout != 0 ) {
262
+ if (useReadQueue ()) {
263
+ throw new IllegalStateException ("Cannot use timeout!=0 if readQueue is enabled" );
264
+ }
202
265
// bulkTransfer will cause data loss with short timeout + high baud rates + continuous transfer
203
266
// https://stackoverflow.com/questions/9108548/android-usb-host-bulktransfer-is-losing-data
204
267
// but mConnection.requestWait(timeout) available since Android 8.0 es even worse,
@@ -216,15 +279,31 @@ protected int read(final byte[] dest, int length, final int timeout, boolean tes
216
279
testConnection (MonotonicClock .millis () < endTime );
217
280
218
281
} else {
219
- final ByteBuffer buf = ByteBuffer .wrap (dest , 0 , length );
220
- if (!mUsbRequest .queue (buf , length )) {
221
- throw new IOException ("Queueing USB request failed" );
282
+ ByteBuffer buf = null ;
283
+ if (useReadQueue ()) {
284
+ if (length != mReadQueueBufferSize ) {
285
+ throw new IllegalStateException ("Cannot use different length if readQueue is enabled" );
286
+ }
287
+ } else {
288
+ buf = ByteBuffer .wrap (dest , 0 , length );
289
+ if (!mReadRequest .queue (buf , length )) {
290
+ throw new IOException ("Queueing USB request failed" );
291
+ }
222
292
}
223
293
final UsbRequest response = mConnection .requestWait ();
224
294
if (response == null ) {
225
295
throw new IOException ("Waiting for USB request failed" );
226
296
}
227
- nread = buf .position ();
297
+ if (useReadQueue ()) {
298
+ buf = (ByteBuffer ) response .getClientData ();
299
+ System .arraycopy (buf .array (), 0 , dest , 0 , buf .position ());
300
+ if (mReadRequest != null ) { // re-queue if connection not closed
301
+ if (!response .queue (buf , buf .capacity ())) {
302
+ throw new IOException ("Queueing USB request failed" );
303
+ }
304
+ }
305
+ }
306
+ nread = Objects .requireNonNull (buf ).position ();
228
307
// Android error propagation is improvable:
229
308
// response != null & nread == 0 can be: connection lost, buffer to small, ???
230
309
if (nread == 0 ) {
@@ -297,7 +376,7 @@ public void write(final byte[] src, int length, final int timeout) throws IOExce
297
376
298
377
@ Override
299
378
public boolean isOpen () {
300
- return mUsbRequest != null ;
379
+ return mReadRequest != null ;
301
380
}
302
381
303
382
@ Override
0 commit comments