1
0
mirror of https://github.com/mik3y/usb-serial-for-android synced 2025-06-07 07:56:20 +00:00

move setReadQueue to UsbSerialPort interface

This commit is contained in:
Kai Morich 2025-02-23 16:12:46 +01:00
parent 90246e5c7b
commit 1efbcf0a38
5 changed files with 62 additions and 46 deletions

View File

@ -1207,52 +1207,54 @@ public class DeviceTest {
@Override public Object getClientData() { count += 1; return super.getClientData(); } @Override public Object getClientData() { count += 1; return super.getClientData(); }
} }
usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD));
int len = usb.serialPort.getReadEndpoint().getMaxPacketSize();
usb.close();
CommonUsbSerialPortWrapper.setReadQueueRequestSupplier(usb.serialPort, CountingUsbRequest::new); CommonUsbSerialPortWrapper.setReadQueueRequestSupplier(usb.serialPort, CountingUsbRequest::new);
CommonUsbSerialPort port = (CommonUsbSerialPort) usb.serialPort; usb.serialPort.setReadQueue(2, 0);
assertEquals(0, usb.serialPort.getReadQueueBufferSize());
port.setReadQueue(2, len);
usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_START)); usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_START));
usb.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); int len = usb.serialPort.getReadEndpoint().getMaxPacketSize();
telnet.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); assertEquals(len, usb.serialPort.getReadQueueBufferSize());
assertEquals(2, port.getReadQueueBufferCount()); assertEquals(2, usb.serialPort.getReadQueueBufferCount());
assertEquals(4, usb.ioManager.getReadQueueBufferCount()); // not set at port yet assertEquals(4, usb.ioManager.getReadQueueBufferCount()); // not set at port yet
assertThrows(IllegalStateException.class, () -> usb.ioManager.setReadQueue(1)); // cannot reduce bufferCount assertThrows(IllegalStateException.class, () -> usb.ioManager.setReadQueue(1)); // cannot reduce bufferCount
usb.ioManager.setReadQueue(2); usb.ioManager.setReadQueue(2);
usb.ioManager.start(); usb.ioManager.start();
port.setReadQueue(4, len); usb.serialPort.setReadQueue(3, 0);
usb.serialPort.setReadQueue(3, len);
usb.ioManager.setReadQueue(4);
usb.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE);
telnet.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE);
// linux kernel does round-robin // linux kernel does round-robin
LinkedList<UsbRequest> requests = CommonUsbSerialPortWrapper.getReadQueueRequests(usb.serialPort); LinkedList<UsbRequest> requests = CommonUsbSerialPortWrapper.getReadQueueRequests(usb.serialPort);
assertNotNull(requests); assertNotNull(requests);
for (int i=0; i<16; i++) { for (int i=0; i<4*4; i++) {
telnet.write(new byte[1]); telnet.write(new byte[1]);
usb.read(1); usb.read(1);
} }
List<Integer> requestCounts; for (UsbRequest request : requests) {
if(usb.serialDriver instanceof FtdiSerialDriver) { int count = ((CountingUsbRequest)request).count;
for (UsbRequest request : requests) { if(usb.serialDriver instanceof FtdiSerialDriver) {
int count = ((CountingUsbRequest)request).count;
assertTrue(String.valueOf(count), count >= 4); assertTrue(String.valueOf(count), count >= 4);
} else {
assertEquals(String.valueOf(count), 4, count);
} }
} else {
requestCounts = requests.stream().map(r -> ((CountingUsbRequest)r).count).collect(Collectors.toList());
assertThat(requestCounts, equalTo(Arrays.asList(4, 4, 4, 4)));
} }
usb.ioManager.setReadQueue(6); usb.ioManager.setReadQueue(6);
for (int i=0; i<18; i++) { for (int i=0; i<3*6; i++) {
telnet.write(new byte[1]); telnet.write(new byte[1]);
usb.read(1); usb.read(1);
} }
requestCounts = requests.stream().map(r -> ((CountingUsbRequest)r).count).collect(Collectors.toList()); for (UsbRequest request : requests) {
if(!(usb.serialDriver instanceof FtdiSerialDriver)) { int count = ((CountingUsbRequest)request).count;
assertThat(requestCounts, equalTo(Arrays.asList(7, 7, 7, 7, 3, 3))); if(usb.serialDriver instanceof FtdiSerialDriver) {
assertTrue(String.valueOf(count), count >= 3);
} else {
assertTrue(String.valueOf(count), count == 7 || count == 3);
}
} }
usb.close(); usb.close();
usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_START)); usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_START));
port.setReadQueue(8, len); usb.serialPort.setReadQueue(8, len);
assertThrows(IllegalStateException.class, () -> usb.serialPort.read(new byte[len], 1) ); // cannot use timeout != 0 assertThrows(IllegalStateException.class, () -> usb.serialPort.read(new byte[len], 1) ); // cannot use timeout != 0
assertThrows(IllegalStateException.class, () -> usb.serialPort.read(new byte[4], 0) ); // cannot use different length assertThrows(IllegalStateException.class, () -> usb.serialPort.read(new byte[4], 0) ); // cannot use different length
assertThrows(IllegalStateException.class, () -> usb.ioManager.start()); // cannot reduce bufferCount assertThrows(IllegalStateException.class, () -> usb.ioManager.start()); // cannot reduce bufferCount

View File

@ -117,24 +117,24 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
} }
} }
/** @Override
* for applications doing permanent read() with timeout=0, multiple buffers can be
* used to copy next data from Linux kernel, while the current data is processed.
* @param bufferCount number of buffers to use for readQueue
* disabled with 0
* @param bufferSize size of each buffer
*/
public void setReadQueue(int bufferCount, int bufferSize) { public void setReadQueue(int bufferCount, int bufferSize) {
if (bufferCount < 0) { if (bufferCount < 0) {
throw new IllegalArgumentException("Invalid bufferCount"); throw new IllegalArgumentException("Invalid bufferCount");
} }
if (bufferCount > 0 && bufferSize <= 0) { if (bufferSize < 0) {
throw new IllegalArgumentException("Invalid bufferSize"); throw new IllegalArgumentException("Invalid bufferSize");
} }
if(isOpen()) { if(isOpen()) {
if (bufferCount < mReadQueueBufferCount) { if (bufferCount < mReadQueueBufferCount) {
throw new IllegalStateException("Cannot reduce bufferCount when port is open"); throw new IllegalStateException("Cannot reduce bufferCount when port is open");
} }
if (bufferSize == 0) {
bufferSize = mReadEndpoint.getMaxPacketSize();
}
if (mReadQueueBufferSize == 0) {
mReadQueueBufferSize = mReadEndpoint.getMaxPacketSize();
}
if (mReadQueueBufferCount != 0 && bufferSize != mReadQueueBufferSize) { if (mReadQueueBufferCount != 0 && bufferSize != mReadQueueBufferSize) {
throw new IllegalStateException("Cannot change bufferSize when port is open"); throw new IllegalStateException("Cannot change bufferSize when port is open");
} }
@ -156,7 +156,9 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
mReadQueueBufferSize = bufferSize; mReadQueueBufferSize = bufferSize;
} }
@Override
public int getReadQueueBufferCount() { return mReadQueueBufferCount; } public int getReadQueueBufferCount() { return mReadQueueBufferCount; }
@Override
public int getReadQueueBufferSize() { return mReadQueueBufferSize; } public int getReadQueueBufferSize() { return mReadQueueBufferSize; }
private boolean useReadQueue() { return mReadQueueBufferCount != 0; } private boolean useReadQueue() { return mReadQueueBufferCount != 0; }

View File

@ -104,6 +104,23 @@ public interface UsbSerialPort extends Closeable {
*/ */
String getSerial(); String getSerial();
/**
* Applications doing permanent {@link #read} with timeout=0 can reduce data loss likelihood
* at high baud rate and continuous data transfer by using multiple buffers to copy next data
* from Linux kernel, while the current data is processed.
* When enabled, {@link #read} can not be called with timeout!=0 or different buffer size.
*
* @param bufferCount number of buffers to use for readQueue.
* Use 0 to disable.
* @param bufferSize size of each buffer.
* Use 0 for optimal size (= getReadEndpoint().getMaxPacketSize()).
* @throws IllegalStateException if port is open and buffer count should be lowered or
* buffer size should be changed.
*/
void setReadQueue(int bufferCount, int bufferSize);
int getReadQueueBufferCount();
int getReadQueueBufferSize();
/** /**
* Opens and initializes the port. Upon success, caller must ensure that * Opens and initializes the port. Upon success, caller must ensure that
* {@link #close()} is eventually called. * {@link #close()} is eventually called.

View File

@ -39,7 +39,8 @@ public class SerialInputOutputManager {
private int mReadTimeout = 0; private int mReadTimeout = 0;
private int mWriteTimeout = 0; private int mWriteTimeout = 0;
private int mReadQueueBufferCount; // = READ_QUEUE_BUFFER_COUNT private int mReadQueueBufferCount = READ_QUEUE_BUFFER_COUNT;
// no mReadQueueBufferSize, using mReadBuffer.size instead
private final Object mReadBufferLock = new Object(); private final Object mReadBufferLock = new Object();
private final Object mWriteBufferLock = new Object(); private final Object mWriteBufferLock = new Object();
@ -68,8 +69,6 @@ public class SerialInputOutputManager {
public SerialInputOutputManager(UsbSerialPort serialPort) { public SerialInputOutputManager(UsbSerialPort serialPort) {
mSerialPort = serialPort; mSerialPort = serialPort;
mReadBuffer = ByteBuffer.allocate(serialPort.getReadEndpoint().getMaxPacketSize()); mReadBuffer = ByteBuffer.allocate(serialPort.getReadEndpoint().getMaxPacketSize());
mReadQueueBufferCount = serialPort instanceof CommonUsbSerialPort ? READ_QUEUE_BUFFER_COUNT : 0;
//readQueueBufferSize fixed to getMaxPacketSize()
} }
public SerialInputOutputManager(UsbSerialPort serialPort, Listener listener) { public SerialInputOutputManager(UsbSerialPort serialPort, Listener listener) {
@ -150,17 +149,16 @@ public class SerialInputOutputManager {
} }
/** /**
* Set read queue. Set buffer size before. * Set read queue, similar to {@link UsbSerialPort#setReadQueue}
* @param bufferCount number of buffers to use for readQueue * except buffer size to be set before with {@link #setReadBufferSize}.
*
* @param bufferCount number of buffers to use for readQueue,
* disable with value 0, * disable with value 0,
* default enabled as value 4 (READ_QUEUE_BUFFER_COUNT) * default enabled as value 4 (READ_QUEUE_BUFFER_COUNT)
*/ */
public void setReadQueue(int bufferCount) { public void setReadQueue(int bufferCount) {
if (!(mSerialPort instanceof CommonUsbSerialPort)) { mSerialPort.setReadQueue(bufferCount, getReadBufferSize());
throw new IllegalArgumentException("only for CommonUsbSerialPort based drivers"); mReadQueueBufferCount = bufferCount; // only store if set ok
}
mReadQueueBufferCount = bufferCount;
((CommonUsbSerialPort) mSerialPort).setReadQueue(getReadQueueBufferCount(), getReadBufferSize());
} }
public int getReadQueueBufferCount() { return mReadQueueBufferCount; } public int getReadQueueBufferCount() { return mReadQueueBufferCount; }
@ -179,9 +177,7 @@ public class SerialInputOutputManager {
* start SerialInputOutputManager in separate threads * start SerialInputOutputManager in separate threads
*/ */
public void start() { public void start() {
if(mSerialPort instanceof CommonUsbSerialPort) { mSerialPort.setReadQueue(mReadQueueBufferCount, getReadBufferSize());
((CommonUsbSerialPort) mSerialPort).setReadQueue(mReadQueueBufferCount, getReadBufferSize());
}
if(mState.compareAndSet(State.STOPPED, State.STARTING)) { if(mState.compareAndSet(State.STOPPED, State.STARTING)) {
mStartuplatch = new CountDownLatch(2); mStartuplatch = new CountDownLatch(2);
new Thread(this::runRead, this.getClass().getSimpleName() + "_read").start(); new Thread(this::runRead, this.getClass().getSimpleName() + "_read").start();

View File

@ -71,9 +71,8 @@ public class CommonUsbSerialPortTest {
// set before open // set before open
port.setReadQueue(0, 0); port.setReadQueue(0, 0);
port.setReadQueue(0, -1);
assertThrows(IllegalArgumentException.class, () -> port.setReadQueue(-1, 1)); assertThrows(IllegalArgumentException.class, () -> port.setReadQueue(-1, 1));
assertThrows(IllegalArgumentException.class, () -> port.setReadQueue(1, 0)); assertThrows(IllegalArgumentException.class, () -> port.setReadQueue(1, -1));
port.setReadQueue(2, 256); port.setReadQueue(2, 256);
assertNull(port.mReadQueueRequests); assertNull(port.mReadQueueRequests);