diff --git a/usbSerialExamples/src/main/java/com/hoho/android/usbserial/examples/TerminalFragment.java b/usbSerialExamples/src/main/java/com/hoho/android/usbserial/examples/TerminalFragment.java index 95a8d759..c0132ad4 100644 --- a/usbSerialExamples/src/main/java/com/hoho/android/usbserial/examples/TerminalFragment.java +++ b/usbSerialExamples/src/main/java/com/hoho/android/usbserial/examples/TerminalFragment.java @@ -141,6 +141,25 @@ public boolean onOptionsItemSelected(MenuItem item) { if (id == R.id.clear) { receiveText.setText(""); return true; + } else if( id == R.id.send_break) { + if(!connected) { + Toast.makeText(getActivity(), "not connected", Toast.LENGTH_SHORT).show(); + } else { + try { + usbSerialPort.setBreak(true); + Thread.sleep(100); // should show progress bar instead of blocking UI thread + usbSerialPort.setBreak(false); + SpannableStringBuilder spn = new SpannableStringBuilder(); + spn.append("send \n"); + spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorSendText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + receiveText.append(spn); + } catch(UnsupportedOperationException ignored) { + Toast.makeText(getActivity(), "BREAK not supported", Toast.LENGTH_SHORT).show(); + } catch(Exception e) { + Toast.makeText(getActivity(), "BREAK failed: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + } + } + return true; } else { return super.onOptionsItemSelected(item); } diff --git a/usbSerialExamples/src/main/res/menu/menu_terminal.xml b/usbSerialExamples/src/main/res/menu/menu_terminal.xml index 77ba4438..07b51a8d 100644 --- a/usbSerialExamples/src/main/res/menu/menu_terminal.xml +++ b/usbSerialExamples/src/main/res/menu/menu_terminal.xml @@ -6,4 +6,8 @@ android:icon="@drawable/ic_delete_white_24dp" android:title="Clear" app:showAsAction="always" /> + diff --git a/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java b/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java index 92868c29..345ccfcb 100644 --- a/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java +++ b/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java @@ -1740,6 +1740,30 @@ public void controlLines() throws Exception { } } + @Test + public void setBreak() throws Exception { + usb.open(); + telnet.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); + usb.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); + doReadWrite(""); + usb.serialPort.setBreak(true); + Thread.sleep(100); + usb.serialPort.setBreak(false); + // RFC2217 has SET_CONTROL + REQ_BREAK_STATE request, but this is not supported by pyserial + // as there is no easy notification on condition. By default break is returned as + // 0 byte on Linux, see https://man7.org/linux/man-pages/man3/termios.3.html -> BRKINT + byte[] data = telnet.read(1); + if (usb.serialDriver instanceof CdcAcmSerialDriver) { + // BREAK forwarding not implemented by arduino_leonardo_bridge.ino + assertThat("", data, equalTo(new byte[]{})); + } else if(isCp21xxRestrictedPort) { + assertThat("", data, equalTo(new byte[]{0x26})); // send the last byte again? + } else { + assertThat("", data, equalTo(new byte[]{0})); + } + doReadWrite(""); + } + @Test public void deviceConnection() throws Exception { byte[] buf = new byte[256]; @@ -1813,6 +1837,11 @@ public void deviceConnection() throws Exception { } catch (IOException ignored) { } } + try { + usb.serialPort.setBreak(true); + fail("setBreak error expected"); + } catch (IOException ignored) { + } usb.close(); try { usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD, UsbWrapper.OpenCloseFlags.NO_DEVICE_CONNECTION)); diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java index fc75d684..26bb7f60 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java @@ -278,6 +278,12 @@ public EnumSet getControlLines() throws IOException { public EnumSet getSupportedControlLines() throws IOException { return EnumSet.of(ControlLine.RTS, ControlLine.DTR); } + + @Override + public void setBreak(boolean value) throws IOException { + sendAcmControlMessage(SEND_BREAK, value ? 0xffff : 0, null); + } + } public static Map getSupportedDevices() { diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Ch34xSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Ch34xSerialDriver.java index 7555bd91..eb3afcb5 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Ch34xSerialDriver.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Ch34xSerialDriver.java @@ -341,6 +341,25 @@ public EnumSet getControlLines() throws IOException { public EnumSet getSupportedControlLines() throws IOException { return EnumSet.allOf(ControlLine.class); } + + @Override + public void setBreak(boolean value) throws IOException { + byte[] req = new byte[2]; + if(controlIn(0x95, 0x1805, 0, req) < 0) { + throw new IOException("Error getting BREAK condition"); + } + if(value) { + req[0] &= ~1; + req[1] &= ~0x40; + } else { + req[0] |= 1; + req[1] |= 0x40; + } + int val = (req[1] & 0xff) << 8 | (req[0] & 0xff); + if(controlOut(0x9a, 0x1805, val) < 0) { + throw new IOException("Error setting BREAK condition"); + } + } } public static Map getSupportedDevices() { diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java index b8bf9c8c..1fec25bf 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java @@ -264,4 +264,7 @@ public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throw new UnsupportedOperationException(); } + @Override + public void setBreak(boolean value) throws IOException { throw new UnsupportedOperationException(); } + } diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Cp21xxSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Cp21xxSerialDriver.java index a65ac5ca..d8fc784e 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Cp21xxSerialDriver.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Cp21xxSerialDriver.java @@ -59,6 +59,7 @@ public class Cp21xxSerialPort extends CommonUsbSerialPort { */ private static final int SILABSER_IFC_ENABLE_REQUEST_CODE = 0x00; private static final int SILABSER_SET_LINE_CTL_REQUEST_CODE = 0x03; + private static final int SILABSER_SET_BREAK_REQUEST_CODE = 0x05; private static final int SILABSER_SET_MHS_REQUEST_CODE = 0x07; private static final int SILABSER_SET_BAUDRATE = 0x1E; private static final int SILABSER_FLUSH_REQUEST_CODE = 0x12; @@ -314,6 +315,10 @@ public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) } } + @Override + public void setBreak(boolean value) throws IOException { + setConfigSingle(SILABSER_SET_BREAK_REQUEST_CODE, value ? 1 : 0); + } } public static Map getSupportedDevices() { diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java index 64b2b843..8f628754 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java @@ -84,6 +84,7 @@ public class FtdiSerialPort extends CommonUsbSerialPort { private boolean baudRateWithPort = false; private boolean dtr = false; private boolean rts = false; + private int breakConfig = 0; public FtdiSerialPort(UsbDevice device, int portNumber) { super(device, portNumber); @@ -280,6 +281,7 @@ public void setParameters(int baudRate, int dataBits, int stopBits, int parity) if (result != 0) { throw new IOException("Setting parameters failed: result=" + result); } + breakConfig = config; } private int getStatus() throws IOException { @@ -366,7 +368,7 @@ public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) int result = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, RESET_REQUEST, RESET_PURGE_RX, mPortNumber+1, null, 0, USB_WRITE_TIMEOUT_MILLIS); if (result != 0) { - throw new IOException("purge write buffer failed: result=" + result); + throw new IOException("Purge write buffer failed: result=" + result); } } @@ -374,11 +376,22 @@ public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) int result = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, RESET_REQUEST, RESET_PURGE_TX, mPortNumber+1, null, 0, USB_WRITE_TIMEOUT_MILLIS); if (result != 0) { - throw new IOException("purge read buffer failed: result=" + result); + throw new IOException("Purge read buffer failed: result=" + result); } } } + @Override + public void setBreak(boolean value) throws IOException { + int config = breakConfig; + if(value) config |= 0x4000; + int result = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SET_DATA_REQUEST, + config, mPortNumber+1,null, 0, USB_WRITE_TIMEOUT_MILLIS); + if (result != 0) { + throw new IOException("Setting BREAK failed: result=" + result); + } + } + public void setLatencyTimer(int latencyTime) throws IOException { int result = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SET_LATENCY_TIMER_REQUEST, latencyTime, mPortNumber+1, null, 0, USB_WRITE_TIMEOUT_MILLIS); diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java index d21595e3..fe1d1517 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java @@ -77,6 +77,7 @@ class ProlificSerialPort extends CommonUsbSerialPort { private static final int SET_LINE_REQUEST = 0x20; // same as CDC SET_LINE_CODING private static final int SET_CONTROL_REQUEST = 0x22; // same as CDC SET_CONTROL_LINE_STATE + private static final int SEND_BREAK_REQUEST = 0x23; // same as CDC SEND_BREAK private static final int GET_CONTROL_REQUEST = 0x87; private static final int STATUS_NOTIFICATION = 0xa1; // similar to CDC SERIAL_STATE but different length @@ -492,6 +493,11 @@ public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) vendorOut(FLUSH_TX_REQUEST, 0, null); } } + + @Override + public void setBreak(boolean value) throws IOException { + ctrlOut(SEND_BREAK_REQUEST, value ? 0xffff : 0, 0, null); + } } public static Map getSupportedDevices() { diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java index 98699846..f6a024c0 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java @@ -248,6 +248,13 @@ public enum ControlLine { RTS, CTS, DTR, DSR, CD, RI }; */ public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException; + /** + * send BREAK condition. + * + * @param value set/reset + */ + public void setBreak(boolean value) throws IOException; + /** * Returns the current state of the connection. */