wip: native libusb can fetch product ID, serial, and android system FDs can be wrapped
parent
b2f67f7ce1
commit
ca630b4569
|
|
@ -118,6 +118,12 @@ if(WIN32)
|
|||
platform/windows/vcp.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
elseif(LIBICSNEO_ENABLE_ANDROIDUSB)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/android/androidusb.cpp
|
||||
)
|
||||
|
||||
else() # Darwin or Linux
|
||||
set(PLATFORM_SRC)
|
||||
|
||||
|
|
@ -168,6 +174,7 @@ else() # Darwin or Linux
|
|||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if(LIBICSNEO_ENABLE_FTD3XX)
|
||||
if(NOT FTD3XX_ROOT) # allow system override
|
||||
include(FetchContent)
|
||||
|
|
@ -367,9 +374,14 @@ if(LIBICSNEO_ENABLE_TCP)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
#target_link_libraries(icsneocpp PRIVATE android log)
|
||||
if(LIBICSNEO_ENABLE_ANDROIDUSB)
|
||||
add_library(libusb SHARED IMPORTED)
|
||||
set_target_properties(libusb PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/third-party/libusb/android/libs/${ANDROID_ABI}/libusb1.0.so)
|
||||
target_include_directories(icsneocpp PRIVATE third-party/libusb/libusb)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_ANDROIDUSB)
|
||||
target_link_libraries(icsneocpp PRIVATE android log libusb)
|
||||
endif()
|
||||
|
||||
# fatfs
|
||||
add_subdirectory(third-party/fatfs)
|
||||
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
|
@ -424,6 +436,11 @@ if(LIBICSNEO_BUILD_ICSNEOC)
|
|||
)
|
||||
target_link_libraries(icsneoc PRIVATE icsneocpp)
|
||||
target_compile_features(icsneoc PRIVATE cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||
if(LIBICSNEO_ENABLE_ANDROIDUSB)
|
||||
target_link_libraries(icsneoc PRIVATE android log)
|
||||
target_include_directories(icsneoc PRIVATE third-party/libusb/libusb)
|
||||
target_compile_definitions(icsneoc PRIVATE ICSNEO_ENABLE_ANDROIDUSB)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_ICSNEOC_STATIC)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
#define ICSNEOC_MAKEDLL
|
||||
|
||||
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||
#include "icsneo/platform/android/androidusb.h"
|
||||
#endif
|
||||
|
||||
#include "icsneo/icsneoc.h"
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/platform/dynamiclib.h"
|
||||
|
|
@ -740,4 +744,13 @@ int icsneo_getDeviceStatus(const neodevice_t* device, void* status, size_t* size
|
|||
*size = rawMessage->data.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||
bool icsneo_addSysFileDescriptor(int sysFd) {
|
||||
return icsneo::ANDROIDUSB::addSystemFD(sysFd);
|
||||
|
||||
}
|
||||
bool icsneo_removeSysFileDescriptor(int sysFd) {
|
||||
return icsneo::ANDROIDUSB::removeSystemFD(sysFd);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||
#include "icsneo/platform/cdcacm.h"
|
||||
#include "icsneo/platform/android/androidusb.h"
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_FTDI
|
||||
|
|
@ -72,7 +72,7 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
|||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||
ANDROIDUSB::Find(newDriverFoundDevices):
|
||||
ANDROIDUSB::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_FTDI
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ public:
|
|||
DriverTTYPathEmpty = 0x3120,
|
||||
DriverWasNotNegOne = 0x3121,
|
||||
DriverTCGetAddrFail = 0x3122,
|
||||
DriverTCSetAddrFail= 0x3123,
|
||||
DriverTCSetAddrFail = 0x3123,
|
||||
|
||||
|
||||
// FTD3XX
|
||||
|
|
@ -163,10 +163,15 @@ public:
|
|||
FTIncorrectDevicePath = FTOK + 31,
|
||||
FTOtherError = FTOK + 32,
|
||||
|
||||
// Android USB
|
||||
AndroidUSBLibusbInitFailed = 0x5000,
|
||||
AndroidUSBFDWrapFailed = 0x5001,
|
||||
|
||||
NoErrorFound = 0xFFFFFFFD,
|
||||
TooManyEvents = 0xFFFFFFFE,
|
||||
Unknown = 0xFFFFFFFF,
|
||||
};
|
||||
};
|
||||
|
||||
enum class Severity : uint8_t {
|
||||
Any = 0, // Used for filtering, should not appear in data
|
||||
EventInfo = 0x10,
|
||||
|
|
|
|||
|
|
@ -837,6 +837,11 @@ extern bool icsneo_getRTC(const neodevice_t* device, uint64_t* output);
|
|||
*/
|
||||
extern bool icsneo_setRTC(const neodevice_t* device, uint64_t input);
|
||||
|
||||
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||
extern bool icsneo_addSysFileDescriptor(int sysFd);
|
||||
extern bool icsneo_removeSysFileDescriptor(int sysFd);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
#ifndef __CDCACM_ANDROID_H_
|
||||
#define __CDCACM_ANDROID_H_
|
||||
#ifndef __ANDROIDUSB_H_
|
||||
#define __ANDROIDUSB_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/driver.h"
|
||||
#include "icsneo/device/neodevice.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "libusb.h"
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
#include <sys/stat.h>
|
||||
|
|
@ -19,7 +20,11 @@ public:
|
|||
/**
|
||||
* Note: This is a driver for all devices which use Android CDC_ACM
|
||||
*/
|
||||
ANDROIDUSB(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {}
|
||||
ANDROIDUSB(const device_eventhandler_t& err, neodevice_t& forDevice)
|
||||
: Driver(err), device(forDevice) {
|
||||
if(auto key = systemFDs.find(device.handle); key != systemFDs.end())
|
||||
libusbDeviceHandle = key->second;
|
||||
}
|
||||
~ANDROIDUSB();
|
||||
static void Find(std::vector<FoundDevice>& found);
|
||||
|
||||
|
|
@ -27,22 +32,15 @@ public:
|
|||
bool isOpen() override;
|
||||
bool close() override;
|
||||
|
||||
void modeChangeIncoming() override;
|
||||
void awaitModeChangeComplete() override;
|
||||
|
||||
static void addSystemFD(int fd);
|
||||
static void removeSystemFD(int fd);
|
||||
static bool addSystemFD(int fd);
|
||||
static bool removeSystemFD(int fd);
|
||||
|
||||
private:
|
||||
neodevice_t& device;
|
||||
static std::unordered_map<int,FoundDevice> systemFDs;
|
||||
std::optional<ino_t> disallowedInode;
|
||||
std::atomic<bool> modeChanging{false};
|
||||
std::thread modeChangeThread;
|
||||
std::mutex modeChangeMutex;
|
||||
std::condition_variable modeChangeCV;
|
||||
libusb_context *ctx = nullptr;
|
||||
libusb_device_handle *libusbDeviceHandle = nullptr;
|
||||
|
||||
static std::string HandleToTTY(neodevice_handle_t handle);
|
||||
inline static std::unordered_map<int,libusb_device_handle*> systemFDs; //android FD, libusb handle
|
||||
|
||||
void readTask() override;
|
||||
void writeTask() override;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,279 @@
|
|||
#include "icsneo/platform/android/androidusb.h"
|
||||
#include "icsneo/device/founddevice.h"
|
||||
#include <dirent.h>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/select.h>
|
||||
#include "libusb.h"
|
||||
#include <android/log.h>
|
||||
#define LOG_TAG "libicsneo_test_androidusb"
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
ANDROIDUSB::~ANDROIDUSB() {
|
||||
if (isOpen())
|
||||
close();
|
||||
}
|
||||
|
||||
bool ANDROIDUSB::addSystemFD(int sysFd) {
|
||||
if ((sysFd != -1) && (systemFDs[sysFd] == nullptr)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ANDROIDUSB::removeSystemFD(int sysFd) {
|
||||
if (sysFd != -1) {
|
||||
return systemFDs.erase(sysFd);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ANDROIDUSB::open() {
|
||||
int ret = 0;
|
||||
if (!isOpen()) {
|
||||
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
struct termios tty = {};
|
||||
struct termios compare = {};
|
||||
|
||||
if(tcgetattr(fd, &tty) != 0) {
|
||||
close();
|
||||
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
|
||||
report(APIEvent::Type::DriverTCGetAddrFail, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
tty.c_cflag |= (CLOCAL | CREAD); // Ignore modem controls
|
||||
tty.c_cflag &= ~CSIZE;
|
||||
tty.c_cflag |= CS8; // 8-bit characters
|
||||
tty.c_cflag &= ~PARENB; // No parity bit
|
||||
tty.c_cflag &= ~CSTOPB; // One stop bit
|
||||
tty.c_cflag &= ~CRTSCTS; // No hardware flow control
|
||||
|
||||
// Non-canonical mode
|
||||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
tty.c_oflag &= ~OPOST;
|
||||
|
||||
// Fetch bytes as they become available
|
||||
// See http://man7.org/linux/man-pages/man3/termios.3.html
|
||||
tty.c_cc[VMIN] = 0;
|
||||
tty.c_cc[VTIME] = 1; // 100ms timeout (1 decisecond, what?)
|
||||
|
||||
if(tcsetattr(fd, TCSAFLUSH, &tty) != 0) { // Flushes input and output buffers as well as setting settings
|
||||
close();
|
||||
report(APIEvent::Type::DriverFailedToOpen, APIEvent::Severity::Error);
|
||||
report(APIEvent::Type::DriverTCSetAddrFail, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(tcgetattr(fd, &compare) != 0 || memcmp(&tty, &compare, sizeof(struct termios)) != 0) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
// Create threads
|
||||
readThread = std::thread(&ANDROIDUSB::readTask, this);
|
||||
writeThread = std::thread(&ANDROIDUSB::writeTask, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ANDROIDUSB::isOpen() {
|
||||
return device.handle >= 0; // Negative fd indicates error or not opened yet
|
||||
}
|
||||
|
||||
bool ANDROIDUSB::close() {
|
||||
if(!isOpen() && !isDisconnected()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
closing = true;
|
||||
|
||||
if(readThread.joinable())
|
||||
readThread.join();
|
||||
|
||||
if(writeThread.joinable())
|
||||
writeThread.join();
|
||||
|
||||
closing = false;
|
||||
disconnected = false;
|
||||
|
||||
systemFDs[device.handle] = nullptr;
|
||||
device.handle = -1;
|
||||
|
||||
uint8_t flush;
|
||||
WriteOperation flushop;
|
||||
while (readQueue.try_dequeue(flush)) {}
|
||||
while (writeQueue.try_dequeue(flushop)) {}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ANDROIDUSB::readTask() {
|
||||
//constexpr size_t READ_BUFFER_SIZE = 2048;
|
||||
//uint8_t readbuf[READ_BUFFER_SIZE];
|
||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
while(!closing && !isDisconnected()) {
|
||||
//fd_set rfds = {0};
|
||||
//struct timeval tv = {0};
|
||||
//FD_SET(fd, &rfds);
|
||||
//tv.tv_usec = 50000; // 50ms
|
||||
//::select(fd + 1, &rfds, NULL, NULL, &tv);
|
||||
ssize_t bytesRead = 0; // ::read(fd, readbuf, READ_BUFFER_SIZE);
|
||||
|
||||
//libusb bulk transfer?
|
||||
//todo!
|
||||
|
||||
if(bytesRead > 0) {
|
||||
#if 0 // Perhaps helpful for debugging :)
|
||||
std::cout << "Read data: (" << bytesRead << ')' << std::hex << std::endl;
|
||||
for(int i = 0; i < bytesRead; i += 16) {
|
||||
for(int j = 0; j < std::min<int>(bytesRead - i, 16); j++)
|
||||
std::cout << std::setw(2) << std::setfill('0') << uint32_t(readbuf[i+j]) << ' ';
|
||||
std::cout << std::endl;
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
#endif
|
||||
|
||||
//readQueue.enqueue_bulk(readbuf, bytesRead);
|
||||
} else {
|
||||
if(!closing && !fdIsValid() && !isDisconnected()) {
|
||||
disconnected = true;
|
||||
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ANDROIDUSB::writeTask() {
|
||||
WriteOperation writeOp;
|
||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
while(!closing && !isDisconnected()) {
|
||||
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
||||
continue;
|
||||
|
||||
const ssize_t totalWriteSize = (ssize_t)writeOp.bytes.size();
|
||||
ssize_t totalWritten = 0;
|
||||
while(totalWritten < totalWriteSize) {
|
||||
const ssize_t writeSize = totalWriteSize - totalWritten;
|
||||
ssize_t actualWritten = writeSize; //::write(fd, writeOp.bytes.data() + totalWritten, writeSize);
|
||||
|
||||
//libusb bulk transfer?
|
||||
//todo!
|
||||
|
||||
if(actualWritten != writeSize) {
|
||||
// If we partially wrote, it's probably EAGAIN but it won't have been set
|
||||
// so don't signal an error unless it's < 0, we'll come back around and
|
||||
// get a -1 to see the real error.
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
|
||||
// We filled the TX FIFO, use select to wait for it to become available again
|
||||
//fd_set wfds = {0};
|
||||
//struct timeval tv = {0};
|
||||
//FD_SET(fd, &wfds);
|
||||
//tv.tv_usec = 50000; // 50ms
|
||||
//::select(fd + 1, nullptr, &wfds, nullptr, &tv);
|
||||
} else if (actualWritten < 0) {
|
||||
if(!fdIsValid()) {
|
||||
if(!isDisconnected()) {
|
||||
disconnected = true;
|
||||
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||
}
|
||||
} else
|
||||
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(actualWritten > 0) {
|
||||
#if 0 // Perhaps helpful for debugging :)
|
||||
std::cout << "Wrote data: (" << actualWritten << ')' << std::hex << std::endl;
|
||||
for(int i = 0; i < actualWritten; i += 16) {
|
||||
for(int j = 0; j < std::min<int>(actualWritten - i, 16); j++)
|
||||
std::cout << std::setw(2) << std::setfill('0') << uint32_t(writeOp.bytes[totalWritten+i+j]) << ' ';
|
||||
std::cout << std::endl;
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
#endif
|
||||
|
||||
totalWritten += actualWritten;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ANDROIDUSB::fdIsValid() {
|
||||
//libusb validate FD
|
||||
return false;
|
||||
}
|
||||
|
||||
void ANDROIDUSB::Find(std::vector<FoundDevice>& found) {
|
||||
int ret = 0;
|
||||
libusb_context *ctx = NULL;
|
||||
libusb_device_handle *libusbDeviceHandle = NULL;
|
||||
ret = libusb_set_option(ctx, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL);
|
||||
if (ret != LIBUSB_SUCCESS) {
|
||||
LOGD("libusb_init failed: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
ret = libusb_init(&ctx);
|
||||
if (ret < 0) {
|
||||
LOGD("libusb_init failed: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
LOGD("libusb_init completed: %d\n", ret);
|
||||
LOGD("SYSTEM FDs size: %d\n", systemFDs.size());
|
||||
for (auto & [fd, libUsbHandle]: systemFDs) {
|
||||
ret = libusb_wrap_sys_device(ctx, (intptr_t) fd, &libusbDeviceHandle);
|
||||
LOGD("Wrapping system FD: %d, return: %s\n", fd, libusb_strerror(ret));
|
||||
if (ret == 0) {
|
||||
FoundDevice device = {};
|
||||
struct libusb_device_descriptor desc;
|
||||
unsigned char outString[255] = {};
|
||||
if(libusbDeviceHandle) {
|
||||
ret = libusb_get_device_descriptor(libusb_get_device(libusbDeviceHandle), &desc);
|
||||
if (ret == 0) {
|
||||
device.productId = desc.idProduct;
|
||||
LOGD("ProductID: %x\n", device.productId);
|
||||
ret = libusb_get_string_descriptor_ascii(libusbDeviceHandle, desc.iSerialNumber,
|
||||
outString, sizeof(outString));
|
||||
|
||||
if (ret > 0) {
|
||||
std::strncpy(device.serial, reinterpret_cast<char *>(outString),
|
||||
sizeof(device.serial));
|
||||
LOGD("Serial Number: %s\n", device.serial);
|
||||
} else {
|
||||
LOGD("Serial number fetch failed!\n");
|
||||
}
|
||||
} else {
|
||||
LOGD("Fetch device descriptor failed for FD: %d, %s\n", fd, libusb_strerror(ret));
|
||||
}
|
||||
} else {
|
||||
LOGD("LIBUSB Device handle was invalid!\n");
|
||||
}
|
||||
device.handle = fd;
|
||||
|
||||
// Add a factory to make the driver
|
||||
device.makeDriver = [&](const device_eventhandler_t &report, neodevice_t &device) {
|
||||
return std::unique_ptr<Driver>(new ANDROIDUSB(report, device));
|
||||
};
|
||||
|
||||
found.push_back(device); // Finally, add device to search results
|
||||
libUsbHandle = libusbDeviceHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 4239bc3a50014b8e6a5a2a59df1fff3b7469543b
|
||||
Loading…
Reference in New Issue