diff --git a/QGCApplication.pro b/QGCApplication.pro index 6dbe2fa68bd555daae5366389ac9eaeca77fd9bc..9d57a82195229a0b60777f6ff0c1df1cfd8bd094 100644 --- a/QGCApplication.pro +++ b/QGCApplication.pro @@ -61,12 +61,15 @@ QT += network \ qml \ quick \ quickwidgets \ - serialport \ sql \ svg \ widgets \ xml \ +!AndroidBuild { + QT += serialport +} + contains(DEFINES, QGC_NOTIFY_TUNES_ENABLED) { QT += multimedia } @@ -78,6 +81,11 @@ QT += testlib # OS Specific settings # +AndroidBuild { + DEFINES += __android__ + DEFINES += __STDC_LIMIT_MACROS +} + MacBuild { QMAKE_INFO_PLIST = Custom-Info.plist ICON = $$BASEDIR/resources/icons/macx.icns @@ -173,9 +181,6 @@ FORMS += \ src/ui/designer/QGCToolWidgetComboBox.ui \ src/ui/designer/QGCXYPlot.ui \ src/ui/HDDisplay.ui \ - src/ui/JoystickAxis.ui \ - src/ui/JoystickButton.ui \ - src/ui/JoystickWidget.ui \ src/ui/Linechart.ui \ src/ui/MainWindow.ui \ src/ui/map/QGCMapTool.ui \ @@ -229,6 +234,13 @@ FORMS += \ src/ui/WaypointList.ui \ src/ui/WaypointViewOnlyView.ui \ +!AndroidBuild { +FORMS += \ + src/ui/JoystickButton.ui \ + src/ui/JoystickAxis.ui \ + src/ui/JoystickWidget.ui +} + HEADERS += \ src/audio/QGCAudioWorker.h \ src/CmdLineOptParser.h \ @@ -250,7 +262,6 @@ HEADERS += \ src/comm/TCPLink.h \ src/comm/UDPLink.h \ src/GAudioOutput.h \ - src/input/JoystickInput.h \ src/LogCompressor.h \ src/MG.h \ src/QGC.h \ @@ -281,7 +292,6 @@ HEADERS += \ src/uas/UASParameterCommsMgr.h \ src/uas/UASParameterDataModel.h \ src/uas/UASWaypointManager.h \ - src/ui/CameraView.h \ src/ui/configuration/ApmHighlighter.h \ src/ui/configuration/console.h \ src/ui/configuration/SerialSettingsDialog.h \ @@ -300,9 +310,6 @@ HEADERS += \ src/ui/HDDisplay.h \ src/ui/HSIDisplay.h \ src/ui/HUD.h \ - src/ui/JoystickAxis.h \ - src/ui/JoystickButton.h \ - src/ui/JoystickWidget.h \ src/ui/linechart/ChartPlot.h \ src/ui/linechart/IncrementalPlot.h \ src/ui/linechart/LinechartPlot.h \ @@ -376,6 +383,15 @@ HEADERS += \ src/ViewWidgets/ViewWidgetController.h \ src/Waypoint.h \ +!AndroidBuild { +HEADERS += \ + src/input/JoystickInput.h \ + src/ui/JoystickAxis.h \ + src/ui/JoystickButton.h \ + src/ui/JoystickWidget.h \ + src/ui/CameraView.h \ +} + SOURCES += \ src/audio/QGCAudioWorker.cpp \ src/CmdLineOptParser.cc \ @@ -393,7 +409,6 @@ SOURCES += \ src/comm/TCPLink.cc \ src/comm/UDPLink.cc \ src/GAudioOutput.cc \ - src/input/JoystickInput.cc \ src/LogCompressor.cc \ src/main.cc \ src/QGC.cc \ @@ -418,7 +433,6 @@ SOURCES += \ src/uas/UASParameterCommsMgr.cc \ src/uas/UASParameterDataModel.cc \ src/uas/UASWaypointManager.cc \ - src/ui/CameraView.cc \ src/ui/configuration/ApmHighlighter.cc \ src/ui/configuration/console.cpp \ src/ui/configuration/SerialSettingsDialog.cc \ @@ -437,9 +451,6 @@ SOURCES += \ src/ui/HDDisplay.cc \ src/ui/HSIDisplay.cc \ src/ui/HUD.cc \ - src/ui/JoystickAxis.cc \ - src/ui/JoystickButton.cc \ - src/ui/JoystickWidget.cc \ src/ui/linechart/ChartPlot.cc \ src/ui/linechart/IncrementalPlot.cc \ src/ui/linechart/LinechartPlot.cc \ @@ -513,6 +524,15 @@ SOURCES += \ src/ViewWidgets/ViewWidgetController.cc \ src/Waypoint.cc \ +!AndroidBuild { +SOURCES += \ + src/input/JoystickInput.cc \ + src/ui/JoystickAxis.cc \ + src/ui/JoystickButton.cc \ + src/ui/JoystickWidget.cc \ + src/ui/CameraView.cc +} + # # Unit Test specific configuration goes here # @@ -524,6 +544,11 @@ SOURCES += \ DebugBuild|WindowsDebugAndRelease { +HEADERS += src/QmlControls/QmlTestWidget.h +SOURCES += src/QmlControls/QmlTestWidget.cc + +!AndroidBuild { + INCLUDEPATH += \ src/qgcunittest @@ -550,7 +575,6 @@ HEADERS += \ src/qgcunittest/MockLinkMissionItemHandler.h \ src/qgcunittest/PX4RCCalibrationTest.h \ src/qgcunittest/UnitTest.h \ - src/QmlControls/QmlTestWidget.h \ src/VehicleSetup/SetupViewTest.h \ SOURCES += \ @@ -575,10 +599,10 @@ SOURCES += \ src/qgcunittest/MockLinkMissionItemHandler.cc \ src/qgcunittest/PX4RCCalibrationTest.cc \ src/qgcunittest/UnitTest.cc \ - src/QmlControls/QmlTestWidget.cc \ src/VehicleSetup/SetupViewTest.cc \ -} +} # DebugBuild|WindowsDebugAndRelease +} # AndroidBuild # # AutoPilot Plugin Support @@ -658,3 +682,26 @@ SOURCES += \ src/FactSystem/FactSystem.cc \ src/FactSystem/FactValidator.cc \ src/FactSystem/ParameterLoader.cc \ + +# Android + +AndroidBuild { + include($$PWD/libs/qtandroidserialport/src/qtandroidserialport.pri) + message("Adding Serial Java Classes") + QT += androidextras + ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + OTHER_FILES += \ + $$PWD/android/AndroidManifest.xml \ + $$PWD/android/res/xml/device_filter.xml \ + $$PWD/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/UsbId.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java \ + $$PWD/android/src/org/qgroundcontrol/qgchelper/UsbDeviceJNI.java \ + $$PWD/android/src/org/qgroundcontrol/qgchelper/UsbIoManager.java +} diff --git a/QGCCommon.pri b/QGCCommon.pri index b1e891af7e713c0f287330ba0aca4b336e66e202..ce362f1fa955fb65489669eef07637fc39a2d3e3 100644 --- a/QGCCommon.pri +++ b/QGCCommon.pri @@ -30,6 +30,10 @@ linux { linux-g++ | linux-g++-64 { message("Linux build") CONFIG += LinuxBuild + } else : android-g++ { + message("Android build") + CONFIG += AndroidBuild + warning("Android build is experimental and not fully functional") } else { error("Unsuported Linux toolchain, only GCC 32- or 64-bit is supported") } diff --git a/QGCSetup.pri b/QGCSetup.pri index ae7e716afcd1e3752c34779784f00ea0b911aad4..cce994557c81bf9a1832c27e47c7a425323283be 100644 --- a/QGCSetup.pri +++ b/QGCSetup.pri @@ -19,6 +19,10 @@ QMAKE_POST_LINK += echo "Copying files" +AndroidBuild { + INSTALLS += $$DESTDIR +} + # # Copy the application resources to the associated place alongside the application # @@ -40,8 +44,10 @@ WindowsBuild { BASEDIR_COPY_RESOURCE_LIST = $$replace(BASEDIR,"/","\\") QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY_DIR \"$$BASEDIR_COPY_RESOURCE_LIST\\flightgear\" \"$$DESTDIR_COPY_RESOURCE_LIST\\flightgear\" } else { - # Make sure to keep both side of this if using the same set of directories - QMAKE_POST_LINK += && $$QMAKE_COPY_DIR $$BASEDIR/flightgear $$DESTDIR_COPY_RESOURCE_LIST + !AndroidBuild { + # Make sure to keep both sides of this if using the same set of directories + QMAKE_POST_LINK += && $$QMAKE_COPY_DIR $$BASEDIR/flightgear $$DESTDIR_COPY_RESOURCE_LIST + } } # diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..21fcc18846fe7a5d125916d7b18a4e8044627469 --- /dev/null +++ b/android/AndroidManifest.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/res/drawable-ldpi/icon.png b/android/res/drawable-ldpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f487d95c0fb9f40af5ecd2d2feea9473bcd30a0a Binary files /dev/null and b/android/res/drawable-ldpi/icon.png differ diff --git a/android/res/xml/device_filter.xml b/android/res/xml/device_filter.xml new file mode 100644 index 0000000000000000000000000000000000000000..a149a80b508d8f8978a9fdd66bfedd4b78ca4046 --- /dev/null +++ b/android/res/xml/device_filter.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java b/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..3dec53a9a78daf83393b11a77147bcc3a21abe86 --- /dev/null +++ b/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java @@ -0,0 +1,252 @@ +package com.hoho.android.usbserial.driver; + +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.util.Log; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * USB CDC/ACM serial driver implementation. + * + * @author mike wakerly (opensource@hoho.com) + * @see Universal + * Serial Bus Class Definitions for Communication Devices, v1.1 + */ +public class CdcAcmSerialDriver extends CommonUsbSerialDriver { + + private final String TAG = CdcAcmSerialDriver.class.getSimpleName(); + + private UsbInterface mControlInterface; + private UsbInterface mDataInterface; + + private UsbEndpoint mControlEndpoint; + private UsbEndpoint mReadEndpoint; + private UsbEndpoint mWriteEndpoint; + + private boolean mRts = false; + private boolean mDtr = false; + + private static final int USB_RECIP_INTERFACE = 0x01; + private static final int USB_RT_ACM = UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE; + + private static final int SET_LINE_CODING = 0x20; // USB CDC 1.1 section 6.2 + private static final int GET_LINE_CODING = 0x21; + private static final int SET_CONTROL_LINE_STATE = 0x22; + private static final int SEND_BREAK = 0x23; + + public CdcAcmSerialDriver(UsbDevice device, UsbDeviceConnection connection) { + super(device, connection); + } + + @Override + public void open() throws IOException { + Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount()); + + Log.d(TAG, "Claiming control interface."); + mControlInterface = mDevice.getInterface(0); + Log.d(TAG, "Control iface=" + mControlInterface); + // class should be USB_CLASS_COMM + + if (!mConnection.claimInterface(mControlInterface, true)) { + throw new IOException("Could not claim control interface."); + } + mControlEndpoint = mControlInterface.getEndpoint(0); + Log.d(TAG, "Control endpoint direction: " + mControlEndpoint.getDirection()); + + Log.d(TAG, "Claiming data interface."); + mDataInterface = mDevice.getInterface(1); + Log.d(TAG, "data iface=" + mDataInterface); + // class should be USB_CLASS_CDC_DATA + + if (!mConnection.claimInterface(mDataInterface, true)) { + throw new IOException("Could not claim data interface."); + } + mReadEndpoint = mDataInterface.getEndpoint(1); + Log.d(TAG, "Read endpoint direction: " + mReadEndpoint.getDirection()); + mWriteEndpoint = mDataInterface.getEndpoint(0); + Log.d(TAG, "Write endpoint direction: " + mWriteEndpoint.getDirection()); + } + + private int sendAcmControlMessage(int request, int value, byte[] buf) { + return mConnection.controlTransfer( + USB_RT_ACM, request, value, 0, buf, buf != null ? buf.length : 0, 5000); + } + + @Override + public void close() throws IOException { + mConnection.close(); + } + + @Override + public int read(byte[] dest, int timeoutMillis) throws IOException { + final int numBytesRead; + synchronized (mReadBufferLock) { + int readAmt = Math.min(dest.length, mReadBuffer.length); + numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt, + timeoutMillis); + if (numBytesRead < 0) { + // This sucks: we get -1 on timeout, not 0 as preferred. + // We *should* use UsbRequest, except it has a bug/api oversight + // where there is no way to determine the number of bytes read + // in response :\ -- http://b.android.com/28023 + return 0; + } + System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead); + } + return numBytesRead; + } + + @Override + public int write(byte[] src, int timeoutMillis) throws IOException { + // TODO(mikey): Nearly identical to FtdiSerial write. Refactor. + int offset = 0; + + while (offset < src.length) { + final int writeLength; + final int amtWritten; + + synchronized (mWriteBufferLock) { + final byte[] writeBuffer; + + writeLength = Math.min(src.length - offset, mWriteBuffer.length); + if (offset == 0) { + writeBuffer = src; + } else { + // bulkTransfer does not support offsets, make a copy. + System.arraycopy(src, offset, mWriteBuffer, 0, writeLength); + writeBuffer = mWriteBuffer; + } + + amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength, + timeoutMillis); + } + if (amtWritten <= 0) { + throw new IOException("Error writing " + writeLength + + " bytes at offset " + offset + " length=" + src.length); + } + + //Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength); + offset += amtWritten; + } + return offset; + } + + @Override + public void setParameters(int baudRate, int dataBits, int stopBits, int parity) { + byte stopBitsByte; + switch (stopBits) { + case STOPBITS_1: stopBitsByte = 0; break; + case STOPBITS_1_5: stopBitsByte = 1; break; + case STOPBITS_2: stopBitsByte = 2; break; + default: throw new IllegalArgumentException("Bad value for stopBits: " + stopBits); + } + + byte parityBitesByte; + switch (parity) { + case PARITY_NONE: parityBitesByte = 0; break; + case PARITY_ODD: parityBitesByte = 1; break; + case PARITY_EVEN: parityBitesByte = 2; break; + case PARITY_MARK: parityBitesByte = 3; break; + case PARITY_SPACE: parityBitesByte = 4; break; + default: throw new IllegalArgumentException("Bad value for parity: " + parity); + } + + byte[] msg = { + (byte) ( baudRate & 0xff), + (byte) ((baudRate >> 8 ) & 0xff), + (byte) ((baudRate >> 16) & 0xff), + (byte) ((baudRate >> 24) & 0xff), + stopBitsByte, + parityBitesByte, + (byte) dataBits}; + sendAcmControlMessage(SET_LINE_CODING, 0, msg); + } + + @Override + public boolean getCD() throws IOException { + return false; // TODO + } + + @Override + public boolean getCTS() throws IOException { + return false; // TODO + } + + @Override + public boolean getDSR() throws IOException { + return false; // TODO + } + + @Override + public boolean getDTR() throws IOException { + return mDtr; + } + + @Override + public void setDTR(boolean value) throws IOException { + mDtr = value; + setDtrRts(); + } + + @Override + public boolean getRI() throws IOException { + return false; // TODO + } + + @Override + public boolean getRTS() throws IOException { + return mRts; + } + + @Override + public void setRTS(boolean value) throws IOException { + mRts = value; + setDtrRts(); + } + + private void setDtrRts() { + int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0); + sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null); + } + + public static Map getSupportedDevices() { + final Map supportedDevices = new LinkedHashMap(); + supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ARDUINO), + new int[] { + UsbId.ARDUINO_UNO, + UsbId.ARDUINO_UNO_R3, + UsbId.ARDUINO_MEGA_2560, + UsbId.ARDUINO_MEGA_2560_R3, + UsbId.ARDUINO_SERIAL_ADAPTER, + UsbId.ARDUINO_SERIAL_ADAPTER_R3, + UsbId.ARDUINO_MEGA_ADK, + UsbId.ARDUINO_MEGA_ADK_R3, + UsbId.ARDUINO_LEONARDO, + }); + supportedDevices.put(Integer.valueOf(UsbId.VENDOR_VAN_OOIJEN_TECH), + new int[] { + UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL, + }); + supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ATMEL), + new int[] { + UsbId.ATMEL_LUFA_CDC_DEMO_APP, + }); + supportedDevices.put(Integer.valueOf(UsbId.VENDOR_LEAFLABS), + new int[] { + UsbId.LEAFLABS_MAPLE, + }); + supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PX4), + new int[] { + UsbId.DEVICE_PX4FMU, + }); + return supportedDevices; + } + +} diff --git a/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java b/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..734933a22238264aa766ab39ec4365b3b4b0f50e --- /dev/null +++ b/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java @@ -0,0 +1,156 @@ +/* Copyright 2013 Google Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * Project home page: http://code.google.com/p/usb-serial-for-android/ + */ + +package com.hoho.android.usbserial.driver; + +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; + +import java.io.IOException; + +/** + * A base class shared by several driver implementations. + * + * @author mike wakerly (opensource@hoho.com) + */ +abstract class CommonUsbSerialDriver implements UsbSerialDriver { + + public static final int DEFAULT_READ_BUFFER_SIZE = 16 * 1024; + public static final int DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024; + + protected final UsbDevice mDevice; + protected final UsbDeviceConnection mConnection; + + protected final Object mReadBufferLock = new Object(); + protected final Object mWriteBufferLock = new Object(); + + /** Internal read buffer. Guarded by {@link #mReadBufferLock}. */ + protected byte[] mReadBuffer; + + /** Internal write buffer. Guarded by {@link #mWriteBufferLock}. */ + protected byte[] mWriteBuffer; + + public CommonUsbSerialDriver(UsbDevice device, UsbDeviceConnection connection) { + mDevice = device; + mConnection = connection; + + mReadBuffer = new byte[DEFAULT_READ_BUFFER_SIZE]; + mWriteBuffer = new byte[DEFAULT_WRITE_BUFFER_SIZE]; + } + + /** + * Returns the currently-bound USB device. + * + * @return the device + */ + @Override + public final UsbDevice getDevice() { + return mDevice; + } + + /** + * Returns the currently-bound USB device connection. + * + * @return the device connection + */ + @Override + public final UsbDeviceConnection getDeviceConnection() { + return mConnection; + } + + + + /** + * Sets the size of the internal buffer used to exchange data with the USB + * stack for read operations. Most users should not need to change this. + * + * @param bufferSize the size in bytes + */ + public final void setReadBufferSize(int bufferSize) { + synchronized (mReadBufferLock) { + if (bufferSize == mReadBuffer.length) { + return; + } + mReadBuffer = new byte[bufferSize]; + } + } + + /** + * Sets the size of the internal buffer used to exchange data with the USB + * stack for write operations. Most users should not need to change this. + * + * @param bufferSize the size in bytes + */ + public final void setWriteBufferSize(int bufferSize) { + synchronized (mWriteBufferLock) { + if (bufferSize == mWriteBuffer.length) { + return; + } + mWriteBuffer = new byte[bufferSize]; + } + } + + @Override + public abstract void open() throws IOException; + + @Override + public abstract void close() throws IOException; + + @Override + public abstract int read(final byte[] dest, final int timeoutMillis) throws IOException; + + @Override + public abstract int write(final byte[] src, final int timeoutMillis) throws IOException; + + @Override + public abstract void setParameters( + int baudRate, int dataBits, int stopBits, int parity) throws IOException; + + @Override + public abstract boolean getCD() throws IOException; + + @Override + public abstract boolean getCTS() throws IOException; + + @Override + public abstract boolean getDSR() throws IOException; + + @Override + public abstract boolean getDTR() throws IOException; + + @Override + public abstract void setDTR(boolean value) throws IOException; + + @Override + public abstract boolean getRI() throws IOException; + + @Override + public abstract boolean getRTS() throws IOException; + + @Override + public abstract void setRTS(boolean value) throws IOException; + + @Override + public boolean purgeHwBuffers(boolean flushReadBuffers, boolean flushWriteBuffers) throws IOException { + return !flushReadBuffers && !flushWriteBuffers; + } + +} + diff --git a/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java b/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..aa151ba1a0e6887ea4b7985a43044dd4eb6fcbd2 --- /dev/null +++ b/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java @@ -0,0 +1,292 @@ +package com.hoho.android.usbserial.driver; + +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.util.Log; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +public class Cp2102SerialDriver extends CommonUsbSerialDriver { + + private static final String TAG = Cp2102SerialDriver.class.getSimpleName(); + + private static final int DEFAULT_BAUD_RATE = 9600; + + private static final int USB_WRITE_TIMEOUT_MILLIS = 5000; + + /* + * Configuration Request Types + */ + private static final int REQTYPE_HOST_TO_DEVICE = 0x41; + + /* + * Configuration Request Codes + */ + private static final int SILABSER_IFC_ENABLE_REQUEST_CODE = 0x00; + private static final int SILABSER_SET_BAUDDIV_REQUEST_CODE = 0x01; + private static final int SILABSER_SET_LINE_CTL_REQUEST_CODE = 0x03; + 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; + + private static final int FLUSH_READ_CODE = 0x0a; + private static final int FLUSH_WRITE_CODE = 0x05; + + /* + * SILABSER_IFC_ENABLE_REQUEST_CODE + */ + private static final int UART_ENABLE = 0x0001; + private static final int UART_DISABLE = 0x0000; + + /* + * SILABSER_SET_BAUDDIV_REQUEST_CODE + */ + private static final int BAUD_RATE_GEN_FREQ = 0x384000; + + /* + * SILABSER_SET_MHS_REQUEST_CODE + */ + private static final int MCR_DTR = 0x0001; + private static final int MCR_RTS = 0x0002; + private static final int MCR_ALL = 0x0003; + + private static final int CONTROL_WRITE_DTR = 0x0100; + private static final int CONTROL_WRITE_RTS = 0x0200; + + private UsbEndpoint mReadEndpoint; + private UsbEndpoint mWriteEndpoint; + + public Cp2102SerialDriver(UsbDevice device, UsbDeviceConnection connection) { + super(device, connection); + } + + private int setConfigSingle(int request, int value) { + return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, value, + 0, null, 0, USB_WRITE_TIMEOUT_MILLIS); + } + + @Override + public void open() throws IOException { + boolean opened = false; + try { + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + UsbInterface usbIface = mDevice.getInterface(i); + if (mConnection.claimInterface(usbIface, true)) { + Log.d(TAG, "claimInterface " + i + " SUCCESS"); + } else { + Log.d(TAG, "claimInterface " + i + " FAIL"); + } + } + + UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1); + for (int i = 0; i < dataIface.getEndpointCount(); i++) { + UsbEndpoint ep = dataIface.getEndpoint(i); + if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { + if (ep.getDirection() == UsbConstants.USB_DIR_IN) { + mReadEndpoint = ep; + } else { + mWriteEndpoint = ep; + } + } + } + + setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_ENABLE); + setConfigSingle(SILABSER_SET_MHS_REQUEST_CODE, MCR_ALL | CONTROL_WRITE_DTR | CONTROL_WRITE_RTS); + setConfigSingle(SILABSER_SET_BAUDDIV_REQUEST_CODE, BAUD_RATE_GEN_FREQ / DEFAULT_BAUD_RATE); +// setParameters(DEFAULT_BAUD_RATE, DEFAULT_DATA_BITS, DEFAULT_STOP_BITS, DEFAULT_PARITY); + opened = true; + } finally { + if (!opened) { + close(); + } + } + } + + @Override + public void close() throws IOException { + setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_DISABLE); + mConnection.close(); + } + + @Override + public int read(byte[] dest, int timeoutMillis) throws IOException { + final int numBytesRead; + synchronized (mReadBufferLock) { + int readAmt = Math.min(dest.length, mReadBuffer.length); + numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt, + timeoutMillis); + if (numBytesRead < 0) { + // This sucks: we get -1 on timeout, not 0 as preferred. + // We *should* use UsbRequest, except it has a bug/api oversight + // where there is no way to determine the number of bytes read + // in response :\ -- http://b.android.com/28023 + return 0; + } + System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead); + } + return numBytesRead; + } + + @Override + public int write(byte[] src, int timeoutMillis) throws IOException { + int offset = 0; + + while (offset < src.length) { + final int writeLength; + final int amtWritten; + + synchronized (mWriteBufferLock) { + final byte[] writeBuffer; + + writeLength = Math.min(src.length - offset, mWriteBuffer.length); + if (offset == 0) { + writeBuffer = src; + } else { + // bulkTransfer does not support offsets, make a copy. + System.arraycopy(src, offset, mWriteBuffer, 0, writeLength); + writeBuffer = mWriteBuffer; + } + + amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength, + timeoutMillis); + } + if (amtWritten <= 0) { + throw new IOException("Error writing " + writeLength + + " bytes at offset " + offset + " length=" + src.length); + } + + //Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength); + offset += amtWritten; + } + return offset; + } + + private void setBaudRate(int baudRate) throws IOException { + byte[] data = new byte[] { + (byte) ( baudRate & 0xff), + (byte) ((baudRate >> 8 ) & 0xff), + (byte) ((baudRate >> 16) & 0xff), + (byte) ((baudRate >> 24) & 0xff) + }; + int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_BAUDRATE, + 0, 0, data, 4, USB_WRITE_TIMEOUT_MILLIS); + if (ret < 0) { + throw new IOException("Error setting baud rate."); + } + } + + @Override + public void setParameters(int baudRate, int dataBits, int stopBits, int parity) + throws IOException { + setBaudRate(baudRate); + + int configDataBits = 0; + switch (dataBits) { + case DATABITS_5: + configDataBits |= 0x0500; + break; + case DATABITS_6: + configDataBits |= 0x0600; + break; + case DATABITS_7: + configDataBits |= 0x0700; + break; + case DATABITS_8: + configDataBits |= 0x0800; + break; + default: + configDataBits |= 0x0800; + break; + } + setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configDataBits); + + int configParityBits = 0; // PARITY_NONE + switch (parity) { + case PARITY_ODD: + configParityBits |= 0x0010; + break; + case PARITY_EVEN: + configParityBits |= 0x0020; + break; + } + setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configParityBits); + + int configStopBits = 0; + switch (stopBits) { + case STOPBITS_1: + configStopBits |= 0; + break; + case STOPBITS_2: + configStopBits |= 2; + break; + } + setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configStopBits); + } + + @Override + public boolean getCD() throws IOException { + return false; + } + + @Override + public boolean getCTS() throws IOException { + return false; + } + + @Override + public boolean getDSR() throws IOException { + return false; + } + + @Override + public boolean getDTR() throws IOException { + return true; + } + + @Override + public void setDTR(boolean value) throws IOException { + } + + @Override + public boolean getRI() throws IOException { + return false; + } + + @Override + public boolean getRTS() throws IOException { + return true; + } + + @Override + public boolean purgeHwBuffers(boolean purgeReadBuffers, + boolean purgeWriteBuffers) throws IOException { + int value = (purgeReadBuffers ? FLUSH_READ_CODE : 0) + | (purgeWriteBuffers ? FLUSH_WRITE_CODE : 0); + + if (value != 0) { + setConfigSingle(SILABSER_FLUSH_REQUEST_CODE, value); + } + + return true; + } + + @Override + public void setRTS(boolean value) throws IOException { + } + + public static Map getSupportedDevices() { + final Map supportedDevices = new LinkedHashMap(); + supportedDevices.put(Integer.valueOf(UsbId.VENDOR_SILAB), + new int[] { + UsbId.SILAB_CP2102 + }); + return supportedDevices; + } + + +} diff --git a/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java b/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..ad3627ff38a75a50b71167ddf50f50b4ee5b1b51 --- /dev/null +++ b/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java @@ -0,0 +1,552 @@ +/* Copyright 2011 Google Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * Project home page: http://code.google.com/p/usb-serial-for-android/ + */ + +package com.hoho.android.usbserial.driver; + +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbRequest; +import android.util.Log; + + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A {@link CommonUsbSerialDriver} implementation for a variety of FTDI devices + *

+ * This driver is based on + * libftdi, and is + * copyright and subject to the following terms: + * + *

+ *   Copyright (C) 2003 by Intra2net AG
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License
+ *   version 2.1 as published by the Free Software Foundation;
+ *
+ *   opensource@intra2net.com
+ *   http://www.intra2net.com/en/developer/libftdi
+ * 
+ * + *

+ *

+ * Some FTDI devices have not been tested; see later listing of supported and + * unsupported devices. Devices listed as "supported" support the following + * features: + *

    + *
  • Read and write of serial data (see {@link #read(byte[], int)} and + * {@link #write(byte[], int)}. + *
  • Setting baud rate (see {@link #setBaudRate(int)}). + *
+ *

+ *

+ * Supported and tested devices: + *

    + *
  • {@value DeviceType#TYPE_R}
  • + *
+ *

+ *

+ * Unsupported but possibly working devices (please contact the author with + * feedback or patches): + *

    + *
  • {@value DeviceType#TYPE_2232C}
  • + *
  • {@value DeviceType#TYPE_2232H}
  • + *
  • {@value DeviceType#TYPE_4232H}
  • + *
  • {@value DeviceType#TYPE_AM}
  • + *
  • {@value DeviceType#TYPE_BM}
  • + *
+ *

+ * + * @author mike wakerly (opensource@hoho.com) + * @see USB Serial + * for Android project page + * @see FTDI Homepage + * @see libftdi + */ +public class FtdiSerialDriver extends CommonUsbSerialDriver { + + public static final int USB_TYPE_STANDARD = 0x00 << 5; + public static final int USB_TYPE_CLASS = 0x00 << 5; + public static final int USB_TYPE_VENDOR = 0x00 << 5; + public static final int USB_TYPE_RESERVED = 0x00 << 5; + + public static final int USB_RECIP_DEVICE = 0x00; + public static final int USB_RECIP_INTERFACE = 0x01; + public static final int USB_RECIP_ENDPOINT = 0x02; + public static final int USB_RECIP_OTHER = 0x03; + + public static final int USB_ENDPOINT_IN = 0x80; + public static final int USB_ENDPOINT_OUT = 0x00; + + public static final int USB_WRITE_TIMEOUT_MILLIS = 5000; + public static final int USB_READ_TIMEOUT_MILLIS = 5000; + + // From ftdi.h + /** + * Reset the port. + */ + private static final int SIO_RESET_REQUEST = 0; + + /** + * Set the modem control register. + */ + private static final int SIO_MODEM_CTRL_REQUEST = 1; + + /** + * Set flow control register. + */ + private static final int SIO_SET_FLOW_CTRL_REQUEST = 2; + + /** + * Set baud rate. + */ + private static final int SIO_SET_BAUD_RATE_REQUEST = 3; + + /** + * Set the data characteristics of the port. + */ + private static final int SIO_SET_DATA_REQUEST = 4; + + private static final int SIO_RESET_SIO = 0; + private static final int SIO_RESET_PURGE_RX = 1; + private static final int SIO_RESET_PURGE_TX = 2; + + public static final int FTDI_DEVICE_OUT_REQTYPE = + UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT; + + public static final int FTDI_DEVICE_IN_REQTYPE = + UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN; + + /** + * Length of the modem status header, transmitted with every read. + */ + private static final int MODEM_STATUS_HEADER_LENGTH = 2; + + private final String TAG = FtdiSerialDriver.class.getSimpleName(); + + private DeviceType mType; + + /** + * FTDI chip types. + */ + private static enum DeviceType { + TYPE_BM, TYPE_AM, TYPE_2232C, TYPE_R, TYPE_2232H, TYPE_4232H; + } + + private int mInterface = 0; /* INTERFACE_ANY */ + + private int mMaxPacketSize = 64; // TODO(mikey): detect + + /** + * Due to http://b.android.com/28023 , we cannot use UsbRequest async reads + * since it gives no indication of number of bytes read. Set this to + * {@code true} on platforms where it is fixed. + */ + private static final boolean ENABLE_ASYNC_READS = false; + + /** + * Filter FTDI status bytes from buffer + * @param src The source buffer (which contains status bytes) + * @param dest The destination buffer to write the status bytes into (can be src) + * @param totalBytesRead Number of bytes read to src + * @param maxPacketSize The USB endpoint max packet size + * @return The number of payload bytes + */ + private final int filterStatusBytes(byte[] src, byte[] dest, int totalBytesRead, int maxPacketSize) { + final int packetsCount = totalBytesRead / maxPacketSize + 1; + for (int packetIdx = 0; packetIdx < packetsCount; ++packetIdx) { + final int count = (packetIdx == (packetsCount - 1)) + ? (totalBytesRead % maxPacketSize) - MODEM_STATUS_HEADER_LENGTH + : maxPacketSize - MODEM_STATUS_HEADER_LENGTH; + if (count > 0) { + System.arraycopy(src, + packetIdx * maxPacketSize + MODEM_STATUS_HEADER_LENGTH, + dest, + packetIdx * (maxPacketSize - MODEM_STATUS_HEADER_LENGTH), + count); + } + } + + return totalBytesRead - (packetsCount * 2); + } + + /** + * Constructor. + * + * @param usbDevice the {@link UsbDevice} to use + * @param usbConnection the {@link UsbDeviceConnection} to use + * @throws UsbSerialRuntimeException if the given device is incompatible + * with this driver + */ + public FtdiSerialDriver(UsbDevice usbDevice, UsbDeviceConnection usbConnection) { + super(usbDevice, usbConnection); + mType = null; + } + + public void reset() throws IOException { + int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST, + SIO_RESET_SIO, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS); + if (result != 0) { + throw new IOException("Reset failed: result=" + result); + } + + // TODO(mikey): autodetect. + mType = DeviceType.TYPE_R; + } + + @Override + public void open() throws IOException { + boolean opened = false; + try { + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + if (mConnection.claimInterface(mDevice.getInterface(i), true)) { + Log.d(TAG, "claimInterface " + i + " SUCCESS"); + } else { + throw new IOException("Error claiming interface " + i); + } + } + reset(); + opened = true; + } finally { + if (!opened) { + close(); + } + } + } + + @Override + public void close() { + mConnection.close(); + } + + @Override + public int read(byte[] dest, int timeoutMillis) throws IOException { + final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(0); + + if (ENABLE_ASYNC_READS) { + final int readAmt; + synchronized (mReadBufferLock) { + // mReadBuffer is only used for maximum read size. + readAmt = Math.min(dest.length, mReadBuffer.length); + } + + final UsbRequest request = new UsbRequest(); + request.initialize(mConnection, endpoint); + + final ByteBuffer buf = ByteBuffer.wrap(dest); + if (!request.queue(buf, readAmt)) { + throw new IOException("Error queueing request."); + } + + final UsbRequest response = mConnection.requestWait(); + if (response == null) { + throw new IOException("Null response"); + } + + final int payloadBytesRead = buf.position() - MODEM_STATUS_HEADER_LENGTH; + if (payloadBytesRead > 0) { + return payloadBytesRead; + } else { + return 0; + } + } else { + final int totalBytesRead; + + synchronized (mReadBufferLock) { + final int readAmt = Math.min(dest.length, mReadBuffer.length); + totalBytesRead = mConnection.bulkTransfer(endpoint, mReadBuffer, + readAmt, timeoutMillis); + + if (totalBytesRead < MODEM_STATUS_HEADER_LENGTH) { + throw new IOException("Expected at least " + MODEM_STATUS_HEADER_LENGTH + " bytes"); + } + + return filterStatusBytes(mReadBuffer, dest, totalBytesRead, endpoint.getMaxPacketSize()); + } + } + } + + @Override + public int write(byte[] src, int timeoutMillis) throws IOException { + final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(1); + int offset = 0; + + while (offset < src.length) { + final int writeLength; + final int amtWritten; + + synchronized (mWriteBufferLock) { + final byte[] writeBuffer; + + writeLength = Math.min(src.length - offset, mWriteBuffer.length); + if (offset == 0) { + writeBuffer = src; + } else { + // bulkTransfer does not support offsets, make a copy. + System.arraycopy(src, offset, mWriteBuffer, 0, writeLength); + writeBuffer = mWriteBuffer; + } + + amtWritten = mConnection.bulkTransfer(endpoint, writeBuffer, writeLength, + timeoutMillis); + } + + if (amtWritten <= 0) { + throw new IOException("Error writing " + writeLength + + " bytes at offset " + offset + " length=" + src.length); + } + + //Log.d(TAG, "Wrote amtWritten=" + amtWritten + " attempted=" + writeLength); + offset += amtWritten; + } + return offset; + } + + private int setBaudRate(int baudRate) throws IOException { + long[] vals = convertBaudrate(baudRate); + long actualBaudrate = vals[0]; + long index = vals[1]; + long value = vals[2]; + int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, + SIO_SET_BAUD_RATE_REQUEST, (int) value, (int) index, + null, 0, USB_WRITE_TIMEOUT_MILLIS); + if (result != 0) { + throw new IOException("Setting baudrate failed: result=" + result); + } + return (int) actualBaudrate; + } + + @Override + public void setParameters(int baudRate, int dataBits, int stopBits, int parity) + throws IOException { + setBaudRate(baudRate); + + int config = dataBits; + + switch (parity) { + case PARITY_NONE: + config |= (0x00 << 8); + break; + case PARITY_ODD: + config |= (0x01 << 8); + break; + case PARITY_EVEN: + config |= (0x02 << 8); + break; + case PARITY_MARK: + config |= (0x03 << 8); + break; + case PARITY_SPACE: + config |= (0x04 << 8); + break; + default: + throw new IllegalArgumentException("Unknown parity value: " + parity); + } + + switch (stopBits) { + case STOPBITS_1: + config |= (0x00 << 11); + break; + case STOPBITS_1_5: + config |= (0x01 << 11); + break; + case STOPBITS_2: + config |= (0x02 << 11); + break; + default: + throw new IllegalArgumentException("Unknown stopBits value: " + stopBits); + } + + int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, + SIO_SET_DATA_REQUEST, config, 0 /* index */, + null, 0, USB_WRITE_TIMEOUT_MILLIS); + if (result != 0) { + throw new IOException("Setting parameters failed: result=" + result); + } + } + + private long[] convertBaudrate(int baudrate) { + // TODO(mikey): Braindead transcription of libfti method. Clean up, + // using more idiomatic Java where possible. + int divisor = 24000000 / baudrate; + int bestDivisor = 0; + int bestBaud = 0; + int bestBaudDiff = 0; + int fracCode[] = { + 0, 3, 2, 4, 1, 5, 6, 7 + }; + + for (int i = 0; i < 2; i++) { + int tryDivisor = divisor + i; + int baudEstimate; + int baudDiff; + + if (tryDivisor <= 8) { + // Round up to minimum supported divisor + tryDivisor = 8; + } else if (mType != DeviceType.TYPE_AM && tryDivisor < 12) { + // BM doesn't support divisors 9 through 11 inclusive + tryDivisor = 12; + } else if (divisor < 16) { + // AM doesn't support divisors 9 through 15 inclusive + tryDivisor = 16; + } else { + if (mType == DeviceType.TYPE_AM) { + // TODO + } else { + if (tryDivisor > 0x1FFFF) { + // Round down to maximum supported divisor value (for + // BM) + tryDivisor = 0x1FFFF; + } + } + } + + // Get estimated baud rate (to nearest integer) + baudEstimate = (24000000 + (tryDivisor / 2)) / tryDivisor; + + // Get absolute difference from requested baud rate + if (baudEstimate < baudrate) { + baudDiff = baudrate - baudEstimate; + } else { + baudDiff = baudEstimate - baudrate; + } + + if (i == 0 || baudDiff < bestBaudDiff) { + // Closest to requested baud rate so far + bestDivisor = tryDivisor; + bestBaud = baudEstimate; + bestBaudDiff = baudDiff; + if (baudDiff == 0) { + // Spot on! No point trying + break; + } + } + } + + // Encode the best divisor value + long encodedDivisor = (bestDivisor >> 3) | (fracCode[bestDivisor & 7] << 14); + // Deal with special cases for encoded value + if (encodedDivisor == 1) { + encodedDivisor = 0; // 3000000 baud + } else if (encodedDivisor == 0x4001) { + encodedDivisor = 1; // 2000000 baud (BM only) + } + + // Split into "value" and "index" values + long value = encodedDivisor & 0xFFFF; + long index; + if (mType == DeviceType.TYPE_2232C || mType == DeviceType.TYPE_2232H + || mType == DeviceType.TYPE_4232H) { + index = (encodedDivisor >> 8) & 0xffff; + index &= 0xFF00; + index |= 0 /* TODO mIndex */; + } else { + index = (encodedDivisor >> 16) & 0xffff; + } + + // Return the nearest baud rate + return new long[] { + bestBaud, index, value + }; + } + + @Override + public boolean getCD() throws IOException { + return false; + } + + @Override + public boolean getCTS() throws IOException { + return false; + } + + @Override + public boolean getDSR() throws IOException { + return false; + } + + @Override + public boolean getDTR() throws IOException { + return false; + } + + @Override + public void setDTR(boolean value) throws IOException { + } + + @Override + public boolean getRI() throws IOException { + return false; + } + + @Override + public boolean getRTS() throws IOException { + return false; + } + + @Override + public void setRTS(boolean value) throws IOException { + } + + @Override + public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException { + if (purgeReadBuffers) { + int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST, + SIO_RESET_PURGE_RX, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS); + if (result != 0) { + throw new IOException("Flushing RX failed: result=" + result); + } + } + + if (purgeWriteBuffers) { + int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST, + SIO_RESET_PURGE_TX, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS); + if (result != 0) { + throw new IOException("Flushing RX failed: result=" + result); + } + } + + return true; + } + + public static Map getSupportedDevices() { + final Map supportedDevices = new LinkedHashMap(); + supportedDevices.put(Integer.valueOf(UsbId.VENDOR_FTDI), + new int[] { + UsbId.FTDI_FT232R, + UsbId.FTDI_FT231X, + }); + /* + supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PX4), + new int[] { + UsbId.DEVICE_PX4FMU + }); + */ + return supportedDevices; + } + +} diff --git a/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java b/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..d6e2d1338d1a1f08051b5f13d2303a526c7a3639 --- /dev/null +++ b/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java @@ -0,0 +1,523 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * Project home page: http://code.google.com/p/usb-serial-for-android/ + */ + +/* + * Ported to usb-serial-for-android + * by Felix Hädicke + * + * Based on the pyprolific driver written + * by Emmanuel Blot + * See https://github.com/eblot/pyftdi + */ + +package com.hoho.android.usbserial.driver; + +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.util.Log; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.LinkedHashMap; +import java.util.Map; + +public class ProlificSerialDriver extends CommonUsbSerialDriver { + private static final int USB_READ_TIMEOUT_MILLIS = 1000; + private static final int USB_WRITE_TIMEOUT_MILLIS = 5000; + + private static final int USB_RECIP_INTERFACE = 0x01; + + private static final int PROLIFIC_VENDOR_READ_REQUEST = 0x01; + private static final int PROLIFIC_VENDOR_WRITE_REQUEST = 0x01; + + private static final int PROLIFIC_VENDOR_OUT_REQTYPE = UsbConstants.USB_DIR_OUT + | UsbConstants.USB_TYPE_VENDOR; + + private static final int PROLIFIC_VENDOR_IN_REQTYPE = UsbConstants.USB_DIR_IN + | UsbConstants.USB_TYPE_VENDOR; + + private static final int PROLIFIC_CTRL_OUT_REQTYPE = UsbConstants.USB_DIR_OUT + | UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE; + + private static final int WRITE_ENDPOINT = 0x02; + private static final int READ_ENDPOINT = 0x83; + private static final int INTERRUPT_ENDPOINT = 0x81; + + private static final int FLUSH_RX_REQUEST = 0x08; + private static final int FLUSH_TX_REQUEST = 0x09; + + private static final int SET_LINE_REQUEST = 0x20; + private static final int SET_CONTROL_REQUEST = 0x22; + + private static final int CONTROL_DTR = 0x01; + private static final int CONTROL_RTS = 0x02; + + private static final int STATUS_FLAG_CD = 0x01; + private static final int STATUS_FLAG_DSR = 0x02; + private static final int STATUS_FLAG_RI = 0x08; + private static final int STATUS_FLAG_CTS = 0x80; + + private static final int STATUS_BUFFER_SIZE = 10; + private static final int STATUS_BYTE_IDX = 8; + + private static final int DEVICE_TYPE_HX = 0; + private static final int DEVICE_TYPE_0 = 1; + private static final int DEVICE_TYPE_1 = 2; + + private int mDeviceType = DEVICE_TYPE_HX; + + private UsbEndpoint mReadEndpoint; + private UsbEndpoint mWriteEndpoint; + private UsbEndpoint mInterruptEndpoint; + + private int mControlLinesValue = 0; + + private int mBaudRate = -1, mDataBits = -1, mStopBits = -1, mParity = -1; + + private int mStatus = 0; + private volatile Thread mReadStatusThread = null; + private final Object mReadStatusThreadLock = new Object(); + boolean mStopReadStatusThread = false; + private IOException mReadStatusException = null; + + private final String TAG = ProlificSerialDriver.class.getSimpleName(); + + private final byte[] inControlTransfer(int requestType, int request, + int value, int index, int length) throws IOException { + byte[] buffer = new byte[length]; + int result = mConnection.controlTransfer(requestType, request, value, + index, buffer, length, USB_READ_TIMEOUT_MILLIS); + if (result != length) { + throw new IOException( + String.format("ControlTransfer with value 0x%x failed: %d", + value, result)); + } + return buffer; + } + + private final void outControlTransfer(int requestType, int request, + int value, int index, byte[] data) throws IOException { + int length = (data == null) ? 0 : data.length; + int result = mConnection.controlTransfer(requestType, request, value, + index, data, length, USB_WRITE_TIMEOUT_MILLIS); + if (result != length) { + throw new IOException( + String.format("ControlTransfer with value 0x%x failed: %d", + value, result)); + } + } + + private final byte[] vendorIn(int value, int index, int length) + throws IOException { + return inControlTransfer(PROLIFIC_VENDOR_IN_REQTYPE, + PROLIFIC_VENDOR_READ_REQUEST, value, index, length); + } + + private final void vendorOut(int value, int index, byte[] data) + throws IOException { + outControlTransfer(PROLIFIC_VENDOR_OUT_REQTYPE, + PROLIFIC_VENDOR_WRITE_REQUEST, value, index, data); + } + + private final void ctrlOut(int request, int value, int index, byte[] data) + throws IOException { + outControlTransfer(PROLIFIC_CTRL_OUT_REQTYPE, request, value, index, + data); + } + + private void doBlackMagic() throws IOException { + vendorIn(0x8484, 0, 1); + vendorOut(0x0404, 0, null); + vendorIn(0x8484, 0, 1); + vendorIn(0x8383, 0, 1); + vendorIn(0x8484, 0, 1); + vendorOut(0x0404, 1, null); + vendorIn(0x8484, 0, 1); + vendorIn(0x8383, 0, 1); + vendorOut(0, 1, null); + vendorOut(1, 0, null); + vendorOut(2, (mDeviceType == DEVICE_TYPE_HX) ? 0x44 : 0x24, null); + } + + private void resetDevice() throws IOException { + purgeHwBuffers(true, true); + } + + private void setControlLines(int newControlLinesValue) throws IOException { + ctrlOut(SET_CONTROL_REQUEST, newControlLinesValue, 0, null); + mControlLinesValue = newControlLinesValue; + } + + private final void readStatusThreadFunction() { + try { + while (!mStopReadStatusThread) { + byte[] buffer = new byte[STATUS_BUFFER_SIZE]; + int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint, + buffer, + STATUS_BUFFER_SIZE, + 500); + if (readBytesCount > 0) { + if (readBytesCount == STATUS_BUFFER_SIZE) { + mStatus = buffer[STATUS_BYTE_IDX] & 0xff; + } else { + throw new IOException( + String.format("Invalid CTS / DSR / CD / RI status buffer received, expected %d bytes, but received %d", + STATUS_BUFFER_SIZE, + readBytesCount)); + } + } + } + } catch (IOException e) { + mReadStatusException = e; + } + } + + private final int getStatus() throws IOException { + if ((mReadStatusThread == null) && (mReadStatusException == null)) { + synchronized (mReadStatusThreadLock) { + if (mReadStatusThread == null) { + byte[] buffer = new byte[STATUS_BUFFER_SIZE]; + int readBytes = mConnection.bulkTransfer(mInterruptEndpoint, + buffer, + STATUS_BUFFER_SIZE, + 100); + if (readBytes != STATUS_BUFFER_SIZE) { + Log.w(TAG, "Could not read initial CTS / DSR / CD / RI status"); + } else { + mStatus = buffer[STATUS_BYTE_IDX] & 0xff; + } + + mReadStatusThread = new Thread(new Runnable() { + @Override + public void run() { + readStatusThreadFunction(); + } + }); + mReadStatusThread.setDaemon(true); + mReadStatusThread.start(); + } + } + } + + /* throw and clear an exception which occured in the status read thread */ + IOException readStatusException = mReadStatusException; + if (mReadStatusException != null) { + mReadStatusException = null; + throw readStatusException; + } + + return mStatus; + } + + private final boolean testStatusFlag(int flag) throws IOException { + return ((getStatus() & flag) == flag); + } + + public ProlificSerialDriver(UsbDevice device, UsbDeviceConnection connection) { + super(device, connection); + } + + @Override + public void open() throws IOException { + UsbInterface usbInterface = mDevice.getInterface(0); + + if (!mConnection.claimInterface(usbInterface, true)) { + throw new IOException("Error claiming Prolific interface 0"); + } + + boolean openSuccessful = false; + try { + for (int i = 0; i < usbInterface.getEndpointCount(); ++i) { + UsbEndpoint currentEndpoint = usbInterface.getEndpoint(i); + + switch (currentEndpoint.getAddress()) { + case READ_ENDPOINT: + mReadEndpoint = currentEndpoint; + break; + + case WRITE_ENDPOINT: + mWriteEndpoint = currentEndpoint; + break; + + case INTERRUPT_ENDPOINT: + mInterruptEndpoint = currentEndpoint; + break; + } + } + + if (mDevice.getDeviceClass() == 0x02) { + mDeviceType = DEVICE_TYPE_0; + } else { + try { + Method getRawDescriptorsMethod + = mConnection.getClass().getMethod("getRawDescriptors"); + byte[] rawDescriptors + = (byte[]) getRawDescriptorsMethod.invoke(mConnection); + byte maxPacketSize0 = rawDescriptors[7]; + if (maxPacketSize0 == 64) { + mDeviceType = DEVICE_TYPE_HX; + } else if ((mDevice.getDeviceClass() == 0x00) + || (mDevice.getDeviceClass() == 0xff)) { + mDeviceType = DEVICE_TYPE_1; + } else { + Log.w(TAG, "Could not detect PL2303 subtype, " + + "Assuming that it is a HX device"); + mDeviceType = DEVICE_TYPE_HX; + } + } catch (NoSuchMethodException e) { + Log.w(TAG, "Method UsbDeviceConnection.getRawDescriptors, " + + "required for PL2303 subtype detection, not " + + "available! Assuming that it is a HX device"); + mDeviceType = DEVICE_TYPE_HX; + } catch (Exception e) { + Log.e(TAG, "An unexpected exception occured while trying " + + "to detect PL2303 subtype", e); + } + } + + setControlLines(mControlLinesValue); + resetDevice(); + + doBlackMagic(); + openSuccessful = true; + } finally { + if (!openSuccessful) { + try { + mConnection.releaseInterface(usbInterface); + } catch (Exception ingored) { + // Do not cover possible exceptions + } + } + } + } + + @Override + public void close() throws IOException { + try { + mStopReadStatusThread = true; + synchronized (mReadStatusThreadLock) { + if (mReadStatusThread != null) { + try { + mReadStatusThread.join(); + } catch (Exception e) { + Log.w(TAG, "An error occured while waiting for status read thread", e); + } + } + } + + resetDevice(); + } finally { + mConnection.releaseInterface(mDevice.getInterface(0)); + } + } + + @Override + public int read(byte[] dest, int timeoutMillis) throws IOException { + synchronized (mReadBufferLock) { + int readAmt = Math.min(dest.length, mReadBuffer.length); + int numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, + readAmt, timeoutMillis); + if (numBytesRead < 0) { + return 0; + } + System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead); + return numBytesRead; + } + } + + @Override + public int write(byte[] src, int timeoutMillis) throws IOException { + int offset = 0; + + while (offset < src.length) { + final int writeLength; + final int amtWritten; + + synchronized (mWriteBufferLock) { + final byte[] writeBuffer; + + writeLength = Math.min(src.length - offset, mWriteBuffer.length); + if (offset == 0) { + writeBuffer = src; + } else { + // bulkTransfer does not support offsets, make a copy. + System.arraycopy(src, offset, mWriteBuffer, 0, writeLength); + writeBuffer = mWriteBuffer; + } + + amtWritten = mConnection.bulkTransfer(mWriteEndpoint, + writeBuffer, writeLength, timeoutMillis); + } + + if (amtWritten <= 0) { + throw new IOException("Error writing " + writeLength + + " bytes at offset " + offset + " length=" + + src.length); + } + + offset += amtWritten; + } + return offset; + } + + @Override + public void setParameters(int baudRate, int dataBits, int stopBits, + int parity) throws IOException { + if ((mBaudRate == baudRate) && (mDataBits == dataBits) + && (mStopBits == stopBits) && (mParity == parity)) { + // Make sure no action is performed if there is nothing to change + return; + } + + byte[] lineRequestData = new byte[7]; + + lineRequestData[0] = (byte) (baudRate & 0xff); + lineRequestData[1] = (byte) ((baudRate >> 8) & 0xff); + lineRequestData[2] = (byte) ((baudRate >> 16) & 0xff); + lineRequestData[3] = (byte) ((baudRate >> 24) & 0xff); + + switch (stopBits) { + case STOPBITS_1: + lineRequestData[4] = 0; + break; + + case STOPBITS_1_5: + lineRequestData[4] = 1; + break; + + case STOPBITS_2: + lineRequestData[4] = 2; + break; + + default: + throw new IllegalArgumentException("Unknown stopBits value: " + stopBits); + } + + switch (parity) { + case PARITY_NONE: + lineRequestData[5] = 0; + break; + + case PARITY_ODD: + lineRequestData[5] = 1; + break; + + case PARITY_EVEN: + lineRequestData[5] = 2; + break; + + case PARITY_MARK: + lineRequestData[5] = 3; + break; + + case PARITY_SPACE: + lineRequestData[5] = 4; + break; + + default: + throw new IllegalArgumentException("Unknown parity value: " + parity); + } + + lineRequestData[6] = (byte) dataBits; + + ctrlOut(SET_LINE_REQUEST, 0, 0, lineRequestData); + + resetDevice(); + + mBaudRate = baudRate; + mDataBits = dataBits; + mStopBits = stopBits; + mParity = parity; + } + + @Override + public boolean getCD() throws IOException { + return testStatusFlag(STATUS_FLAG_CD); + } + + @Override + public boolean getCTS() throws IOException { + return testStatusFlag(STATUS_FLAG_CTS); + } + + @Override + public boolean getDSR() throws IOException { + return testStatusFlag(STATUS_FLAG_DSR); + } + + @Override + public boolean getDTR() throws IOException { + return ((mControlLinesValue & CONTROL_DTR) == CONTROL_DTR); + } + + @Override + public void setDTR(boolean value) throws IOException { + int newControlLinesValue; + if (value) { + newControlLinesValue = mControlLinesValue | CONTROL_DTR; + } else { + newControlLinesValue = mControlLinesValue & ~CONTROL_DTR; + } + setControlLines(newControlLinesValue); + } + + @Override + public boolean getRI() throws IOException { + return testStatusFlag(STATUS_FLAG_RI); + } + + @Override + public boolean getRTS() throws IOException { + return ((mControlLinesValue & CONTROL_RTS) == CONTROL_RTS); + } + + @Override + public void setRTS(boolean value) throws IOException { + int newControlLinesValue; + if (value) { + newControlLinesValue = mControlLinesValue | CONTROL_RTS; + } else { + newControlLinesValue = mControlLinesValue & ~CONTROL_RTS; + } + setControlLines(newControlLinesValue); + } + + @Override + public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException { + if (purgeReadBuffers) { + vendorOut(FLUSH_RX_REQUEST, 0, null); + } + + if (purgeWriteBuffers) { + vendorOut(FLUSH_TX_REQUEST, 0, null); + } + + return true; + } + + public static Map getSupportedDevices() { + final Map supportedDevices = new LinkedHashMap(); + supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PROLIFIC), + new int[] { UsbId.PROLIFIC_PL2303, }); + return supportedDevices; + } +} + diff --git a/android/src/com/hoho/android/usbserial/driver/UsbId.java b/android/src/com/hoho/android/usbserial/driver/UsbId.java new file mode 100644 index 0000000000000000000000000000000000000000..618fbc844b398d5179c566933761188a2553e810 --- /dev/null +++ b/android/src/com/hoho/android/usbserial/driver/UsbId.java @@ -0,0 +1,69 @@ +/* Copyright 2012 Google Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * Project home page: http://code.google.com/p/usb-serial-for-android/ + */ +package com.hoho.android.usbserial.driver; + +/** + * Registry of USB vendor/product ID constants. + * + * Culled from various sources; see + * usb.ids for one listing. + * + * @author mike wakerly (opensource@hoho.com) + */ +public final class UsbId { + + public static final int VENDOR_FTDI = 0x0403; + public static final int FTDI_FT232R = 0x6001; + public static final int FTDI_FT231X = 0x6015; + + public static final int VENDOR_PX4 = 0x26AC; + public static final int DEVICE_PX4FMU = 0x11; + + public static final int VENDOR_ATMEL = 0x03EB; + public static final int ATMEL_LUFA_CDC_DEMO_APP = 0x2044; + + public static final int VENDOR_ARDUINO = 0x2341; + public static final int ARDUINO_UNO = 0x0001; + public static final int ARDUINO_MEGA_2560 = 0x0010; + public static final int ARDUINO_SERIAL_ADAPTER = 0x003b; + public static final int ARDUINO_MEGA_ADK = 0x003f; + public static final int ARDUINO_MEGA_2560_R3 = 0x0042; + public static final int ARDUINO_UNO_R3 = 0x0043; + public static final int ARDUINO_MEGA_ADK_R3 = 0x0044; + public static final int ARDUINO_SERIAL_ADAPTER_R3 = 0x0044; + public static final int ARDUINO_LEONARDO = 0x8036; + + public static final int VENDOR_VAN_OOIJEN_TECH = 0x16c0; + public static final int VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL = 0x0483; + + public static final int VENDOR_LEAFLABS = 0x1eaf; + public static final int LEAFLABS_MAPLE = 0x0004; + + public static final int VENDOR_SILAB = 0x10c4; + public static final int SILAB_CP2102 = 0xea60; + + public static final int VENDOR_PROLIFIC = 0x067b; + public static final int PROLIFIC_PL2303 = 0x2303; + + private UsbId() { + throw new IllegalAccessError("Non-instantiable class."); + } + +} diff --git a/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java b/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..ed4426fc4d540238fc2cae9706adb41602a3a7a4 --- /dev/null +++ b/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java @@ -0,0 +1,228 @@ +/* Copyright 2011 Google Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * Project home page: http://code.google.com/p/usb-serial-for-android/ + */ + +package com.hoho.android.usbserial.driver; + +import java.io.IOException; + +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; + +/** + * Driver interface for a USB serial device. + * + * @author mike wakerly (opensource@hoho.com) + */ +public interface UsbSerialDriver { + + /** 5 data bits. */ + public static final int DATABITS_5 = 5; + + /** 6 data bits. */ + public static final int DATABITS_6 = 6; + + /** 7 data bits. */ + public static final int DATABITS_7 = 7; + + /** 8 data bits. */ + public static final int DATABITS_8 = 8; + + /** No flow control. */ + public static final int FLOWCONTROL_NONE = 0; + + /** RTS/CTS input flow control. */ + public static final int FLOWCONTROL_RTSCTS_IN = 1; + + /** RTS/CTS output flow control. */ + public static final int FLOWCONTROL_RTSCTS_OUT = 2; + + /** XON/XOFF input flow control. */ + public static final int FLOWCONTROL_XONXOFF_IN = 4; + + /** XON/XOFF output flow control. */ + public static final int FLOWCONTROL_XONXOFF_OUT = 8; + + /** No parity. */ + public static final int PARITY_NONE = 0; + + /** Odd parity. */ + public static final int PARITY_ODD = 1; + + /** Even parity. */ + public static final int PARITY_EVEN = 2; + + /** Mark parity. */ + public static final int PARITY_MARK = 3; + + /** Space parity. */ + public static final int PARITY_SPACE = 4; + + /** 1 stop bit. */ + public static final int STOPBITS_1 = 1; + + /** 1.5 stop bits. */ + public static final int STOPBITS_1_5 = 3; + + /** 2 stop bits. */ + public static final int STOPBITS_2 = 2; + + /** + * Returns the currently-bound USB device. + * + * @return the device + */ + public UsbDevice getDevice(); + + + /** + * Returns the currently-bound USB device. + * + * @return the device + */ + public UsbDeviceConnection getDeviceConnection(); + + /** + * Opens and initializes the device as a USB serial device. Upon success, + * caller must ensure that {@link #close()} is eventually called. + * + * @throws IOException on error opening or initializing the device. + */ + public void open() throws IOException; + + /** + * Closes the serial device. + * + * @throws IOException on error closing the device. + */ + public void close() throws IOException; + + /** + * Reads as many bytes as possible into the destination buffer. + * + * @param dest the destination byte buffer + * @param timeoutMillis the timeout for reading + * @return the actual number of bytes read + * @throws IOException if an error occurred during reading + */ + public int read(final byte[] dest, final int timeoutMillis) throws IOException; + + /** + * Writes as many bytes as possible from the source buffer. + * + * @param src the source byte buffer + * @param timeoutMillis the timeout for writing + * @return the actual number of bytes written + * @throws IOException if an error occurred during writing + */ + public int write(final byte[] src, final int timeoutMillis) throws IOException; + + /** + * Sets various serial port parameters. + * + * @param baudRate baud rate as an integer, for example {@code 115200}. + * @param dataBits one of {@link #DATABITS_5}, {@link #DATABITS_6}, + * {@link #DATABITS_7}, or {@link #DATABITS_8}. + * @param stopBits one of {@link #STOPBITS_1}, {@link #STOPBITS_1_5}, or + * {@link #STOPBITS_2}. + * @param parity one of {@link #PARITY_NONE}, {@link #PARITY_ODD}, + * {@link #PARITY_EVEN}, {@link #PARITY_MARK}, or + * {@link #PARITY_SPACE}. + * @throws IOException on error setting the port parameters + */ + public void setParameters( + int baudRate, int dataBits, int stopBits, int parity) throws IOException; + + /** + * Gets the CD (Carrier Detect) bit from the underlying UART. + * + * @return the current state, or {@code false} if not supported. + * @throws IOException if an error occurred during reading + */ + public boolean getCD() throws IOException; + + /** + * Gets the CTS (Clear To Send) bit from the underlying UART. + * + * @return the current state, or {@code false} if not supported. + * @throws IOException if an error occurred during reading + */ + public boolean getCTS() throws IOException; + + /** + * Gets the DSR (Data Set Ready) bit from the underlying UART. + * + * @return the current state, or {@code false} if not supported. + * @throws IOException if an error occurred during reading + */ + public boolean getDSR() throws IOException; + + /** + * Gets the DTR (Data Terminal Ready) bit from the underlying UART. + * + * @return the current state, or {@code false} if not supported. + * @throws IOException if an error occurred during reading + */ + public boolean getDTR() throws IOException; + + /** + * Sets the DTR (Data Terminal Ready) bit on the underlying UART, if + * supported. + * + * @param value the value to set + * @throws IOException if an error occurred during writing + */ + public void setDTR(boolean value) throws IOException; + + /** + * Gets the RI (Ring Indicator) bit from the underlying UART. + * + * @return the current state, or {@code false} if not supported. + * @throws IOException if an error occurred during reading + */ + public boolean getRI() throws IOException; + + /** + * Gets the RTS (Request To Send) bit from the underlying UART. + * + * @return the current state, or {@code false} if not supported. + * @throws IOException if an error occurred during reading + */ + public boolean getRTS() throws IOException; + + /** + * Sets the RTS (Request To Send) bit on the underlying UART, if + * supported. + * + * @param value the value to set + * @throws IOException if an error occurred during writing + */ + public void setRTS(boolean value) throws IOException; + + /** + * Flush non-transmitted output data and / or non-read input data + * @param flushRX {@code true} to flush non-transmitted output data + * @param flushTX {@code true} to flush non-read input data + * @return {@code true} if the operation was successful, or + * {@code false} if the operation is not supported by the driver or device + * @throws IOException if an error occurred during flush + */ + public boolean purgeHwBuffers(boolean flushRX, boolean flushTX) throws IOException; + +} diff --git a/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java b/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java new file mode 100644 index 0000000000000000000000000000000000000000..0cfb366e457f5957f84e42e48f7e10613f47cbb4 --- /dev/null +++ b/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java @@ -0,0 +1,250 @@ +/* Copyright 2011 Google Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * Project home page: http://code.google.com/p/usb-serial-for-android/ + */ + +package com.hoho.android.usbserial.driver; + +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbManager; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Helper class which finds compatible {@link UsbDevice}s and creates + * {@link UsbSerialDriver} instances. + * + *

+ * You don't need a Prober to use the rest of the library: it is perfectly + * acceptable to instantiate driver instances manually. The Prober simply + * provides convenience functions. + * + *

+ * For most drivers, the corresponding {@link #probe(UsbManager, UsbDevice)} + * method will either return an empty list (device unknown / unsupported) or a + * singleton list. However, multi-port drivers may return multiple instances. + * + * @author mike wakerly (opensource@hoho.com) + */ +public enum UsbSerialProber { + + // TODO(mikey): Too much boilerplate. + + /** + * Prober for {@link FtdiSerialDriver}. + * + * @see FtdiSerialDriver + */ + FTDI_SERIAL { + @Override + public List probe(final UsbManager manager, final UsbDevice usbDevice) { + if (!testIfSupported(usbDevice, FtdiSerialDriver.getSupportedDevices())) { + return Collections.emptyList(); + } + final UsbDeviceConnection connection = manager.openDevice(usbDevice); + if (connection == null) { + return Collections.emptyList(); + } + final UsbSerialDriver driver = new FtdiSerialDriver(usbDevice, connection); + return Collections.singletonList(driver); + } + }, + + CDC_ACM_SERIAL { + @Override + public List probe(UsbManager manager, UsbDevice usbDevice) { + if (!testIfSupported(usbDevice, CdcAcmSerialDriver.getSupportedDevices())) { + return Collections.emptyList(); + } + final UsbDeviceConnection connection = manager.openDevice(usbDevice); + if (connection == null) { + return Collections.emptyList(); + } + final UsbSerialDriver driver = new CdcAcmSerialDriver(usbDevice, connection); + return Collections.singletonList(driver); + } + }, + + SILAB_SERIAL { + @Override + public List probe(final UsbManager manager, final UsbDevice usbDevice) { + if (!testIfSupported(usbDevice, Cp2102SerialDriver.getSupportedDevices())) { + return Collections.emptyList(); + } + final UsbDeviceConnection connection = manager.openDevice(usbDevice); + if (connection == null) { + return Collections.emptyList(); + } + final UsbSerialDriver driver = new Cp2102SerialDriver(usbDevice, connection); + return Collections.singletonList(driver); + } + }, + + PROLIFIC_SERIAL { + @Override + public List probe(final UsbManager manager, final UsbDevice usbDevice) { + if (!testIfSupported(usbDevice, ProlificSerialDriver.getSupportedDevices())) { + return Collections.emptyList(); + } + final UsbDeviceConnection connection = manager.openDevice(usbDevice); + if (connection == null) { + return Collections.emptyList(); + } + final UsbSerialDriver driver = new ProlificSerialDriver(usbDevice, connection); + return Collections.singletonList(driver); + } + }; + + /** + * Tests the supplied {@link UsbDevice} for compatibility with this enum + * member, returning one or more driver instances if compatible. + * + * @param manager the {@link UsbManager} to use + * @param usbDevice the raw {@link UsbDevice} to use + * @return zero or more {@link UsbSerialDriver}, depending on compatibility + * (never {@code null}). + */ + protected abstract List probe(final UsbManager manager, final UsbDevice usbDevice); + + /** + * Creates and returns a new {@link UsbSerialDriver} instance for the first + * compatible {@link UsbDevice} found on the bus. If none are found, + * returns {@code null}. + * + *

+ * The order of devices is undefined, therefore if there are multiple + * devices on the bus, the chosen device may not be predictable (clients + * should use {@link #findAllDevices(UsbManager)} instead). + * + * @param usbManager the {@link UsbManager} to use. + * @return the first available {@link UsbSerialDriver}, or {@code null} if + * none are available. + */ + public static UsbSerialDriver findFirstDevice(final UsbManager usbManager) { + for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) { + for (final UsbSerialProber prober : values()) { + final List probedDevices = prober.probe(usbManager, usbDevice); + if (!probedDevices.isEmpty()) { + return probedDevices.get(0); + } + } + } + return null; + } + + /** + * Creates a new {@link UsbSerialDriver} instance for all compatible + * {@link UsbDevice}s found on the bus. If no compatible devices are found, + * the list will be empty. + * + * @param usbManager + * @return + */ + public static List findAllDevices(final UsbManager usbManager) { + final List result = new ArrayList(); + Log.i("QGC_UsbSerialProber", "Looking for USB devices"); + // For each UsbDevice, call probe() for each prober. + for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) { + Log.i("QGC_UsbSerialProber", "Probing device: " + usbDevice.getDeviceName() + " mid: " + usbDevice.getVendorId() + " pid: " + usbDevice.getDeviceId()); + result.addAll(probeSingleDevice(usbManager, usbDevice)); + } + return result; + } + + /** + * Special method for testing a specific device for driver support, + * returning any compatible driver(s). + * + *

+ * Clients should ordinarily use {@link #findAllDevices(UsbManager)}, which + * operates against the entire bus of devices. This method is useful when + * testing against only a single target is desired. + * + * @param usbManager the {@link UsbManager} to use. + * @param usbDevice the device to test against. + * @return a list containing zero or more {@link UsbSerialDriver} instances. + */ + public static List probeSingleDevice(final UsbManager usbManager, + UsbDevice usbDevice) + { + final List result = new ArrayList(); + for (final UsbSerialProber prober : values()) { + final List probedDevices = prober.probe(usbManager, usbDevice); + result.addAll(probedDevices); + } + return result; + } + + /** + * Deprecated; Use {@link #findFirstDevice(UsbManager)}. + * + * @param usbManager + * @return + */ + @Deprecated + public static UsbSerialDriver acquire(final UsbManager usbManager) { + return findFirstDevice(usbManager); + } + + /** + * Deprecated; use {@link #probeSingleDevice(UsbManager, UsbDevice)}. + * + * @param usbManager + * @param usbDevice + * @return + */ + @Deprecated + public static UsbSerialDriver acquire(final UsbManager usbManager, final UsbDevice usbDevice) { + final List probedDevices = probeSingleDevice(usbManager, usbDevice); + if (!probedDevices.isEmpty()) { + return probedDevices.get(0); + } + return null; + } + + /** + * Returns {@code true} if the given device is found in the driver's + * vendor/product map. + * + * @param usbDevice the device to test + * @param supportedDevices map of vendor IDs to product ID(s) + * @return {@code true} if supported + */ + private static boolean testIfSupported(final UsbDevice usbDevice, + final Map supportedDevices) { + final int[] supportedProducts = supportedDevices.get( + Integer.valueOf(usbDevice.getVendorId())); + if (supportedProducts == null) { + return false; + } + + final int productId = usbDevice.getProductId(); + for (int supportedProductId : supportedProducts) { + if (productId == supportedProductId) { + return true; + } + } + return false; + } + +} diff --git a/android/src/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java b/android/src/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java new file mode 100644 index 0000000000000000000000000000000000000000..b48607c59da80c203031a252bcf1ee09d3a9b160 --- /dev/null +++ b/android/src/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java @@ -0,0 +1,46 @@ +/* + * Copyright 2011 Google Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +package com.hoho.android.usbserial.driver; + +/** + * Generic unchecked exception for the usbserial package. + * + * @author mike wakerly (opensource@hoho.com) + */ +@SuppressWarnings("serial") +public class UsbSerialRuntimeException extends RuntimeException { + + public UsbSerialRuntimeException() { + super(); + } + + public UsbSerialRuntimeException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + } + + public UsbSerialRuntimeException(String detailMessage) { + super(detailMessage); + } + + public UsbSerialRuntimeException(Throwable throwable) { + super(throwable); + } + +} diff --git a/android/src/org/qgroundcontrol/qgchelper/UsbDeviceJNI.java b/android/src/org/qgroundcontrol/qgchelper/UsbDeviceJNI.java new file mode 100644 index 0000000000000000000000000000000000000000..79d2597a116ef52b5539ebeb3cc223b43fe55f20 --- /dev/null +++ b/android/src/org/qgroundcontrol/qgchelper/UsbDeviceJNI.java @@ -0,0 +1,637 @@ +package org.qgroundcontrol.qgchelper; + +/* Copyright 2013 Google Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * Project home page: http://code.google.com/p/usb-serial-for-android/ + */ +/////////////////////////////////////////////////////////////////////////////////////////// +// Written by: Mike Goza April 2014 +// +// These routines interface with the Android USB Host devices for serial port communication. +// The code uses the usb-serial-for-android software library. The UsbDeviceJNI class is the +// interface to the C++ routines through jni calls. Do not change the functions without also +// changing the corresponding calls in the C++ routines or you will break the interface. +// +//////////////////////////////////////////////////////////////////////////////////////////// + +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.io.IOException; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.*; +import android.widget.Toast; +import android.util.Log; + +import com.hoho.android.usbserial.driver.*; +import org.qtproject.qt5.android.bindings.QtActivity; +import org.qtproject.qt5.android.bindings.QtApplication; + +public class UsbDeviceJNI extends QtActivity +{ + public static int BAD_PORT = 0; + private static UsbDeviceJNI m_instance; + private static UsbManager m_manager; // ANDROID USB HOST CLASS + private static List m_devices; // LIST OF CURRENT DEVICES + private static HashMap m_openedDevices; // LIST OF OPENED DEVICES + private static HashMap m_ioManager; // THREADS FOR LISTENING FOR INCOMING DATA + private static HashMap m_userData; // CORRESPONDING USER DATA FOR OPENED DEVICES. USED IN DISCONNECT CALLBACK + // USED TO DETECT WHEN A DEVICE HAS BEEN UNPLUGGED + private BroadcastReceiver m_UsbReceiver = null; + private final static ExecutorService m_Executor = Executors.newSingleThreadExecutor(); + + private final static UsbIoManager.Listener m_Listener = + new UsbIoManager.Listener() + { + @Override + public void onRunError(Exception eA, int userDataA) + { + nativeDeviceException(userDataA, eA.getMessage()); + } + + @Override + public void onNewData(final byte[] dataA, int userDataA) + { + nativeDeviceNewData(userDataA, dataA); + } + }; + + private static final String TAG = "QGC_UsbDeviceJNI"; + + // NATIVE C++ FUNCTION THAT WILL BE CALLED IF THE DEVICE IS UNPLUGGED + private static native void nativeDeviceHasDisconnected(int userDataA); + private static native void nativeDeviceException(int userDataA, String messageA); + private static native void nativeDeviceNewData(int userDataA, byte[] dataA); + + //////////////////////////////////////////////////////////////////////////////////////////////// + // + // Constructor. Only used once to create the initial instance for the static functions. + // + //////////////////////////////////////////////////////////////////////////////////////////////// + public UsbDeviceJNI() + { + m_instance = this; + m_openedDevices = new HashMap(); + m_userData = new HashMap(); + m_ioManager = new HashMap(); + Log.i(TAG, "Instance created"); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Find all current devices that match the device filter described in the androidmanifest.xml and the + // device_filter.xml + // + ///////////////////////////////////////////////////////////////////////////////////////////////////////// + private static boolean getCurrentDevices() + { + if (m_instance == null) + return false; + + if (m_manager == null) + m_manager = (UsbManager)m_instance.getSystemService(Context.USB_SERVICE); + + if (m_devices != null) + m_devices.clear(); + + m_devices = UsbSerialProber.findAllDevices(m_manager); + + return true; + } + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // List all available devices that are not already open. It returns the serial port info + // in a : separated string array. Each string entry consists of the following: + // + // DeviceName:Company:ProductId:VendorId + // + ///////////////////////////////////////////////////////////////////////////////////////////////////////// + public static String[] availableDevicesInfo() + { + // GET THE LIST OF CURRENT DEVICES + if (!getCurrentDevices()) + { + Log.e(TAG, "UsbDeviceJNI instance not present"); + return null; + } + + // MAKE SURE WE HAVE ENTRIES + if (m_devices.size() <= 0) + { + //Log.e(TAG, "No USB devices found"); + return null; + } + + if (m_openedDevices == null) + { + Log.e(TAG, "m_openedDevices is null"); + return null; + } + + int countL = 0; + int iL; + + // CHECK FOR ALREADY OPENED DEVICES AND DON"T INCLUDE THEM IN THE COUNT + for (iL=0; iL 0) + { + final Listener listener = getListener(); + if (listener != null) + { + final byte[] data = new byte[len]; + mReadBuffer.get(data, 0, len); + listener.onNewData(data, mUserData); + } + mReadBuffer.clear(); + } +/* + // Handle outgoing data. + byte[] outBuff = null; + synchronized (mWriteBuffer) + { + if (mWriteBuffer.position() > 0) + { + len = mWriteBuffer.position(); + outBuff = new byte[len]; + mWriteBuffer.rewind(); + mWriteBuffer.get(outBuff, 0, len); + mWriteBuffer.clear(); + } + } + if (outBuff != null) + mDriver.write(outBuff, READ_WAIT_MILLIS); +*/ + } +} + diff --git a/libs/qextserialport/src/qextserialport_unix.cpp b/libs/qextserialport/src/qextserialport_unix.cpp index 97d7ac1a3416edba120f09479e8126fc94a983fd..78c486568147216a35916a2fbe1c49cb0eb7b444 100755 --- a/libs/qextserialport/src/qextserialport_unix.cpp +++ b/libs/qextserialport/src/qextserialport_unix.cpp @@ -120,7 +120,11 @@ bool QextSerialPortPrivate::close_sys() bool QextSerialPortPrivate::flush_sys() { +#ifdef __android__ + ::ioctl(fd, TCSADRAIN, ¤tTermios); +#else ::tcdrain(fd); +#endif return true; } diff --git a/libs/qtandroidserialport/src/qserialport.cpp b/libs/qtandroidserialport/src/qserialport.cpp new file mode 100644 index 0000000000000000000000000000000000000000..992f06bfcfb79ae69efa7315cc198e300553e5f5 --- /dev/null +++ b/libs/qtandroidserialport/src/qserialport.cpp @@ -0,0 +1,1375 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Copyright (C) 2012 Andre Hartmann +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialport.h" +#include "qserialportinfo.h" +#include "qserialportinfo_p.h" + +#ifdef Q_OS_WINCE +#include "qserialport_wince_p.h" +#elif defined (Q_OS_WIN) +#include "qserialport_win_p.h" +#elif defined (Q_OS_SYMBIAN) +#include "qserialport_symbian_p.h" +#elif defined (Q_OS_ANDROID) +#include "qserialport_android_p.h" +#elif defined (Q_OS_UNIX) +#include "qserialport_unix_p.h" +#else +#error Unsupported OS +#endif + +#ifndef SERIALPORT_BUFFERSIZE +# define SERIALPORT_BUFFERSIZE 16384 +#endif + +#include + +QT_BEGIN_NAMESPACE + +QSerialPortPrivateData::QSerialPortPrivateData(QSerialPort *q) + : readBufferMaxSize(0) + , readBuffer(SERIALPORT_BUFFERSIZE) + , writeBuffer(SERIALPORT_BUFFERSIZE) + , error(QSerialPort::NoError) + , inputBaudRate(9600) + , outputBaudRate(9600) + , dataBits(QSerialPort::Data8) + , parity(QSerialPort::NoParity) + , stopBits(QSerialPort::OneStop) + , flowControl(QSerialPort::NoFlowControl) + , policy(QSerialPort::IgnorePolicy) +#if QT_DEPRECATED_SINCE(5,3) + , settingsRestoredOnClose(true) +#endif + , q_ptr(q) +{ +} + +int QSerialPortPrivateData::timeoutValue(int msecs, int elapsed) +{ + if (msecs == -1) + return msecs; + msecs -= elapsed; + return qMax(msecs, 0); +} + +/*! + \class QSerialPort + + \brief Provides functions to access serial ports. + + \reentrant + \ingroup serialport-main + \inmodule QtSerialPort + \since 5.1 + + You can get information about the available serial ports using the + QSerialPortInfo helper class, which allows an enumeration of all the serial + ports in the system. This is useful to obtain the correct name of the + serial port you want to use. You can pass an object + of the helper class as an argument to the setPort() or setPortName() + methods to assign the desired serial device. + + After setting the port, you can open it in read-only (r/o), write-only + (w/o), or read-write (r/w) mode using the open() method. + + \note The serial port is always opened with exclusive access + (that is, no other process or thread can access an already opened serial port). + + Having successfully opened, QSerialPort tries to determine the current + configuration of the port and initializes itself. You can reconfigure the + port to the desired setting using the setBaudRate(), setDataBits(), + setParity(), setStopBits(), and setFlowControl() methods. + + There are a couple of properties to work with the pinout signals namely: + QSerialPort::dataTerminalReady, QSerialPort::requestToSend. It is also + possible to use the pinoutSignals() method to query the current pinout + signals set. + + Once you know that the ports are ready to read or write, you can + use the read() or write() methods. Alternatively the + readLine() and readAll() convenience methods can also be invoked. + If not all the data is read at once, the remaining data will + be available for later as new incoming data is appended to the + QSerialPort's internal read buffer. You can limit the size of the read + buffer using setReadBufferSize(). + + Use the close() method to close the port and cancel the I/O operations. + + See the following example: + + \code + int numRead = 0, numReadTotal = 0; + char buffer[50]; + + forever { + numRead = serial.read(buffer, 50); + + // Do whatever with the array + + numReadTotal += numRead; + if (numRead == 0 && !serial.waitForReadyRead()) + break; + } + \endcode + + If \l{QIODevice::}{waitForReadyRead()} returns false, the + connection has been closed or an error has occurred. + + Programming with a blocking serial port is radically different from + programming with a non-blocking serial port. A blocking serial port + does not require an event loop and typically leads to simpler code. + However, in a GUI application, blocking serial port should only be + used in non-GUI threads, to avoid freezing the user interface. + + For more details about these approaches, refer to the + \l {Examples}{example} applications. + + The QSerialPort class can also be used with QTextStream and QDataStream's + stream operators (operator<<() and operator>>()). There is one issue to be + aware of, though: make sure that enough data is available before attempting + to read by using the operator>>() overloaded operator. + + \sa QSerialPortInfo +*/ + +/*! + \enum QSerialPort::Direction + + This enum describes the possible directions of the data transmission. + + \note This enumeration is used for setting the baud rate of the device + separately for each direction on some operating systems (for example, + POSIX-like). + + \value Input Input direction. + \value Output Output direction. + \value AllDirections Simultaneously in two directions. +*/ + +/*! + \enum QSerialPort::BaudRate + + This enum describes the baud rate which the communication device operates + with. + + \note Only the most common standard baud rates are listed in this enum. + + \value Baud1200 1200 baud. + \value Baud2400 2400 baud. + \value Baud4800 4800 baud. + \value Baud9600 9600 baud. + \value Baud19200 19200 baud. + \value Baud38400 38400 baud. + \value Baud57600 57600 baud. + \value Baud115200 115200 baud. + \value UnknownBaud Unknown baud. This value is obsolete. It is provided to + keep old source code working. We strongly advise against + using it in new code. + + \sa QSerialPort::baudRate +*/ + +/*! + \enum QSerialPort::DataBits + + This enum describes the number of data bits used. + + \value Data5 The number of data bits in each character is 5. It + is used for Baudot code. It generally only makes + sense with older equipment such as teleprinters. + \value Data6 The number of data bits in each character is 6. It + is rarely used. + \value Data7 The number of data bits in each character is 7. It + is used for true ASCII. It generally only makes + sense with older equipment such as teleprinters. + \value Data8 The number of data bits in each character is 8. It + is used for most kinds of data, as this size matches + the size of a byte. It is almost universally used in + newer applications. + \value UnknownDataBits Unknown number of bits. This value is obsolete. It + is provided to keep old source code working. We + strongly advise against using it in new code. + + \sa QSerialPort::dataBits +*/ + +/*! + \enum QSerialPort::Parity + + This enum describes the parity scheme used. + + \value NoParity No parity bit it sent. This is the most common + parity setting. Error detection is handled by the + communication protocol. + \value EvenParity The number of 1 bits in each character, including + the parity bit, is always even. + \value OddParity The number of 1 bits in each character, including + the parity bit, is always odd. It ensures that at + least one state transition occurs in each character. + \value SpaceParity Space parity. The parity bit is sent in the space + signal condition. It does not provide error + detection information. + \value MarkParity Mark parity. The parity bit is always set to the + mark signal condition (logical 1). It does not + provide error detection information. + \value UnknownParity Unknown parity. This value is obsolete. It is + provided to keep old source code working. We + strongly advise against using it in new code. + + \sa QSerialPort::parity +*/ + +/*! + \enum QSerialPort::StopBits + + This enum describes the number of stop bits used. + + \value OneStop 1 stop bit. + \value OneAndHalfStop 1.5 stop bits. This is only for the Windows platform. + \value TwoStop 2 stop bits. + \value UnknownStopBits Unknown number of stop bits. This value is obsolete. + It is provided to keep old source code working. We + strongly advise against using it in new code. + + \sa QSerialPort::stopBits +*/ + +/*! + \enum QSerialPort::FlowControl + + This enum describes the flow control used. + + \value NoFlowControl No flow control. + \value HardwareControl Hardware flow control (RTS/CTS). + \value SoftwareControl Software flow control (XON/XOFF). + \value UnknownFlowControl Unknown flow control. This value is obsolete. It + is provided to keep old source code working. We + strongly advise against using it in new code. + + \sa QSerialPort::flowControl +*/ + +/*! + \enum QSerialPort::PinoutSignal + + This enum describes the possible RS-232 pinout signals. + + \value NoSignal No line active + \value TransmittedDataSignal TxD (Transmitted Data). This value is + obsolete. It is provided to keep old + source code working. We strongly + advise against using it in new code. + \value ReceivedDataSignal RxD (Received Data). This value is + obsolete. It is provided to keep old + source code working. We strongly + advise against using it in new code. + \value DataTerminalReadySignal DTR (Data Terminal Ready). + \value DataCarrierDetectSignal DCD (Data Carrier Detect). + \value DataSetReadySignal DSR (Data Set Ready). + \value RingIndicatorSignal RNG (Ring Indicator). + \value RequestToSendSignal RTS (Request To Send). + \value ClearToSendSignal CTS (Clear To Send). + \value SecondaryTransmittedDataSignal STD (Secondary Transmitted Data). + \value SecondaryReceivedDataSignal SRD (Secondary Received Data). + + \sa pinoutSignals(), QSerialPort::dataTerminalReady, + QSerialPort::requestToSend +*/ + +/*! + \enum QSerialPort::DataErrorPolicy + \obsolete + + This enum describes the policies for the received symbols + while parity errors were detected. + + \value SkipPolicy Skips the bad character. + \value PassZeroPolicy Replaces bad character with zero. + \value IgnorePolicy Ignores the error for a bad character. + \value StopReceivingPolicy Stops data reception on error. + \value UnknownPolicy Unknown policy. + + \sa QSerialPort::dataErrorPolicy +*/ + +/*! + \enum QSerialPort::SerialPortError + + This enum describes the errors that may be contained by the + QSerialPort::error property. + + \value NoError No error occurred. + + \value DeviceNotFoundError An error occurred while attempting to + open an non-existing device. + + \value PermissionError An error occurred while attempting to + open an already opened device by another + process or a user not having enough permission + and credentials to open. + + \value OpenError An error occurred while attempting to open an + already opened device in this object. + + \value NotOpenError This error occurs when an operation is executed + that can only be successfully performed if the + device is open. This value was introduced in + QtSerialPort 5.2. + + \value ParityError Parity error detected by the hardware while + reading data. + + \value FramingError Framing error detected by the hardware while + reading data. + + \value BreakConditionError Break condition detected by the hardware on + the input line. + + \value WriteError An I/O error occurred while writing the data. + + \value ReadError An I/O error occurred while reading the data. + + \value ResourceError An I/O error occurred when a resource becomes + unavailable, e.g. when the device is + unexpectedly removed from the system. + + \value UnsupportedOperationError The requested device operation is not + supported or prohibited by the running operating + system. + + \value TimeoutError A timeout error occurred. This value was + introduced in QtSerialPort 5.2. + + \value UnknownError An unidentified error occurred. + \sa QSerialPort::error +*/ + + + +/*! + Constructs a new serial port object with the given \a parent. +*/ +QSerialPort::QSerialPort(QObject *parent) + : QIODevice(parent) + , d_ptr(new QSerialPortPrivate(this)) +{} + +/*! + Constructs a new serial port object with the given \a parent + to represent the serial port with the specified \a name. + + The name should have a specific format; see the setPort() method. +*/ +QSerialPort::QSerialPort(const QString &name, QObject *parent) + : QIODevice(parent) + , d_ptr(new QSerialPortPrivate(this)) +{ + setPortName(name); +} + +/*! + Constructs a new serial port object with the given \a parent + to represent the serial port with the specified helper class + \a serialPortInfo. +*/ +QSerialPort::QSerialPort(const QSerialPortInfo &serialPortInfo, QObject *parent) + : QIODevice(parent) + , d_ptr(new QSerialPortPrivate(this)) +{ + setPort(serialPortInfo); +} + +/*! + Closes the serial port, if necessary, and then destroys object. +*/ +QSerialPort::~QSerialPort() +{ + /**/ + if (isOpen()) + close(); + delete d_ptr; +} + +/*! + Sets the \a name of the serial port. + + The name of the serial port can be passed as either a short name or + the long system location if necessary. + + \sa portName(), QSerialPortInfo +*/ +void QSerialPort::setPortName(const QString &name) +{ + Q_D(QSerialPort); + d->systemLocation = QSerialPortInfoPrivate::portNameToSystemLocation(name); +} + +/*! + Sets the port stored in the serial port info instance \a serialPortInfo. + + \sa portName(), QSerialPortInfo +*/ +void QSerialPort::setPort(const QSerialPortInfo &serialPortInfo) +{ + Q_D(QSerialPort); + d->systemLocation = serialPortInfo.systemLocation(); +} + +/*! + Returns the name set by setPort() or passed to the QSerialPort constructor. + This name is short, i.e. it is extracted and converted from the internal + variable system location of the device. The conversion algorithm is + platform specific: + \table + \header + \li Platform + \li Brief Description + \row + \li Windows + \li Removes the prefix "\\\\.\\" or "//./" from the system location + and returns the remainder of the string. + \row + \li Windows CE + \li Removes the suffix ":" from the system location + and returns the remainder of the string. + \row + \li Symbian + \li Returns the system location as it is, + as it is equivalent to the port name. + \row + \li Unix, BSD + \li Removes the prefix "/dev/" from the system location + and returns the remainder of the string. + \endtable + + \sa setPort(), QSerialPortInfo::portName() +*/ +QString QSerialPort::portName() const +{ + Q_D(const QSerialPort); + return QSerialPortInfoPrivate::portNameFromSystemLocation(d->systemLocation); +} + +/*! + \reimp + + Opens the serial port using OpenMode \a mode, and then returns true if + successful; otherwise returns false and sets an error code which can be + obtained by calling the error() method. + + \note The method returns false if opening the port is successful, but could + not set any of the port settings successfully. In that case, the port is + closed automatically not to leave the port around with incorrect settings. + + \warning The \a mode has to be QIODevice::ReadOnly, QIODevice::WriteOnly, + or QIODevice::ReadWrite. Other modes are unsupported. + + \sa QIODevice::OpenMode, setPort() +*/ +bool QSerialPort::open(OpenMode mode) +{ + Q_D(QSerialPort); + + if (isOpen()) { + setError(QSerialPort::OpenError); + return false; + } + + // Define while not supported modes. + static const OpenMode unsupportedModes = Append | Truncate | Text | Unbuffered; + if ((mode & unsupportedModes) || mode == NotOpen) { + setError(QSerialPort::UnsupportedOperationError); + return false; + } + + clearError(); + if (!d->open(mode)) + return false; + + if (!d->setBaudRate() + || !d->setDataBits(d->dataBits) + || !d->setParity(d->parity) + || !d->setStopBits(d->stopBits) + || !d->setFlowControl(d->flowControl)) { + d->close(); + return false; + } + + QIODevice::open(mode); + return true; +} + +/*! + \reimp + + \note The serial port has to be open before trying to close it; otherwise + sets the NotOpenError error code. + + \sa QIODevice::close() +*/ +void QSerialPort::close() +{ + Q_D(QSerialPort); + if (!isOpen()) { + setError(QSerialPort::NotOpenError); + return; + } + + QIODevice::close(); + d->close(); +} + +/*! + \property QSerialPort::settingsRestoredOnClose + \brief the flag which specifies to restore the previous settings when closing + the serial port. + \obsolete + + If this flag is true, the settings will be restored; otherwise not. + The default state of the QSerialPort class is to restore the + settings. +*/ +#if QT_DEPRECATED_SINCE(5,3) +void QSerialPort::setSettingsRestoredOnClose(bool restore) +{ + Q_D(QSerialPort); + + if (d->settingsRestoredOnClose != restore) { + d->settingsRestoredOnClose = restore; + emit settingsRestoredOnCloseChanged(d->settingsRestoredOnClose); + } +} + +bool QSerialPort::settingsRestoredOnClose() const +{ + Q_D(const QSerialPort); + return d->settingsRestoredOnClose; +} +#endif // QT_DEPRECATED_SINCE(5,3) +/*! + \fn void QSerialPort::settingsRestoredOnCloseChanged(bool restore) + \obsolete + + This signal is emitted after the flag which specifies to restore the + previous settings while closing the serial port has been changed. The new + flag which specifies to restore the previous settings while closing the serial + port is passed as \a restore. + + \sa QSerialPort::settingsRestoredOnClose +*/ + +/*! + \property QSerialPort::baudRate + \brief the data baud rate for the desired direction + + If the setting is successful or set before opening the port, returns true; + otherwise returns false and sets an error code which can be obtained by + accessing the value of the QSerialPort::error property. To set the baud + rate, use the enumeration QSerialPort::BaudRate or any positive qint32 + value. + + \note If the setting is set before opening the port, the actual serial port + setting is done automatically in the \l{QSerialPort::open()} method right + after that the opening of the port succeeds. + + \warning Setting the AllDirections flag is only supported on + the Windows, Windows CE, and Symbian platforms. + + \warning Returns equal baud rate in any direction on Windows, Windows CE, and + Symbian. + + The default value is Baud9600, i.e. 9600 bits per second. +*/ +bool QSerialPort::setBaudRate(qint32 baudRate, Directions directions) +{ + Q_D(QSerialPort); + + if (!isOpen() || d->setBaudRate(baudRate, directions)) { + if (directions & QSerialPort::Input) { + if (d->inputBaudRate != baudRate) + d->inputBaudRate = baudRate; + else + directions &= ~QSerialPort::Input; + } + + if (directions & QSerialPort::Output) { + if (d->outputBaudRate != baudRate) + d->outputBaudRate = baudRate; + else + directions &= ~QSerialPort::Output; + } + + if (directions) + emit baudRateChanged(baudRate, directions); + + return true; + } + + return false; +} + +qint32 QSerialPort::baudRate(Directions directions) const +{ + Q_D(const QSerialPort); + if (directions == QSerialPort::AllDirections) + return d->inputBaudRate == d->outputBaudRate ? + d->inputBaudRate : -1; + return directions & QSerialPort::Input ? d->inputBaudRate : d->outputBaudRate; +} + +/*! + \fn void QSerialPort::baudRateChanged(qint32 baudRate, Directions directions) + + This signal is emitted after the baud rate has been changed. The new baud + rate is passed as \a baudRate and directions as \a directions. + + \sa QSerialPort::baudRate +*/ + +/*! + \property QSerialPort::dataBits + \brief the data bits in a frame + + If the setting is successful or set before opening the port, returns + true; otherwise returns false and sets an error code which can be obtained + by accessing the value of the QSerialPort::error property. + + \note If the setting is set before opening the port, the actual serial port + setting is done automatically in the \l{QSerialPort::open()} method right + after that the opening of the port succeeds. + + The default value is Data8, i.e. 8 data bits. +*/ +bool QSerialPort::setDataBits(DataBits dataBits) +{ + Q_D(QSerialPort); + + if (!isOpen() || d->setDataBits(dataBits)) { + if (d->dataBits != dataBits) { + d->dataBits = dataBits; + emit dataBitsChanged(d->dataBits); + } + return true; + } + + return false; +} + +QSerialPort::DataBits QSerialPort::dataBits() const +{ + Q_D(const QSerialPort); + return d->dataBits; +} + +/*! + \fn void QSerialPort::dataBitsChanged(DataBits dataBits) + + This signal is emitted after the data bits in a frame has been changed. The + new data bits in a frame is passed as \a dataBits. + + \sa QSerialPort::dataBits +*/ + + +/*! + \property QSerialPort::parity + \brief the parity checking mode + + If the setting is successful or set before opening the port, returns true; + otherwise returns false and sets an error code which can be obtained by + accessing the value of the QSerialPort::error property. + + \note If the setting is set before opening the port, the actual serial port + setting is done automatically in the \l{QSerialPort::open()} method right + after that the opening of the port succeeds. + + The default value is NoParity, i.e. no parity. +*/ +bool QSerialPort::setParity(Parity parity) +{ + Q_D(QSerialPort); + + if (!isOpen() || d->setParity(parity)) { + if (d->parity != parity) { + d->parity = parity; + emit parityChanged(d->parity); + } + return true; + } + + return false; +} + +QSerialPort::Parity QSerialPort::parity() const +{ + Q_D(const QSerialPort); + return d->parity; +} + +/*! + \fn void QSerialPort::parityChanged(Parity parity) + + This signal is emitted after the parity checking mode has been changed. The + new parity checking mode is passed as \a parity. + + \sa QSerialPort::parity +*/ + +/*! + \property QSerialPort::stopBits + \brief the number of stop bits in a frame + + If the setting is successful or set before opening the port, returns true; + otherwise returns false and sets an error code which can be obtained by + accessing the value of the QSerialPort::error property. + + \note If the setting is set before opening the port, the actual serial port + setting is done automatically in the \l{QSerialPort::open()} method right + after that the opening of the port succeeds. + + The default value is OneStop, i.e. 1 stop bit. +*/ +bool QSerialPort::setStopBits(StopBits stopBits) +{ + Q_D(QSerialPort); + + if (!isOpen() || d->setStopBits(stopBits)) { + if (d->stopBits != stopBits) { + d->stopBits = stopBits; + emit stopBitsChanged(d->stopBits); + } + return true; + } + + return false; +} + +QSerialPort::StopBits QSerialPort::stopBits() const +{ + Q_D(const QSerialPort); + return d->stopBits; +} + +/*! + \fn void QSerialPort::stopBitsChanged(StopBits stopBits) + + This signal is emitted after the number of stop bits in a frame has been + changed. The new number of stop bits in a frame is passed as \a stopBits. + + \sa QSerialPort::stopBits +*/ + +/*! + \property QSerialPort::flowControl + \brief the desired flow control mode + + If the setting is successful or set before opening the port, returns true; + otherwise returns false and sets an error code which can be obtained by + accessing the value of the QSerialPort::error property. + + \note If the setting is set before opening the port, the actual serial port + setting is done automatically in the \l{QSerialPort::open()} method right + after that the opening of the port succeeds. + + The default value is NoFlowControl, i.e. no flow control. +*/ +bool QSerialPort::setFlowControl(FlowControl flowControl) +{ + Q_D(QSerialPort); + + if (!isOpen() || d->setFlowControl(flowControl)) { + if (d->flowControl != flowControl) { + d->flowControl = flowControl; + emit flowControlChanged(d->flowControl); + } + return true; + } + + return false; +} + +QSerialPort::FlowControl QSerialPort::flowControl() const +{ + Q_D(const QSerialPort); + return d->flowControl; +} + +/*! + \fn void QSerialPort::flowControlChanged(FlowControl flow) + + This signal is emitted after the flow control mode has been changed. The + new flow control mode is passed as \a flow. + + \sa QSerialPort::flowControl +*/ + +/*! + \property QSerialPort::dataTerminalReady + \brief the state (high or low) of the line signal DTR + + Returns true on success, false otherwise. + If the flag is true then the DTR signal is set to high; otherwise low. + + \note The serial port has to be open before trying to set or get this + property; otherwise false is returned and the error code is set to + NotOpenError. + + \sa pinoutSignals() +*/ +bool QSerialPort::setDataTerminalReady(bool set) +{ + Q_D(QSerialPort); + + if (!isOpen()) { + setError(QSerialPort::NotOpenError); + qWarning("%s: device not open", Q_FUNC_INFO); + return false; + } + + const bool dataTerminalReady = isDataTerminalReady(); + const bool retval = d->setDataTerminalReady(set); + if (retval && (dataTerminalReady != set)) + emit dataTerminalReadyChanged(set); + + return retval; +} + +bool QSerialPort::isDataTerminalReady() +{ + Q_D(QSerialPort); + return d->pinoutSignals() & QSerialPort::DataTerminalReadySignal; +} + +/*! + \fn void QSerialPort::dataTerminalReadyChanged(bool set) + + This signal is emitted after the state (high or low) of the line signal DTR + has been changed. The new the state (high or low) of the line signal DTR is + passed as \a set. + + \sa QSerialPort::dataTerminalReady +*/ + +/*! + \property QSerialPort::requestToSend + \brief the state (high or low) of the line signal RTS + + Returns true on success, false otherwise. + If the flag is true then the RTS signal is set to high; otherwise low. + + \note The serial port has to be open before trying to set or get this + property; otherwise false is returned and the error code is set to + NotOpenError. + + \sa pinoutSignals() +*/ +bool QSerialPort::setRequestToSend(bool set) +{ + Q_D(QSerialPort); + + if (!isOpen()) { + setError(QSerialPort::NotOpenError); + qWarning("%s: device not open", Q_FUNC_INFO); + return false; + } + + const bool requestToSend = isRequestToSend(); + const bool retval = d->setRequestToSend(set); + if (retval && (requestToSend != set)) + emit requestToSendChanged(set); + + return retval; +} + +bool QSerialPort::isRequestToSend() +{ + Q_D(QSerialPort); + return d->pinoutSignals() & QSerialPort::RequestToSendSignal; +} + +/*! + \fn void QSerialPort::requestToSendChanged(bool set) + + This signal is emitted after the state (high or low) of the line signal RTS + has been changed. The new the state (high or low) of the line signal RTS is + passed as \a set. + + \sa QSerialPort::requestToSend +*/ + +/*! + Returns the state of the line signals in a bitmap format. + + From this result, it is possible to allocate the state of the + desired signal by applying a mask "AND", where the mask is + the desired enumeration value from QSerialPort::PinoutSignals. + + \note This method performs a system call, thus ensuring that the line signal + states are returned properly. This is necessary when the underlying + operating systems cannot provide proper notifications about the changes. + + \note The serial port has to be open before trying to get the pinout + signals; otherwise returns NoSignal and sets the NotOpenError error code. + + \sa QSerialPort::dataTerminalReady, QSerialPort::requestToSend +*/ +QSerialPort::PinoutSignals QSerialPort::pinoutSignals() +{ + Q_D(QSerialPort); + + if (!isOpen()) { + setError(QSerialPort::NotOpenError); + qWarning("%s: device not open", Q_FUNC_INFO); + return QSerialPort::NoSignal; + } + + return d->pinoutSignals(); +} + +/*! + This function writes as much as possible from the internal write + buffer to the underlying serial port without blocking. If any data + was written, this function returns true; otherwise returns false. + + Call this function for sending the buffered data immediately to the serial + port. The number of bytes successfully written depends on the operating + system. In most cases, this function does not need to be called, because the + QSerialPort class will start sending data automatically once control is + returned to the event loop. In the absence of an event loop, call + waitForBytesWritten() instead. + + \note The serial port has to be open before trying to flush any buffered + data; otherwise returns false and sets the NotOpenError error code. + + \sa write(), waitForBytesWritten() +*/ +bool QSerialPort::flush() +{ + Q_D(QSerialPort); + + if (!isOpen()) { + setError(QSerialPort::NotOpenError); + qWarning("%s: device not open", Q_FUNC_INFO); + return false; + } + + return d->flush(); +} + +/*! + Discards all characters from the output or input buffer, depending on + given directions \a directions. This includes clearing the internal class buffers and + the UART (driver) buffers. Also terminate pending read or write operations. + If successful, returns true; otherwise returns false. + + \note The serial port has to be open before trying to clear any buffered + data; otherwise returns false and sets the NotOpenError error code. +*/ +bool QSerialPort::clear(Directions directions) +{ + Q_D(QSerialPort); + + if (!isOpen()) { + setError(QSerialPort::NotOpenError); + qWarning("%s: device not open", Q_FUNC_INFO); + return false; + } + + if (directions & Input) + d->readBuffer.clear(); + if (directions & Output) + d->writeBuffer.clear(); + return d->clear(directions); +} + +/*! + \reimp + + Returns true if no more data is currently available for reading; otherwise + returns false. + + This function is most commonly used when reading data from the + serial port in a loop. For example: + + \code + // This slot is connected to QSerialPort::readyRead() + void QSerialPortClass::readyReadSlot() + { + while (!port.atEnd()) { + QByteArray data = port.read(100); + .... + } + } + \endcode + + \sa bytesAvailable(), readyRead() + */ +bool QSerialPort::atEnd() const +{ + Q_D(const QSerialPort); + return QIODevice::atEnd() && (!isOpen() || (d->readBuffer.size() == 0)); +} + +/*! + \property QSerialPort::dataErrorPolicy + \brief the error policy for how the process receives characters in the case where + a parity error is detected. + \obsolete + + If the setting is successful, returns true; otherwise returns false. The + default policy set is IgnorePolicy. + + \note The serial port has to be open before trying to set this property; + otherwise returns false and sets the NotOpenError error code. This is a bit + unusual as opposed to the regular Qt property settings of a class. However, + this is a special use case since the property is set through the interaction + with the kernel and hardware. Hence, the two scenarios cannot be completely + compared to each other. +*/ +#if QT_DEPRECATED_SINCE(5, 2) +bool QSerialPort::setDataErrorPolicy(DataErrorPolicy policy) +{ + Q_D(QSerialPort); + + if (!isOpen()) { + setError(QSerialPort::NotOpenError); + qWarning("%s: device not open", Q_FUNC_INFO); + return false; + } + + const bool ret = d->policy == policy || d->setDataErrorPolicy(policy); + if (ret && (d->policy != policy)) { + d->policy = policy; + emit dataErrorPolicyChanged(d->policy); + } + + return ret; +} + +QSerialPort::DataErrorPolicy QSerialPort::dataErrorPolicy() const +{ + Q_D(const QSerialPort); + return d->policy; +} +#endif // QT_DEPRECATED_SINCE(5, 2) +/*! + \fn void QSerialPort::dataErrorPolicyChanged(DataErrorPolicy policy) + \obsolete + + This signal is emitted after the error policy for how the process receives + characters in case of parity error detection has been changed. The new error + policy for how the process receives the character in case of parity error + detection is passed as \a policy. + + \sa QSerialPort::dataErrorPolicy +*/ + +/*! + \property QSerialPort::error + \brief the error status of the serial port + + The I/O device status returns an error code. For example, if open() + returns false, or a read/write operation returns -1, this property can + be used to figure out the reason why the operation failed. + + The error code is set to the default QSerialPort::NoError after a call to + clearError() +*/ +QSerialPort::SerialPortError QSerialPort::error() const +{ + Q_D(const QSerialPort); + return d->error; +} + +void QSerialPort::clearError() +{ + setError(QSerialPort::NoError); +} + +/*! + \fn void QSerialPort::error(SerialPortError error) + + This signal is emitted after the error has been changed. The new error + is passed as \a error. + + \sa QSerialPort::error +*/ + +/*! + Returns the size of the internal read buffer. This limits the + amount of data that the client can receive before calling the read() + or readAll() methods. + + A read buffer size of 0 (the default) means that the buffer has + no size limit, ensuring that no data is lost. + + \sa setReadBufferSize(), read() +*/ +qint64 QSerialPort::readBufferSize() const +{ + Q_D(const QSerialPort); + return d->readBufferMaxSize; +} + +/*! + Sets the size of QSerialPort's internal read buffer to be \a + size bytes. + + If the buffer size is limited to a certain size, QSerialPort + will not buffer more than this size of data. The special case of a buffer + size of 0 means that the read buffer is unlimited and all + incoming data is buffered. This is the default. + + This option is useful if the data is only read at certain points + in time (for instance in a real-time streaming application) or if the serial + port should be protected against receiving too much data, which may + eventually cause the application to run out of memory. + + \sa readBufferSize(), read() +*/ +void QSerialPort::setReadBufferSize(qint64 size) +{ + Q_D(QSerialPort); + + if (d->readBufferMaxSize == size) + return; + d->readBufferMaxSize = size; +} + +/*! + \reimp + + Always returns true. The serial port is a sequential device. +*/ +bool QSerialPort::isSequential() const +{ + return true; +} + +/*! + \reimp + + Returns the number of incoming bytes that are waiting to be read. + + \sa bytesToWrite(), read() +*/ +qint64 QSerialPort::bytesAvailable() const +{ + Q_D(const QSerialPort); + return d->readBuffer.size() + QIODevice::bytesAvailable(); +} + +/*! + \reimp + + Returns the number of bytes that are waiting to be written. The + bytes are written when control goes back to the event loop or + when flush() is called. + + \sa bytesAvailable(), flush() +*/ +qint64 QSerialPort::bytesToWrite() const +{ + Q_D(const QSerialPort); + return d->bytesToWrite() + QIODevice::bytesToWrite(); +} + +/*! + \reimp + + Returns true if a line of data can be read from the serial port; + otherwise returns false. + + \sa readLine() +*/ +bool QSerialPort::canReadLine() const +{ + Q_D(const QSerialPort); + const bool hasLine = (d->readBuffer.size() > 0) && d->readBuffer.canReadLine(); + return hasLine || QIODevice::canReadLine(); +} + +/*! + \reimp + + This function blocks until new data is available for reading and the + \l{QIODevice::}{readyRead()} signal has been emitted. The function + will timeout after \a msecs milliseconds. + + The function returns true if the readyRead() signal is emitted and + there is new data available for reading; otherwise it returns false + (if an error occurred or the operation timed out). + + \sa waitForBytesWritten() +*/ +bool QSerialPort::waitForReadyRead(int msecs) +{ + Q_D(QSerialPort); + return d->waitForReadyRead(msecs); +} + +/*! + \fn Handle QSerialPort::handle() const + \since 5.2 + + If the platform is supported and the serial port is open, returns the native + serial port handle; otherwise returns -1. + + \warning This function is for expert use only; use it at your own risk. + Furthermore, this function carries no compatibility promise between minor + Qt releases. +*/ + +/*! + \reimp +*/ +bool QSerialPort::waitForBytesWritten(int msecs) +{ + Q_D(QSerialPort); + return d->waitForBytesWritten(msecs); +} + +/*! + Sends a continuous stream of zero bits during a specified period + of time \a duration in msec if the terminal is using asynchronous + serial data. If successful, returns true; otherwise returns false. + + If the duration is zero then zero bits are transmitted by at least + 0.25 seconds, but no more than 0.5 seconds. + + If the duration is non zero then zero bits are transmitted within a certain + period of time depending on the implementation. + + \note The serial port has to be open before trying to send a break + duration; otherwise returns false and sets the NotOpenError error code. + + \sa setBreakEnabled() +*/ +bool QSerialPort::sendBreak(int duration) +{ + Q_D(QSerialPort); + + if (!isOpen()) { + setError(QSerialPort::NotOpenError); + qWarning("%s: device not open", Q_FUNC_INFO); + return false; + } + + return d->sendBreak(duration); +} + +/*! + Controls the signal break, depending on the flag \a set. + If successful, returns true; otherwise returns false. + + If \a set is true then enables the break transmission; otherwise disables. + + \note The serial port has to be open before trying to set break enabled; + otherwise returns false and sets the NotOpenError error code. + + \sa sendBreak() +*/ +bool QSerialPort::setBreakEnabled(bool set) +{ + Q_D(QSerialPort); + + if (!isOpen()) { + setError(QSerialPort::NotOpenError); + qWarning("%s: device not open", Q_FUNC_INFO); + return false; + } + + return d->setBreakEnabled(set); +} + +/*! + \reimp +*/ +qint64 QSerialPort::readData(char *data, qint64 maxSize) +{ + Q_D(QSerialPort); +#ifdef Q_OS_ANDROID + qint64 retL = d->readBuffer.read(data, maxSize); + d->startReadThread(); + return retL; +#else + return d->readData(data, maxSize); +#endif +} + +/*! + \reimp +*/ +qint64 QSerialPort::readLineData(char *data, qint64 maxSize) +{ +#ifdef Q_OS_ANDROID + qint64 retL = QIODevice::readLineData(data, maxSize); + Q_D(QSerialPort); + d->startReadThread(); + return retL; +#else + return QIODevice::readLineData(data, maxSize); +#endif +} + +/*! + \reimp +*/ +qint64 QSerialPort::writeData(const char *data, qint64 maxSize) +{ + Q_D(QSerialPort); + return d->writeData(data, maxSize); +} + +void QSerialPort::setError(QSerialPort::SerialPortError serialPortError, const QString &errorString) +{ + Q_D(QSerialPort); + + d->error = serialPortError; + + if (errorString.isNull()) + setErrorString(qt_error_string(-1)); + else + setErrorString(errorString); + + emit error(serialPortError); +} + +#include "moc_qserialport.cpp" + +QT_END_NAMESPACE diff --git a/libs/qtandroidserialport/src/qserialport.h b/libs/qtandroidserialport/src/qserialport.h new file mode 100644 index 0000000000000000000000000000000000000000..c637a3f79d3fb7d7efb20bfa6df0cbc8dd4bf965 --- /dev/null +++ b/libs/qtandroidserialport/src/qserialport.h @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2013 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORT_H +#define QSERIALPORT_H + +#include + +QT_BEGIN_NAMESPACE + +class QSerialPortInfo; +class QSerialPortPrivate; + +class QSerialPort : public QIODevice +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QSerialPort) + + Q_PROPERTY(qint32 baudRate READ baudRate WRITE setBaudRate NOTIFY baudRateChanged) + Q_PROPERTY(DataBits dataBits READ dataBits WRITE setDataBits NOTIFY dataBitsChanged) + Q_PROPERTY(Parity parity READ parity WRITE setParity NOTIFY parityChanged) + Q_PROPERTY(StopBits stopBits READ stopBits WRITE setStopBits NOTIFY stopBitsChanged) + Q_PROPERTY(FlowControl flowControl READ flowControl WRITE setFlowControl NOTIFY flowControlChanged) +#if QT_DEPRECATED_SINCE(5, 2) + Q_PROPERTY(DataErrorPolicy dataErrorPolicy READ dataErrorPolicy WRITE setDataErrorPolicy NOTIFY dataErrorPolicyChanged) +#endif + Q_PROPERTY(bool dataTerminalReady READ isDataTerminalReady WRITE setDataTerminalReady NOTIFY dataTerminalReadyChanged) + Q_PROPERTY(bool requestToSend READ isRequestToSend WRITE setRequestToSend NOTIFY requestToSendChanged) + Q_PROPERTY(SerialPortError error READ error RESET clearError NOTIFY error) +#if QT_DEPRECATED_SINCE(5, 3) + Q_PROPERTY(bool settingsRestoredOnClose READ settingsRestoredOnClose WRITE setSettingsRestoredOnClose NOTIFY settingsRestoredOnCloseChanged) +#endif + + Q_ENUMS(BaudRate DataBits Parity StopBits FlowControl DataErrorPolicy SerialPortError) + Q_FLAGS(Directions PinoutSignals) + +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) + typedef void* Handle; +#else + typedef int Handle; +#endif + +public: + + enum Direction { + Input = 1, + Output = 2, + AllDirections = Input | Output + }; + Q_DECLARE_FLAGS(Directions, Direction) + + enum BaudRate { + Baud1200 = 1200, + Baud2400 = 2400, + Baud4800 = 4800, + Baud9600 = 9600, + Baud19200 = 19200, + Baud38400 = 38400, + Baud57600 = 57600, + Baud115200 = 115200, + UnknownBaud = -1 + }; + + enum DataBits { + Data5 = 5, + Data6 = 6, + Data7 = 7, + Data8 = 8, + UnknownDataBits = -1 + }; + + enum Parity { + NoParity = 0, + EvenParity = 2, + OddParity = 3, + SpaceParity = 4, + MarkParity = 5, + UnknownParity = -1 + }; + + enum StopBits { + OneStop = 1, + OneAndHalfStop = 3, + TwoStop = 2, + UnknownStopBits = -1 + }; + + enum FlowControl { + NoFlowControl, + HardwareControl, + SoftwareControl, + UnknownFlowControl = -1 + }; + + enum PinoutSignal { + NoSignal = 0x00, + TransmittedDataSignal = 0x01, + ReceivedDataSignal = 0x02, + DataTerminalReadySignal = 0x04, + DataCarrierDetectSignal = 0x08, + DataSetReadySignal = 0x10, + RingIndicatorSignal = 0x20, + RequestToSendSignal = 0x40, + ClearToSendSignal = 0x80, + SecondaryTransmittedDataSignal = 0x100, + SecondaryReceivedDataSignal = 0x200 + }; + Q_DECLARE_FLAGS(PinoutSignals, PinoutSignal) + +#if QT_DEPRECATED_SINCE(5, 2) +#if defined _MSC_VER +#pragma deprecated(UnknownBaud) +#pragma deprecated(UnknownDataBits) +#pragma deprecated(UnknownParity) +#pragma deprecated(UnknownStopBits) +#pragma deprecated(UnknownFlowControl) +#pragma deprecated(TransmittedDataSignal) +#pragma deprecated(ReceivedDataSignal) +#endif +#endif + +#if QT_DEPRECATED_SINCE(5, 2) + enum DataErrorPolicy { + SkipPolicy, + PassZeroPolicy, + IgnorePolicy, + StopReceivingPolicy, + UnknownPolicy = -1 + }; +#endif + + enum SerialPortError { + NoError, + DeviceNotFoundError, + PermissionError, + OpenError, + ParityError, + FramingError, + BreakConditionError, + WriteError, + ReadError, + ResourceError, + UnsupportedOperationError, + UnknownError, + TimeoutError, + NotOpenError + }; + + explicit QSerialPort(QObject *parent = Q_NULLPTR); + explicit QSerialPort(const QString &name, QObject *parent = Q_NULLPTR); + explicit QSerialPort(const QSerialPortInfo &info, QObject *parent = Q_NULLPTR); + virtual ~QSerialPort(); + + void setPortName(const QString &name); + QString portName() const; + + void setPort(const QSerialPortInfo &info); + + bool open(OpenMode mode) Q_DECL_OVERRIDE; + void close() Q_DECL_OVERRIDE; + +#if QT_DEPRECATED_SINCE(5, 3) + QT_DEPRECATED void setSettingsRestoredOnClose(bool restore); + QT_DEPRECATED bool settingsRestoredOnClose() const; +#endif + + bool setBaudRate(qint32 baudRate, Directions directions = AllDirections); + qint32 baudRate(Directions directions = AllDirections) const; + + bool setDataBits(DataBits dataBits); + DataBits dataBits() const; + + bool setParity(Parity parity); + Parity parity() const; + + bool setStopBits(StopBits stopBits); + StopBits stopBits() const; + + bool setFlowControl(FlowControl flowControl); + FlowControl flowControl() const; + + bool setDataTerminalReady(bool set); + bool isDataTerminalReady(); + + bool setRequestToSend(bool set); + bool isRequestToSend(); + + PinoutSignals pinoutSignals(); + + bool flush(); + bool clear(Directions directions = AllDirections); + bool atEnd() const Q_DECL_OVERRIDE; + +#if QT_DEPRECATED_SINCE(5, 2) + QT_DEPRECATED bool setDataErrorPolicy(DataErrorPolicy policy = IgnorePolicy); + QT_DEPRECATED DataErrorPolicy dataErrorPolicy() const; +#endif + + SerialPortError error() const; + void clearError(); + + qint64 readBufferSize() const; + void setReadBufferSize(qint64 size); + + bool isSequential() const Q_DECL_OVERRIDE; + + qint64 bytesAvailable() const Q_DECL_OVERRIDE; + qint64 bytesToWrite() const Q_DECL_OVERRIDE; + bool canReadLine() const Q_DECL_OVERRIDE; + + bool waitForReadyRead(int msecs) Q_DECL_OVERRIDE; + bool waitForBytesWritten(int msecs) Q_DECL_OVERRIDE; + + bool sendBreak(int duration = 0); + bool setBreakEnabled(bool set = true); + + Handle handle() const; + +Q_SIGNALS: + void baudRateChanged(qint32 baudRate, QSerialPort::Directions directions); + void dataBitsChanged(QSerialPort::DataBits dataBits); + void parityChanged(QSerialPort::Parity parity); + void stopBitsChanged(QSerialPort::StopBits stopBits); + void flowControlChanged(QSerialPort::FlowControl flowControl); + void dataErrorPolicyChanged(QSerialPort::DataErrorPolicy policy); + void dataTerminalReadyChanged(bool set); + void requestToSendChanged(bool set); + void error(QSerialPort::SerialPortError serialPortError); + void settingsRestoredOnCloseChanged(bool restore); + +protected: + qint64 readData(char *data, qint64 maxSize) Q_DECL_OVERRIDE; + qint64 readLineData(char *data, qint64 maxSize) Q_DECL_OVERRIDE; + qint64 writeData(const char *data, qint64 maxSize) Q_DECL_OVERRIDE; + +private: + void setError(QSerialPort::SerialPortError error, const QString &errorString = QString()); + + QSerialPortPrivate * const d_ptr; + + Q_DISABLE_COPY(QSerialPort) + +#if defined (Q_OS_WIN32) + Q_PRIVATE_SLOT(d_func(), bool _q_completeAsyncCommunication()) + Q_PRIVATE_SLOT(d_func(), bool _q_completeAsyncRead()) + Q_PRIVATE_SLOT(d_func(), bool _q_completeAsyncWrite()) + Q_PRIVATE_SLOT(d_func(), bool _q_startAsyncWrite()) +#endif +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSerialPort::Directions) +Q_DECLARE_OPERATORS_FOR_FLAGS(QSerialPort::PinoutSignals) + +QT_END_NAMESPACE + +#endif // QSERIALPORT_H diff --git a/libs/qtandroidserialport/src/qserialport_android.cpp b/libs/qtandroidserialport/src/qserialport_android.cpp new file mode 100644 index 0000000000000000000000000000000000000000..53b64440d13005b235f10b59245872fde58fb257 --- /dev/null +++ b/libs/qtandroidserialport/src/qserialport_android.cpp @@ -0,0 +1,867 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2012 Laszlo Papp +** Copyright (C) 2012 Andre Hartmann +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Written by: S. Michael Goza 2014 +// Adapted for QGC by: Gus Grubba 2015 + + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "qserialport_android_p.h" + +QT_BEGIN_NAMESPACE + +#define BAD_PORT 0 + +static const char V_jniClassName[] {"org/qgroundcontrol/qgchelper/UsbDeviceJNI"}; +static const char V_TAG[] {"QGC_QSerialPort"}; + +static void jniDeviceHasDisconnected(JNIEnv *envA, jobject thizA, jint userDataA) +{ + Q_UNUSED(envA); + Q_UNUSED(thizA); + if (userDataA != 0) + ((QSerialPortPrivate *)userDataA)->q_ptr->close(); +} + +static void jniDeviceNewData(JNIEnv *envA, jobject thizA, jint userDataA, jbyteArray dataA) +{ + Q_UNUSED(thizA); + if (userDataA != 0) + { + jbyte *bytesL = envA->GetByteArrayElements(dataA, NULL); + jsize lenL = envA->GetArrayLength(dataA); + ((QSerialPortPrivate *)userDataA)->newDataArrived((char *)bytesL, lenL); + envA->ReleaseByteArrayElements(dataA, bytesL, JNI_ABORT); + } +} + +static void jniDeviceException(JNIEnv *envA, jobject thizA, jint userDataA, jstring messageA) +{ + Q_UNUSED(thizA); + if(userDataA != 0) + { + const char *stringL = envA->GetStringUTFChars(messageA, NULL); + QString strL = QString::fromUtf8(stringL); + envA->ReleaseStringUTFChars(messageA, stringL); + if(envA->ExceptionCheck()) + envA->ExceptionClear(); + ((QSerialPortPrivate *)userDataA)->exceptionArrived(strL); + } +} + +QSerialPortPrivate::QSerialPortPrivate(QSerialPort *q) + : QSerialPortPrivateData(q) + , descriptor(-1) + , isCustomBaudRateSupported(false) + , emittedBytesWritten(false) + , pendingBytesWritten(0) + , hasRegisteredFunctions(false) + , jniDataBits(8) + , jniStopBits(1) + , jniParity(0) + , internalWriteTimeoutMsec(0) + , isReadStopped(true) +{ +} + +bool QSerialPortPrivate::open(QIODevice::OpenMode mode) +{ + rwMode = mode; + __android_log_print(ANDROID_LOG_INFO, V_TAG, "Opening %s", systemLocation.toLatin1().data()); + + QAndroidJniObject jnameL = QAndroidJniObject::fromString(systemLocation); + deviceId = QAndroidJniObject::callStaticMethod( + V_jniClassName, + "open", + "(Ljava/lang/String;I)I", + jnameL.object(), + (jint)this); + + isReadStopped = false; + + if (deviceId == BAD_PORT) + { + __android_log_print(ANDROID_LOG_ERROR, V_TAG, "Error opening %s", systemLocation.toLatin1().data()); + q_ptr->setError(QSerialPort::DeviceNotFoundError); + return false; + } + + descriptor = QAndroidJniObject::callStaticMethod( + V_jniClassName, + "getDeviceHandle", + "(I)I", + deviceId); + + if (!hasRegisteredFunctions) + { + // REGISTER THE C++ FUNCTION WITH JNI + QAndroidJniEnvironment envL; + + JNINativeMethod methodsL[] { + {"nativeDeviceHasDisconnected", "(I)V", reinterpret_cast(jniDeviceHasDisconnected)}, + {"nativeDeviceNewData", "(I[B)V", reinterpret_cast(jniDeviceNewData)}, + {"nativeDeviceException", "(ILjava/lang/String;)V", reinterpret_cast(jniDeviceException)} + }; + + QAndroidJniObject javaClassL(V_jniClassName); + jclass objectClassL = envL->GetObjectClass(javaClassL.object()); + jint valL = envL->RegisterNatives(objectClassL, methodsL, sizeof(methodsL) / sizeof(methodsL[0])); + envL->DeleteLocalRef(objectClassL); + hasRegisteredFunctions = true; + + if (envL->ExceptionCheck()) + envL->ExceptionClear(); + + if(valL < 0) { + __android_log_print(ANDROID_LOG_ERROR, V_TAG, "Error registering methods"); + q_ptr->setError(QSerialPort::OpenError); + return false; + } + } + + if (rwMode == QIODevice::WriteOnly) + stopReadThread(); + + return true; +} + +void QSerialPortPrivate::close() +{ + if (deviceId == BAD_PORT) + return; + + __android_log_print(ANDROID_LOG_INFO, V_TAG, "Closing %s", systemLocation.toLatin1().data()); + jboolean resultL = QAndroidJniObject::callStaticMethod( + V_jniClassName, + "close", + "(I)Z", + deviceId); + + descriptor = -1; + isCustomBaudRateSupported = false; + pendingBytesWritten = 0; + deviceId = BAD_PORT; + + if (!resultL) + q_ptr->setErrorString(QStringLiteral("Closing device failed")); +} + +bool QSerialPortPrivate::setParameters(int baudRateA, int dataBitsA, int stopBitsA, int parityA) +{ + if (deviceId == BAD_PORT) + { + q_ptr->setError(QSerialPort::NotOpenError); + return false; + } + + jboolean resultL = QAndroidJniObject::callStaticMethod(V_jniClassName, + "setParameters", + "(IIIII)Z", + deviceId, + baudRateA, + dataBitsA, + stopBitsA, + parityA); + + if(resultL) + { + // SET THE JNI VALUES TO WHAT WAS SENT + inputBaudRate = outputBaudRate = baudRateA; + jniDataBits = dataBitsA; + jniStopBits = stopBitsA; + jniParity = parityA; + } + + return resultL; +} + + + +void QSerialPortPrivate::stopReadThread() +{ + if (isReadStopped) + return; + + QAndroidJniObject::callStaticMethod(V_jniClassName, + "stopIoManager", + "(I)V", + deviceId); + isReadStopped = true; +} + + + +void QSerialPortPrivate::startReadThread() +{ + if (!isReadStopped) + return; + + QAndroidJniObject::callStaticMethod(V_jniClassName, + "startIoManager", + "(I)V", + deviceId); + isReadStopped = false; +} + + + + + +QSerialPort::PinoutSignals QSerialPortPrivate::pinoutSignals() +{ + return QSerialPort::NoSignal; +} + + + + +bool QSerialPortPrivate::setDataTerminalReady(bool set) +{ + if (deviceId == BAD_PORT) + { + q_ptr->setError(QSerialPort::NotOpenError); + return false; + } + + return QAndroidJniObject::callStaticMethod(V_jniClassName, + "setDataTerminalReady", + "(IZ)Z", + deviceId, + set); +} + + + + +bool QSerialPortPrivate::setRequestToSend(bool set) +{ + if (deviceId == BAD_PORT) + { + q_ptr->setError(QSerialPort::NotOpenError); + return false; + } + + return QAndroidJniObject::callStaticMethod(V_jniClassName, + "setRequestToSend", + "(IZ)Z", + deviceId, + set); +} + + + + +bool QSerialPortPrivate::flush() +{ + return writeDataOneShot(); +} + + + + +bool QSerialPortPrivate::clear(QSerialPort::Directions directions) +{ + if (deviceId == BAD_PORT) + { + q_ptr->setError(QSerialPort::NotOpenError); + return false; + } + + bool inputL = false; + bool outputL = false; + + if (directions == QSerialPort::AllDirections) + inputL = outputL = true; + else + { + if (directions & QSerialPort::Input) + inputL = true; + + if (directions & QSerialPort::Output) + outputL = true; + } + + return QAndroidJniObject::callStaticMethod(V_jniClassName, + "purgeBuffers", + "(IZZ)Z", + deviceId, + inputL, + outputL); +} + + + + +bool QSerialPortPrivate::sendBreak(int duration) +{ + Q_UNUSED(duration); + return true; +} + + + + +bool QSerialPortPrivate::setBreakEnabled(bool set) +{ + Q_UNUSED(set); + return true; +} + + + + +void QSerialPortPrivate::startWriting() +{ + writeDataOneShot(); +} + + + + +bool QSerialPortPrivate::waitForReadyRead(int msecs) +{ + int origL = readBuffer.size(); + + if (origL > 0) + return true; + + for (int iL=0; iLpolicy = policy; + return true; +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void QSerialPortPrivate::newDataArrived(char *bytesA, int lengthA) +{ + Q_Q(QSerialPort); + + int bytesToReadL = lengthA; + + // Always buffered, read data from the port into the read buffer + if (readBufferMaxSize && (bytesToReadL > (readBufferMaxSize - readBuffer.size()))) { + bytesToReadL = readBufferMaxSize - readBuffer.size(); + if (bytesToReadL <= 0) { + // Buffer is full. User must read data from the buffer + // before we can read more from the port. + stopReadThread(); + return; + } + } + + char *ptr = readBuffer.reserve(bytesToReadL); + memcpy(ptr, bytesA, bytesToReadL); + + emit q->readyRead(); +} + + + +void QSerialPortPrivate::exceptionArrived(QString strA) +{ + q_ptr->setErrorString(strA); +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool QSerialPortPrivate::writeDataOneShot() +{ + Q_Q(QSerialPort); + + pendingBytesWritten = -1; + + while (!writeBuffer.isEmpty()) + { + pendingBytesWritten = writeToPort(writeBuffer.readPointer(), writeBuffer.nextDataBlockSize()); + + if (pendingBytesWritten <= 0) + { + QSerialPort::SerialPortError errorL = decodeSystemError(); + if (errorL != QSerialPort::ResourceError) + errorL = QSerialPort::WriteError; + q->setError(errorL); + return false; + } + + writeBuffer.free(pendingBytesWritten); + + emit q->bytesWritten(pendingBytesWritten); + } + + return (pendingBytesWritten < 0)? false: true; +} + + + +QSerialPort::SerialPortError QSerialPortPrivate::decodeSystemError() const +{ + QSerialPort::SerialPortError error; + switch (errno) { + case ENODEV: + error = QSerialPort::DeviceNotFoundError; + break; + case EACCES: + error = QSerialPort::PermissionError; + break; + case EBUSY: + error = QSerialPort::PermissionError; + break; + case EAGAIN: + error = QSerialPort::ResourceError; + break; + case EIO: + error = QSerialPort::ResourceError; + break; + case EBADF: + error = QSerialPort::ResourceError; + break; + default: + error = QSerialPort::UnknownError; + break; + } + return error; +} + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +qint64 QSerialPortPrivate::writeToPort(const char *data, qint64 maxSize) +{ + if (deviceId == BAD_PORT) + { + q_ptr->setError(QSerialPort::NotOpenError); + return 0; + } + + QAndroidJniEnvironment envL; + jbyteArray jarrayL = envL->NewByteArray(maxSize); + envL->SetByteArrayRegion(jarrayL, 0, maxSize, (jbyte *)data); + int resultL = QAndroidJniObject::callStaticMethod(V_jniClassName, + "write", + "(I[BI)I", + deviceId, + jarrayL, + internalWriteTimeoutMsec); + + if (envL->ExceptionCheck()) + { + envL->ExceptionClear(); + q_ptr->setErrorString(QStringLiteral("Writing to the device threw an exception")); + envL->DeleteLocalRef(jarrayL); + return 0; + } + + envL->DeleteLocalRef(jarrayL); + + return resultL; +} + + + + +static inline bool evenParity(quint8 c) +{ + c ^= c >> 4; //(c7 ^ c3)(c6 ^ c2)(c5 ^ c1)(c4 ^ c0) + c ^= c >> 2; //[(c7 ^ c3)(c5 ^ c1)][(c6 ^ c2)(c4 ^ c0)] + c ^= c >> 1; + return c & 1; //(c7 ^ c3)(c5 ^ c1)(c6 ^ c2)(c4 ^ c0) +} + +typedef QMap BaudRateMap; + +// The OS specific defines can be found in termios.h + +static const BaudRateMap createStandardBaudRateMap() +{ + BaudRateMap baudRateMap; + +#ifdef B50 + baudRateMap.insert(50, B50); +#endif + +#ifdef B75 + baudRateMap.insert(75, B75); +#endif + +#ifdef B110 + baudRateMap.insert(110, B110); +#endif + +#ifdef B134 + baudRateMap.insert(134, B134); +#endif + +#ifdef B150 + baudRateMap.insert(150, B150); +#endif + +#ifdef B200 + baudRateMap.insert(200, B200); +#endif + +#ifdef B300 + baudRateMap.insert(300, B300); +#endif + +#ifdef B600 + baudRateMap.insert(600, B600); +#endif + +#ifdef B1200 + baudRateMap.insert(1200, B1200); +#endif + +#ifdef B1800 + baudRateMap.insert(1800, B1800); +#endif + +#ifdef B2400 + baudRateMap.insert(2400, B2400); +#endif + +#ifdef B4800 + baudRateMap.insert(4800, B4800); +#endif + +#ifdef B7200 + baudRateMap.insert(7200, B7200); +#endif + +#ifdef B9600 + baudRateMap.insert(9600, B9600); +#endif + +#ifdef B14400 + baudRateMap.insert(14400, B14400); +#endif + +#ifdef B19200 + baudRateMap.insert(19200, B19200); +#endif + +#ifdef B28800 + baudRateMap.insert(28800, B28800); +#endif + +#ifdef B38400 + baudRateMap.insert(38400, B38400); +#endif + +#ifdef B57600 + baudRateMap.insert(57600, B57600); +#endif + +#ifdef B76800 + baudRateMap.insert(76800, B76800); +#endif + +#ifdef B115200 + baudRateMap.insert(115200, B115200); +#endif + +#ifdef B230400 + baudRateMap.insert(230400, B230400); +#endif + +#ifdef B460800 + baudRateMap.insert(460800, B460800); +#endif + +#ifdef B500000 + baudRateMap.insert(500000, B500000); +#endif + +#ifdef B576000 + baudRateMap.insert(576000, B576000); +#endif + +#ifdef B921600 + baudRateMap.insert(921600, B921600); +#endif + +#ifdef B1000000 + baudRateMap.insert(1000000, B1000000); +#endif + +#ifdef B1152000 + baudRateMap.insert(1152000, B1152000); +#endif + +#ifdef B1500000 + baudRateMap.insert(1500000, B1500000); +#endif + +#ifdef B2000000 + baudRateMap.insert(2000000, B2000000); +#endif + +#ifdef B2500000 + baudRateMap.insert(2500000, B2500000); +#endif + +#ifdef B3000000 + baudRateMap.insert(3000000, B3000000); +#endif + +#ifdef B3500000 + baudRateMap.insert(3500000, B3500000); +#endif + +#ifdef B4000000 + baudRateMap.insert(4000000, B4000000); +#endif + + return baudRateMap; +} + + + + +static const BaudRateMap& standardBaudRateMap() +{ + static const BaudRateMap baudRateMap = createStandardBaudRateMap(); + return baudRateMap; +} + + + + +qint32 QSerialPortPrivate::baudRateFromSetting(qint32 setting) +{ + return standardBaudRateMap().key(setting); +} + + + + +qint32 QSerialPortPrivate::settingFromBaudRate(qint32 baudRate) +{ + return standardBaudRateMap().value(baudRate); +} + + + + +QList QSerialPortPrivate::standardBaudRates() +{ + return standardBaudRateMap().keys(); +} + + + + +QSerialPort::Handle QSerialPort::handle() const +{ + Q_D(const QSerialPort); + return d->descriptor; +} + +qint64 QSerialPortPrivate::bytesToWrite() const +{ + return writeBuffer.size(); +} + +qint64 QSerialPortPrivate::writeData(const char *data, qint64 maxSize) +{ + return writeToPort(data, maxSize); +} + +QT_END_NAMESPACE + diff --git a/libs/qtandroidserialport/src/qserialport_android_p.h b/libs/qtandroidserialport/src/qserialport_android_p.h new file mode 100644 index 0000000000000000000000000000000000000000..f105c1fd74f0441ede8327a2b9bd297aa5bef232 --- /dev/null +++ b/libs/qtandroidserialport/src/qserialport_android_p.h @@ -0,0 +1,133 @@ +#ifndef QSERIALPORT_ANDROID_P_H +#define QSERIALPORT_ANDROID_P_H +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialport_p.h" + +#include +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +class QSerialPortPrivate : public QSerialPortPrivateData +{ + Q_DECLARE_PUBLIC(QSerialPort) + +public: + QSerialPortPrivate(QSerialPort *q); + + bool open(QIODevice::OpenMode mode); + void close(); + + QSerialPort::PinoutSignals pinoutSignals(); + + bool setDataTerminalReady(bool set); + bool setRequestToSend(bool set); + + bool flush(); + bool clear(QSerialPort::Directions directions); + + bool sendBreak(int duration); + bool setBreakEnabled(bool set); + + void startWriting(); + + bool waitForReadyRead(int msecs); + bool waitForBytesWritten(int msecs); + + bool setBaudRate(); + bool setBaudRate(qint32 baudRate, QSerialPort::Directions directions); + bool setDataBits(QSerialPort::DataBits dataBits); + bool setParity(QSerialPort::Parity parity); + bool setStopBits(QSerialPort::StopBits stopBits); + bool setFlowControl(QSerialPort::FlowControl flowControl); + bool setDataErrorPolicy(QSerialPort::DataErrorPolicy policy); + + bool startAsyncWrite(); + bool completeAsyncWrite(); + void newDataArrived(char *bytesA, int lengthA); + void exceptionArrived(QString strA); + + void stopReadThread(); + void startReadThread(); + qint64 bytesToWrite() const; + qint64 writeData(const char *data, qint64 maxSize); + + static qint32 baudRateFromSetting(qint32 setting); + static qint32 settingFromBaudRate(qint32 baudRate); + + static QList standardBaudRates(); + + int descriptor; + bool isCustomBaudRateSupported; + + bool emittedBytesWritten; + + qint64 pendingBytesWritten; + +private: + QIODevice::OpenMode rwMode; + int deviceId; + bool hasRegisteredFunctions; + int jniDataBits; + int jniStopBits; + int jniParity; + qint64 internalWriteTimeoutMsec; + bool isReadStopped; + + bool setParameters(int baudRateA, int dataBitsA, int stopBitsA, int parityA); + + QSerialPort::SerialPortError decodeSystemError() const; + + qint64 writeToPort(const char *data, qint64 maxSize); + + bool writeDataOneShot(); +}; + +QT_END_NAMESPACE + +#endif // QSERIALPORT_ANDROID_P_H + diff --git a/libs/qtandroidserialport/src/qserialport_p.h b/libs/qtandroidserialport/src/qserialport_p.h new file mode 100644 index 0000000000000000000000000000000000000000..3ba91b9b282197380850f913ab708a685de6049d --- /dev/null +++ b/libs/qtandroidserialport/src/qserialport_p.h @@ -0,0 +1,338 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORT_P_H +#define QSERIALPORT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qserialport.h" + +QT_BEGIN_NAMESPACE + +class QGCRingBuffer +{ +public: + explicit inline QGCRingBuffer(int growth = 4096) : + head(0), tail(0), tailBuffer(0), basicBlockSize(growth), bufferSize(0) { + buffers.append(QByteArray()); + } + + inline int nextDataBlockSize() const { + return (tailBuffer == 0 ? tail : buffers.first().size()) - head; + } + + inline const char *readPointer() const { + return buffers.isEmpty() ? 0 : (buffers.first().constData() + head); + } + + // access the bytes at a specified position + // the out-variable length will contain the amount of bytes readable + // from there, e.g. the amount still the same QByteArray + inline const char *readPointerAtPosition(qint64 pos, qint64 &length) const { + if (pos >= 0) { + pos += head; + for (int i = 0; i < buffers.size(); ++i) { + length = (i == tailBuffer ? tail : buffers[i].size()); + if (length > pos) { + length -= pos; + return buffers[i].constData() + pos; + } + pos -= length; + } + } + + length = 0; + return 0; + } + + inline void free(int bytes) { + while (bytes > 0) { + int blockSize = buffers.first().size() - head; + + if (tailBuffer == 0 || blockSize > bytes) { + bufferSize -= bytes; + if (bufferSize <= 0) + clear(); // try to minify/squeeze us + else + head += bytes; + return; + } + + bufferSize -= blockSize; + bytes -= blockSize; + buffers.removeFirst(); + --tailBuffer; + head = 0; + } + } + + inline char *reserve(int bytes) { + if (bytes <= 0) + return 0; + + // if need buffer reallocation + if (tail + bytes > buffers.last().size()) { + if (tail >= basicBlockSize) { + // shrink this buffer to its current size + buffers.last().resize(tail); + + // create a new QByteArray + buffers.append(QByteArray()); + ++tailBuffer; + tail = 0; + } + buffers.last().resize(qMax(basicBlockSize, tail + bytes)); + } + + char *writePtr = buffers.last().data() + tail; + bufferSize += bytes; + tail += bytes; + return writePtr; + } + + inline void truncate(int pos) { + if (pos < size()) + chop(size() - pos); + } + + inline void chop(int bytes) { + while (bytes > 0) { + if (tailBuffer == 0 || tail > bytes) { + bufferSize -= bytes; + if (bufferSize <= 0) + clear(); // try to minify/squeeze us + else + tail -= bytes; + return; + } + + bufferSize -= tail; + bytes -= tail; + buffers.removeLast(); + --tailBuffer; + tail = buffers.last().size(); + } + } + + inline bool isEmpty() const { + return tailBuffer == 0 && tail == 0; + } + + inline int getChar() { + if (isEmpty()) + return -1; + char c = *readPointer(); + free(1); + return int(uchar(c)); + } + + inline void putChar(char c) { + char *ptr = reserve(1); + *ptr = c; + } + + inline void ungetChar(char c) { + --head; + if (head < 0) { + buffers.prepend(QByteArray()); + buffers.first().resize(basicBlockSize); + head = basicBlockSize - 1; + ++tailBuffer; + } + buffers.first()[head] = c; + ++bufferSize; + } + + inline int size() const { + return bufferSize; + } + + inline void clear() { + buffers.erase(buffers.begin() + 1, buffers.end()); + buffers.first().clear(); + + head = tail = 0; + tailBuffer = 0; + bufferSize = 0; + } + + inline int indexOf(char c) const { + int index = 0; + int j = head; + for (int i = 0; i < buffers.size(); ++i) { + const char *ptr = buffers[i].constData() + j; + j = index + (i == tailBuffer ? tail : buffers[i].size()) - j; + + while (index < j) { + if (*ptr++ == c) + return index; + ++index; + } + j = 0; + } + return -1; + } + + inline int indexOf(char c, int maxLength) const { + int index = 0; + int j = head; + for (int i = 0; index < maxLength && i < buffers.size(); ++i) { + const char *ptr = buffers[i].constData() + j; + j = qMin(index + (i == tailBuffer ? tail : buffers[i].size()) - j, maxLength); + + while (index < j) { + if (*ptr++ == c) + return index; + ++index; + } + j = 0; + } + return -1; + } + + inline int read(char *data, int maxLength) { + int bytesToRead = qMin(size(), maxLength); + int readSoFar = 0; + while (readSoFar < bytesToRead) { + int bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar, nextDataBlockSize()); + if (data) + memcpy(data + readSoFar, readPointer(), bytesToReadFromThisBlock); + readSoFar += bytesToReadFromThisBlock; + free(bytesToReadFromThisBlock); + } + return readSoFar; + } + + // read an unspecified amount (will read the first buffer) + inline QByteArray read() { + if (bufferSize == 0) + return QByteArray(); + + QByteArray qba(buffers.takeFirst()); + + qba.reserve(0); // avoid that resizing needlessly reallocates + if (tailBuffer == 0) { + qba.resize(tail); + tail = 0; + buffers.append(QByteArray()); + } else { + --tailBuffer; + } + qba.remove(0, head); // does nothing if head is 0 + head = 0; + bufferSize -= qba.size(); + return qba; + } + + // append a new buffer to the end + inline void append(const QByteArray &qba) { + if (tail == 0) { + buffers.last() = qba; + } else { + buffers.last().resize(tail); + buffers.append(qba); + ++tailBuffer; + } + tail = qba.size(); + bufferSize += tail; + } + + inline int skip(int length) { + return read(0, length); + } + + inline int readLine(char *data, int maxLength) { + if (!data || --maxLength <= 0) + return -1; + + int i = indexOf('\n', maxLength); + i = read(data, i >= 0 ? (i + 1) : maxLength); + + // Terminate it. + data[i] = '\0'; + return i; + } + + inline bool canReadLine() const { + return indexOf('\n') >= 0; + } + +private: + QList buffers; + int head, tail; + int tailBuffer; // always buffers.size() - 1 + const int basicBlockSize; + int bufferSize; +}; + +class QSerialPortPrivateData +{ +public: + enum IoConstants { + ReadChunkSize = 512 + }; + + QSerialPortPrivateData(QSerialPort *q); + int timeoutValue(int msecs, int elapsed); + + qint64 readBufferMaxSize; + QGCRingBuffer readBuffer; + QGCRingBuffer writeBuffer; + QSerialPort::SerialPortError error; + QString systemLocation; + qint32 inputBaudRate; + qint32 outputBaudRate; + QSerialPort::DataBits dataBits; + QSerialPort::Parity parity; + QSerialPort::StopBits stopBits; + QSerialPort::FlowControl flowControl; + QSerialPort::DataErrorPolicy policy; + bool settingsRestoredOnClose; + QSerialPort * const q_ptr; +}; + +QT_END_NAMESPACE + +#endif // QSERIALPORT_P_H diff --git a/libs/qtandroidserialport/src/qserialportinfo.cpp b/libs/qtandroidserialport/src/qserialportinfo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26b275d6d398f8f2265a741a7dd86978f6243996 --- /dev/null +++ b/libs/qtandroidserialport/src/qserialportinfo.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialportinfo.h" +#include "qserialportinfo_p.h" +#include "qserialport.h" + +QT_BEGIN_NAMESPACE + + +/*! + \class QSerialPortInfo + + \brief Provides information about existing serial ports. + + \ingroup serialport-main + \inmodule QtSerialPort + \since 5.1 + + Use the static functions to generate a list of QSerialPortInfo + objects. Each QSerialPortInfo object in the list represents a single + serial port and can be queried for the port name, system location, + description, and manufacturer. The QSerialPortInfo class can also be + used as an input parameter for the setPort() method of the QSerialPort + class. + + \sa QSerialPort +*/ + +/*! + Constructs an empty QSerialPortInfo object. + + \sa isNull() +*/ +QSerialPortInfo::QSerialPortInfo() +{ +} + +/*! + Constructs a copy of \a other. +*/ +QSerialPortInfo::QSerialPortInfo(const QSerialPortInfo &other) + : d_ptr(other.d_ptr ? new QSerialPortInfoPrivate(*other.d_ptr) : Q_NULLPTR) +{ +} + +/*! + Constructs a QSerialPortInfo object from serial \a port. +*/ +QSerialPortInfo::QSerialPortInfo(const QSerialPort &port) +{ + foreach (const QSerialPortInfo &serialPortInfo, availablePorts()) { + if (port.portName() == serialPortInfo.portName()) { + *this = serialPortInfo; + break; + } + } +} + +/*! + Constructs a QSerialPortInfo object from serial port \a name. + + This constructor finds the relevant serial port among the available ones + according to the port name \a name, and constructs the serial port info + instance for that port. +*/ +QSerialPortInfo::QSerialPortInfo(const QString &name) +{ + foreach (const QSerialPortInfo &serialPortInfo, availablePorts()) { + if (name == serialPortInfo.portName()) { + *this = serialPortInfo; + break; + } + } +} + +QSerialPortInfo::QSerialPortInfo(const QSerialPortInfoPrivate &dd) + : d_ptr(new QSerialPortInfoPrivate(dd)) +{ +} + +/*! + Destroys the QSerialPortInfo object. References to the values in the + object become invalid. +*/ +QSerialPortInfo::~QSerialPortInfo() +{ +} + +/*! \fn void QSerialPortInfo::swap(QSerialPortInfo &other) + + Swaps QSerialPortInfo \a other with this QSerialPortInfo. This operation is + very fast and never fails. +*/ +void QSerialPortInfo::swap(QSerialPortInfo &other) +{ + d_ptr.swap(other.d_ptr); +} + +/*! + Sets the QSerialPortInfo object to be equal to \a other. +*/ +QSerialPortInfo& QSerialPortInfo::operator=(const QSerialPortInfo &other) +{ + QSerialPortInfo(other).swap(*this); + return *this; +} + +/*! + Returns the name of the serial port. + + \sa systemLocation() +*/ +QString QSerialPortInfo::portName() const +{ + Q_D(const QSerialPortInfo); + return !d ? QString() : d->portName; +} + +/*! + Returns the system location of the serial port. + + \sa portName() +*/ +QString QSerialPortInfo::systemLocation() const +{ + Q_D(const QSerialPortInfo); + return !d ? QString() : d->device; +} + +/*! + Returns the description string of the serial port, + if available; otherwise returns an empty string. + + \sa manufacturer(), serialNumber() +*/ +QString QSerialPortInfo::description() const +{ + Q_D(const QSerialPortInfo); + return !d ? QString() : d->description; +} + +/*! + Returns the manufacturer string of the serial port, + if available; otherwise returns an empty string. + + \sa description(), serialNumber() +*/ +QString QSerialPortInfo::manufacturer() const +{ + Q_D(const QSerialPortInfo); + return !d ? QString() : d->manufacturer; +} + +/*! + \since 5.3 + + Returns the serial number string of the serial port, + if available; otherwise returns an empty string. + + \note The serial number may include letters. + + \sa description(), manufacturer() +*/ +QString QSerialPortInfo::serialNumber() const +{ + Q_D(const QSerialPortInfo); + return !d ? QString() : d->serialNumber; +} + +/*! + Returns the 16-bit vendor number for the serial port, if available; + otherwise returns zero. + + \sa hasVendorIdentifier(), productIdentifier(), hasProductIdentifier() +*/ +quint16 QSerialPortInfo::vendorIdentifier() const +{ + Q_D(const QSerialPortInfo); + return !d ? 0 : d->vendorIdentifier; +} + +/*! + Returns the 16-bit product number for the serial port, if available; + otherwise returns zero. + + \sa hasProductIdentifier(), vendorIdentifier(), hasVendorIdentifier() +*/ +quint16 QSerialPortInfo::productIdentifier() const +{ + Q_D(const QSerialPortInfo); + return !d ? 0 : d->productIdentifier; +} + +/*! + Returns true if there is a valid 16-bit vendor number present; otherwise + returns false. + + \sa vendorIdentifier(), productIdentifier(), hasProductIdentifier() +*/ +bool QSerialPortInfo::hasVendorIdentifier() const +{ + Q_D(const QSerialPortInfo); + return !d ? false : d->hasVendorIdentifier; +} + +/*! + Returns true if there is a valid 16-bit product number present; otherwise + returns false. + + \sa productIdentifier(), vendorIdentifier(), hasVendorIdentifier() +*/ +bool QSerialPortInfo::hasProductIdentifier() const +{ + Q_D(const QSerialPortInfo); + return !d ? false : d->hasProductIdentifier; +} + +/*! + \fn bool QSerialPortInfo::isNull() const + + Returns whether this QSerialPortInfo object holds a + serial port definition. + + \sa isBusy() +*/ + +/*! + \fn bool QSerialPortInfo::isBusy() const + + Returns true if serial port is busy; + otherwise returns false. + + \sa isNull() +*/ + +/*! + \fn bool QSerialPortInfo::isValid() const + \obsolete + + Returns true if serial port is present on system; + otherwise returns false. + + \sa isNull(), isBusy() +*/ + +/*! + \fn QList QSerialPortInfo::standardBaudRates() + + Returns a list of available standard baud rates supported by + the current serial port. +*/ + +/*! + \fn QList QSerialPortInfo::availablePorts() + + Returns a list of available serial ports on the system. +*/ + +QT_END_NAMESPACE diff --git a/libs/qtandroidserialport/src/qserialportinfo.h b/libs/qtandroidserialport/src/qserialportinfo.h new file mode 100644 index 0000000000000000000000000000000000000000..6fe1662e822be6403f0ab740639051dc176e7a3f --- /dev/null +++ b/libs/qtandroidserialport/src/qserialportinfo.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Denis Shienkov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORTINFO_H +#define QSERIALPORTINFO_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QSerialPort; +class QSerialPortInfoPrivate; +class QSerialPortInfoPrivateDeleter; + +class QSerialPortInfo +{ + Q_DECLARE_PRIVATE(QSerialPortInfo) +public: + QSerialPortInfo(); + explicit QSerialPortInfo(const QSerialPort &port); + explicit QSerialPortInfo(const QString &name); + QSerialPortInfo(const QSerialPortInfo &other); + ~QSerialPortInfo(); + + QSerialPortInfo& operator=(const QSerialPortInfo &other); + void swap(QSerialPortInfo &other); + + QString portName() const; + QString systemLocation() const; + QString description() const; + QString manufacturer() const; + QString serialNumber() const; + + quint16 vendorIdentifier() const; + quint16 productIdentifier() const; + + bool hasVendorIdentifier() const; + bool hasProductIdentifier() const; + + bool isNull() const; + bool isBusy() const; +#if QT_DEPRECATED_SINCE(5, 2) + QT_DEPRECATED bool isValid() const; +#endif + + static QList standardBaudRates(); + static QList availablePorts(); + + QScopedPointer d_ptr; + +private: + QSerialPortInfo(const QSerialPortInfoPrivate &dd); + friend QList availablePortsByUdev(bool &ok); + friend QList availablePortsBySysfs(bool &ok); + friend QList availablePortsByFiltersOfDevices(bool &ok); +}; + +inline bool QSerialPortInfo::isNull() const +{ return !d_ptr; } + +QT_END_NAMESPACE + +#endif // QSERIALPORTINFO_H diff --git a/libs/qtandroidserialport/src/qserialportinfo_android.cpp b/libs/qtandroidserialport/src/qserialportinfo_android.cpp new file mode 100644 index 0000000000000000000000000000000000000000..50db905fce310dc969b7810b4461da5066df3d77 --- /dev/null +++ b/libs/qtandroidserialport/src/qserialportinfo_android.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qserialportinfo.h" +#include "qserialportinfo_p.h" +#include "qserialport_android_p.h" + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE +static const char V_jniClassName[] {"org/qgroundcontrol/qgchelper/UsbDeviceJNI"}; +static const char V_TAG[] {"QGC_QSerialPortInfo"}; + +QList availablePortsByFiltersOfDevices() +{ + QList serialPortInfoList; + + __android_log_print(ANDROID_LOG_INFO, V_TAG, "Collecting device list"); + QAndroidJniObject resultL = QAndroidJniObject::callStaticObjectMethod( + V_jniClassName, + "availableDevicesInfo", + "()[Ljava/lang/String;"); + + if (!resultL.isValid()) { + __android_log_print(ANDROID_LOG_ERROR, V_TAG, "Error from availableDevicesInfo"); + return serialPortInfoList; + } + + QAndroidJniEnvironment envL; + jobjectArray objArrayL = resultL.object(); + int countL = envL->GetArrayLength(objArrayL); + + for (int iL=0; iLGetObjectArrayElement(objArrayL, iL)); + const char *rawStringL = envL->GetStringUTFChars(stringL, 0); + __android_log_print(ANDROID_LOG_INFO, V_TAG, "Adding device: %s", rawStringL); + QStringList strListL = QString::fromUtf8(rawStringL).split(QStringLiteral(":")); + envL->ReleaseStringUTFChars(stringL, rawStringL); + + infoL.d_ptr->portName = strListL[0]; + infoL.d_ptr->device = strListL[0]; + infoL.d_ptr->manufacturer = strListL[1]; + infoL.d_ptr->productIdentifier = strListL[2].toInt(); + infoL.d_ptr->hasProductIdentifier = (infoL.d_ptr->productIdentifier != 0) ? true: false; + infoL.d_ptr->vendorIdentifier = strListL[3].toInt(); + infoL.d_ptr->hasVendorIdentifier = (infoL.d_ptr->vendorIdentifier != 0) ? true: false; + + serialPortInfoList.append(infoL); + } + + return serialPortInfoList; +} + +QList availablePortsBySysfs() +{ + return availablePortsByFiltersOfDevices(); +} + +QList availablePortsByUdev() +{ + return availablePortsByFiltersOfDevices(); +} + +QList QSerialPortInfo::availablePorts() +{ + return availablePortsByFiltersOfDevices(); +} + +QList QSerialPortInfo::standardBaudRates() +{ + return QSerialPortPrivate::standardBaudRates(); +} + +bool QSerialPortInfo::isBusy() const +{ + QAndroidJniObject jstrL = QAndroidJniObject::fromString(d_ptr->portName); + jboolean resultL = QAndroidJniObject::callStaticMethod( + V_jniClassName, + "isDeviceNameOpen", + "(Ljava/lang/String;)Z", + jstrL.object()); + return resultL; +} + +bool QSerialPortInfo::isValid() const +{ + QAndroidJniObject jstrL = QAndroidJniObject::fromString(d_ptr->portName); + jboolean resultL = QAndroidJniObject::callStaticMethod( + V_jniClassName, + "isDeviceNameValid", + "(Ljava/lang/String;)Z", + jstrL.object()); + return resultL; +} + +QString QSerialPortInfoPrivate::portNameToSystemLocation(const QString &source) +{ + return (source.startsWith(QLatin1Char('/')) + || source.startsWith(QStringLiteral("./")) + || source.startsWith(QStringLiteral("../"))) + ? source : (QStringLiteral("/dev/") + source); +} + +QString QSerialPortInfoPrivate::portNameFromSystemLocation(const QString &source) +{ + return source.startsWith(QStringLiteral("/dev/")) + ? source.mid(5) : source; +} + +QT_END_NAMESPACE + diff --git a/libs/qtandroidserialport/src/qserialportinfo_p.h b/libs/qtandroidserialport/src/qserialportinfo_p.h new file mode 100644 index 0000000000000000000000000000000000000000..c9f108638f68f5dd3835e00a031bc9701b9a6bcd --- /dev/null +++ b/libs/qtandroidserialport/src/qserialportinfo_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2011-2012 Denis Shienkov +** Copyright (C) 2011 Sergey Belyashov +** Copyright (C) 2012 Laszlo Papp +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtSerialPort module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSERIALPORTINFO_P_H +#define QSERIALPORTINFO_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QSerialPortInfoPrivate +{ +public: + QSerialPortInfoPrivate() + : vendorIdentifier(0) + , productIdentifier(0) + , hasVendorIdentifier(false) + , hasProductIdentifier(false) + { + } + + ~QSerialPortInfoPrivate() + { + } + + static QString portNameToSystemLocation(const QString &source); + static QString portNameFromSystemLocation(const QString &source); + + QString portName; + QString device; + QString description; + QString manufacturer; + QString serialNumber; + + quint16 vendorIdentifier; + quint16 productIdentifier; + + bool hasVendorIdentifier; + bool hasProductIdentifier; +}; + +class QSerialPortInfoPrivateDeleter +{ +public: + static void cleanup(QSerialPortInfoPrivate *p) { + delete p; + } +}; + +QT_END_NAMESPACE + +#endif // QSERIALPORTINFO_P_H diff --git a/libs/qtandroidserialport/src/qtandroidserialport.pri b/libs/qtandroidserialport/src/qtandroidserialport.pri new file mode 100644 index 0000000000000000000000000000000000000000..9bf07c0ad88a78d7f41b4c8f24171de371f7d7d6 --- /dev/null +++ b/libs/qtandroidserialport/src/qtandroidserialport.pri @@ -0,0 +1,28 @@ +android { + + QT += androidextras + + INCLUDEPATH += $$PWD + + PUBLIC_HEADERS += \ + $$PWD/qserialport.h \ + $$PWD/qserialportinfo.h + + PRIVATE_HEADERS += \ + $$PWD/qserialport_p.h \ + $$PWD/qserialportinfo_p.h \ + $$PWD/qserialport_android_p.h + + SOURCES += \ + $$PWD/qserialport.cpp \ + $$PWD/qserialportinfo.cpp \ + $$PWD/qserialport_android.cpp \ + $$PWD/qserialportinfo_android.cpp + + CONFIG += mobility + + INCLUDEPATH += $$PWD/qt4support/include + + HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS + +} diff --git a/src/QGC.h b/src/QGC.h index 2ffca0600b2f428b388703fa77075fbae2d54f86..35f58e14ede8136a4d21ff2edb64c3ca60bfa14e 100644 --- a/src/QGC.h +++ b/src/QGC.h @@ -60,6 +60,9 @@ inline bool isinf(T value) #define isinf(x) std::isinf(x) #endif #endif +#ifdef __android__ +#define isinf(x) std::isinf(x) +#endif namespace QGC { diff --git a/src/QGCFileDialog.cc b/src/QGCFileDialog.cc index 25c667ea928685dd4ffa8a46cef47e300d70ab07..b3160b50c59354bddec766c0c736512dc205f626 100644 --- a/src/QGCFileDialog.cc +++ b/src/QGCFileDialog.cc @@ -26,8 +26,10 @@ #include #include "MainWindow.h" #ifdef QT_DEBUG +#ifndef __android__ #include "UnitTest.h" #endif +#endif QString QGCFileDialog::getExistingDirectory( QWidget* parent, @@ -38,9 +40,11 @@ QString QGCFileDialog::getExistingDirectory( _validate(options); #ifdef QT_DEBUG +#ifndef __android__ if (qgcApp()->runningUnitTests()) { return UnitTest::_getExistingDirectory(parent, caption, dir, options); } else +#endif #endif { return QFileDialog::getExistingDirectory(parent, caption, dir, options); @@ -57,9 +61,11 @@ QString QGCFileDialog::getOpenFileName( _validate(options); #ifdef QT_DEBUG +#ifndef __android__ if (qgcApp()->runningUnitTests()) { return UnitTest::_getOpenFileName(parent, caption, dir, filter, options); } else +#endif #endif { return QFileDialog::getOpenFileName(parent, caption, dir, filter, NULL, options); @@ -76,9 +82,11 @@ QStringList QGCFileDialog::getOpenFileNames( _validate(options); #ifdef QT_DEBUG +#ifndef __android__ if (qgcApp()->runningUnitTests()) { return UnitTest::_getOpenFileNames(parent, caption, dir, filter, options); } else +#endif #endif { return QFileDialog::getOpenFileNames(parent, caption, dir, filter, NULL, options); @@ -97,9 +105,11 @@ QString QGCFileDialog::getSaveFileName( _validate(options); #ifdef QT_DEBUG +#ifndef __android__ if (qgcApp()->runningUnitTests()) { return UnitTest::_getSaveFileName(parent, caption, dir, filter, defaultSuffix, options); } else +#endif #endif { QString defaultSuffixCopy(defaultSuffix); diff --git a/src/QGCMessageBox.h b/src/QGCMessageBox.h index dfda32d18a7bac8e3721784d09ee07f9deb60b63..91402a9d033711b930b8230e4cfba2ca4f3459e5 100644 --- a/src/QGCMessageBox.h +++ b/src/QGCMessageBox.h @@ -29,8 +29,10 @@ #include "MainWindow.h" #include "QGCApplication.h" #ifdef QT_DEBUG +#ifndef __android__ #include "UnitTest.h" #endif +#endif /// @file /// @brief Subclass of QMessageBox which re-implements the static public functions. There are two reasons for this: @@ -104,10 +106,12 @@ private: } #ifdef QT_DEBUG +#ifndef __android__ if (qgcApp()->runningUnitTests()) { qDebug() << "QGCMessageBox (unit testing)" << title << text; return UnitTest::_messageBox(icon, title, text, buttons, defaultButton); } else +#endif #endif { #ifdef Q_OS_MAC diff --git a/src/VehicleSetup/PX4Bootloader.cc b/src/VehicleSetup/PX4Bootloader.cc index deab12ea492b22da07ec10be313e206825f72268..d5cd1346b885bf79ce4b9461b55313e1640908b9 100644 --- a/src/VehicleSetup/PX4Bootloader.cc +++ b/src/VehicleSetup/PX4Bootloader.cc @@ -28,7 +28,11 @@ #include "PX4Bootloader.h" #include +#ifdef __android__ +#include "qserialportinfo.h" +#else #include +#endif #include #include diff --git a/src/VehicleSetup/PX4FirmwareUpgradeThread.cc b/src/VehicleSetup/PX4FirmwareUpgradeThread.cc index 784948eb239a612e1abb0d6c266519242357bf57..0f581facf9834551e9778c1aa5b025fc3558cac8 100644 --- a/src/VehicleSetup/PX4FirmwareUpgradeThread.cc +++ b/src/VehicleSetup/PX4FirmwareUpgradeThread.cc @@ -29,7 +29,11 @@ #include "PX4Bootloader.h" #include +#ifdef __android__ +#include "qserialportinfo.h" +#else #include +#endif #include PX4FirmwareUpgradeThreadWorker::PX4FirmwareUpgradeThreadWorker(QObject* parent) : diff --git a/src/comm/LinkConfiguration.cc b/src/comm/LinkConfiguration.cc index b3a351d456346fa4aedf84014976a5f2b36cc701..5e974f30af0602e66240e3ce6fd265b30c9f65ca 100644 --- a/src/comm/LinkConfiguration.cc +++ b/src/comm/LinkConfiguration.cc @@ -33,8 +33,10 @@ This file is part of the QGROUNDCONTROL project #include "TCPLink.h" #ifdef UNITTEST_BUILD +#ifndef __android__ #include "MockLink.h" #endif +#endif #define LINK_SETTING_ROOT "LinkConfigurations" @@ -92,9 +94,11 @@ LinkConfiguration* LinkConfiguration::createSettings(int type, const QString& na config = new TCPConfiguration(name); break; #ifdef UNITTEST_BUILD +#ifndef __android__ case LinkConfiguration::TypeMock: config = new MockConfiguration(name); break; +#endif #endif } return config; @@ -118,9 +122,11 @@ LinkConfiguration* LinkConfiguration::duplicateSettings(LinkConfiguration* sourc dupe = new TCPConfiguration(dynamic_cast(source)); break; #ifdef UNITTEST_BUILD +#ifndef __android__ case TypeMock: dupe = new MockConfiguration(dynamic_cast(source)); break; +#endif #endif } return dupe; diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 91e63ed5c20ee97ad6ffeb7360db866f2b2cd3d9..10b5af4e48099ce518e8d306c88925f47b41dad0 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -32,7 +32,11 @@ This file is part of the QGROUNDCONTROL project #include #include #include +#ifdef __android__ +#include "qserialportinfo.h" +#else #include +#endif #include "LinkManager.h" #include "MainWindow.h" @@ -85,9 +89,11 @@ LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config) pLink = new TCPLink(dynamic_cast(config)); break; #ifdef UNITTEST_BUILD +#ifndef __android__ case LinkConfiguration::TypeMock: pLink = new MockLink(dynamic_cast(config)); break; +#endif #endif } if(pLink) { @@ -379,10 +385,12 @@ void LinkManager::loadLinkConfigurationList() pLink->setPreferred(preferred); break; #ifdef UNITTEST_BUILD +#ifndef __android__ case LinkConfiguration::TypeMock: pLink = (LinkConfiguration*)new MockConfiguration(name); pLink->setPreferred(false); break; +#endif #endif } if(pLink) { diff --git a/src/comm/LinkManager.h b/src/comm/LinkManager.h index 0fba35a4ab63882564d84e586016743776aaa387..3fdca2ab4ff0247fb944fc1f1746ef74a4baef3e 100644 --- a/src/comm/LinkManager.h +++ b/src/comm/LinkManager.h @@ -40,8 +40,10 @@ This file is part of the PIXHAWK project #include "TCPLink.h" #ifdef UNITTEST_BUILD +#ifndef __android__ #include "MockLink.h" #endif +#endif #include "ProtocolInterface.h" #include "QGCSingleton.h" diff --git a/src/comm/SerialLink.cc b/src/comm/SerialLink.cc index 415b77c6fee779d8023340d70baede7f817d1c7d..9cd1f2b3fbb0d6995244822a9a47e70384456f5c 100644 --- a/src/comm/SerialLink.cc +++ b/src/comm/SerialLink.cc @@ -12,8 +12,14 @@ #include #include #include + +#ifdef __android__ +#include "qserialport.h" +#include "qserialportinfo.h" +#else #include #include +#endif #include "SerialLink.h" #include "QGC.h" diff --git a/src/comm/SerialLink.h b/src/comm/SerialLink.h index 29bdb6a69aa69ce712a048201bf1433c7228ed51..8f2011e83b84e2bf42f738be511c5ff68274873f 100644 --- a/src/comm/SerialLink.h +++ b/src/comm/SerialLink.h @@ -40,7 +40,11 @@ class SerialLink; #include #include #include +#ifdef __android__ +#include "qserialport.h" +#else #include +#endif #include #include diff --git a/src/main.cc b/src/main.cc index 0f1615dec9eeea202e1c3e826c96cb888c2659ec..5f6dc5f28707f8de438222e23c660205e7a34680 100644 --- a/src/main.cc +++ b/src/main.cc @@ -35,7 +35,9 @@ This file is part of the QGROUNDCONTROL project #include "MainWindow.h" #include "configuration.h" #ifdef QT_DEBUG +#ifndef __android__ #include "UnitTest.h" +#endif #include "CmdLineOptParser.h" #ifdef Q_OS_WIN #include @@ -156,6 +158,7 @@ int main(int argc, char *argv[]) int exitCode; +#ifndef __android__ #ifdef QT_DEBUG if (runUnitTests) { if (!app->_initForUnitTests()) { @@ -171,6 +174,7 @@ int main(int argc, char *argv[]) } exitCode = -failures; } else +#endif #endif { if (!app->_initForNormalAppBoot()) { diff --git a/src/qgcunittest/UnitTest.h b/src/qgcunittest/UnitTest.h index bbc78acb191500f23203e74edba0fb0f7569e2a1..5e46eff7fdbe777bef4809c97e7332506e6fa582 100644 --- a/src/qgcunittest/UnitTest.h +++ b/src/qgcunittest/UnitTest.h @@ -28,6 +28,7 @@ #ifndef UNITTEST_H #define UNITTEST_H +#ifndef __android__ #include #include @@ -186,4 +187,5 @@ private: QSharedPointer _unitTest; }; +#endif // Android #endif diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index 144a4f0ebf6a8a45d2c909ced9f2f8b73c191d91..8d20f4ea787267c36ae8c8e1e684c80c12e612f4 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -45,7 +45,9 @@ This file is part of the QGROUNDCONTROL project #include "MAVLinkProtocol.h" #include "QGCWaypointListMulti.h" #include "MainWindow.h" +#ifndef __android__ #include "JoystickWidget.h" +#endif #include "GAudioOutput.h" #include "QGCToolWidget.h" #include "QGCMAVLinkLogPlayer.h" @@ -193,8 +195,9 @@ MainWindow::MainWindow(QSplashScreen* splashScreen) connectCommonActions(); // Connect user interface devices emit initStatusChanged(tr("Initializing joystick interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141)); +#ifndef __android__ joystick = new JoystickInput(); - +#endif #ifdef QGC_MOUSE_ENABLED_WIN emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141)); mouseInput = new Mouse3DInput(this); @@ -315,6 +318,7 @@ MainWindow::~MainWindow() delete _simulationLink; _simulationLink = NULL; } +#ifndef __android__ if (joystick) { joystick->shutdown(); @@ -322,6 +326,7 @@ MainWindow::~MainWindow() delete joystick; joystick = NULL; } +#endif // Delete all UAS objects for (int i=0;i<_commsWidgetList.size();i++) { @@ -945,7 +950,11 @@ void MainWindow::showRoadMap() void MainWindow::showSettings() { +#ifndef __android__ SettingsDialog settings(joystick, this); +#else + SettingsDialog settings(this); +#endif settings.exec(); } @@ -1268,7 +1277,11 @@ void MainWindow::hideSplashScreen(void) void MainWindow::manageLinks() { +#ifndef __android__ SettingsDialog settings(joystick, this, SettingsDialog::ShowCommLinks); +#else + SettingsDialog settings(this, SettingsDialog::ShowCommLinks); +#endif settings.exec(); } diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 95d78384cc24c3eefa697e132b8cbab483e8bf79..03963e500d120ae19fbff343381474450c6c577d 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -48,7 +48,9 @@ This file is part of the QGROUNDCONTROL project #include "CameraView.h" #include "UASListWidget.h" #include "MAVLinkSimulationLink.h" +#ifndef __android__ #include "input/JoystickInput.h" +#endif #if (defined QGC_MOUSE_ENABLED_WIN) | (defined QGC_MOUSE_ENABLED_LINUX) #include "Mouse6dofInput.h" #endif // QGC_MOUSE_ENABLED_WIN @@ -252,7 +254,9 @@ protected: QPointer fileWidget; +#ifndef __android__ JoystickInput* joystick; ///< The joystick manager for QGC +#endif #ifdef QGC_MOUSE_ENABLED_WIN /** @brief 3d Mouse support (WIN only) */ diff --git a/src/ui/SerialConfigurationWindow.cc b/src/ui/SerialConfigurationWindow.cc index 4b04dc9a207aa83f5320bb3f33c3e380e6d1bac0..7f9e21cd7f7e44d24e8f6ad16f402180b93fa269 100644 --- a/src/ui/SerialConfigurationWindow.cc +++ b/src/ui/SerialConfigurationWindow.cc @@ -32,7 +32,12 @@ This file is part of the QGROUNDCONTROL project #include #include #include + +#ifdef __android__ +#include "qserialportinfo.h" +#else #include +#endif #include #include diff --git a/src/ui/SettingsDialog.cc b/src/ui/SettingsDialog.cc index d8561886557ed08f743df99cb6e4a4e66ab56039..56e3fcfb51ff48a45f09b6e37f8ea7ce87d8873f 100644 --- a/src/ui/SettingsDialog.cc +++ b/src/ui/SettingsDialog.cc @@ -28,7 +28,9 @@ #include "MainWindow.h" #include "ui_SettingsDialog.h" +#ifndef __android__ #include "JoystickWidget.h" +#endif #include "LinkManager.h" #include "MAVLinkProtocol.h" #include "MAVLinkSettingsWidget.h" @@ -39,7 +41,11 @@ #include "QGCMessageBox.h" #include "MainToolBar.h" +#ifndef __android__ SettingsDialog::SettingsDialog(JoystickInput *joystick, QWidget *parent, int showTab, Qt::WindowFlags flags) : +#else +SettingsDialog::SettingsDialog(QWidget *parent, int showTab, Qt::WindowFlags flags) : +#endif QDialog(parent, flags), _mainWindow(MainWindow::instance()), _ui(new Ui::SettingsDialog) @@ -52,13 +58,17 @@ _ui(new Ui::SettingsDialog) move(position.topLeft()); QGCLinkConfiguration* pLinkConf = new QGCLinkConfiguration(this); +#ifndef __android__ JoystickWidget* pJoystickConf = new JoystickWidget(joystick, this); +#endif MAVLinkSettingsWidget* pMavsettings = new MAVLinkSettingsWidget(MAVLinkProtocol::instance(), this); // Add the link settings pane _ui->tabWidget->addTab(pLinkConf, "Comm Links"); +#ifndef __android__ // Add the joystick settings pane _ui->tabWidget->addTab(pJoystickConf, "Controllers"); +#endif // Add the MAVLink settings pane _ui->tabWidget->addTab(pMavsettings, "MAVLink"); @@ -102,9 +112,11 @@ _ui(new Ui::SettingsDialog) case ShowCommLinks: _ui->tabWidget->setCurrentWidget(pLinkConf); break; +#ifndef __android__ case ShowControllers: _ui->tabWidget->setCurrentWidget(pJoystickConf); break; +#endif case ShowMavlink: _ui->tabWidget->setCurrentWidget(pMavsettings); break; diff --git a/src/ui/SettingsDialog.h b/src/ui/SettingsDialog.h index b8a3f8bedd287e7f0486b5f58b79844b41d5e868..e06128847169c839be9bb8e49672bd574d83956a 100644 --- a/src/ui/SettingsDialog.h +++ b/src/ui/SettingsDialog.h @@ -45,7 +45,11 @@ public: ShowMavlink }; +#ifdef __android__ + SettingsDialog(QWidget *parent = 0, int showTab = ShowDefault, Qt::WindowFlags flags = Qt::Sheet); +#else SettingsDialog(JoystickInput *joystick, QWidget *parent = 0, int showTab = ShowDefault, Qt::WindowFlags flags = Qt::Sheet); +#endif ~SettingsDialog(); public slots: diff --git a/src/ui/configuration/SerialSettingsDialog.cc b/src/ui/configuration/SerialSettingsDialog.cc index 83418c9d992ccd9d535a94941590dbf3a6489646..2b5c6b4792174cc2365cd21a3e1dcdcb45bd7d72 100644 --- a/src/ui/configuration/SerialSettingsDialog.cc +++ b/src/ui/configuration/SerialSettingsDialog.cc @@ -37,8 +37,13 @@ This file is part of the APM_PLANNER project #include "terminalconsole.h" #include "ui_SerialSettingsDialog.h" +#ifdef __android__ +#include "qserialport.h" +#include "qserialportinfo.h" +#else #include #include +#endif #include #include diff --git a/src/ui/configuration/SerialSettingsDialog.h b/src/ui/configuration/SerialSettingsDialog.h index 79eff0e72c70f247c033a1a84c18da83a7b4648b..3aec3deca971943a2b4a51347863e2eb2e793544 100644 --- a/src/ui/configuration/SerialSettingsDialog.h +++ b/src/ui/configuration/SerialSettingsDialog.h @@ -37,7 +37,11 @@ This file is part of the APM_PLANNER project #define SERIALSETTINGSDIALOG_H #include +#ifdef __android__ +#include "qserialport.h" +#else #include +#endif namespace Ui { class SerialSettingsDialog; diff --git a/src/ui/configuration/terminalconsole.cpp b/src/ui/configuration/terminalconsole.cpp index 54a090f51620cbd48c1a89577dcd48a510a5e66a..fe1a0d7fc89740446abd2a70f3a92dd682d1f579 100644 --- a/src/ui/configuration/terminalconsole.cpp +++ b/src/ui/configuration/terminalconsole.cpp @@ -45,8 +45,13 @@ This file is part of the APM_PLANNER project #include #include #include +#ifdef __android__ +#include "qserialport.h" +#include "qserialportinfo.h" +#else #include #include +#endif TerminalConsole::TerminalConsole(QWidget *parent) : QWidget(parent), diff --git a/src/ui/configuration/terminalconsole.h b/src/ui/configuration/terminalconsole.h index f100a6ef24e83fdeed46bc8100af3f5daf65f6eb..909416edd95a613842fcc7318617347125b2aa20 100644 --- a/src/ui/configuration/terminalconsole.h +++ b/src/ui/configuration/terminalconsole.h @@ -39,7 +39,11 @@ This file is part of the APM_PLANNER project #include "SerialSettingsDialog.h" #include +#ifdef __android__ +#include "qserialport.h" +#else #include +#endif namespace Ui { class TerminalConsole; diff --git a/src/ui/designer/QGCParamSlider.h b/src/ui/designer/QGCParamSlider.h index 9ca92423bfa616b9821f00f041704e31b5499d1d..ab3048dbb05a6def730af20539b0d70fd0664cbb 100644 --- a/src/ui/designer/QGCParamSlider.h +++ b/src/ui/designer/QGCParamSlider.h @@ -3,7 +3,9 @@ #include #include +#ifndef __android__ #include +#endif #include "QGCToolWidgetItem.h" diff --git a/src/ui/designer/QGCToolWidgetComboBox.h b/src/ui/designer/QGCToolWidgetComboBox.h index 785d606a4ec7a860097a1a51c154d8ee026d2e5a..9b4d162f4c43b5b55fac050efb37c3a4da6ea4aa 100644 --- a/src/ui/designer/QGCToolWidgetComboBox.h +++ b/src/ui/designer/QGCToolWidgetComboBox.h @@ -3,8 +3,9 @@ #include #include +#ifndef __android__ #include - +#endif #include "QGCToolWidgetItem.h" class QGCUASParamManagerInterface; diff --git a/src/ui/toolbar/MainToolBar.cc b/src/ui/toolbar/MainToolBar.cc index 560c664e9ad3a356ad6e45febccddbe04a4b086a..876214ac7184372a7f343344ceeb748216a227ec 100644 --- a/src/ui/toolbar/MainToolBar.cc +++ b/src/ui/toolbar/MainToolBar.cc @@ -64,8 +64,13 @@ MainToolBar::MainToolBar(QWidget* parent) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); setObjectName("MainToolBar"); +#ifdef __android__ + setMinimumHeight(120); + setMaximumHeight(120); +#else setMinimumHeight(40); setMaximumHeight(40); +#endif setMinimumWidth(MainWindow::instance()->minimumWidth()); // Get rid of layout default margins QLayout* pl = layout(); diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index efed1d7f74dac39c0b5de8e73180bd867f534206..f5890994738c834bfb95cf981f612ea98604b3c8 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -38,13 +38,14 @@ import QGroundControl.MainToolBar 1.0 import QGroundControl.ScreenTools 1.0 Rectangle { + id: toolBarHolder property var qgcPal: QGCPalette { id: palette; colorGroupEnabled: true } property ScreenTools screenTools: ScreenTools { } - property int cellSpacerSize: 4 - property int cellHeight: 30 - property int cellRadius: 3 + property int cellSpacerSize: getProportionalDimmension(4) + property int cellHeight: getProportionalDimmension(30) + property int cellRadius: getProportionalDimmension(3) property var colorBlue: "#1a6eaa" property var colorGreen: "#079527" @@ -57,9 +58,16 @@ Rectangle { property var colorGreenText: (qgcPal.globalTheme === QGCPalette.Light) ? "#046b1b" : "#00d930" property var colorWhiteText: (qgcPal.globalTheme === QGCPalette.Light) ? "#343333" : "#f0f0f0" - id: toolBarHolder color: qgcPal.windowShade + Component.onCompleted: { + console.log(cellSpacerSize, cellHeight, cellRadius); + } + + function getProportionalDimmension(val) { + return toolBarHolder.height * val / 40 + } + function getMessageColor() { if(mainToolBar.messageType === MainToolBar.MessageNone) return qgcPal.button; @@ -126,14 +134,14 @@ Rectangle { id: row1 height: cellHeight anchors.left: parent.left - spacing: 4 + spacing: getProportionalDimmension(4) anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 10 + anchors.leftMargin: getProportionalDimmension(10) Row { id: row11 height: cellHeight - spacing: -12 + spacing: -getProportionalDimmension(12) anchors.verticalCenter: parent.verticalCenter Connections { target: screenTools @@ -149,7 +157,7 @@ Rectangle { QGCToolBarButton { id: setupButton - width: 90 + width: getProportionalDimmension(90) height: cellHeight exclusiveGroup: mainActionGroup text: qsTr("Setup") @@ -163,7 +171,7 @@ Rectangle { QGCToolBarButton { id: planButton - width: 90 + width: getProportionalDimmension(90) height: cellHeight exclusiveGroup: mainActionGroup text: qsTr("Plan") @@ -177,7 +185,7 @@ Rectangle { QGCToolBarButton { id: flyButton - width: 90 + width: getProportionalDimmension(90) height: cellHeight exclusiveGroup: mainActionGroup text: qsTr("Fly") @@ -191,7 +199,7 @@ Rectangle { QGCToolBarButton { id: analyzeButton - width: 90 + width: getProportionalDimmension(90) height: cellHeight exclusiveGroup: mainActionGroup text: qsTr("Analyze") @@ -213,7 +221,7 @@ Rectangle { Rectangle { id: messages - width: (mainToolBar.messageCount > 99) ? 70 : 60 + width: (mainToolBar.messageCount > 99) ? getProportionalDimmension(70) : getProportionalDimmension(60) height: cellHeight visible: (mainToolBar.connectionCount > 0) && (mainToolBar.showMessages) anchors.verticalCenter: parent.verticalCenter @@ -226,11 +234,11 @@ Rectangle { Image { id: messageIcon source: getMessageIcon(); - height: 16 + height: getProportionalDimmension(16) fillMode: Image.PreserveAspectFit anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - anchors.leftMargin: 10 + anchors.leftMargin: getProportionalDimmension(10) } Rectangle { @@ -256,8 +264,8 @@ Rectangle { visible: (messages.showTriangle) && (mainToolBar.messageCount > 0) anchors.bottom: parent.bottom anchors.right: parent.right - anchors.bottomMargin: 3 - anchors.rightMargin: 3 + anchors.bottomMargin: getProportionalDimmension(3) + anchors.rightMargin: getProportionalDimmension(3) } Timer { @@ -306,7 +314,7 @@ Rectangle { Rectangle { id: satelitte - width: 60 + width: getProportionalDimmension(60) height: cellHeight visible: showMavStatus() && (mainToolBar.showGPS) anchors.verticalCenter: parent.verticalCenter @@ -317,11 +325,11 @@ Rectangle { Image { source: "qrc:/res/Gps"; - height: 24 + height: getProportionalDimmension(24) fillMode: Image.PreserveAspectFit anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - anchors.leftMargin: 10 + anchors.leftMargin: getProportionalDimmension(10) mipmap: true smooth: true } @@ -333,7 +341,7 @@ Rectangle { font.weight: Font.DemiBold anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right - anchors.rightMargin: 10 + anchors.rightMargin: getProportionalDimmension(10) horizontalAlignment: Text.AlignRight color: colorWhite } @@ -341,7 +349,7 @@ Rectangle { Rectangle { id: battery - width: 80 + width: getProportionalDimmension(80) height: cellHeight visible: showMavStatus() && (mainToolBar.showBattery) anchors.verticalCenter: parent.verticalCenter @@ -352,11 +360,11 @@ Rectangle { Image { source: getBatteryIcon(); - height: 20 + height: getProportionalDimmension(20) fillMode: Image.PreserveAspectFit anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - anchors.leftMargin: 6 + anchors.leftMargin: getProportionalDimmension(6) mipmap: true smooth: true } @@ -368,7 +376,7 @@ Rectangle { font.weight: Font.DemiBold anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right - anchors.rightMargin: 8 + anchors.rightMargin: getProportionalDimmension(8) horizontalAlignment: Text.AlignRight color: colorWhite } @@ -377,7 +385,7 @@ Rectangle { Column { visible: showMavStatus() height: cellHeight * 0.85 - width: 80 + width: getProportionalDimmension(80) anchors.verticalCenter: parent.verticalCenter Rectangle { @@ -422,7 +430,7 @@ Rectangle { Rectangle { id: modeStatus - width: 90 + width: getProportionalDimmension(90) height: cellHeight visible: showMavStatus() color: "#00000000" @@ -442,7 +450,7 @@ Rectangle { Rectangle { id: connectionStatus - width: 160 + width: getProportionalDimmension(160) height: cellHeight visible: (mainToolBar.connectionCount > 0 && mainToolBar.mavPresent && mainToolBar.heartbeatTimeout != 0) anchors.verticalCenter: parent.verticalCenter @@ -469,8 +477,8 @@ Rectangle { spacing: cellSpacerSize anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 10 - anchors.rightMargin: 10 + anchors.leftMargin: getProportionalDimmension(10) + anchors.rightMargin: getProportionalDimmension(10) Menu { id: connectMenu @@ -501,7 +509,7 @@ Rectangle { QGCButton { id: connectButton - width: 100 + width: getProportionalDimmension(100) visible: mainToolBar.connectionCount === 0 text: qsTr("Connect") menu: connectMenu @@ -510,7 +518,7 @@ Rectangle { QGCButton { id: disconnectButton - width: 100 + width: getProportionalDimmension(100) visible: mainToolBar.connectionCount === 1 text: qsTr("Disconnect") anchors.verticalCenter: parent.verticalCenter @@ -539,7 +547,7 @@ Rectangle { QGCButton { id: multidisconnectButton - width: 100 + width: getProportionalDimmension(100) text: "Disconnect" visible: mainToolBar.connectionCount > 1 menu: disconnectMenu @@ -550,8 +558,7 @@ Rectangle { // Progress bar Rectangle { - readonly property int progressBarHeight: 3 - + readonly property int progressBarHeight: getProportionalDimmension(3) y: parent.height - progressBarHeight height: progressBarHeight width: parent.width * mainToolBar.progressBarValue