/*
* The MIT License
*
* Copyright 2011 Peter Kocsis <p. kocsis. 2. 7182 at gmail.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ftdi;
import com.sun.jna.Memory;
import com.sun.jna.ptr.ByteByReference;
import com.sun.jna.ptr.IntByReference;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Java class to communicate easily to a FTDI device.
* @author Peter Kocsis <p. kocsis. 2. 7182 at gmail.com>
*/
public class FTDevice {
static private final FTD2XX ftd2xx = FTD2XX.INSTANCE;
private final int devID, devLocationID, flag;
private final DeviceType devType;
private int ftHandle;
private final String devSerialNumber, devDescription;
private FTDeviceInputStream fTDeviceInputStream = null;
private FTDeviceOutputStream fTDeviceOutputStream = null;
private FTDevice(DeviceType devType, int devID, int devLocationID,
String devSerialNumber, String devDescription, int ftHandle,
int flag) {
this.devType = devType;
this.devID = devID;
this.devLocationID = devLocationID;
this.devSerialNumber = devSerialNumber;
this.devDescription = devDescription;
this.ftHandle = ftHandle;
this.flag = flag;
}
/**
* Get device description.
* @return device description
*/
public String getDevDescription() {
return devDescription;
}
/**
* Get device ID.
* @return device ID
*/
public int getDevID() {
return devID;
}
/**
* Get device serial number.
* @return device serial number
*/
public String getDevSerialNumber() {
return devSerialNumber;
}
/**
* Get device type.
* @return device type.
*/
public DeviceType getDevType() {
return devType;
}
/**
* Get device location.
* @return device location.
*/
public int getDevLocationID() {
return devLocationID;
}
/**
* Get device flag.
* @return flag.
*/
public int getFlag() {
return flag;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof FTDevice)) {
return false;
}
FTDevice eq = (FTDevice) obj;
return eq.ftHandle == this.ftHandle;
}
@Override
public int hashCode() {
int hash = 5;
hash = 97 * hash + this.ftHandle;
return hash;
}
@Override
public String toString() {
return "FTDevice{" + "devDescription=" + devDescription
+ ", devSerialNumber=" + devSerialNumber + '}';
}
private static void ensureFTStatus(int ftstatus) throws FTD2XXException {
if (!(ftstatus == FT_STATUS.OK.constant())) {
throw new FTD2XXException(ftstatus);
}
}
private static FTDevice getXthDevice(int Xth) throws FTD2XXException {
IntByReference flag = new IntByReference();
IntByReference devType = new IntByReference();
IntByReference devID = new IntByReference();
IntByReference locID = new IntByReference();
IntByReference ftHandle = new IntByReference();
Memory devSerNum = new Memory(16);
Memory devDesc = new Memory(64);
ensureFTStatus(ftd2xx.FT_GetDeviceInfoDetail(Xth, flag, devType, devID,
locID, devSerNum, devDesc, ftHandle));
return new FTDevice(DeviceType.values()[devType.getValue()],
devID.getValue(), locID.getValue(), devSerNum.getString(0),
devDesc.getString(0), ftHandle.getValue(), flag.getValue());
}
/**
* Get the connected FTDI devices. It will not contain opened devices.
* @return List contain available FTDI devices.
* @throws FTD2XXException If something goes wrong.
*/
public static List<FTDevice> getDevices() throws FTD2XXException {
return getDevices(false);
}
/**
* Get the connected FTDI devices.
* @param isIncludeOpenedDevices Would you like to see opened devices?
* @return List contain available FTDI devices.
* @throws FTD2XXException If something goes wrong.
*/
public static List<FTDevice> getDevices(boolean isIncludeOpenedDevices)
throws FTD2XXException {
IntByReference devNum = new IntByReference();
ensureFTStatus(ftd2xx.FT_CreateDeviceInfoList(devNum));
ArrayList<FTDevice> devs = new ArrayList<FTDevice>(devNum.getValue());
for (int i = 0; i < devNum.getValue(); i++) {
FTDevice device = getXthDevice(i);
//device is occupied?
if (isIncludeOpenedDevices) {
devs.add(device);
} else {
if ((device.flag & FTD2XX.FT_FLAGS_OPENED) == 0) {
devs.add(device);
}
}
}
Logger.getLogger(FTDevice.class.getName()).log(Level.INFO,
"Found devs: {0} (All:{1})",
new Object[]{devs.size(), devNum.getValue()});
return devs;
}
/**
* Get the connected FTDI devices. It will not contain opened devices.
* @param description Filtering option, exact match need.
* @return List contain available FTDI devices.
* @throws FTD2XXException If something goes wrong.
*/
public static List<FTDevice> getDevicesByDescription(String description)
throws FTD2XXException {
IntByReference devNum = new IntByReference();
ensureFTStatus(ftd2xx.FT_CreateDeviceInfoList(devNum));
ArrayList<FTDevice> devs = new ArrayList<FTDevice>(devNum.getValue());
for (int i = 0; i < devNum.getValue(); i++) {
FTDevice device = getXthDevice(i);
if (((device.flag & FTD2XX.FT_FLAGS_OPENED) == 0)
&& description.equals(device.devDescription)) {
devs.add(device);
}
}
Logger.getLogger(FTDevice.class.getName()).log(Level.INFO,
"Found devs: {0} (All:{1})",
new Object[]{devs.size(), devNum.getValue()});
return devs;
}
/**
* Get the connected FTDI devices. It will not contain opened devices.
* @param serialNumber Filtering option, exact match need.
* @return List contain available FTDI devices.
* @throws FTD2XXException If something goes wrong.
*/
public static List<FTDevice> getDevicesBySerialNumber(String serialNumber)
throws FTD2XXException {
IntByReference devNum = new IntByReference();
ensureFTStatus(ftd2xx.FT_CreateDeviceInfoList(devNum));
ArrayList<FTDevice> devs = new ArrayList<FTDevice>(devNum.getValue());
for (int i = 0; i < devNum.getValue(); i++) {
FTDevice device = getXthDevice(i);
if (((device.getFlag() & FTD2XX.FT_FLAGS_OPENED) == 0)
&& serialNumber.equals(device.devSerialNumber)) {
devs.add(device);
}
}
Logger.getLogger(FTDevice.class.getName()).log(Level.INFO,
"Found devs: {0} (All:{1})",
new Object[]{devs.size(), devNum.getValue()});
return devs;
}
/**
* Get the connected FTDI devices. It will not contain opened devices.
* @param deviceType Filtering option.
* @return List contain available FTDI devices.
* @throws FTD2XXException If something goes wrong.
*/
public static List<FTDevice> getDevicesByDeviceType(DeviceType deviceType)
throws FTD2XXException {
IntByReference devNum = new IntByReference();
ensureFTStatus(ftd2xx.FT_CreateDeviceInfoList(devNum));
ArrayList<FTDevice> devs = new ArrayList<FTDevice>(devNum.getValue());
for (int i = 0; i < devNum.getValue(); i++) {
FTDevice device = getXthDevice(i);
if (((device.flag & FTD2XX.FT_FLAGS_OPENED) == 0)
&& device.devType.equals(deviceType)) {
devs.add(device);
}
}
Logger.getLogger(FTDevice.class.getName()).log(Level.INFO,
"Found devs: {0} (All:{1})",
new Object[]{devs.size(), devNum.getValue()});
return devs;
}
/**
* Open connection with device.
* @throws FTD2XXException If something goes wrong.
*/
public void open() throws FTD2XXException {
Memory memory = new Memory(16);
memory.setString(0, devSerialNumber);
IntByReference handle = new IntByReference();
ensureFTStatus(ftd2xx.FT_OpenEx(memory, FTD2XX.FT_OPEN_BY_SERIAL_NUMBER,
handle));
this.ftHandle = handle.getValue();
}
/**
* Close connection with device.
* @throws FTD2XXException If something goes wrong.
*/
public void close() throws FTD2XXException {
ensureFTStatus(ftd2xx.FT_Close(ftHandle));
}
/**
* Set desired baud rate.
* @param baudRate The baud rate.
* @throws FTD2XXException If something goes wrong.
*/
public void setBaudRate(long baudRate) throws FTD2XXException {
ensureFTStatus(ftd2xx.FT_SetBaudRate(ftHandle, (int) baudRate));
}
/**
* This function sets the data characteristics for the device
* @param wordLength Number of bits per word
* @param stopBits Number of stop bits
* @param parity Parity
* @throws FTD2XXException If something goes wrong.
*/
public void setDataCharacteristics(WordLength wordLength, StopBits stopBits,
Parity parity) throws FTD2XXException {
ensureFTStatus(ftd2xx.FT_SetDataCharacteristics(ftHandle,
(byte) wordLength.constant(), (byte) stopBits.constant(),
(byte) parity.constant()));
}
/**
* Set the read and write timeouts for the device.
* @param readTimeout Read timeout in milliseconds.
* @param writeTimeout Write timeout in milliseconds.
* @throws FTD2XXException If something goes wrong.
*/
public void setTimeouts(long readTimeout, long writeTimeout)
throws FTD2XXException {
ensureFTStatus(ftd2xx.FT_SetTimeouts(ftHandle, (int) readTimeout,
(int) writeTimeout));
}
/**
* Sets the flow control for the device.
* @param flowControl Flow control type.
* @throws FTD2XXException If something goes wrong.
*/
public void setFlowControl(FlowControl flowControl) throws FTD2XXException {
ensureFTStatus(ftd2xx.FT_SetFlowControl(ftHandle,
(short) flowControl.constant(), (byte) 0, (byte) 0));
}
/**
* Sets the flow control for the device.
* @param flowControl Flow control type.
* @param uXon Character used to signal Xon. Only used if flow control is
* FT_FLOW_XON_XOFF
* @param uXoff Character used to signal Xoff. Only used if flow control is
* FT_FLOW_XON_XOFF
* @throws FTD2XXException If something goes wrong.
*/
public void setFlowControl(FlowControl flowControl, byte uXon, byte uXoff)
throws FTD2XXException {
ensureFTStatus(ftd2xx.FT_SetFlowControl(ftHandle,
(short) flowControl.constant(), uXon, uXoff));
}
/**
* Set the Data Terminal Ready (DTR) control signal.
* @param status Status of DTR signal.
* @throws FTD2XXException If something goes wrong.
*/
public void setDtr(boolean status) throws FTD2XXException {
if (status) {
ensureFTStatus(ftd2xx.FT_SetDtr(ftHandle));
} else {
ensureFTStatus(ftd2xx.FT_ClrDtr(ftHandle));
}
}
/**
* Set the Request To Send (RTS) control signal
* @param status Status of RTS signal.
* @throws FTD2XXException If something goes wrong.
*/
public void setRts(boolean status) throws FTD2XXException {
if (status) {
ensureFTStatus(ftd2xx.FT_SetRts(ftHandle));
} else {
ensureFTStatus(ftd2xx.FT_ClrRts(ftHandle));
}
}
/**
* Gets the modem status and line status from the device.
* @return Modem and line statuses
* @throws FTD2XXException If something goes wrong.
*/
public EnumSet<DeviceStatus> getDeviceStatus() throws FTD2XXException {
IntByReference modstat = new IntByReference();
ensureFTStatus(ftd2xx.FT_GetModemStatus(ftHandle, modstat));
return DeviceStatus.parseToEnumset(modstat.getValue());
}
/**
* Gets the number of bytes in the receive queue.
* @return The number of bytes in the receive queue
* @throws FTD2XXException If something goes wrong.
*/
public int getQueueStatus() throws FTD2XXException {
IntByReference reference = new IntByReference();
ensureFTStatus(ftd2xx.FT_GetQueueStatus(ftHandle, reference));
return reference.getValue();
}
/**
* Purge receive or transmit buffers in the device.
* @param rxBuffer Will rxBuffer be purged?
* @param txBuffer Will txBuffer be purged?
* @throws FTD2XXException If something goes wrong.
*/
public void purgeBuffer(boolean rxBuffer, boolean txBuffer)
throws FTD2XXException {
int mask = 0;
if (rxBuffer) {
mask |= Purge.PURGE_RX.constant();
}
if (txBuffer) {
mask |= Purge.PURGE_TX.constant();
}
ensureFTStatus(ftd2xx.FT_Purge(ftHandle, mask));
}
/**
* Send a reset command to the device.
* @throws FTD2XXException If something goes wrong.
*/
public void resetDevice() throws FTD2XXException {
ensureFTStatus(ftd2xx.FT_ResetDevice(ftHandle));
}
/**
* Set the latency timer value.
* @param timer Latency timer value in milliseconds.
* Valid range is 2 – 255.
* @throws FTD2XXException If something goes wrong.
* @throws IllegalArgumentException If timer was not in range 2 - 255.
*/
public void setLatencyTimer(short timer) throws FTD2XXException,
IllegalArgumentException {
if (!((timer > 2) && (timer < 255))) {
throw new IllegalArgumentException("Valid range is 2 – 255!");
}
ensureFTStatus(ftd2xx.FT_SetLatencyTimer(ftHandle, (byte) timer));
}
/**
* Get the current value of the latency timer.
* @return latency timer value.
* @throws FTD2XXException If something goes wrong.
*/
public short getLatencyTimer() throws FTD2XXException {
ByteByReference byReference = new ByteByReference();
ensureFTStatus(ftd2xx.FT_GetLatencyTimer(ftHandle, byReference));
return (short) ((short) byReference.getValue() & 0xFF);
}
/**
* Enables different chip modes.
* @param ucMask Required value for bit mode mask. This sets up which bits
* are inputs and outputs. A bit value of 0 sets the corresponding pin to
* an input, a bit value of 1 sets the corresponding pin to an output. In
* the case of CBUS Bit Bang, the upper nibble of this value controls which
* pins are inputs and outputs, while the lower nibble controls which of the
* outputs are high and low.
* @param bitMode Mode value.
* @throws FTD2XXException If something goes wrong.
*/
public void setBitMode(byte ucMask, BitModes bitMode)
throws FTD2XXException {
ensureFTStatus(ftd2xx.FT_SetBitMode(ftHandle, ucMask,
(byte) bitMode.constant()));
}
/**
* Gets the instantaneous value of the data bus.
* @return instantaneous data bus value
* @throws FTD2XXException If something goes wrong.
*/
public BitModes getBitMode() throws FTD2XXException {
ByteByReference byt = new ByteByReference();
ensureFTStatus(ftd2xx.FT_GetBitmode(ftHandle, byt));
return BitModes.parse(byt.getValue());
}
/**
* Set the USB request transfer size.
* This function can be used to change the transfer sizes from the default
* transfer size of 4096 bytes to better suit the application requirements.
* Transfer sizes must be set to a multiple of 64 bytes between 64 bytes and
* 64k bytes.
* When FT_SetUSBParameters is called, the change comes into effect
* immediately and any data that was held in the driver at the time of the
* change is lost. Note that, at present, only dwInTransferSize is
* supported.
* @param inTransferSize Transfer size for USB IN request
* @param outTransferSize Transfer size for USB OUT request
* @throws FTD2XXException If something goes wrong.
*/
public void setUSBParameters(int inTransferSize, int outTransferSize)
throws FTD2XXException {
ensureFTStatus(ftd2xx.FT_SetUSBParameters(ftHandle, inTransferSize,
outTransferSize));
}
/**
* Program the EEPROM data
* @param programData EEPROM to program
* @throws FTD2XXException If something goes wrong.
*/
public void writeEEPROM(EEPROMData programData)
throws FTD2XXException {
ensureFTStatus(ftd2xx.FT_EE_Program(ftHandle,
programData.ft_program_data));
}
/**
* Read device EEPROM data
* @return EEPROM data
* @throws FTD2XXException If something goes wrong.
*/
public EEPROMData readEEPROM() throws FTD2XXException {
FTD2XX.FT_PROGRAM_DATA.ByReference ftByReference =
new FTD2XX.FT_PROGRAM_DATA.ByReference();
ensureFTStatus(ftd2xx.FT_EE_Read(ftHandle, ftByReference));
return new EEPROMData(ftByReference);
}
/**
* Get the available size of the EEPROM user area
* @return available size in bytes, of the EEPROM user area
* @throws FTD2XXException If something goes wrong.
*/
public int getEEPROMUserAreaSize() throws FTD2XXException {
IntByReference size = new IntByReference();
ensureFTStatus(ftd2xx.FT_EE_UASize(ftHandle, size));
return size.getValue();
}
/**
* Read the contents of the EEPROM user area
* @param numberOfBytes Size in bytes, to be read
* @return User EEPROM content
* @throws FTD2XXException If something goes wrong.
*/
public byte[] readEEPROMUserArea(int numberOfBytes)
throws FTD2XXException {
IntByReference actually = new IntByReference();
Memory dest = new Memory(numberOfBytes);
ensureFTStatus(ftd2xx.FT_EE_UARead(ftHandle, dest, numberOfBytes,
actually));
return dest.getByteArray(0, actually.getValue());
}
/**
* Read all contents of the EEPROM user area
* @return User EEPROM content
* @throws FTD2XXException If something goes wrong.
*/
public byte[] readFullEEPROMUserArea()
throws FTD2XXException {
int numberOfBytes = getEEPROMUserAreaSize();
return readEEPROMUserArea(numberOfBytes);
}
/**
* Read all contents of the EEPROM user area as String
* @return User EEPROM content as String
* @throws FTD2XXException If something goes wrong.
*/
public String readFullEEPROMUserAreaAsString() throws IOException {
IntByReference actually = new IntByReference();
int numberOfBytes = getEEPROMUserAreaSize();
Memory dest = new Memory(numberOfBytes);
ensureFTStatus(ftd2xx.FT_EE_UARead(ftHandle, dest, numberOfBytes,
actually));
return dest.getString(0);
}
/**
* Write data into the EEPROM user area
* @param data byte[] to write
* @throws FTD2XXException If something goes wrong.
*/
public void writeEEPROMUserArea(byte[] data) throws FTD2XXException {
Memory source = new Memory(data.length);
source.write(0, data, 0, data.length);
ensureFTStatus(ftd2xx.FT_EE_UAWrite(ftHandle, source, data.length));
}
/**
* Write string into the EEPROM user area
* @param data byte[] to write
* @throws FTD2XXException If something goes wrong.
*/
public void writeEEPROMUserArea(String data) throws FTD2XXException {
Memory source = new Memory(data.length());
source.setString(0, data);
ensureFTStatus(ftd2xx.FT_EE_UAWrite(ftHandle, source, data.length()));
}
/**
* Write bytes to device.
* @param bytes Byte array to send
* @param offset Start index
* @param length Amount of bytes to write
* @return Number of bytes actually written
* @throws FTD2XXException If something goes wrong.
*/
public int write(byte[] bytes, int offset, int length)
throws FTD2XXException {
Memory memory = new Memory(length);
memory.write(0, bytes, offset, length);
IntByReference wrote = new IntByReference();
ensureFTStatus(ftd2xx.FT_Write(ftHandle, memory, length, wrote));
return wrote.getValue();
}
/**
* Write bytes to device.
* @param bytes Byte array to send
* @return Number of bytes actually written
* @throws FTD2XXException If something goes wrong.
*/
public int write(byte[] bytes) throws FTD2XXException {
return write(bytes, 0, bytes.length);
}
/**
* Write byte to device.
* @param b Byte to send (0..255)
* @return It was success?
* @throws FTD2XXException
*/
public boolean write(int b) throws FTD2XXException {
byte[] c = new byte[1];
c[0] = (byte) b;
return (write(c) == 1) ? true : false;
}
/**
* Read bytes from device.
* @param bytes Bytes array to store read bytes
* @param offset Start index.
* @param lenght Amount of bytes to read
* @return Number of bytes actually read
* @throws FTD2XXException If something goes wrong.
*/
public int read(byte[] bytes, int offset, int lenght)
throws FTD2XXException {
Memory memory = new Memory(lenght);
IntByReference read = new IntByReference();
ensureFTStatus(ftd2xx.FT_Read(ftHandle, memory, lenght, read));
memory.read(0, bytes, offset, lenght);
return read.getValue();
}
/**
* Read bytes from device.
* @param bytes Bytes array to store read bytes
* @return Number of bytes actually read
* @throws FTD2XXException If something goes wrong.
*/
public int read(byte[] bytes) throws FTD2XXException {
return read(bytes, 0, bytes.length);
}
/**
* Read a byte from device.
* @return The byte what read or -1;
* @throws FTD2XXException
*/
public int read() throws FTD2XXException {
byte[] c = new byte[1];
int ret = read(c);
return (ret == 1) ? ((int) c[0] & 0xFF) : -1;
}
/**
* Read given bytes from device.
* @param number How many bytes do you want to read?
* @return Read bytes
* @throws FTD2XXException If something goes wrong.
*/
public byte[] read(int number)
throws FTD2XXException {
byte[] ret = new byte[number];
int actually = read(ret);
if (actually != number) {
byte[] shrink = new byte[actually];
System.arraycopy(ret, 0, shrink, 0, actually);
return shrink;
} else {
return ret;
}
}
/**
* Get an InputStream to device.
* @return InputStream
*/
public InputStream getInputStream() {
if (fTDeviceInputStream == null) {
fTDeviceInputStream = new FTDeviceInputStream(this);
}
return fTDeviceInputStream;
}
/**
* Get an OutputStream to device.
* @return OutputStream
*/
public OutputStream getOutputStream() {
if (fTDeviceOutputStream == null) {
fTDeviceOutputStream = new FTDeviceOutputStream(this);
}
return fTDeviceOutputStream;
}
@Override
@SuppressWarnings("FinalizeDeclaration")
protected void finalize() throws Throwable {
try {
close();
} catch (FTD2XXException ex) {
}
super.finalize();
}
}