mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
android usb serial: Prepare device / driver select
This commit contains the serial_android_usb part of the changes proposed in issue #2657. What's implemented: - A data structure that contains all the data that can be used to describe an usb device (including user-facing string). - A function to get a list of all attached usb devices (optionally with selectable driver class). - Changes in the serial_android_usb_open-function and in the Java part to use the information about the usb device and optionally selected driver when connecting. This commit keeps compatibility with the current UI-Code in the case that only one USB-Device is connected. If two devices are connected, only the first one is tried. There are still some small things to do: - Change the user-facing string to something more descriptive. - Parts which aren't uesd anymore when the UI-Part is implemented are simply marked as obsolete (to keep compatibility for now). But generally it seems to work. [Dirk Hohndel: some white space / coding style adjustments] Signed-off-by: Christof Arnosti <charno@charno.ch> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
822b05bec4
commit
a34a81d120
4 changed files with 147 additions and 25 deletions
|
@ -14,6 +14,8 @@ import android.content.Intent;
|
||||||
import org.subsurfacedivelog.mobile.SubsurfaceMobileActivity;
|
import org.subsurfacedivelog.mobile.SubsurfaceMobileActivity;
|
||||||
|
|
||||||
import java.lang.System;
|
import java.lang.System;
|
||||||
|
import java.lang.Class;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.Thread;
|
import java.lang.Thread;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -83,38 +85,68 @@ public class AndroidSerial {
|
||||||
this.usbSerialPort = usbSerialPort;
|
this.usbSerialPort = usbSerialPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AndroidSerial open_android_serial()
|
public static AndroidSerial open_android_serial(UsbDevice usbDevice, String driverClassName)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Log.d(TAG, "in " + Thread.currentThread().getStackTrace()[2].getMethodName());
|
Log.d(TAG, "in " + Thread.currentThread().getStackTrace()[2].getMethodName());
|
||||||
// Find all available drivers from attached devices.
|
// Find all available drivers from attached devices.
|
||||||
Context context = SubsurfaceMobileActivity.getAppContext();
|
Context context = SubsurfaceMobileActivity.getAppContext();
|
||||||
UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||||
ProbeTable usbSerialProbetable = UsbSerialProber.getDefaultProbeTable();
|
|
||||||
|
|
||||||
usbSerialProbetable.addProduct(0x0403, 0xf460, FtdiSerialDriver.class); // Oceanic Custom PID
|
if (usbDevice == null) {
|
||||||
usbSerialProbetable.addProduct(0x0403, 0xf680, FtdiSerialDriver.class); // Suunto Custom PID
|
Log.e(TAG, "usbDevice == null");
|
||||||
usbSerialProbetable.addProduct(0x0403, 0x87d0, FtdiSerialDriver.class); // Cressi (Leonardo) Custom PID
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
usbSerialProbetable.addProduct(0x04B8, 0x0521, ProlificSerialDriver.class); // Mares (Nemo Sport) / Cressi Custom PID
|
UsbSerialDriver driver = null;
|
||||||
usbSerialProbetable.addProduct(0x04B8, 0x0521, ProlificSerialDriver.class); // Zeagle Custom PID
|
|
||||||
usbSerialProbetable.addProduct(0xFFFF, 0x0005, CdcAcmSerialDriver.class); // Mares Icon HD Custom PID
|
|
||||||
|
|
||||||
UsbSerialProber usbSerialProber = new UsbSerialProber(usbSerialProbetable);
|
if (driverClassName.length() == 0) {
|
||||||
|
ProbeTable usbSerialProbetable = UsbSerialProber.getDefaultProbeTable();
|
||||||
|
|
||||||
List<UsbSerialDriver> availableDrivers = usbSerialProber.findAllDrivers(manager);
|
usbSerialProbetable.addProduct(0x0403, 0xf460, FtdiSerialDriver.class); // Oceanic Custom PID
|
||||||
if (availableDrivers.isEmpty()) {
|
usbSerialProbetable.addProduct(0x0403, 0xf680, FtdiSerialDriver.class); // Suunto Custom PID
|
||||||
Log.w(TAG, "no usb-to-serial devices found!");
|
usbSerialProbetable.addProduct(0x0403, 0x87d0, FtdiSerialDriver.class); // Cressi (Leonardo) Custom PID
|
||||||
return null;
|
|
||||||
|
usbSerialProbetable.addProduct(0x04B8, 0x0521, ProlificSerialDriver.class); // Mares (Nemo Sport) / Cressi Custom PID
|
||||||
|
usbSerialProbetable.addProduct(0x04B8, 0x0521, ProlificSerialDriver.class); // Zeagle Custom PID
|
||||||
|
usbSerialProbetable.addProduct(0xFFFF, 0x0005, CdcAcmSerialDriver.class); // Mares Icon HD Custom PID
|
||||||
|
|
||||||
|
UsbSerialProber usbSerialProber = new UsbSerialProber(usbSerialProbetable);
|
||||||
|
|
||||||
|
driver = usbSerialProber.probeDevice(usbDevice);
|
||||||
|
|
||||||
|
if (driver == null) {
|
||||||
|
Log.w(TAG, "Could not find a driver for the usb device " + usbDevice);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "Using autodetected driver class " + driver.getClass().getSimpleName());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
final Class<? extends UsbSerialDriver> driverClass = Class.forName("com.hoho.android.usbserial.driver." + driverClassName).asSubclass(UsbSerialDriver.class);
|
||||||
|
|
||||||
|
if (driverClass == null) {
|
||||||
|
Log.w(TAG, "Could not find driver class " + driverClassName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Constructor<? extends UsbSerialDriver> ctor =
|
||||||
|
driverClass.getConstructor(UsbDevice.class);
|
||||||
|
driver = ctor.newInstance(usbDevice);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Could not load user-specified driver class " + driverClassName, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Using user-specified driver class " + driver.getClass().getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open a connection to the first available driver.
|
// Open a connection to the first available driver.
|
||||||
UsbSerialDriver driver = availableDrivers.get(0);
|
UsbDeviceConnection connection = manager.openDevice(usbDevice);
|
||||||
UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
|
|
||||||
|
|
||||||
if (connection == null) {
|
if (connection == null) {
|
||||||
manager.requestPermission(driver.getDevice(), PendingIntent.getBroadcast(context, 0, new Intent("org.subsurfacedivelog.mobile.USB_PERMISSION"), 0));
|
manager.requestPermission(usbDevice, PendingIntent.getBroadcast(context, 0, new Intent("org.subsurfacedivelog.mobile.USB_PERMISSION"), 0));
|
||||||
Log.w(TAG, "Could not open device!");
|
Log.w(TAG, "Could not open device. Requesting permission.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,14 @@
|
||||||
|
|
||||||
#include <QAndroidJniObject>
|
#include <QAndroidJniObject>
|
||||||
#include <QAndroidJniEnvironment>
|
#include <QAndroidJniEnvironment>
|
||||||
|
#include <QtAndroid>
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
|
#include "serial_usb_android.h"
|
||||||
|
|
||||||
#define INFO(context, fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__, "INFO: " fmt "\n", ##__VA_ARGS__)
|
#define INFO(context, fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__, "INFO: " fmt "\n", ##__VA_ARGS__)
|
||||||
#define ERROR(context, fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__, "ERROR: " fmt "\n", ##__VA_ARGS__)
|
#define ERROR(context, fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__, "ERROR: " fmt "\n", ##__VA_ARGS__)
|
||||||
#define TRACE INFO
|
#define TRACE INFO
|
||||||
|
@ -84,8 +87,7 @@ static dc_status_t serial_usb_android_purge(void *io, dc_direction_t direction)
|
||||||
return static_cast<dc_status_t>(device->callMethod<jint>("purge", "(I)I", direction));
|
return static_cast<dc_status_t>(device->callMethod<jint>("purge", "(I)I", direction));
|
||||||
}
|
}
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t serial_usb_android_configure(void *io, unsigned int baudrate, unsigned int databits, dc_parity_t parity,
|
||||||
serial_usb_android_configure(void *io, unsigned int baudrate, unsigned int databits, dc_parity_t parity,
|
|
||||||
dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol)
|
dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol)
|
||||||
{
|
{
|
||||||
TRACE (device->context, "%s: baudrate=%i, databits=%i, parity=%i, stopbits=%i, flowcontrol=%i", __FUNCTION__,
|
TRACE (device->context, "%s: baudrate=%i, databits=%i, parity=%i, stopbits=%i, flowcontrol=%i", __FUNCTION__,
|
||||||
|
@ -148,7 +150,6 @@ static dc_status_t serial_usb_android_read(void *io, void *data, size_t size, si
|
||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static dc_status_t serial_usb_android_write(void *io, const void *data, size_t size, size_t *actual)
|
static dc_status_t serial_usb_android_write(void *io, const void *data, size_t size, size_t *actual)
|
||||||
{
|
{
|
||||||
TRACE (device->context, "%s: size: %i", __FUNCTION__, size);
|
TRACE (device->context, "%s: size: %i", __FUNCTION__, size);
|
||||||
|
@ -172,7 +173,7 @@ static dc_status_t serial_usb_android_write(void *io, const void *data, size_t s
|
||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
dc_status_t serial_usb_android_open(dc_iostream_t **iostream, dc_context_t *context)
|
dc_status_t serial_usb_android_open(dc_iostream_t **iostream, dc_context_t *context, QAndroidJniObject usbDevice, std::string driverClassName)
|
||||||
{
|
{
|
||||||
TRACE(device->contxt, "%s", __FUNCTION__);
|
TRACE(device->contxt, "%s", __FUNCTION__);
|
||||||
|
|
||||||
|
@ -191,11 +192,84 @@ dc_status_t serial_usb_android_open(dc_iostream_t **iostream, dc_context_t *cont
|
||||||
|
|
||||||
QAndroidJniObject localdevice = QAndroidJniObject::callStaticObjectMethod("org/subsurfacedivelog/mobile/AndroidSerial",
|
QAndroidJniObject localdevice = QAndroidJniObject::callStaticObjectMethod("org/subsurfacedivelog/mobile/AndroidSerial",
|
||||||
"open_android_serial",
|
"open_android_serial",
|
||||||
"()Lorg/subsurfacedivelog/mobile/AndroidSerial;");
|
"(Landroid/hardware/usb/UsbDevice;Ljava/lang/String;)Lorg/subsurfacedivelog/mobile/AndroidSerial;",
|
||||||
if (localdevice == nullptr) {
|
usbDevice.object<jobject>(),
|
||||||
|
QAndroidJniObject::fromString(driverClassName.c_str()).object());
|
||||||
|
if (localdevice == nullptr)
|
||||||
return DC_STATUS_IO;
|
return DC_STATUS_IO;
|
||||||
}
|
|
||||||
QAndroidJniObject *device = new QAndroidJniObject(localdevice);
|
QAndroidJniObject *device = new QAndroidJniObject(localdevice);
|
||||||
TRACE(device->contxt, "%s", "calling dc_custom_open())");
|
TRACE(device->contxt, "%s", "calling dc_custom_open())");
|
||||||
return dc_custom_open(iostream, context, DC_TRANSPORT_SERIAL, &callbacks, device);
|
return dc_custom_open(iostream, context, DC_TRANSPORT_SERIAL, &callbacks, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<android_usb_serial_device_descriptor> serial_usb_android_get_devices(bool driverSelection)
|
||||||
|
{
|
||||||
|
std::vector<std::string> driverNames;
|
||||||
|
if (driverSelection)
|
||||||
|
driverNames = { "", "CdcAcmSerialDriver", "Ch34xSerialDriver", "Cp21xxSerialDriver", "FtdiSerialDriver", "ProlificSerialDriver" };
|
||||||
|
else
|
||||||
|
driverNames = {""};
|
||||||
|
|
||||||
|
// Get the current main activity of the application.
|
||||||
|
QAndroidJniObject activity = QtAndroid::androidActivity();
|
||||||
|
QAndroidJniObject usb_service = QAndroidJniObject::fromString("usb");
|
||||||
|
QAndroidJniEnvironment env;
|
||||||
|
|
||||||
|
// Get UsbManager from activity
|
||||||
|
QAndroidJniObject usbManager = activity.callObjectMethod("getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", QAndroidJniObject::fromString("usb").object());
|
||||||
|
|
||||||
|
//UsbDevice[] arrayOfDevices = usbManager.getDeviceList().values().toArray();
|
||||||
|
QAndroidJniObject deviceListHashMap = usbManager.callObjectMethod("getDeviceList","()Ljava/util/HashMap;");
|
||||||
|
QAndroidJniObject deviceListCollection = deviceListHashMap.callObjectMethod("values", "()Ljava/util/Collection;");
|
||||||
|
jint numDevices = deviceListCollection.callMethod<jint>("size");
|
||||||
|
QAndroidJniObject arrayOfDevices = deviceListCollection.callObjectMethod("toArray", "()[Ljava/lang/Object;");
|
||||||
|
|
||||||
|
// Special case to keep a generic user-facing name if only one device is present.
|
||||||
|
if (numDevices == 1 && !driverSelection) {
|
||||||
|
// UsbDevice usbDevice = arrayOfDevices[0]
|
||||||
|
jobject value = env->GetObjectArrayElement(arrayOfDevices.object<jobjectArray>(), 0);
|
||||||
|
QAndroidJniObject usbDevice(value);
|
||||||
|
return std::vector<android_usb_serial_device_descriptor> { {QAndroidJniObject(usbDevice), "", "USB Connection"} };
|
||||||
|
} else {
|
||||||
|
std::vector<android_usb_serial_device_descriptor> retval;
|
||||||
|
for (int i = 0; i < numDevices ; i++) {
|
||||||
|
// UsbDevice usbDevice = arrayOfDevices[i]
|
||||||
|
jobject value = env->GetObjectArrayElement(arrayOfDevices.object<jobjectArray>(), i);
|
||||||
|
QAndroidJniObject usbDevice(value);
|
||||||
|
|
||||||
|
// std::string deviceName = usbDevice.getDeviceName()
|
||||||
|
QAndroidJniObject usbDeviceNameString = usbDevice.callObjectMethod<jstring>("getDeviceName");
|
||||||
|
const char *charArray = env->GetStringUTFChars(usbDeviceNameString.object<jstring>(), nullptr);
|
||||||
|
std::string deviceName(charArray);
|
||||||
|
env->ReleaseStringUTFChars(usbDeviceNameString.object<jstring>(), charArray);
|
||||||
|
|
||||||
|
// TODO the deviceName should probably be something better... Currently it's the /dev-filename.
|
||||||
|
|
||||||
|
for (std::string driverName : driverNames) {
|
||||||
|
std::string uiDeviceName;
|
||||||
|
if (driverName != "")
|
||||||
|
uiDeviceName = deviceName + " (" + driverName + ")";
|
||||||
|
else
|
||||||
|
uiDeviceName = deviceName + " (autoselect driver)";
|
||||||
|
retval.push_back({QAndroidJniObject(usbDevice), driverName, uiDeviceName});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For testing and compatibility only, can be removed after the UI changes. Behaves exactly like the "old"
|
||||||
|
* implementation if only one device is attached.
|
||||||
|
*/
|
||||||
|
dc_status_t serial_usb_android_open(dc_iostream_t **iostream, dc_context_t *context)
|
||||||
|
{
|
||||||
|
std::vector<android_usb_serial_device_descriptor> devices = serial_usb_android_get_devices(false);
|
||||||
|
|
||||||
|
if(devices.empty())
|
||||||
|
return DC_STATUS_NODEVICE;
|
||||||
|
|
||||||
|
return serial_usb_android_open(iostream, context, devices[0].usbDevice, devices[0].className);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
16
core/serial_usb_android.h
Normal file
16
core/serial_usb_android.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef SERIAL_USB_ANDROID_H
|
||||||
|
#define SERIAL_USB_ANDROID_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/* USB Device Information */
|
||||||
|
struct android_usb_serial_device_descriptor {
|
||||||
|
QAndroidJniObject usbDevice; /* the UsbDevice */
|
||||||
|
std::string className; /* the driver class name. If empty, then "autodetect" */
|
||||||
|
std::string uiRepresentation; /* The string that can be used for the user interface. */
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<android_usb_serial_device_descriptor> serial_usb_android_get_devices(bool driverSelection);
|
||||||
|
|
||||||
|
#endif
|
|
@ -89,7 +89,7 @@ int main(int argc, char **argv)
|
||||||
void set_non_bt_addresses()
|
void set_non_bt_addresses()
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
connectionListModel.addAddress("usb-serial");
|
connectionListModel.addAddress("usb-serial"); /* obsolete, can be removed when the new USB device selection is implemented. */
|
||||||
#elif defined(Q_OS_LINUX) // since this is in the else, it does NOT include Android
|
#elif defined(Q_OS_LINUX) // since this is in the else, it does NOT include Android
|
||||||
connectionListModel.addAddress("/dev/ttyS0");
|
connectionListModel.addAddress("/dev/ttyS0");
|
||||||
connectionListModel.addAddress("/dev/ttyS1");
|
connectionListModel.addAddress("/dev/ttyS1");
|
||||||
|
|
Loading…
Reference in a new issue