com.serialpundit.serial.SerialComManager Maven / Gradle / Ivy
Show all versions of sp-tty Show documentation
/*
* This file is part of SerialPundit.
*
* Copyright (C) 2014-2016, Rishi Gupta. All rights reserved.
*
* The SerialPundit is DUAL LICENSED. It is made available under the terms of the GNU Affero
* General Public License (AGPL) v3.0 for non-commercial use and under the terms of a commercial
* license for commercial use of this software.
*
* The SerialPundit 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.
*/
package com.serialpundit.serial;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.TreeMap;
import com.serialpundit.core.SerialComPlatform;
import com.serialpundit.core.SerialComSystemProperty;
import com.serialpundit.core.SerialComException;
import com.serialpundit.serial.comdb.SerialComDBRelease;
import com.serialpundit.serial.ftp.ISerialComXmodemProgress;
import com.serialpundit.serial.ftp.ISerialComYmodemProgress;
import com.serialpundit.serial.ftp.SerialComFTPCMDAbort;
import com.serialpundit.serial.ftp.SerialComXModem;
import com.serialpundit.serial.ftp.SerialComXModem1K;
import com.serialpundit.serial.ftp.SerialComXModemCRC;
import com.serialpundit.serial.ftp.SerialComYModem1K;
import com.serialpundit.serial.ftp.SerialComYModemCRC;
import com.serialpundit.serial.ftp.SerialComYModemG;
import com.serialpundit.serial.mapper.SerialComPortMapper;
import com.serialpundit.serial.nullmodem.SerialComNullModem;
import com.serialpundit.serial.vendor.SerialComVendorLib;
import com.serialpundit.serial.internal.ISerialIOStream;
import com.serialpundit.serial.internal.SerialComCompletionDispatcher;
import com.serialpundit.serial.internal.SerialComDBReleaseJNIBridge;
import com.serialpundit.serial.internal.SerialComLooper;
import com.serialpundit.serial.internal.SerialComPortHandleInfo;
import com.serialpundit.serial.internal.SerialComPortJNIBridge;
import com.serialpundit.serial.internal.SerialComPortMapperJNIBridge;
import com.serialpundit.serial.internal.SerialComPortsList;
import com.serialpundit.serial.internal.ISerialComFTPProgress;
/**
* Root of SerialPundit.
*
* Native layer if fails to throw exception when an error occurs would log error message to STDERR file.
* It is assumed that Java application running on production systems will deploy a Java logger which will
* redirect STDERR messages to a log file. This way if an error occurs and native layer could not throw
* exception for example in out of memory case it will still be logged for later analysis.
*
*
* @author Rishi Gupta
* @version 1.0.4
*/
public final class SerialComManager {
/**Production release version of the SerialPundit.
*/
public static final String JAVA_LIB_VERSION = "1.0.4";
/** Pre-defined enum constants for baud rate values.
*/
public enum BAUDRATE {
B0(0), B50(50), B75(75), B110(110), B134(134), B150(150), B200(200), B300(300), B600(600),
B1200(1200), B1800(1800), B2400(2400), B4800(4800), B9600(9600), B14400(14400), B19200(19200),
B28800(28800), B38400(38400), B56000(56000), B57600(57600), B115200(115200), B128000(128000),
B153600(153600), B230400(230400), B256000(256000), B460800(460800), B500000(500000),
B576000(576000), B921600(921600), B1000000(1000000), B1152000(1152000), B1500000(1500000),
B2000000(2000000), B2500000(2500000), B3000000(3000000), B3500000(3500000), B4000000(4000000),
BCUSTOM(251);
private int value;
private BAUDRATE(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined enum constants for number of data bits in a serial frame.
*/
public enum DATABITS {
/** Serial frame will contain 5 data bits.
*/
DB_5(5),
/** Serial frame will contain 6 data bits.
*/
DB_6(6),
/** Serial frame will contain 7 data bits.
*/
DB_7(7),
/** Serial frame will contain 8 data bits.
*/
DB_8(8);
private int value;
private DATABITS(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined enum constants for number of stop bits in a serial frame.
*/
public enum STOPBITS {
/** Number of stop bits in one frame is 1.
*/
SB_1(1),
/** Number of stop bits in one frame is 1.5.
*/
SB_1_5(4),
/** Number of stop bits in one frame is 2.
*/
SB_2(2);
private int value;
private STOPBITS(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined enum constants for enabling type of parity in a serial frame.
*/
public enum PARITY {
/** The uart frame does not contain any parity bit. Errors are handled by application for example
* using CRC algorithm.*/
P_NONE(1),
/** The number of bits in the frame with the value one is odd. If the sum of bits
* with a value of 1 is odd in the frame, the parity bit's value is set to zero.
* If the sum of bits with a value of 1 is even in the frame, the parity bit value
* is set to 1, making the total count of 1's in the frame an odd number.
*/
P_ODD(2),
/** The number of bits in the frame with the value one is even. The number
* of bits whose value is 1 in a frame is counted. If that total is odd,
* the parity bit value is set to 1, making the total count of 1's in the frame
* an even number. If the count of ones in a frame a is already even,
* the parity bit's value remains 0.
* Odd parity may be more fruitful since it ensures that at least one state
* transition occurs in each character, which makes it more reliable as compared
* even parity.
* Even parity is a special case of a cyclic redundancy check (CRC),
* where the 1-bit CRC is generated by the polynomial x+1.
*/
P_EVEN(3),
/** The parity bit is set to the mark signal condition (logical 1). An application
* may use the 9th (parity) bit for some form of addressing or special signaling.
* The mark parity is also known as stick parity.
*/
P_MARK(4),
/** The parity bit is set to the space signal condition (logical 0). The mark
* and space parity is uncommon, as it adds no error detection information. The space parity
* is also known as stick parity.
*/
P_SPACE(5);
private int value;
private PARITY(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined enum constants for controlling data flow between DTE and DCE or two serial devices.
*/
public enum FLOWCONTROL {
/** No flow control; application is responsible to manage data buffers. Application can
* assert or de-assert RTS/CTS or DTR/DSR signals explicitly.
*/
NONE(1),
/** Operating system (or driver) will assert or de-assert RTS/CTS lines as per the amount of
* data in input buffers.
*/
RTS_CTS(2),
/** Operating system (or driver) will assert or de-assert DTR/DSR lines as per the amount of
* data in input buffers.
*/
DTR_DSR(3),
/** Operating system (or driver) will send XON or XOFF characters as per the amount of data
* in input buffers. Upon reception of XOFF system will stop transmitting data and vice-versa.
*/
XON_XOFF(4);
private int value;
private FLOWCONTROL(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined enum constants for defining endianness of data to be sent over serial port.
*/
public enum ENDIAN {
/** Little endian data format. The least significant byte (LSB) value is at the lowest
* address.
*/
E_LITTLE(1),
/** Big endian data format. The most significant byte (MSB) value is at the lowest address.
*/
E_BIG(2),
/** Platform default.
*/
E_DEFAULT(3);
private int value;
private ENDIAN(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined enum constants for defining number of bytes given data can be represented in.
*/
public enum NUMOFBYTES {
/** Integer value requires 16 bits.
*/
NUM_2(2),
/** Integer value requires 32 bits.
*/
NUM_4(4);
private int value;
private NUMOFBYTES(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined enum constants for defining file transfer protocol to use.
*/
public enum FTPPROTO {
/** XMODEM protocol with three variants checksum, CRC and 1k.
*/
XMODEM(1),
/** YMODEM protocol with two variants CRC and 1k.
*/
YMODEM(2),
/** coming soon
*/
ZMODEM(3);
private int value;
private FTPPROTO(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined enum constants for defining variant of file transfer protocol to use.
*/
public enum FTPVAR {
/** 1 byte checksum with 128 byte data block variant for X modem protocol. If you are new
* to FTP protocols start with this.
*/
CHKSUM(1),
/** 2 bytes CRC with 128 byte data block variant for X/Y modem protocols.
*/
CRC(2),
/** 2 byte CRC with 128/1024 bytes data block variant for X/Y modem protocols.
*/
VAR1K(3),
/** 2 byte CRC with 128/1024 bytes data block Ymodem-G variant of Y modem protocol.
*/
VARG(4);
private int value;
private FTPVAR(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined enum constants for defining behavior of byte stream.
*/
public enum SMODE {
/** Read / Write operation will block till data is available.
*/
BLOCKING(1),
/** Read / Write operation will not block till data is available.
*/
NONBLOCKING(2);
private int value;
private SMODE(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Default number of bytes (1024) to read from serial port.
*/
public static final int DEFAULT_READBYTECOUNT = 1024;
/** Clear to send mask bit constant for UART control line. Integer constant with value 0x01.
*/
public static final int CTS = 0x01; // 0000001
/** Data set ready mask bit constant for UART control line. Integer constant with value 0x02.
*/
public static final int DSR = 0x02; // 0000010
/** Data carrier detect mask bit constant for UART control line. Integer constant with value 0x04.
*/
public static final int DCD = 0x04; // 0000100
/** Ring indicator mask bit constant for UART control line. Integer constant with value 0x08.
*/
public static final int RI = 0x08; // 0001000
/** Loop indicator mask bit constant for UART control line. Integer constant with value 0x10.
*/
public static final int LOOP = 0x10; // 0010000
/** Request to send mask bit constant for UART control line. Integer constant with value 0x20.
*/
public static final int RTS = 0x20; // 0100000
/** Data terminal ready mask bit constant for UART control line. Integer constant with value 0x40.
*/
public static final int DTR = 0x40; // 1000000
/** The value indicating instance of SerialComInByteStream class is desired. Integer constant with value 0x32.
*/
public static final int InputStream = 0X32;
/** The value indicating instance of SerialComOutByteStream class is desired. Integer constant with value 0x33.
*/
public static final int OutputStream = 0X33;
/** The exception message indicating that a blocked read method has been unblocked
* and made to return to caller explicitly (irrespective there was data to read or not).
*/
public static final String EXP_UNBLOCKIO = "I/O operation unblocked !";
// This provides guaranteed log(n) time cost for the containsKey, get, put and remove operations.
// It maps opened handle of serial device to its information object. This map may be accessed in
// locked state for maintaining integrity and consistency whenever required.
private final TreeMap mPortHandleInfo = new TreeMap();
private int osType = SerialComPlatform.OS_UNKNOWN;
private int cpuArch = SerialComPlatform.ARCH_UNKNOWN;
private int abiType = SerialComPlatform.ABI_UNKNOWN;
private SerialComPlatform mSerialComPlatform;
private final SerialComSystemProperty mSerialComSystemProperty;
private final SerialComPortJNIBridge mComPortJNIBridge;
private final SerialComCompletionDispatcher mEventCompletionDispatcher;
private final SerialComPortsList mSerialComPortsList;
private final Object lockB = new Object();
private static final Object lockA = new Object();
private static boolean nativeLibLoadAndInitAlready = false;
private static SerialComVendorLib mSerialComVendorLib;
private static SerialComNullModem mSerialComNullModem;
private static SerialComPortMapperJNIBridge mSerialComPortMapperJNIBridge;
private static SerialComDBReleaseJNIBridge mSerialComDBReleaseJNIBridge;
// Whenever an exception/error occurs in native function, it throws that exception.
// When java method return from native call, extra check is added to make error
// detection more robust. If for some unexpected reason JVM does not throw exception
// then an extra check will make exception to be thrown in java layer.
/**
* Allocates a new SerialComManager object. Identify operating system type, CPU architecture, prepares
* environment required for running this library, initiates extraction and loading of native libraries.
*
* The native shared library will be extracted in folder named 'sp_tuartx1' inside system/user 'temp'
* folder or user home folder if access to 'temp' folder is denied.
*
* On ARM Linux embedded platform it is possible to run entire 32 bit user space on a 64 bit ARMv8 processor.
* In such scenarios, the 32 bit and 64 bit shared libraries must not be mixed. Consider either full 32 bit user
* space root file system or entire root file system to be 64 bit.
*
* Please contact author of this library if you want to use CPU optimized native shared libraries in your
* embedded product for achieving maximum performance.
*
* @throws IOException if file operations on "/proc/cpuinfo" fails for Linux on ARM platform, if java system
* properties can not be accessed, if file "/proc/cpuinfo" can not be found for Linux on ARM platform,
* if native libraries are not found or can not be loaded/linked and initialized. If appropriate
* files/directories can not be read or written.
* @throws IllegalArgumentException if directoryPath is null, directoryPath is empty, loadedLibName is null
* or empty.
*/
public SerialComManager() throws IOException {
mSerialComSystemProperty = new SerialComSystemProperty();
mSerialComPlatform = new SerialComPlatform(mSerialComSystemProperty);
if(osType == SerialComPlatform.OS_UNKNOWN) {
osType = mSerialComPlatform.getOSType();
if(osType == SerialComPlatform.OS_UNKNOWN) {
throw new SerialComException("Could not identify operating system. Please report your environemnt to us so that we can add support for it !");
}
}
if(cpuArch == SerialComPlatform.ARCH_UNKNOWN) {
cpuArch = mSerialComPlatform.getCPUArch(osType);
if(cpuArch == SerialComPlatform.ARCH_UNKNOWN) {
throw new SerialComException("Could not identify CPU architecture. Please report your environemnt to us so that we can add support for it !");
}
}
if((cpuArch == SerialComPlatform.ARCH_ARMV8) || (cpuArch == SerialComPlatform.ARCH_ARMV7) || (cpuArch == SerialComPlatform.ARCH_ARMV6) ||
(cpuArch == SerialComPlatform.ARCH_ARMV5)) {
if(osType == SerialComPlatform.OS_LINUX) {
abiType = mSerialComPlatform.getABIType();
}else {
throw new SerialComException("Please report to us your environemnt to us !");
}
}
synchronized(SerialComManager.lockA) {
mComPortJNIBridge = new SerialComPortJNIBridge();
if(nativeLibLoadAndInitAlready == false) {
SerialComPortJNIBridge.loadNativeLibrary(null, null, mSerialComSystemProperty, osType, cpuArch, abiType, false);
mComPortJNIBridge.initNativeLib();
nativeLibLoadAndInitAlready = true;
}
}
mEventCompletionDispatcher = new SerialComCompletionDispatcher(mComPortJNIBridge, mPortHandleInfo);
mSerialComPortsList = new SerialComPortsList(mComPortJNIBridge, osType);
}
/**
* Allocates a new SerialComManager object. Identify operating system type, CPU architecture, prepares
* environment required for running this library, initiates extraction and loading of native libraries.
*
* By default native shared library will be extracted in temp folder. If this constructor is used then,
* It extracts native shared library in the folder specified by argument directoryPath and gives library name
* specified by loadedLibName. If the argument createDirectory is true, it will create directory (including
* parent if it does not exist) as specified by directoryPath, otherwise user should make sure that this directory
* exist before calling this constructor.
*
*
* - Sometimes system administrator may have put some restriction on tmp/temp folder or the there may some
* other inevitable situations like anti-virus program causing trouble when using temp folder. This constructor
* will help in handling such situations.
*
* Two or more absolutely independent vendors may package this library into their product's jar file. Now
* when using default constructor both will extract and use the same folder and library name resulting in inconsistent
* software. This constructor handle this situation by providing vendor specific isolated environment.
*
* This may also increase security as the folder may be given specific user permissions. To extract library in
* directory "/home/xxx/yyy", name it lib2 and create directory yyy an example is given below.
* SerialComManager scm = new SerialComManager("lib2", "/home/xxx/yyy", true);
*
* - If hotDeploy is true than a different strategy is used to load the shared native libraries. This option can be
* true if this software is used on tomcat server for hot deployment purpose or may be during development. If two or
* more web applications use this library and both do the hot deployment, than loadedLibName should be different
* for both the applications. This will prevent unnecessary conflicts or unforeseen side effects.
*
*
* @param loadedLibName library name without extension (do not append .so, .dll or .dylib etc.).
* @param directoryPath absolute path of directory to be used for purpose of extraction.
* @param createDirectory true if directory is to be created otherwise false if given directory already exist.
* @param hotDeploy true if hot deployment is to be supported otherwise false.
* @throws IOException if file operations on "/proc/cpuinfo" fails for Linux on ARM platform, if java system
* properties can not be accessed, if file "/proc/cpuinfo" can not be found for Linux on ARM platform,
* if native libraries are not found or can not be loaded/linked and initialized. If appropriate
* files/directories can not be read or written.
* @throws IllegalArgumentException if directoryPath is null, directoryPath is empty, loadedLibName is null
* or empty.
*/
public SerialComManager(String loadedLibName, String directoryPath, final boolean createDirectory,
boolean hotDeploy) throws IOException {
if(directoryPath == null) {
throw new IllegalArgumentException("Argument directoryPath can not be null !");
}
if(directoryPath.length() == 0) {
throw new IllegalArgumentException("Argument directoryPath can not be empty string !");
}
if(createDirectory == true) {
File extractionDirectory = new File(directoryPath);
extractionDirectory.mkdirs();
}
if(loadedLibName == null) {
throw new IllegalArgumentException("Argument loadedLibName can not be null !");
}
if(loadedLibName.length() == 0) {
throw new IllegalArgumentException("Argument loadedLibName can not be empty string !");
}
mSerialComSystemProperty = new SerialComSystemProperty();
mSerialComPlatform = new SerialComPlatform(mSerialComSystemProperty);
if(osType == SerialComPlatform.OS_UNKNOWN) {
osType = mSerialComPlatform.getOSType();
if(osType == SerialComPlatform.OS_UNKNOWN) {
throw new SerialComException("Could not identify operating system. Please report your environment to us !");
}
}
if(cpuArch == SerialComPlatform.ARCH_UNKNOWN) {
cpuArch = mSerialComPlatform.getCPUArch(osType);
if(cpuArch == SerialComPlatform.ARCH_UNKNOWN) {
throw new SerialComException("Could not identify CPU architecture. Please report your environment to us !");
}
}
if((cpuArch == SerialComPlatform.ARCH_ARMV8) || (cpuArch == SerialComPlatform.ARCH_ARMV7) || (cpuArch == SerialComPlatform.ARCH_ARMV6) ||
(cpuArch == SerialComPlatform.ARCH_ARMV5)) {
if(osType == SerialComPlatform.OS_LINUX) {
abiType = mSerialComPlatform.getABIType();
}else {
throw new SerialComException("Please report to us your environment to us !");
}
}
synchronized(SerialComManager.lockA) {
mComPortJNIBridge = new SerialComPortJNIBridge();
if(nativeLibLoadAndInitAlready == false) {
SerialComPortJNIBridge.loadNativeLibrary(directoryPath, loadedLibName, mSerialComSystemProperty, osType, cpuArch, abiType, hotDeploy);
mComPortJNIBridge.initNativeLib();
nativeLibLoadAndInitAlready = true;
}
}
mEventCompletionDispatcher = new SerialComCompletionDispatcher(mComPortJNIBridge, mPortHandleInfo);
mSerialComPortsList = new SerialComPortsList(mComPortJNIBridge, osType);
}
/**
* Gives library versions of java and native library implementations for serial port communication.
*
* @return Java and C library versions implementing this library.
* @throws SerialComException if native library version could not be determined.
*/
public String getLibraryVersions() throws SerialComException {
String version = null;
String nativeLibversion = mComPortJNIBridge.getNativeLibraryVersion();
if(nativeLibversion != null) {
version = "Java lib version: " + JAVA_LIB_VERSION + "\n" + "Native lib version: " + nativeLibversion;
}else {
version = "Java lib version: " + JAVA_LIB_VERSION + "\n" + "Native lib version: ?????";
}
return version;
}
/**
* Returns all available UART style ports available on this system, otherwise an empty array of strings,
* if no serial style port is found in the system.
*
* This should find regular UART ports, hardware/software virtual COM ports, port server, USB-UART
* converter, bluetooth/3G dongles, ports connected through USB hub/expander, serial card, serial controller,
* pseudo terminals, printers and virtual modems etc.
*
* This method may be used to find valid serial ports for communications before opening them for writing
* more robust application.
*
* Note : The BIOS may ignore UART ports on a PCI card and therefore BIOS settings has to be corrected
* if you modified default BIOS in OS.
*
* @return Available serial ports name for windows, full path with name for Unix like OS, returns
* empty array if no ports are found.
* @throws SerialComException if an I/O error occurs.
*/
public String[] listAvailableComPorts() throws SerialComException {
String[] availablePorts = mSerialComPortsList.listAvailableComPorts();
if(availablePorts != null) {
return availablePorts;
}else {
return new String[] { };
}
}
/**
* Opens a serial port for communication. If an attempt is made to open a port which is already
* opened, an exception will be thrown.
*
*
* - For Linux and Mac OS X, if exclusiveOwnerShip is true, before this method returns, the caller
* will either be exclusive owner or not. If the caller is successful in becoming exclusive owner than
* all the attempt to open the same port again will cause native code to return error. Note that a root
* owned process (root user) will still be able to open the port.
*
*
For Windows the exclusiveOwnerShip must be true as it does not allow sharing COM ports. An
* exception is thrown if exclusiveOwnerShip is set to false. For Solaris, exclusiveOwnerShip should be
* set to false as of now. On Unix-like system this method uses ioctl command TIOCEXCL for exclusive access.
* and not lock files
*
* - This method will clear both input and output buffers of drivers (or operating system).
*
* When the serial port is opened DTR and RTS lines will be raised by default by this library. Sometimes,
* DTR acts as a modem on-hook/off-hook control for other end. Modern modems are highly flexible in their dependency,
* working and configurations. It is best to consult modem manual. If the application design need DTR/RTS
* not to be asserted when port is opened custom drivers can be used or hardware can be modified for this
* purpose. Alternatively, if the application is to be run on Windows operating system only, then modifying
* INF file or registry key may help in not raising DTR/RTS when port is opened. Typically in Windows DTR/RTS
* is raised due to enumeration sequence (serenum).
*
* In Unix jargon a dial-in TTY device is used for terminals, modems and printers etc. and requires DCD to be
* high for operation. When used with a modem, the port will wait for carrier before sending out the login prompt to
* end user. It is for this reason typically DTR of one end is connected to DSR of other end. When the terminal is
* turned off, any associated jobs are killed, and the user is logged out. Unlike dial-in the dial-out TTY device does
* not require DCD to be high. Once connection is made DCD may go high. Loss of the DCD signal may cause the jobs
* to be killed and the user will be automatically logged off.
*
* - On some hardware when opening a serial port TXD, RXD, RTS or DTR lines may show glitch which may unintentionally
* trigger other end. This may be due to hardware or a driver bug.
*
*
* This method is thread safe.
*
* @param portName name of the port to be opened for communication.
* @param enableRead allows application to read bytes from this port.
* @param enableWrite allows application to write bytes to this port.
* @param exclusiveOwnerShip application wants to become exclusive owner of this port or not.
* @return handle of the port successfully opened.
* @throws IllegalStateException if trying to become exclusive owner when port is already opened.
* @throws IllegalArgumentException if portName is null or invalid length, or if both enableRead and
* enableWrite are set to false, if trying to open port in Windows without being exclusive owner.
* @throws SerialComException if the port can be opened for some reason.
*/
public long openComPort(final String portName, boolean enableRead, boolean enableWrite, boolean exclusiveOwnerShip) throws SerialComException {
long handle = 0;
SerialComPortHandleInfo handleInfo = null;
if(portName == null) {
throw new IllegalArgumentException("Argument portName can not be null !");
}
String portNameVal = portName.trim();
if(portNameVal.length() == 0) {
throw new IllegalArgumentException("Name of the port to be opened can not be empty string !");
}
if((enableRead == false) && (enableWrite == false)) {
throw new IllegalArgumentException("Arguments enableRead and enableWrite both can not be set to false !");
}
if(osType == SerialComPlatform.OS_WINDOWS) {
// For windows COM port can not be shared, so throw exception
if(exclusiveOwnerShip == false) {
throw new IllegalArgumentException("Windows OS does not allow COM port sharing; exclusiveOwnerShip must be true !");
}
}
synchronized(lockB) {
/* Try to reduce transitions from java to JNI layer as it is possible here by performing check in java layer itself. */
if(exclusiveOwnerShip == true) {
for (Map.Entry entry : mPortHandleInfo.entrySet()) {
handleInfo = entry.getValue();
if(handleInfo != null) {
if(handleInfo.containsPort(portNameVal)) {
throw new IllegalStateException("The port " + portNameVal + " is already opened. Exclusive ownership can not be claimed !");
}
}
}
}
handle = mComPortJNIBridge.openComPort(portNameVal, enableRead, enableWrite, exclusiveOwnerShip);
if(handle < 0) {
/* JNI should have already thrown exception, this is an extra check to increase reliability of program. */
throw new SerialComException("Could not open the port " + portNameVal + ". Please retry !");
}
mPortHandleInfo.put(handle, new SerialComPortHandleInfo(portNameVal, handle, null, null, null));
}
return handle;
}
/**
* Close the serial port. Application should unregister listeners if it has registered any before
* calling this method.
*
* DTR line is dropped when port is closed.
*
* If a blocking operation is in progress like readBytesBlocking() etc. than first such operation must be
* unblocked and then only closeComPort() should be called. It is invalid application design to violate this rule.
*
* This method is thread safe.
*
* @param handle of the port to be closed.
* @return Return true if the serial port is closed.
* @throws SerialComException if invalid handle is passed or when it fails in closing the port.
* @throws IllegalStateException if application tries to close port while data/event listeners,
* or input/output byte streams exist.
*/
public boolean closeComPort(long handle) throws SerialComException {
SerialComPortHandleInfo handleInfo = null;
synchronized(lockB) {
handleInfo = mPortHandleInfo.get(handle);
if(handleInfo == null) {
throw new SerialComException("Given handle is alien to me !");
}
/* Proper clean up requires that sw/hw resources should be freed before closing the serial port */
if(handleInfo.getDataListener() != null) {
throw new IllegalStateException("Closing port without unregistering data listener is not allowed to prevent inconsistency !");
}
if(handleInfo.getEventListener() != null) {
throw new IllegalStateException("Closing port without unregistering event listener is not allowed to prevent inconsistency !");
}
if(handleInfo.getSerialComInByteStream() != null) {
throw new IllegalStateException("Input byte stream must be closed before closing the serial port !");
}
if(handleInfo.getSerialComOutByteStream() != null) {
throw new IllegalStateException("Output byte stream must be closed before closing the serial port !");
}
int ret = mComPortJNIBridge.closeComPort(handle);
if(ret < 0) {
throw new SerialComException("Could not close the given serial port. Please retry !");
}
/* delete info about this port/handle from global information object. */
mPortHandleInfo.remove(handle);
}
return true;
}
/**
* This method writes bytes from the specified byte type buffer. If the method returns false, the
* application should try to re-send bytes. The data has been transmitted out of serial port when this
* method returns.
*
*
* - When using flow control, if the transmission has been stopped because other serial port's buffer
* is getting full, this method may return 0, indicating that no data bytes were sent out of serial port.
*
* If large amount of data need to be written, it is advisable to break it into chunks of data of
* size for example 2KB each.
*
* - Writing empty buffer i.e. zero length array is not allowed.
*
* It should be noted that on Linux system reading from the terminal after a disconnect causes
* an end-of-file condition, and writing causes an EIO error to be returned. The terminal device must
* be closed and reopened to clear the condition.
*
* - The delay parameter may be used where explicit flow control is not implemented and the firmware
* is not fast enough to accommodate large and fast data sent to serial device. To control speed and amount
* of data sent to serial device either add delay between successive bytes or add delay after host has sent
* some particular number of bytes.
*
*
* @param handle handle of the opened port on which to write bytes.
* @param buffer byte type buffer containing bytes to be written to port.
* @param delay time gap (in milliseconds) between transmitting two successive bytes.
* @return number of bytes written.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if buffer is null or delay is negative.
*/
public int writeBytes(long handle, final byte[] buffer, int delay) throws SerialComException {
if((buffer == null) || (buffer.length == 0)) {
throw new IllegalArgumentException("Argument buffer can not be null or empty !");
}
if(delay < 0) {
throw new IllegalArgumentException("Argument delay can not be negative !");
}
int ret = mComPortJNIBridge.writeBytes(handle, buffer, delay);
if(ret < 0) {
throw new SerialComException("Could not write data to serial port. Please retry !");
}
return ret;
}
/**
* Utility method to call writeBytes without delay between successive bytes.
* The writeBytes(handle, buffer) method for class SerialComManager has the same effect
* as:
* writeBytes(handle, buffer, 0)
*
* @param handle handle of the opened port on which to write bytes.
* @param buffer byte type buffer containing bytes to be written to port.
* @return number of bytes written.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if buffer is null.
*/
public int writeBytes(long handle, byte[] buffer) throws SerialComException {
return writeBytes(handle, buffer, 0);
}
/**
* This method writes a single byte to the specified port. The data has been transmitted
* out of serial port when this method returns.
*
* @param handle handle of the opened port on which to write byte.
* @param dataByte byte to be written to port.
* @return number of bytes written.
* @throws SerialComException if an I/O error occurs.
*/
public int writeSingleByte(long handle, byte dataByte) throws SerialComException {
int ret = mComPortJNIBridge.writeSingleByte(handle, dataByte);
if(ret < 0) {
/* extra check */
throw new SerialComException("Could not write given byte to serial port. Please retry !");
}
return ret;
}
/**
* This method writes a string to the specified port. The library internally converts string
* to byte buffer. The data has been transmitted out of serial port when this method returns.
*
* @param handle handle of the opened port on which to write byte.
* @param data the string to be send to port.
* @param delay interval between two successive bytes while sending string.
* @return true on success false otherwise.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if data is null or empty string.
*/
public boolean writeString(long handle, String data, int delay) throws SerialComException {
int ret = 0;
if((data == null) || (data.length() == 0)) {
throw new IllegalArgumentException("Argument data can not be null or an empty string !");
}
byte[] dataToBeSent = data.getBytes();
ret = writeBytes(handle, dataToBeSent, delay);
if(ret != dataToBeSent.length) {
return false;
}
return true;
}
/**
* This method writes a string to the specified port. The library internally converts string to byte buffer.
* The data has been transmitted out of serial port when this method returns.
*
* @param handle handle of the opened port on which to write byte.
* @param data the string to be send to port.
* @param charset the character set into which given string will be encoded.
* @param delay time gap between transmitting two successive bytes in this string.
* @return true on success false otherwise.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if data is null or empty string.
* @throws UnsupportedEncodingException if string encoding/decoding fails.
*/
public boolean writeString(long handle, final String data, Charset charset, int delay) throws UnsupportedEncodingException, SerialComException {
int ret = 0;
if((data == null) || (data.length() == 0)) {
throw new IllegalArgumentException("Argument data can not be null or an empty string !");
}
byte[] dataToBeSent = data.getBytes(charset);
ret = writeBytes(handle, dataToBeSent, delay);
if(ret != dataToBeSent.length) {
return false;
}
return true;
}
/**
* Different CPU and OS will have different endianness. It is therefore we handle the endianness
* conversion as per the requirement. If the given integer is in range −32,768 to 32,767, only two
* bytes will be needed. In such case we might like to send only 2 bytes to serial port. On the other
* hand application might be implementing some custom protocol so that the data must be 4 bytes
* (irrespective of its range) in order to be interpreted correctly by the receiver terminal. This method
* assumes that integer value can be represented by 32 or less number of bits. On x86_64 architecture,
* loss of precision will occur if the integer value is of more than 32 bit.
*
* The data has been transmitted physically out of serial port when this method returns.
*
* In java numbers are represented in 2's complement, so number 650 whose binary representation is
* 0000001010001010 is printed byte by byte, then will be printed as 1 and -118, because 10001010 in 2's
* complement is negative number.
*
* @param handle handle of the opened port on which to write byte.
* @param data an integer number to be sent to port.
* @param delay interval between two successive bytes .
* @param endianness big or little endian sequence to be followed while sending bytes representing
* this integer.
* @param numOfBytes number of bytes this integer can be represented in.
* @return number of bytes written.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if endianness or numOfBytes is null.
*/
public int writeSingleInt(long handle, int data, int delay, ENDIAN endianness, NUMOFBYTES numOfBytes) throws SerialComException {
byte[] buffer = null;
if(endianness == null) {
throw new IllegalArgumentException("Argument endianness can not be null !");
}
if(numOfBytes == null) {
throw new IllegalArgumentException("Argument numOfBytes can not be null !");
}
if(numOfBytes.getValue() == 2) { // conversion to two bytes data
buffer = new byte[2];
if(endianness.getValue() == 1) { // Little endian
buffer[1] = (byte) (data >>> 8);
buffer[0] = (byte) data;
}else { // big endian/default (java is big endian by default)
buffer[1] = (byte) data;
buffer[0] = (byte) (data >>> 8);
}
return writeBytes(handle, buffer, delay);
}else { // conversion to four bytes data
buffer = new byte[4];
if(endianness.getValue() == 1) { // Little endian
buffer[3] = (byte) (data >>> 24);
buffer[2] = (byte) (data >>> 16);
buffer[1] = (byte) (data >>> 8);
buffer[0] = (byte) data;
}else { // big endian/default (java is big endian by default)
buffer[3] = (byte) data;
buffer[2] = (byte) (data >>> 8);
buffer[1] = (byte) (data >>> 16);
buffer[0] = (byte) (data >>> 24);
}
return writeBytes(handle, buffer, delay);
}
}
/**
* This method send an array of integers on the specified port. The data has been transmitted
* out of serial port when this method returns.
*
* @param handle handle of the opened port on which to write byte.
* @param buffer an array of integers to be sent to port.
* @param delay interval between two successive bytes .
* @param endianness big or little endian sequence to be followed while sending bytes representing
* this integer.
* @param numOfBytes number of bytes this integer can be represented in.
* @return number of bytes written.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if endianness or numOfBytes is null.
*/
public int writeIntArray(long handle, final int[] buffer, int delay, ENDIAN endianness, NUMOFBYTES numOfBytes) throws SerialComException {
byte[] localBuf = null;
if(endianness == null) {
throw new IllegalArgumentException("Argument endianness can not be null !");
}
if(numOfBytes == null) {
throw new IllegalArgumentException("Argument numOfBytes can not be null !");
}
if(numOfBytes.getValue() == 2) {
localBuf = new byte[2 * buffer.length];
if(endianness.getValue() == 1) { // little endian
int a = 0;
for(int b=0; b>> 8);
a++;
}
}else { // big/default endian
int c = 0;
for(int d=0; d>> 8);
c++;
localBuf[c] = (byte) buffer[d];
c++;
}
}
return writeBytes(handle, localBuf, delay);
}else {
localBuf = new byte[4 * buffer.length];
if(endianness.getValue() == 1) { // little endian
int e = 0;
for(int f=0; f>> 8);
e++;
localBuf[e] = (byte) (buffer[f] >>> 16);
e++;
localBuf[e] = (byte) (buffer[f] >>> 24);
e++;
}
}else { // big/default endian
int g = 0;
for(int h=0; h>> 8);
g++;
localBuf[g] = (byte) (buffer[h] >>> 16);
g++;
localBuf[g] = (byte) (buffer[h] >>> 24);
g++;
}
}
return writeBytes(handle, localBuf, delay);
}
}
/**
* Writes the bytes from the given direct byte buffer using facilities of the underlying JVM
* and operating system. When this method returns data would have sent out of serial port physically.
*
* Consider using this method when developing applications based on Bluetooth serial port profile
* or applications like printing document using printer.
*
* This method does not modify the direct byte buffer attributes position, capacity, limit and mark.
* The application design is expected to take care of this as and when required in appropriate manner.
* Further, this method does not consume or modify the data in the given buffer.
*
* @param handle handle of the serial port on which to write bytes.
* @param buffer direct byte buffer containing bytes to be written to port.
* @param offset location from where to start sending data out of serial port.
* @param length number of bytes from offset to sent to serial port.
* @return number of bytes sent to serial port.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if buffer is null, or if position or limit is negative,
* or if given buffer is not direct byte buffer, or if length > (buffer.capacity() - offset).
*/
public int writeBytesDirect(long handle, ByteBuffer buffer, int offset, int length) throws SerialComException {
if(buffer == null) {
throw new IllegalArgumentException("Argument buffer can not be null !");
}
if((offset < 0) || (length < 0)) {
throw new IllegalArgumentException("Argument offset or length can not be negative !");
}
if(!buffer.isDirect()) {
throw new IllegalArgumentException("Given buffer is not a direct byte buffer !");
}
if(length > (buffer.capacity() - offset)) {
throw new IllegalArgumentException("Index violation detected !");
}
if(length == 0) {
return 0;
}
int ret = mComPortJNIBridge.writeBytesDirect(handle, buffer, offset, length);
if(ret < 0) {
throw new SerialComException("Could not write given data to serial port. Please retry !");
}
return ret;
}
/**
* Write bytes from given buffer to the given handle in blocking mode.
*
*
* - Writing empty buffer i.e. zero length array is not allowed.
*
* Same context should not be used for both reading and writing.
*
*
* @param handle handle of the opened port on which to write bytes.
* @param buffer byte type buffer containing bytes to be written to port.
* @param context context value obtained form call to createBlockingIOContext method.
* @return number of bytes sent to serial port.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if buffer is null.
*/
public int writeBytesBlocking(long handle, byte[] buffer, long context) throws SerialComException {
if(buffer == null) {
throw new IllegalArgumentException("Argument buffer can not be null !");
}
if(buffer.length == 0) {
return 0;
}
int ret = mComPortJNIBridge.writeBytesBlocking(handle, buffer, context);
if(ret < 0) {
throw new SerialComException("Could not write data to serial port. Please retry !");
}
return ret;
}
/**
* Reads the bytes from the serial port into the given direct byte buffer using facilities of
* the underlying JVM and operating system.
*
* This method does not modify the direct byte buffer attributes position, capacity, limit and mark.
* The application design is expected to take care of this as and when required in appropriate manner.
*
* @param handle handle of the serial port from which to read data bytes.
* @param buffer direct byte buffer into which data bytes will be placed.
* @param offset location in byte buffer from which to start saving data.
* @param length number of bytes from offset to read in buffer.
* @return number of bytes read from serial port, 0 if length is 0.
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if buffer is null, or if position or limit is negative, or if
* given buffer is not direct byte buffer.
*/
public int readBytesDirect(long handle, ByteBuffer buffer, int offset, int length) throws SerialComException {
if(buffer == null) {
throw new IllegalArgumentException("Argument buffer can not be null !");
}
if((offset < 0) || (length < 0)) {
throw new IllegalArgumentException("Argument offset or length can not be negative !");
}
if(!buffer.isDirect()) {
throw new IllegalArgumentException("Argument buffer is not direct byte buffer !");
}
if(length > (buffer.capacity() - offset)) {
throw new IllegalArgumentException("Index violation detected !");
}
if(length == 0) {
return 0;
}
int ret = mComPortJNIBridge.readBytesDirect(handle, buffer, offset, length);
if(ret < 0) {
throw new SerialComException("Could not read data from serial port and place into direct byte buffer. Please retry !");
}
return ret;
}
/**
* Prepares a context that should be passed to readBytesBlocking, writeBytesBlocking,
* readBytes, unblockBlockingIOOperation and destroyBlockingIOContext methods.
*
* @return context value that should be passed to destroyBlockingIOContext, readBytesBlocking
* and writeBytesBlocking methods.
* @throws SerialComException if an I/O error occurs.
*/
public long createBlockingIOContext() throws SerialComException {
long ret = mComPortJNIBridge.createBlockingIOContext();
if(ret < 0) {
throw new SerialComException("Could not create blocking I/O context. Please retry !");
}
return ret;
}
/**
* Unblocks any blocked operation if it exist. This causes closing of serial port possible
* gracefully and return the worker thread that called blocking read/write to return and proceed
* as per application design.
*
* @param context context obtained from call to createBlockingIOContext method for blocking
* I/O operations.
* @return true if blocked operation was unblocked successfully.
* @throws SerialComException if an I/O error occurs.
*/
public boolean unblockBlockingIOOperation(long context) throws SerialComException {
int ret = mComPortJNIBridge.unblockBlockingIOOperation(context);
if(ret < 0) {
throw new SerialComException("Could not unblock the blocked I/O operation. Please retry !");
}
return true;
}
/**
* Destroys the context that was created by a call to createBlockingIOContext method for
* blocking I/O operations uses.
*
* @param context context obtained from call to createBlockingIOContext method for blocking
* I/O operations.
* @return true if the context gets destroyed successfully.
* @throws SerialComException if an I/O error occurs.
*/
public boolean destroyBlockingIOContext(long context) throws SerialComException {
int ret = mComPortJNIBridge.destroyBlockingIOContext(context);
if(ret < 0) {
throw new SerialComException("Could not destroy blocking I/O context. Please retry !");
}
return true;
}
/**
* Read specified number of bytes from given serial port and stay blocked till bytes arrive
* at serial port.
* 1. If data is read from serial port, array of bytes containing data is returned.
* 2. If there was no data in serial port to read, null is returned. Note that this case is
* not possible however for blocking read call.
*
*
* - The number of bytes to read must be greater than or equal to 1 and less than or equal to
* 2048 (1 <= byteCount <= 2048). This method may return less than the requested number of bytes
* due to reasons like, there is less data in operating system buffer (serial port) or operating
* system returned less data which is also legal.
*
* When no data at serial port has arrived and application wishes to unblock and return the control
* to the caller for example because application now wants to close the serial port, it should
* call unblockBlockingIOOperation() passing the same context to this method.
*
* - If using blocking context, it is advised to catch SerialComException exception and check if
* this contain message SerialComManager.EXP_UNBLOCKIO. This message indicates that no error occurred
* while waiting/reading data from serial port but application has explicitly asked to return.
*
*
* @param handle of the serial port from which to read bytes.
* @param byteCount number of bytes to read from serial port.
* @param context context obtained by a call to createBlockingIOContext method.
* @return array of bytes read from port if read succeeds or null if read fails.
* @throws SerialComException if an I/O error occurs or if byteCount is greater than 2048.
*/
public byte[] readBytesBlocking(long handle, int byteCount, long context) throws SerialComException {
if(byteCount > 2048) {
throw new SerialComException("Number of bytes to read can not be greater than 2048 !");
}
byte[] buffer = null;
buffer = mComPortJNIBridge.readBytesBlocking(handle, byteCount, context);
if(buffer != null) {
// data read from serial port, pass to application
return buffer;
}else {
// not possible for blocking call, just keeping it
return null;
}
}
/**
* Read specified number of data bytes from the given serial port.
*
*
* - If data is read from serial port, array of bytes containing data is returned.
*
* If there was no data at serial port to read, null is returned.
*
* - The number of bytes to read must be greater than or equal to 1 and less than or equal to
* 2048 (1 <= byteCount <= 2048). This method may return less than the requested number of bytes
* due to reasons like, there is less data in operating system buffer (serial port) or operating
* system returned less data which is also legal.
*
*
* @param handle of the serial port from which to read bytes.
* @param byteCount number of bytes to read from serial port.
* @return array of bytes read from port or null.
* @throws SerialComException if an I/O error occurs or if byteCount is greater than 2048.
*/
public byte[] readBytes(long handle, int byteCount) throws SerialComException {
if(byteCount > 2048) {
throw new SerialComException("Number of bytes to read can not be greater than 2048 !");
}
byte[] buffer = mComPortJNIBridge.readBytes(handle, byteCount);
if(buffer != null) {
return buffer; // data read from serial port, pass it the to application
}else {
return null; // serial port does not have any data
}
}
/**
* If user does not specify any count, library try to read DEFAULT_READBYTECOUNT (1024 bytes)
* bytes as default value.
*
* It has same effect as readBytes(handle, 1024)
*
* @param handle of the port from which to read bytes.
* @return array of bytes read from port or null.
* @throws SerialComException if an I/O error occurs.
*/
public byte[] readBytes(long handle) throws SerialComException {
return readBytes(handle, DEFAULT_READBYTECOUNT);
}
/**
* Reads data from serial port and converts it into string.
*
* It constructs a new string by decoding the specified array of bytes using the platform's
* default character set. The length of the new string is a function of the character set, and hence
* may not be equal to the length of the byte array read from serial port.
*
* @param handle of port from which to read bytes.
* @param byteCount number of bytes to read from this port.
* @return string constructed from data read from serial port or null.
* @throws SerialComException if an I/O error occurs or if byteCount is greater than 2048.
*/
public String readString(long handle, int byteCount) throws SerialComException {
byte[] buffer = readBytes(handle, byteCount);
if(buffer != null) {
return new String(buffer);
}
return null;
}
/**
* This method reads data from serial port and converts it into string.
*
* It Constructs a new string by decoding the specified array of bytes using the platform's
* default character set. The length of the new string is a function of the charset, and hence may not be
* equal to the length of the byte array read from serial port.
*
* Note that the length of data bytes read using this method can not be greater than
* DEFAULT_READBYTECOUNT i.e. 1024.
*
* @param handle of the port from which to read bytes.
* @return string constructed from data read from serial port or null.
* @throws SerialComException if an I/O error occurs.
*/
public String readString(long handle) throws SerialComException {
return readString(handle, DEFAULT_READBYTECOUNT);
}
/**
* This is a utility method to read a single byte from serial port.
*
* Its effect is same as readBytes(handle, 1)
*
* @param handle of the port from which to read byte.
* @return array of length 1 representing 1 byte data read from serial port or null.
* @throws SerialComException if an I/O error occurs.
*/
public byte[] readSingleByte(long handle) throws SerialComException {
return readBytes(handle, 1);
}
/**
* Reads data bytes from serial port into given buffer also providing info about framing, parity etc errors.
* This method may be used in application design which needs to poll serial port continuously for presence of data.
*
*
* - This method can be used in blocking mode or non-blocking mode. If context is -1, this method will
* not block. For blocking behavior pass context value obtained from a call to createBlockingIOContext method.
* If a valid context is passed, this method will block until there is data to read from serial port.
*
* When no data at serial port has arrived and application wishes to unblock and return the control
* to the caller for example because application now wants to close the serial port, it should
* call unblockBlockingIOOperation() passing the same context which is passed to this method.
*
* - If using blocking context, it is advised to catch SerialComException exception and check if
* this contain message SerialComManager.EXP_UNBLOCKIO. This message indicates that no error occurred
* while waiting/reading data from serial port but application has explicitly asked to return.
*
* To find if a framing or parity errors has happened while receiving data or not, call methods as
* appropriate on lineErr reference.
*
*
* @param handle of the port from which to read data bytes.
* @param buffer data byte buffer in which bytes from serial port will be saved.
* @param offset index in given byte array at which first data byte will be placed.
* @param length number of bytes to read into given buffer (0 <= length <= 2048).
* @param context context obtained by call to createBlockingIOContext method for blocking behavior
* or -1 for non-blocking behavior.
* @param lineErr instance of class SerialComLineErrors that will carry line error information or null
* if parity/framing errors should not be checked.
*
* @return number of bytes read from serial port.
* @throws SerialComException if an I/O error occurs.
* @throws NullPointerException if buffer
is null
.
* @throws IndexOutOfBoundsException if offset is negative, length is negative, or length is
* greater than buffer.length - offset.
*/
public int readBytes(long handle, byte[] buffer, int offset, int length, long context, SerialComLineErrors lineErr) throws SerialComException {
if(buffer == null) {
throw new NullPointerException("Null data buffer passed to read operation !");
}
if((offset < 0) || (length < 0) || (length > (buffer.length - offset))) {
throw new IndexOutOfBoundsException("Index violation detected in given byte array !");
}
if(length > 2048) {
throw new IllegalArgumentException("Argument length can not be greater than 2048 !");
}
if(length == 0) {
return 0;
}
int numberOfBytesRead = mComPortJNIBridge.readBytesP(handle, buffer, offset, length, context, lineErr);
if(numberOfBytesRead < 0) {
throw new SerialComException("Could not read data from serial port. Please retry !");
}
return numberOfBytesRead;
}
/**
* This method configures the rate at which communication will occur and the format of UART frame.
* This method must be called before configureComPortControl method.
*
*
* [1] Most of the DTE/DCE (hardware) does not support different baud rates for transmission and reception
* and therefore this method takes only single value applicable to both transmission and reception.
*
* - All serial devices/drivers/operating systems does not support all the baud rates (maximum change in signal
* per second), stop bits, data bits etc. Please consult hardware and software manuals as appropriate.
*
* If parity is enabled, the parity bit will be removed from UART frame before passing it to this library.
* All hardware/driver/operating systems does not support all parity. Please check your manual. Note that the datasheet
* of some uart devices does not mention that mark/space parity is supported, however they properly support it as
* mentioned in their register setting details. If a parity error is detected in received data, the error is not cleared
* until LSR/LCR registers are read by driver. So if parity detection is used application should enquire its status.
*
* - Some USB-UART devices supports non-standard baudrates. How to set these baudrate is device/driver and operating
* system specific.
*
*
* @param handle of opened port to which this configuration applies to.
* @param dataBits number of data bits in one frame (refer DATABITS enum in SerialComManager class for this).
* @param stopBits number of stop bits in one frame (refer STOPBITS enum in SerialComManager class for this).
* @param parity of the frame (refer PARITY enum in SerialComManager class for this).
* @param baudRate of the frame (refer BAUDRATE enum in SerialComManager class for this).
* @param custBaud custom baudrate if the desired rate is not included in BAUDRATE enum.
* @return true on success false otherwise.
* @throws SerialComException if invalid handle is passed or an error occurs in configuring the port.
* @throws IllegalArgumentException if dataBits or stopBits or parity or baudRate is null, or if custBaud is zero or negative.
*/
public boolean configureComPortData(long handle, DATABITS dataBits, STOPBITS stopBits, PARITY parity, BAUDRATE baudRate, int custBaud) throws SerialComException {
int baudRateTranslated = 0;
int custBaudTranslated = 0;
int baudRateGiven = 0;
if(dataBits == null) {
throw new IllegalArgumentException("Argument dataBits can not be null !");
}
if(stopBits == null) {
throw new IllegalArgumentException("Argument stopBits can not be null !");
}
if(parity == null) {
throw new IllegalArgumentException("Argument parity can not be null !");
}
if(baudRate == null) {
throw new IllegalArgumentException("Argument baudRate can not be null !");
}
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
baudRateGiven = baudRate.getValue();
if(baudRateGiven != 251) {
baudRateTranslated = baudRateGiven;
custBaudTranslated = 0;
}else {
// custom baud rate
if(custBaud <= 0) {
throw new IllegalArgumentException("Argument baudRate can not be negative or zero !");
}
baudRateTranslated = baudRateGiven;
custBaudTranslated = custBaud;
}
int ret = mComPortJNIBridge.configureComPortData(handle, dataBits.getValue(), stopBits.getValue(), parity.getValue(), baudRateTranslated, custBaudTranslated);
if(ret < 0) {
/* extra check */
throw new SerialComException("Could not configure the serial port. Please retry !");
}
return true;
}
/**
* This method configures the way data communication will be controlled between DTE and DCE.
* This specifies flow control and actions that will be taken when an error is encountered in
* communication.
*
*
* - Some serial devices does not support some flow controls scheme. Please refer to their manuals.
*
* It is advisable not to use same XON and XOFF character as opertaing system framework or driver
* may check for special characters one after the other and will process them. For example while processing
* data received at serial port, if driver sees XON it will instruct device to start transmission. But when
* moving further in the processing function, it checks for XOFF and sees that XOFF character has been received
* and therefore it will stop the transmission.
*
*
* @param handle of opened port to which need to be configured.
* @param flowctrl flow control, how data flow will be controlled (refer FLOWCONTROL enum for this).
* @param xon character representing on condition if software flow control is used.
* @param xoff character representing off condition if software flow control is used.
* @param ParFraError true if parity and frame errors are to be checked false otherwise.
* @param overFlowErr true if overflow error is to be detected false otherwise.
* @return true on success false otherwise.
* @throws SerialComException if invalid handle is passed or an error occurs in configuring the port.
* @throws IllegalArgumentException if flowctrl is null.
*/
public boolean configureComPortControl(long handle, FLOWCONTROL flowctrl, char xon, char xoff, boolean ParFraError, boolean overFlowErr) throws SerialComException {
if(flowctrl == null) {
throw new IllegalArgumentException("Argument flowctrl can not be null !");
}
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
int xonCh = (int) xon;
int xoffCh = (int) xoff;
int ret = mComPortJNIBridge.configureComPortControl(handle, flowctrl.getValue(), ((byte) xonCh), ((byte) xoffCh), ParFraError, overFlowErr);
if(ret < 0) {
/* extra check */
throw new SerialComException("Could not configure serial port. Please retry !");
}
return true;
}
/**
* This method gives currently applicable settings associated with particular serial port.
* The values are bit mask so that application can manipulate them to get required information.
*
* For Unix-like OS the order is : c_iflag, c_oflag, c_cflag, c_lflag, c_line, c_cc[0], c_cc[1], c_cc[2], c_cc[3]
* c_cc[4], c_cc[5], c_cc[6], c_cc[7], c_cc[8], c_cc[9], c_cc[10], c_cc[11], c_cc[12], c_cc[13], c_cc[14],
* c_cc[15], c_cc[16], c_ispeed and c_ospeed.
*
* For Windows OS the order is :DCBlength, BaudRate, fBinary, fParity, fOutxCtsFlow, fOutxDsrFlow, fDtrControl,
* fDsrSensitivity, fTXContinueOnXoff, fOutX, fInX, fErrorChar, fNull, fRtsControl, fAbortOnError, fDummy2,
* wReserved, XonLim, XoffLim, ByteSize, Parity, StopBits, XonChar, XoffChar, ErrorChar, StopBits, EvtChar,
* wReserved1.
*
* @param handle of the opened port.
* @return array of string giving configuration.
* @throws SerialComException if invalid handle is passed or an error occurs while reading current settings.
*/
public String[] getCurrentConfiguration(long handle) throws SerialComException {
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
if(osType != SerialComPlatform.OS_WINDOWS) {
// for unix-like os
int[] config = mComPortJNIBridge.getCurrentConfigurationU(handle);
String[] configuration = new String[config.length];
if(config[0] < 0) {
throw new SerialComException("Could not determine current configuration. Please retry !");
}
// if an error occurs, config[0] will contain error code, otherwise actual data
for(int x=0; xThis method assert/de-assert RTS line of serial port. Set "true" for asserting signal,
* false otherwise. This changes the state of RTS line electrically.
*
*
* - RTS and DTR lines can be asserted or de-asserted even when using no flow control on
* serial port.
*
* The RS-232 standard defines the voltage levels that correspond to logical one and logical
* zero levels for the data transmission and the control signal lines. Valid signals are either
* in the range of +3 to +15 volts or the range −3 to −15 volts with respect to the ground/common
* pin; consequently, the range between −3 to +3 volts is not a valid RS-232 level.
*
* - In asserted condition, voltage at pin number 7 (RTS signal) will be greater than 3 volts.
* Voltage 5.0 volts was observed when using USB-UART converter :
* http://www.amazon.in/Bafo-USB-Serial-Converter-DB9/dp/B002SCRCDG.
*
* On some hardware IC, signals may be active low and therefore for actual voltage datasheet
* should be consulted. Also please check if the driver supports setting RTS/DTR lines or not.
*
*
* @param handle of the opened port.
* @param enabled if true RTS will be asserted and vice-versa.
* @return true on success.
* @throws SerialComException if system is unable to complete requested operation.
*/
public boolean setRTS(long handle, boolean enabled) throws SerialComException {
int ret = mComPortJNIBridge.setRTS(handle, enabled);
if(ret < 0) {
throw new SerialComException("Could not set RTS line to desired state. Please retry !");
}
return true;
}
/**
* This method assert/de-assert DTR line of serial port. Set "true" for asserting signal, false
* otherwise. This changes the state of RTS line electrically.
*
*
* - RTS and DTR lines can be asserted or de-asserted even when a serial port is configured as
* 'flow control none'.
*
* It is possible to establish PPP connections to transmit binary data over a two (or more) wire
* interface with full handshaking and modem control signaling if the driver is configured for this.
* Refer application note from FTDI for details : AN232B-09 Using the Modem Emulation Mode in FTDI's
* VCP Driver.
*
* - If the DTR/DSR line is not used DTR can be connected to DSR locally like a loop back connection.
* The vendor written firmware treats change in DSR line as hardware interrupt and executes interrupt
* service routine. The application firmware can lower/raise the DTR line whenever needed.
*
*
* @param handle of the opened port.
* @param enabled if true DTR will be asserted and vice-versa.
* @return true on success.
* @throws SerialComException if system is unable to complete requested operation.
*/
public boolean setDTR(long handle, boolean enabled) throws SerialComException {
int ret = mComPortJNIBridge.setDTR(handle, enabled);
if(ret < 0) {
throw new SerialComException("Could not set DTR line to desired state. Please retry !");
}
return true;
}
/**
* This method associate a data looper with the given listener. This looper will keep delivering new data whenever
* it is made available from native data collection and dispatching subsystem.
* Note that listener will start receiving new data, even before this method returns.
*
* Application (listener) should implement ISerialComDataListener and override onNewSerialDataAvailable method.
*
* The SerialPundit can manage upto 1024 listeners corresponding to 1024 port handles. Application should not register
* data listener more than once for the same port otherwise it will lead to inconsistent state.
* This method is thread safe.
*
* @param handle of the serial port for which given listener will listen for availability of data bytes.
* @param dataListener instance of class which implements ISerialComDataListener interface.
* @return true on success false otherwise.
* @throws SerialComException if invalid handle passed, handle is null or data listener already exist for this handle.
* @throws IllegalArgumentException if dataListener is null.
*/
public boolean registerDataListener(long handle, final ISerialComDataListener dataListener) throws SerialComException {
SerialComPortHandleInfo handleInfo = null;
if(dataListener == null) {
throw new IllegalArgumentException("Argument dataListener can not be null !");
}
synchronized(lockB) {
handleInfo = mPortHandleInfo.get(handle);
if(handleInfo == null) {
throw new SerialComException("Given handle is alien to me !");
}
if(handleInfo.getDataListener() != null) {
throw new SerialComException("Data listener already exist for this handle. A handle can have only one data listener !");
}
return mEventCompletionDispatcher.setUpDataLooper(handle, handleInfo, dataListener);
}
}
/**
* This method destroys complete java and native looper subsystem associated with this particular data listener. This has no
* effect on event looper subsystem. This method returns only after native thread has been terminated successfully.
*
* This method is thread safe.
*
* @param handle handle of the serial port for which this data listener was registered.
* @param dataListener instance of class which implemented ISerialComDataListener interface.
* @return true on success false otherwise.
* @throws SerialComException if null value is passed in dataListener field.
* @throws IllegalArgumentException if dataListener is null.
*/
public boolean unregisterDataListener(long handle, final ISerialComDataListener dataListener) throws SerialComException {
SerialComPortHandleInfo handleInfo = null;
if(dataListener == null) {
throw new IllegalArgumentException("Argument dataListener can not be null !");
}
synchronized(lockB) {
handleInfo = mPortHandleInfo.get(handle);
if(handleInfo == null) {
throw new SerialComException("Given handle is alien to me !");
}
if(mEventCompletionDispatcher.destroyDataLooper(handle, handleInfo, dataListener)) {
return true;
}
}
return false;
}
/**
* This method associate a event looper with the given listener. This looper will keep delivering new event whenever
* it is made available from native event collection and dispatching subsystem.
*
* Application (listener) should implement ISerialComEventListener and override onNewSerialEvent method.
*
* By default all four events are dispatched to listener. However, application can mask events through setEventsMask()
* method. In current implementation, native code sends all the events irrespective of mask and we actually filter
* them in java layers, to decide whether this should be sent to application or not (as per the mask set by
* setEventsMask() method).
*
* Before calling this method, make sure that port has been configured for hardware flow control using configureComPortControl
* method. Application should not register event listener more than once for the same port otherwise it will lead to inconsistent
* state.
*
* This method is thread safe.
*
* @param handle of the port opened.
* @param eventListener instance of class which implements ISerialComEventListener interface.
* @return true on success false otherwise.
* @throws SerialComException if invalid handle passed, handle is null or event listener already exist for this handle.
* @throws IllegalArgumentException if eventListener is null.
*/
public boolean registerLineEventListener(long handle, final ISerialComEventListener eventListener) throws SerialComException {
SerialComPortHandleInfo handleInfo = null;
if(eventListener == null) {
throw new IllegalArgumentException("Argument eventListener can not be null !");
}
synchronized(lockB) {
handleInfo = mPortHandleInfo.get(handle);
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
if(handleInfo.getEventListener() != null) {
throw new SerialComException("Event listener already exist for this handle. A handle can have only one event listener !");
}
return mEventCompletionDispatcher.setUpEventLooper(handle, handleInfo, eventListener);
}
}
/**
* This method destroys complete java and native looper subsystem associated with this particular event listener. This has no
* effect on data looper subsystem.
*
* This method is thread safe.
*
* @param handle handle for which this listener was registered.
* @param eventListener instance of class which implemented ISerialComEventListener interface.
* @return true on success false otherwise.
* @throws SerialComException if an error occurs.
* @throws IllegalArgumentException if eventListener is null.
*/
public boolean unregisterLineEventListener(long handle, final ISerialComEventListener eventListener) throws SerialComException {
SerialComPortHandleInfo handleInfo = null;
if(eventListener == null) {
throw new IllegalArgumentException("Argument eventListener can not be null !");
}
synchronized(lockB) {
handleInfo = mPortHandleInfo.get(handle);
if(handleInfo == null) {
throw new SerialComException("Given handle is alien to me !");
}
if(mEventCompletionDispatcher.destroyEventLooper(handle, handleInfo, eventListener)) {
return true;
}
}
return false;
}
/**
* This pauses delivering events to application. The events kept accumulating in queue.
*
* @param eventListener instance of class which implemented ISerialComEventListener interface.
* @return true on success false otherwise.
* @throws SerialComException if null is passed for eventListener field.
* @throws IllegalArgumentException if eventListener is null.
*/
public boolean pauseListeningEvents(final ISerialComEventListener eventListener) throws SerialComException {
if(eventListener == null) {
throw new IllegalArgumentException("Argument eventListener can not be null !");
}
if(mEventCompletionDispatcher.pauseListeningEvents(eventListener)) {
return true;
}
return false;
}
/**
* Resume delivering events kept in queue to application.
*
* @param eventListener is an instance of class which implements ISerialComEventListener.
* @return true on success false otherwise.
* @throws SerialComException if error occurs.
* @throws IllegalArgumentException if eventListener is null.
*/
public boolean resumeListeningEvents(final ISerialComEventListener eventListener) throws SerialComException {
if(eventListener == null) {
throw new IllegalArgumentException("Argument eventListener can not be null !");
}
if(mEventCompletionDispatcher.resumeListeningEvents(eventListener)) {
return true;
}
return false;
}
/**
* This method gives more fine tune control to application for tuning performance and behavior of read
* operations to leverage OS specific facility for read operation. The read operations can be optimized for
* receiving for example high volume data speedily or low volume data but received in burst mode.
*
* If more than one client has opened the same port, then all the clients will be affected by new
* settings. When this method is called application should make sure that previous read or write operation
* is not in progress.
*
* Under multithreading scenarios, if thread is blocked on read method due to timeout configured using
* this method and other thread tries to close port, the read method may cause the the thread which called
* read methid not to return. It is therefore either timeout should be kept short or two threads must be
* synchronized etc.
*
* [1] Time out for read call for unix like OS can be set using vtime. Use formula vtime = time out in
* milliseconds / 100. For example to wait for 100 milliseconds, vtime = 1 and vmin = 0.
*
* @param handle of the opened port.
* @param vmin c_cc[VMIN] field of termios structure (applicable for unix like OS only).
* @param vtime c_cc[VTIME] field of termios structure (10th of a second, applicable for unix like OS only).
* @param rit ReadIntervalTimeout field of COMMTIMEOUTS structure (applicable for windows OS only).
* @param rttm ReadTotalTimeoutMultiplier field of COMMTIMEOUTS structure (applicable for windows OS only).
* @param rttc ReadTotalTimeoutConstant field of COMMTIMEOUTS structure (applicable for windows OS only).
* @return true on success false otherwise.
* @throws SerialComException if wrong handle is passed or operation can not be done successfully.
* @throws IllegalArgumentException if invalid combination of arguments is passed.
*/
public boolean fineTuneReadBehaviour(long handle, int vmin, int vtime, int rit, int rttm, int rttc) throws SerialComException {
int ret = 0;
if(osType == SerialComPlatform.OS_WINDOWS) {
if((rit < 0) || (rttm < 0) || (rttc < 0)) {
throw new IllegalArgumentException("Argument(s) rit, rttm and rttc can not be neagative !");
}
}else {
if((vmin == 0) && (vtime == 0)) {
throw new IllegalArgumentException("Both vmin and vtime can not be zero !");
}
if((vmin < 0) || (vtime < 0)) {
throw new IllegalArgumentException("The vmin and vtime can not be negative !");
}
}
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
ret = mComPortJNIBridge.fineTuneRead(handle, vmin, vtime, rit, rttm, rttc);
if(ret < 0) {
throw new SerialComException("Could not set the given parameters. Please retry !");
}
return true;
}
/**
* Defines for which line events registered event listener will be called.
*
* In future we may shift modifying mask in the native code itself, so as to prevent JNI transitions.
* This filters what events should be sent to application. Note that, although we sent only those event
* for which user has set mask, however native code send all the events to java layer as of now.
*
* @param eventListener instance of class which implemented ISerialComEventListener interface.
* @return true on success false otherwise.
* @throws SerialComException if invalid listener is passed.
* @throws IllegalArgumentException if eventListener is null.
*/
public boolean setEventsMask(final ISerialComEventListener eventListener, int newMask) throws SerialComException {
SerialComPortHandleInfo handleInfo = null;
SerialComLooper looper = null;
ISerialComEventListener mEventListener = null;
if(eventListener == null) {
throw new IllegalArgumentException("Argument eventListener can not be null !");
}
for (Map.Entry entry : mPortHandleInfo.entrySet()) {
handleInfo = entry.getValue();
if(handleInfo != null) {
if(handleInfo.containsEventListener(eventListener)) {
looper = handleInfo.getLooper();
mEventListener = handleInfo.getEventListener();
break;
}
}
}
if(looper != null && mEventListener != null) {
looper.setEventsMask(newMask);
return true;
}else {
throw new SerialComException("This listener is not registered !");
}
}
/**
* This method return currently applicable mask for events on serial port.
*
* @param eventListener instance of class which implemented ISerialComEventListener interface.
* @return an integer containing bit fields representing mask.
* @throws SerialComException if null or wrong listener is passed.
* @throws IllegalArgumentException if eventListener is null.
*/
public int getEventsMask(final ISerialComEventListener eventListener) throws SerialComException {
SerialComPortHandleInfo handleInfo = null;
SerialComLooper looper = null;
ISerialComEventListener mEventListener = null;
if(eventListener == null) {
throw new IllegalArgumentException("Argument eventListener can not be null !");
}
for (Map.Entry entry : mPortHandleInfo.entrySet()) {
handleInfo = entry.getValue();
if(handleInfo != null) {
if(handleInfo.containsEventListener(eventListener)) {
looper = handleInfo.getLooper();
mEventListener = handleInfo.getEventListener();
break;
}
}
}
if(looper != null && mEventListener != null) {
return looper.getEventsMask();
}else {
throw new SerialComException("This listener is not registered !");
}
}
/**
* Discards data sent to port but not transmitted, or data received but not read. Some device/OS/driver might
* not have support for this, but most of them may have.
* If there is some data to be pending for transmission, it will be discarded and therefore no longer sent.
* If the application wants to make sure that all data has been transmitted before discarding anything, it must
* first flush data and then call this method.
*
* @param handle of the opened port.
* @param clearRxBuffer if true receive buffer will be cleared otherwise will be left untouched.
* @param clearTxBuffer if true transmit buffer will be cleared otherwise will be left untouched.
* @return true on success.
* @throws SerialComException if invalid handle is passed or operation can not be completed successfully.
* @throws IllegalArgumentException if both purgeTxBuffer and purgeRxBuffer are false.
*/
public boolean clearPortIOBuffers(long handle, boolean clearRxBuffer, boolean clearTxBuffer) throws SerialComException {
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
if((clearRxBuffer == false) && (clearTxBuffer == false)) {
throw new IllegalArgumentException("Both arguments clearRxBuffer and clearTxBuffer can not be false !");
}
int ret = mComPortJNIBridge.clearPortIOBuffers(handle, clearRxBuffer, clearTxBuffer);
if(ret < 0) {
throw new SerialComException("Could not clear serial port buffers. Please retry !");
}
return true;
}
/**
* Assert a break condition on the specified port for the duration expressed in milliseconds.
* If the line is held in the logic low condition (space in UART jargon) for longer than a character
* time, this is a break condition that can be detected by the UART.
*
*
*
* - A "break condition" occurs when the receiver input is at the "space" level for longer than some duration
* of time, typically, for more than a character time. This is not necessarily an error, but appears to the
* receiver as a character of all zero bits with a framing error. The term "break" derives from current loop
* Signaling, which was the traditional signaling used for tele-typewriters. The "spacing" condition of a
* current loop line is indicated by no current flowing, and a very long period of no current flowing is often
* caused by a break or other fault in the line.
*
* Recognizing break condition on line is the responsibility of the UART IC, but if for some reason (such as a
* limited UART that does not implement this functionality) the UART fails to do so, reception of a break will
* manifest itself as a large number of framing errors. Also sometimes default drivers built into operating system
* may not provide break detection facility.
*
*
* - If the duration parameter is 0, it will result in fastest way this library can set and unset break
* condition.
*
* All UART devices (or driver) may not support all break timings. For example CP2105 can set break for from
* 1 to 125 ms or for infinite time. Developers should consult data sheet to know device capabilities. Sometimes
* hardware may also report break as all 0 bits byte data.
*
* - In some application designs break signals can be used for packet synchronization. For example; presence
* of break indicate start of packet or end of packet. If the host processor is in sleep mode, some UART hardware
* may wake the host processor when they detect a break condition or start bit.
*
* Dedicated ICs like ELM627 can be used to detect break condition and toggle GPIO lines which may in
* turn be connected to reset pin of micro-controller. Such schemes are used where the hardware can not detect break
* condition on line.
*
*
* @param handle of the opened serial port.
* @param duration the time in milliseconds for which break will be active.
* @return true on success.
* @throws SerialComException if invalid handle is passed or operation can not be successfully completed.
* @throws IllegalArgumentException if duration is negative.
*/
public boolean sendBreak(long handle, int duration) throws SerialComException {
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
if(duration < 0) {
throw new IllegalArgumentException("Argument duration can not be negative !");
}
int ret = mComPortJNIBridge.sendBreak(handle, duration);
if(ret < 0) {
throw new SerialComException("Could not set the break condition for given duration !");
}
return true;
}
/**
* This method gives the number of interrupts on serial line that have occurred. The interrupt count is in following
* order in array beginning from index 0 and ending at index 11 :
* CTS, DSR, RING, CARRIER DETECT, RECEIVER BUFFER, TRANSMIT BUFFER, FRAME ERROR, OVERRUN ERROR, PARITY ERROR,
* BREAK AND BUFFER OVERRUN.
*
* This is applicable for Linux onle. For other operating systems, this will return 0 for all the indexes.
*
* @param handle of the port opened on which interrupts might have occurred.
* @return array of integers containing values corresponding to each interrupt source.
* @throws SerialComException if invalid handle is passed or operation can not be performed successfully.
*/
public int[] getInterruptCount(long handle) throws SerialComException {
int[] interruptsCount;
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
interruptsCount = mComPortJNIBridge.getInterruptCount(handle);
if(interruptsCount == null) {
throw new SerialComException("Unknown error occurred !");
}
return interruptsCount;
}
/**
* Gives status of serial port's control lines as supported by underlying operating system.
* The sequence of status in returned array is :
*
* Linux OS : CTS, DSR, DCD, RI, LOOP, RTS, DTR respectively.
* MAC OS X : CTS, DSR, DCD, RI, 0, RTS, DTR respectively.
* Windows OS : CTS, DSR, DCD, RI, 0, 0, 0 respectively.
*
* @param handle of the port whose status is to be read.
* @return status of UART port control lines.
* @throws SerialComException if invalid handle is passed or operation can not be completed successfully.
*/
public int[] getLinesStatus(long handle) throws SerialComException {
int[] status = null;
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
status = mComPortJNIBridge.getLinesStatus(handle);
if(status == null) {
throw new SerialComException("Failed to get line status for the given handle. Please retry !");
}
return status;
}
/**
* Gives the name of the driver who is driving the given serial port. The use case scenarios are:
*
*
* - Suppose exactly same two temperature sensors are connected to system via usb-uart converters. For
* example 1st sensor senses ambient temperature and is connected to computer via FT232. Second temperature
* sensor is connected via CP2102 to computer. Now when ft232 and cp2102 is plugged into computer we do not know
* which /dev/ttyUSBxx belong to which sensor (usb-uart). In this case use this API to find which device node
* belongs to which converter. For cp2102 driver will be cp210x where as for ft232 it will be ftdi_sio.
*
* If exactly same usb-uart devices are used to connected two sensors then use findComPortFromUSBAttributes
* API in USB module giving unique serial numbers of ft232. This will tell which device node belongs to which
* sensor.
*
* - It can be used where legacy serial ports have to be found. For example in Windows suppose there is a permanent
* RS232 port on mother board and a FT232 USB-UART is also attached. Let us say FT232 is COM1 and the one on mother
* board is COM2. Now calling this method by passing COM1 and then COM2 can easily tell that COM2 is the port we need.
*
* This can also be used where two end products based on same usb-uart converter are connected to the compueter
* and they use different drivers.
*
*
* @param comPortName name only for windows (for ex; COM52), full path for unix-like os (for ex; /dev/ttyUSB0).
* @return name of driver serving given serial port.
* @throws SerialComException if operation can not be completed successfully.
* @throws IllegalArgumentException if argument comPortName is null or is an empty string.
*/
public String findDriverServingComPort(String comPortName) throws SerialComException {
if(comPortName == null) {
throw new IllegalArgumentException("Argument comPortName can not be null !");
}
if(comPortName.length() == 0) {
throw new IllegalArgumentException("Argument comPortName can not be empty string !");
}
if(comPortName.length() > 256) {
// linux have 256 as maximum length of file name.
throw new IllegalArgumentException("Argument comPortName string can not be greater than 256 in length !");
}
String driverName = mComPortJNIBridge.findDriverServingComPort(comPortName);
if(driverName == null) {
throw new SerialComException("Failed to find driver serving the given serial port. Please retry !");
}
return driverName;
}
/**
* Gives the address and IRQ number associated with the given serial port. Applicable manily for serial port
* built into the mother board itself.
*
* @param handle handle of the opened serial port.
* @return string containing address and irq number in hexadecimal represenation.
* @throws SerialComException if operation can not be completed successfully.
*/
public String findIRQnumberForComPort(long handle) throws SerialComException {
String addressAndIRQ = mComPortJNIBridge.findIRQnumberForComPort(handle);
if(addressAndIRQ == null) {
throw new SerialComException("Failed to find IRQ and address for the given serial port. Please retry !");
}
return addressAndIRQ;
}
/**
* Get number of bytes in input and output port buffers used by operating system for instance tty buffers
* in Unix like systems. Sequence of data in array is : Input buffer byte count, Output buffer byte count.
*
* It should be noted that some chipset specially USB to UART converters might have FIFO buffers in chipset
* itself. For example FT232R has internal buffers controlled by FIFO CONTROLLERS. For this reason this method
* should be tested carefully if application is using USB-UART converters. This is driver and OS specific scenario.
*
* @param handle of the opened port for which counts need to be determined.
* @return array containing number of bytes in input and output buffer.
* @throws SerialComException if invalid handle is passed or operation can not be completed successfully.
*/
public int[] getByteCountInPortIOBuffer(long handle) throws SerialComException {
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
int[] numBytesInfo = mComPortJNIBridge.getByteCount(handle);
if(numBytesInfo == null) {
throw new SerialComException("Could not determine number of bytes in buffer. Please retry !");
}
return numBytesInfo;
}
/**
* This method gives the port name with which given handle is associated. If the given handle is
* unknown to this library, null is returned. A serial port is known to this if it was opened using
* SerialComManager's openComPort() method.
*
* @param handle for which the port name is to be found.
* @return port name corresponding to the given handle.
* @throws SerialComException if invalid handle is passed.
*/
public String getPortName(long handle) throws SerialComException {
SerialComPortHandleInfo handleInfo = null;
handleInfo = mPortHandleInfo.get(handle);
if(handleInfo == null) {
throw new SerialComException("Given handle is alien to me !");
}
return handleInfo.getOpenedPortName();
}
/**
* Send given file(s) using specified file transfer protocol.
*
* For Xmodem trasnfer the element at 0th index of fileToSend array must represent a regular file.
* If the length of fileToSend array is more than one, only 1st file will be sent.
*
* For Ymodem transfer, the fileToSend array should contain all the files to be transffered to
* receiver end where all the elements in fileToSend array represent regular files.
*
* Xmodem protocol is widely used for flashing executable images in microcontroller via UART. For
* example the secondary bootloader in LPC2000 can update the user application code in on chip flash
* via UART with 1K XMODEM protocol. Xmodem protocol is also used for taling to network routers.
*
* If you want to make user defined firmware, consider creating an IDE where end user can place widget,
* define layout etc. graphically and then when he clicks on save button a firmware hex file gets generated.
* This file is then flashed into end product as firmware.
*
* @param handle of the port on which file is to be sent.
* @param fileToSend File instance representing file to be sent.
* @param ftpProto file transfer protocol to use for communication over serial port.
* @param ftpVariant variant of file transfer protocol to use.
* @param textMode if true file will be sent as text file (ASCII mode), if false file will be sent as
* binary file. The text file must contain only valid ASCII characters.
* @param progressListener object of class which implements ISerialComXmodemProgress interface and is
* interested in knowing how many blocks have been sent to file receiver till now. If progressListener
* is null, update will not be delivered to application.
* @param transferState if application wish to abort sending file at instant of time due to any reason, it can
* call abortTransfer method on this object. If the application does not wishes to abort sending file
* explicitly transferState can be null.
* @return true on success, false if application instructed to abort.
* @throws SerialComException if invalid handle is passed, if receiver sent abort command.
* @throws SecurityException If a security manager exists and its SecurityManager.checkRead(java.lang.String)
* method denies read access to the file.
* @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or
* for some other reason cannot be opened for reading.
* @throws SerialComTimeOutException if timeout occurs as per file transfer protocol.
* @throws IOException if error occurs while reading data from file to be sent.
* @throws IllegalArgumentException if fileToSend or ftpProto or ftpVariant or ftpMode argument is null.
*/
public boolean sendFile(long handle, final File[] fileToSend, FTPPROTO ftpProto, FTPVAR ftpVariant,
boolean textMode, ISerialComFTPProgress progressListener, SerialComFTPCMDAbort transferState) throws IOException {
if((fileToSend == null) || (fileToSend.length == 0)) {
throw new IllegalArgumentException("Argument fileToSend can not be null or of zero length !");
}
if(ftpProto == null) {
throw new IllegalArgumentException("Argument ftpProto can not be null !");
}
if(ftpVariant == null) {
throw new IllegalArgumentException("Argument ftpVariant can not be null !");
}
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
int protocol = ftpProto.getValue();
int variant = ftpVariant.getValue();
if(protocol == 1) {
if(!fileToSend[0].isFile()) {
throw new IllegalArgumentException("For Xmodem fileToSend[0] must be an existing regular file !");
}
if(progressListener != null) {
if(!(progressListener instanceof ISerialComXmodemProgress)) {
throw new IllegalArgumentException("Implement ISerialComXmodemProgress for non-null progressListener !");
}
}
switch(variant) {
case 1:
SerialComXModem xmodem = new SerialComXModem(this, handle, fileToSend[0], textMode, (ISerialComXmodemProgress)progressListener, transferState, osType);
return xmodem.sendFileX();
case 2:
SerialComXModemCRC xmodemc = new SerialComXModemCRC(this, handle, fileToSend[0], textMode, (ISerialComXmodemProgress)progressListener, transferState, osType);
return xmodemc.sendFileX();
case 3:
SerialComXModem1K xmodemk = new SerialComXModem1K(this, handle, fileToSend[0], textMode, (ISerialComXmodemProgress)progressListener, transferState, osType);
return xmodemk.sendFileX();
default:
throw new IllegalArgumentException("This variant is not applicable for Xmodem transfer !");
}
}
else if(protocol == 2) {
switch(variant) {
case 2:
SerialComYModemCRC ymodemc = new SerialComYModemCRC(this, handle, fileToSend, textMode, (ISerialComYmodemProgress)progressListener, transferState, osType);
return ymodemc.sendFileY();
case 3:
SerialComYModem1K ymodemk = new SerialComYModem1K(this, handle, fileToSend, textMode, (ISerialComYmodemProgress)progressListener, transferState, osType);
return ymodemk.sendFileY();
case 4:
SerialComYModemG ymodemg = new SerialComYModemG(this, handle, fileToSend, textMode, (ISerialComYmodemProgress)progressListener, transferState, osType);
return ymodemg.sendFileY();
default:
throw new IllegalArgumentException("This variant is not applicable for Ymodem transfer !");
}
}
else if(protocol == 3) {
}
else {
}
return false;
}
/**
* Receives file using specified file transfer protocol.
*
* For Xmodem transfer the fileToReceive must represent a regular file. For Ymodem transfer fileToReceive
* must represent a directory in which received files will be saved.
*
* @param handle of the serial port on which file is to be sent.
* @param fileToReceive File instance representing file/folder to be sent.
* @param ftpProto file transfer protocol (FTPPROTO_XXX) to use for communication over serial port.
* @param ftpVariant variant of file transfer protocol (FTPVAR_XXX) to use.
* @param textMode if true file will be received as text file (ASCII mode), if false file will be received
* as binary file.
* @param progressListener object of class which implements ISerialComXmodemProgress interface and is interested
* in knowing how many blocks have been received from file sender till now. If progressListener is null,
* update will not be delivered to application.
* @param transferState if application wish to abort receiving file at instant of time due to any reason, it can
* call abortTransfer method on this object. If the application does not wishes to abort receiving file
* explicitly transferState can be null.
* @return true on success, false if application instructed to abort.
* @throws SerialComException if invalid handle is passed, if sender sent abort command.
* @throws SecurityException If a security manager exists and its SecurityManager.checkRead(java.lang.String) method
* denies read access to the file.
* @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some
* other reason cannot be opened for reading.
* @throws SerialComTimeOutException if timeout occurs as per file transfer protocol.
* @throws IOException if error occurs while reading data from file to be sent.
* @throws IllegalArgumentException if fileToReceive or ftpProto or ftpVariant or ftpMode argument is null.
*/
public boolean receiveFile(long handle, final File fileToReceive, FTPPROTO ftpProto, FTPVAR ftpVariant,
boolean textMode, ISerialComFTPProgress progressListener, SerialComFTPCMDAbort transferState) throws IOException {
if(fileToReceive == null) {
throw new IllegalArgumentException("Argument fileToReceive can not be null !");
}
if(ftpProto == null) {
throw new IllegalArgumentException("Argument ftpProto can not be null !");
}
if(ftpVariant == null) {
throw new IllegalArgumentException("Argument ftpVariant can not be null !");
}
if(mPortHandleInfo.get(handle) == null) {
throw new SerialComException("Given handle is alien to me !");
}
int protocol = ftpProto.getValue();
int variant = ftpVariant.getValue();
if(protocol == 1) {
if(!fileToReceive.isFile()) {
throw new IllegalArgumentException("The fileToReceive must be an existing regular file for Xmodem transfer !");
}
if(progressListener != null) {
if(!(progressListener instanceof ISerialComXmodemProgress)) {
throw new IllegalArgumentException("Implement ISerialComXmodemProgress for non-null progressListener !");
}
}
switch(variant) {
case 1:
SerialComXModem xmodem = new SerialComXModem(this, handle, fileToReceive, textMode, (ISerialComXmodemProgress)progressListener, transferState, osType);
return xmodem.receiveFileX();
case 2:
SerialComXModemCRC xmodemc = new SerialComXModemCRC(this, handle, fileToReceive, textMode, (ISerialComXmodemProgress)progressListener, transferState, osType);
return xmodemc.receiveFileX();
case 3:
SerialComXModem1K xmodemk = new SerialComXModem1K(this, handle, fileToReceive, textMode, (ISerialComXmodemProgress)progressListener, transferState, osType);
return xmodemk.receiveFileX();
default:
throw new IllegalArgumentException("This variant is not applicable for Xmodem transfer !");
}
}
else if(protocol == 2) {
if(!fileToReceive.isDirectory() || !fileToReceive.canWrite()) {
throw new IllegalArgumentException("The fileToReceive must be a writable directory for Ymodem transfer !");
}
switch(variant) {
case 2:
SerialComYModemCRC ymodemc = new SerialComYModemCRC(this, handle, fileToReceive, textMode, (ISerialComYmodemProgress)progressListener, transferState, osType);
return ymodemc.receiveFileY();
case 3:
SerialComYModem1K ymodemk = new SerialComYModem1K(this, handle, fileToReceive, textMode, (ISerialComYmodemProgress)progressListener, transferState, osType);
return ymodemk.receiveFileY();
case 4:
SerialComYModemG ymodemg = new SerialComYModemG(this, handle, fileToReceive, textMode, (ISerialComYmodemProgress)progressListener, transferState, osType);
return ymodemg.receiveFileY();
default:
throw new IllegalArgumentException("This variant is not applicable for Ymodem transfer !");
}
}
else if(protocol == 3) {
}
else {
}
return false;
}
/**
* Factory method to create stream of type specified by streamType in blocking or non-blocking mode.
*
* If streamType is SerialComManager.OutputStream, an instance of class SerialComOutByteStream is returned.
* If streamType is SerialComManager.InputStream, an instance of class SerialComInByteStream is returned.
*
* If streamMode is SMODE.NONBLOCKING, read/write to serial port is non-blocking. If streamMode is SMODE.BLOCKING,
* read/write to serial port is blocking.
*
* @param streamType one of the values; SerialComManager.OutputStream or SerialComManager.InputStream.
* @param handle handle of the opened serial port which this stream will wrap internally.
* @param streamMode enum value SMODE.BLOCKING or SMODE.NONBLOCKING.
* @return instance of stream (SerialComInByteStream/SerialComOutByteStream) as per given streamType.
* @throws SerialComException if input stream already exist for this handle or invalid handle is passed.
* @throws IllegalArgumentException if streamMode is null or invalid streamType is passed.
*/
public ISerialIOStream getIOStreamInstance(int streamType, long handle, SMODE streamMode) throws SerialComException {
if(streamMode == null) {
throw new IllegalArgumentException("Argument streamMode can not be null !");
}
SerialComPortHandleInfo handleInfo = null;
handleInfo = mPortHandleInfo.get(handle);
if(handleInfo == null) {
throw new SerialComException("Given handle is alien to me !");
}
switch(streamType) {
case SerialComManager.InputStream :
SerialComInByteStream scis = null;
scis = handleInfo.getSerialComInByteStream();
if(scis == null) {
scis = new SerialComInByteStream(this, handleInfo, handle, streamMode);
handleInfo.setSerialComInByteStream(scis);
}else {
// if 2nd attempt is made to create already existing input stream, throw exception
throw new SerialComException("Input byte stream already exist for this handle !");
}
return scis;
case SerialComManager.OutputStream :
SerialComOutByteStream scos = handleInfo.getSerialComOutByteStream();
if(scos == null) {
scos = new SerialComOutByteStream(this, handleInfo, handle, streamMode);
handleInfo.setSerialComOutByteStream(scos);
}else {
// if 2nd attempt is made to create already existing output stream, throw exception
throw new SerialComException("Output byte stream already exist for this handle !");
}
return scos;
default :
throw new IllegalArgumentException("Argument streamType is invalid !");
}
}
/**
* Gives an instance of the class which implements API defined by vendor in their propriety library.
*
* For example; if vendorLibIdentifier is SerialComVendorLib.VLIB_FTDI_D2XX, an instance of SerialComFTDID2XX
* class is returned.
*
* @param vendorLibIdentifier one of the constant VLIB_XXXX_XXXX in SerialComVendorLib class.
* @param libDirectory absolute directory path where vendor library is placed.
* @param vlibName full name of the vendor library (for ex. libftd2xx.so.1.1.12).
* @return an object of SerialComVendorLib class on which vendor specific API calls can be made otherwise null.
* @throws IOException if java system properties can not be accessed, if invalid vendorLibIdentifier is passed.
* if native libraries are not found or can not be loaded/linked, if libDirectory does not exist, or is not
* a regular directory or is not writtable, If native library can not be initialized.
* @throws IllegalArgumentException if vlibName is null or empty string.
*/
public SerialComVendorLib getVendorLibFromFactory(int vendorLibIdentifier, String libDirectory, String vlibName) throws IOException {
File baseDir = new File(libDirectory.trim());
if(!baseDir.exists()) {
throw new SerialComException("The directory " + libDirectory + " does not exist !");
}
if(!baseDir.isDirectory()) {
throw new SerialComException("The location " + libDirectory + " is not a directory !");
}
if(!baseDir.canWrite()) {
throw new SerialComException("The directory " + libDirectory + " is not writeable (permissions ??) !");
}
if(vlibName == null) {
throw new IllegalArgumentException("Argument vlibName can not be null !");
}
if(vlibName.length() == 0) {
throw new IllegalArgumentException("Argument vlibName can not be empty string !");
}
if(mSerialComVendorLib != null) {
return mSerialComVendorLib.getVendorLibInstance(vendorLibIdentifier, baseDir, vlibName, cpuArch, osType, mSerialComSystemProperty);
}
mSerialComVendorLib = new SerialComVendorLib();
return mSerialComVendorLib.getVendorLibInstance(vendorLibIdentifier, baseDir, vlibName, cpuArch, osType, mSerialComSystemProperty);
}
/**
* Allocate, initialize and return an instance of SerialComPortMapper class on whom APIs can
* be called to map or unmap a serial port alias.
*
* This method will extract native library in directory as specified by directoryPath argument
* or default directory will be used if directoryPath is null. The native library loaded will be given
* name as specified by loadedLibName argument or default name will be used if loadedLibName is null.
*
* @param directoryPath absolute path of directory to be used for extraction.
* @param loadedLibName library name without extension (do not append .so, .dll or .dylib etc.).
* @return instance of SerialComPortMapper class on which various methods can be invoked.
* @throws IOException if file operations on "/proc/cpuinfo" fails for Linux on ARM platform, if java system
* properties can not be accessed, if file "/proc/cpuinfo" can not be found for Linux on ARM platform,
* if native libraries are not found or can not be loaded/linked. If appropriate files/directories can
* not be read or written, If native library can not be initialized.
* @throws IllegalArgumentException if directoryPath is null, directoryPath is empty, loadedLibName is null
* or empty.
*/
public SerialComPortMapper getSerialComPortMapperInstance(String directoryPath, String loadedLibName) throws SerialComException {
if(mSerialComPortMapperJNIBridge == null) {
mSerialComPortMapperJNIBridge = new SerialComPortMapperJNIBridge();
SerialComPortMapperJNIBridge.loadNativeLibrary(directoryPath, loadedLibName, mSerialComSystemProperty, osType, cpuArch, abiType);
}
return new SerialComPortMapper(mSerialComPortMapperJNIBridge);
}
/**
* Allocate, initialize and return an instance of SerialComDBRelease class on whom APIs can
* be called to release COM ports in use. This methed is applicable for Windows operating system only.
*
* The Windows operating system maintains a database of all COM ports. This database is typically supports
* com port numbers from 1(COM1) to 256(COM256). This database can get exhausted for example if more than 256
* USB-UART devices are connected one after the other in system. This generally happens in factory testing
* environment. Using the APIs in SerialComDBRelease class, test application can manage the port assignment and
* its release programatically making production testing faster and less cumbersome.
*
* This method will extract native library in directory as specified by directoryPath
* argument or default directory will be used if directoryPath is null. The native library
* loaded will be given name as specified by loadedLibName argument or default name will be
* used if loadedLibName is null.
*
* @param directoryPath absolute path of directory to be used for extraction.
* @param loadedLibName library name without extension (do not append .so, .dll or .dylib etc.).
* @return instance of SerialComDBRelease on which various methods can be invoked.
* @throws IOException if file operations on "/proc/cpuinfo" fails for Linux on ARM platform, if java system
* properties can not be accessed, if file "/proc/cpuinfo" can not be found for Linux on ARM platform,
* if native libraries are not found or can not be loaded/linked. If appropriate files/directories can
* not be read or written, If native library can not be initialized.
* @throws IllegalArgumentException if directoryPath is null, directoryPath is empty, loadedLibName is null
* or empty.
*/
public SerialComDBRelease getSerialComDBReleaseInstance(String directoryPath, String loadedLibName) throws IOException {
if(mSerialComDBReleaseJNIBridge == null) {
mSerialComDBReleaseJNIBridge = new SerialComDBReleaseJNIBridge();
SerialComDBReleaseJNIBridge.loadNativeLibrary(directoryPath, loadedLibName, mSerialComSystemProperty, osType, cpuArch, abiType);
}
return new SerialComDBRelease(mSerialComDBReleaseJNIBridge);
}
/**
* Provides an instance of SerialComNullModem class for managing virtual serial device, null modem,
* loop back and custom pinout connected virtual serial devices.
*
* @return an instance of SerialComNullModem class.
* @throws IOException if any error occurs while handling null modem driver specific files.
*/
public SerialComNullModem getSerialComNullModemInstance() throws IOException {
if(mSerialComNullModem == null) {
mSerialComNullModem = new SerialComNullModem(mComPortJNIBridge);
}
return mSerialComNullModem;
}
}