kopia lustrzana https://github.com/felHR85/UsbSerial
385 wiersze
8.8 KiB
Java
385 wiersze
8.8 KiB
Java
package com.felhr.usbserial;
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
import com.felhr.deviceids.CH34xIds;
|
|
import com.felhr.deviceids.CP210xIds;
|
|
import com.felhr.deviceids.FTDISioIds;
|
|
import com.felhr.deviceids.PL2303Ids;
|
|
|
|
import android.hardware.usb.UsbConstants;
|
|
import android.hardware.usb.UsbDevice;
|
|
import android.hardware.usb.UsbDeviceConnection;
|
|
import android.hardware.usb.UsbEndpoint;
|
|
import android.hardware.usb.UsbInterface;
|
|
import android.hardware.usb.UsbRequest;
|
|
|
|
public abstract class UsbSerialDevice implements UsbSerialInterface
|
|
{
|
|
private static final String CLASS_ID = UsbSerialDevice.class.getSimpleName();
|
|
|
|
private static boolean mr1Version;
|
|
protected final UsbDevice device;
|
|
protected final UsbDeviceConnection connection;
|
|
|
|
protected static final int USB_TIMEOUT = 5000;
|
|
|
|
protected SerialBuffer serialBuffer;
|
|
|
|
protected WorkerThread workerThread;
|
|
protected WriteThread writeThread;
|
|
protected ReadThread readThread;
|
|
|
|
// Get Android version if version < 4.3 It is not going to be asynchronous read operations
|
|
static
|
|
{
|
|
if(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.JELLY_BEAN_MR1)
|
|
mr1Version = true;
|
|
else
|
|
mr1Version = false;
|
|
}
|
|
|
|
public UsbSerialDevice(UsbDevice device, UsbDeviceConnection connection)
|
|
{
|
|
this.device = device;
|
|
this.connection = connection;
|
|
serialBuffer = new SerialBuffer(mr1Version);
|
|
}
|
|
|
|
public static UsbSerialDevice createUsbSerialDevice(UsbDevice device, UsbDeviceConnection connection)
|
|
{
|
|
return createUsbSerialDevice(device, connection, -1);
|
|
}
|
|
|
|
public static UsbSerialDevice createUsbSerialDevice(UsbDevice device, UsbDeviceConnection connection, int iface)
|
|
{
|
|
/*
|
|
* It checks given vid and pid and will return a custom driver or a CDC serial driver.
|
|
* When CDC is returned open() method is even more important, its response will inform about if it can be really
|
|
* opened as a serial device with a generic CDC serial driver
|
|
*/
|
|
int vid = device.getVendorId();
|
|
int pid = device.getProductId();
|
|
|
|
if(FTDISioIds.isDeviceSupported(vid, pid))
|
|
return new FTDISerialDevice(device, connection, iface);
|
|
else if(CP210xIds.isDeviceSupported(vid, pid))
|
|
return new CP2102SerialDevice(device, connection, iface);
|
|
else if(PL2303Ids.isDeviceSupported(vid, pid))
|
|
return new PL2303SerialDevice(device, connection, iface);
|
|
else if(CH34xIds.isDeviceSupported(vid, pid))
|
|
return new CH34xSerialDevice(device, connection, iface);
|
|
else if(isCdcDevice(device))
|
|
return new CDCSerialDevice(device, connection, iface);
|
|
else
|
|
return null;
|
|
}
|
|
|
|
// Common Usb Serial Operations (I/O Asynchronous)
|
|
@Override
|
|
public abstract boolean open();
|
|
|
|
@Override
|
|
public void write(byte[] buffer)
|
|
{
|
|
serialBuffer.putWriteBuffer(buffer);
|
|
}
|
|
|
|
@Override
|
|
public int read(UsbReadCallback mCallback)
|
|
{
|
|
if(mr1Version)
|
|
{
|
|
workerThread.setCallback(mCallback);
|
|
workerThread.getUsbRequest().queue(serialBuffer.getReadBuffer(), SerialBuffer.DEFAULT_READ_BUFFER_SIZE);
|
|
}else
|
|
{
|
|
readThread.setCallback(mCallback);
|
|
//readThread.start();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
@Override
|
|
public abstract void close();
|
|
|
|
// Serial port configuration
|
|
@Override
|
|
public abstract void setBaudRate(int baudRate);
|
|
@Override
|
|
public abstract void setDataBits(int dataBits);
|
|
@Override
|
|
public abstract void setStopBits(int stopBits);
|
|
@Override
|
|
public abstract void setParity(int parity);
|
|
@Override
|
|
public abstract void setFlowControl(int flowControl);
|
|
|
|
//Debug options
|
|
public void debug(boolean value)
|
|
{
|
|
if(serialBuffer != null)
|
|
serialBuffer.debug(value);
|
|
}
|
|
|
|
private boolean isFTDIDevice()
|
|
{
|
|
return (this instanceof FTDISerialDevice);
|
|
}
|
|
|
|
public static boolean isCdcDevice(UsbDevice device)
|
|
{
|
|
int iIndex = device.getInterfaceCount();
|
|
for(int i=0;i<=iIndex-1;i++)
|
|
{
|
|
UsbInterface iface = device.getInterface(i);
|
|
if(iface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* WorkerThread waits for request notifications from IN endpoint
|
|
*/
|
|
protected class WorkerThread extends Thread
|
|
{
|
|
private UsbReadCallback callback;
|
|
private UsbRequest requestIN;
|
|
private AtomicBoolean working;
|
|
|
|
public WorkerThread()
|
|
{
|
|
working = new AtomicBoolean(true);
|
|
}
|
|
|
|
@Override
|
|
public void run()
|
|
{
|
|
while(working.get())
|
|
{
|
|
UsbRequest request = connection.requestWait();
|
|
if(request != null && request.getEndpoint().getType() == UsbConstants.USB_ENDPOINT_XFER_BULK
|
|
&& request.getEndpoint().getDirection() == UsbConstants.USB_DIR_IN)
|
|
{
|
|
byte[] data = serialBuffer.getDataReceived();
|
|
|
|
// FTDI devices reserves two first bytes of an IN endpoint with info about
|
|
// modem and Line.
|
|
if(isFTDIDevice())
|
|
{
|
|
if(data.length > 2)
|
|
{
|
|
data = FTDISerialDevice.FTDIUtilities.adaptArray(data);
|
|
// Clear buffer, execute the callback
|
|
serialBuffer.clearReadBuffer();
|
|
onReceivedData(data);
|
|
}
|
|
}else
|
|
{
|
|
// Clear buffer, execute the callback
|
|
serialBuffer.clearReadBuffer();
|
|
onReceivedData(data);
|
|
}
|
|
// Queue a new request
|
|
requestIN.queue(serialBuffer.getReadBuffer(), SerialBuffer.DEFAULT_READ_BUFFER_SIZE);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setCallback(UsbReadCallback callback)
|
|
{
|
|
this.callback = callback;
|
|
}
|
|
|
|
public void setUsbRequest(UsbRequest request)
|
|
{
|
|
this.requestIN = request;
|
|
}
|
|
|
|
public UsbRequest getUsbRequest()
|
|
{
|
|
return requestIN;
|
|
}
|
|
|
|
private void onReceivedData(byte[] data)
|
|
{
|
|
if(callback != null)
|
|
callback.onReceivedData(data);
|
|
}
|
|
|
|
public void stopWorkingThread()
|
|
{
|
|
working.set(false);
|
|
}
|
|
}
|
|
|
|
protected class WriteThread extends Thread
|
|
{
|
|
private UsbEndpoint outEndpoint;
|
|
private AtomicBoolean working;
|
|
|
|
public WriteThread()
|
|
{
|
|
working = new AtomicBoolean(true);
|
|
}
|
|
|
|
@Override
|
|
public void run()
|
|
{
|
|
while(working.get())
|
|
{
|
|
byte[] data = serialBuffer.getWriteBuffer();
|
|
connection.bulkTransfer(outEndpoint, data, data.length, USB_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
public void setUsbEndpoint(UsbEndpoint outEndpoint)
|
|
{
|
|
this.outEndpoint = outEndpoint;
|
|
}
|
|
|
|
public void stopWriteThread()
|
|
{
|
|
working.set(false);
|
|
}
|
|
}
|
|
|
|
protected class ReadThread extends Thread
|
|
{
|
|
private UsbReadCallback callback;
|
|
private UsbEndpoint inEndpoint;
|
|
private AtomicBoolean working;
|
|
|
|
public ReadThread()
|
|
{
|
|
working = new AtomicBoolean(true);
|
|
}
|
|
|
|
public void setCallback(UsbReadCallback callback)
|
|
{
|
|
this.callback = callback;
|
|
}
|
|
|
|
@Override
|
|
public void run()
|
|
{
|
|
byte[] dataReceived = null;
|
|
|
|
while(working.get())
|
|
{
|
|
int numberBytes;
|
|
if(inEndpoint != null)
|
|
numberBytes = connection.bulkTransfer(inEndpoint, serialBuffer.getBufferCompatible(),
|
|
SerialBuffer.DEFAULT_READ_BUFFER_SIZE, 0);
|
|
else
|
|
numberBytes = 0;
|
|
|
|
if(numberBytes > 0)
|
|
{
|
|
dataReceived = serialBuffer.getDataReceivedCompatible(numberBytes);
|
|
|
|
// FTDI devices reserve two first bytes of an IN endpoint with info about
|
|
// modem and Line.
|
|
if(isFTDIDevice())
|
|
{
|
|
if(dataReceived.length > 2)
|
|
{
|
|
dataReceived = FTDISerialDevice.FTDIUtilities.adaptArray(dataReceived);
|
|
onReceivedData(dataReceived);
|
|
}
|
|
}else
|
|
{
|
|
onReceivedData(dataReceived);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setUsbEndpoint(UsbEndpoint inEndpoint)
|
|
{
|
|
this.inEndpoint = inEndpoint;
|
|
}
|
|
|
|
public void stopReadThread()
|
|
{
|
|
working.set(false);
|
|
}
|
|
|
|
private void onReceivedData(byte[] data)
|
|
{
|
|
if(callback != null)
|
|
callback.onReceivedData(data);
|
|
}
|
|
}
|
|
|
|
protected void setThreadsParams(UsbRequest request, UsbEndpoint endpoint)
|
|
{
|
|
if(mr1Version)
|
|
{
|
|
workerThread.setUsbRequest(request);
|
|
writeThread.setUsbEndpoint(endpoint);
|
|
}else
|
|
{
|
|
readThread.setUsbEndpoint(request.getEndpoint());
|
|
writeThread.setUsbEndpoint(endpoint);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Kill workingThread; This must be called when closing a device
|
|
*/
|
|
protected void killWorkingThread()
|
|
{
|
|
if(mr1Version && workerThread != null)
|
|
{
|
|
workerThread.stopWorkingThread();
|
|
workerThread = null;
|
|
}else if(!mr1Version && readThread != null)
|
|
{
|
|
readThread.stopReadThread();
|
|
readThread = null;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Restart workingThread if it has been killed before
|
|
*/
|
|
protected void restartWorkingThread()
|
|
{
|
|
if(mr1Version && workerThread == null)
|
|
{
|
|
workerThread = new WorkerThread();
|
|
workerThread.start();
|
|
while(!workerThread.isAlive()){} // Busy waiting
|
|
}else if(!mr1Version && readThread == null)
|
|
{
|
|
readThread = new ReadThread();
|
|
readThread.start();
|
|
while(!readThread.isAlive()){} // Busy waiting
|
|
}
|
|
}
|
|
|
|
protected void killWriteThread()
|
|
{
|
|
if(writeThread != null)
|
|
{
|
|
writeThread.stopWriteThread();
|
|
writeThread = null;
|
|
serialBuffer.resetWriteBuffer();
|
|
}
|
|
}
|
|
|
|
protected void restartWriteThread()
|
|
{
|
|
if(writeThread == null)
|
|
{
|
|
writeThread = new WriteThread();
|
|
writeThread.start();
|
|
while(!writeThread.isAlive()){} // Busy waiting
|
|
}
|
|
}
|
|
}
|