blob: 5095b3d50c94e3a1098f0b71dbb9cbb312b712bf [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>
#include "log.h"
#include "bitfield.h"
#include "queue.h"
#include "io_usb.h"
#include "usb_internal.h"
#include "system_java.h"
#define JAVA_OBJ_USB(name) "android/hardware/usb/Usb" name
#define JAVA_OBJ_USB_DEVICE JAVA_OBJ_USB("Device")
#define JAVA_OBJ_USB_INTERFACE JAVA_OBJ_USB("Interface")
#define JAVA_OBJ_USB_ENDPOINT JAVA_OBJ_USB("Endpoint")
#define JAVA_OBJ_USB_CONNECTION JAVA_OBJ_USB("DeviceConnection")
#define JAVA_SIG_USB_DEVICE JAVA_SIG_OBJECT(JAVA_OBJ_USB_DEVICE)
#define JAVA_SIG_USB_INTERFACE JAVA_SIG_OBJECT(JAVA_OBJ_USB_INTERFACE)
#define JAVA_SIG_USB_ENDPOINT JAVA_SIG_OBJECT(JAVA_OBJ_USB_ENDPOINT)
#define JAVA_SIG_USB_CONNECTION JAVA_SIG_OBJECT(JAVA_OBJ_USB_CONNECTION)
typedef struct {
JNIEnv *env;
jobject device;
UsbDeviceDescriptor descriptor;
} UsbHostDevice;
static Queue *usbHostDevices = NULL;
struct UsbDeviceExtensionStruct {
const UsbHostDevice *host;
jobject connection;
jobject interface;
int fileDescriptor;
};
struct UsbEndpointExtensionStruct {
UsbEndpoint *endpoint;
jobject object;
};
static jclass usbHelperClass = NULL;
static jclass usbDeviceClass = NULL;
static jclass usbInterfaceClass = NULL;
static jclass usbConnectionClass = NULL;
static int
usbFindHelperClass (JNIEnv *env) {
return findJavaClass(env, &usbHelperClass, JAVA_OBJ_BRLTTY("UsbHelper"));
}
static jobject
usbGetDeviceIterator (JNIEnv *env) {
if (usbFindHelperClass(env)) {
static jmethodID method = 0;
if (findJavaStaticMethod(env, &method, usbHelperClass, "getDeviceIterator",
JAVA_SIG_METHOD(JAVA_SIG_ITERATOR, ))) {
jobject iterator = (*env)->CallStaticObjectMethod(env, usbHelperClass, method);
if (iterator) return iterator;
clearJavaException(env, 1);
errno = EIO;
}
}
return NULL;
}
static jobject
usbGetNextDevice (JNIEnv *env, jobject iterator) {
if (usbFindHelperClass(env)) {
static jmethodID method = 0;
if (findJavaStaticMethod(env, &method, usbHelperClass, "getNextDevice",
JAVA_SIG_METHOD(JAVA_SIG_USB_DEVICE, JAVA_SIG_ITERATOR))) {
jobject device = (*env)->CallStaticObjectMethod(env, usbHelperClass, method, iterator);
if (device) return device;
clearJavaException(env, 1);
}
}
return NULL;
}
static jobject
usbGetDeviceInterface (JNIEnv *env, jobject device, jint identifier) {
if (usbFindHelperClass(env)) {
static jmethodID method = 0;
if (findJavaStaticMethod(env, &method, usbHelperClass, "getDeviceInterface",
JAVA_SIG_METHOD(JAVA_SIG_USB_INTERFACE,
JAVA_SIG_USB_DEVICE // device
JAVA_SIG_INT // identifier
))) {
jobject interface = (*env)->CallStaticObjectMethod(env, usbHelperClass, method, device, identifier);
if (interface) return interface;
clearJavaException(env, 1);
errno = EIO;
}
}
return NULL;
}
static jobject
usbGetInterfaceEndpoint (JNIEnv *env, jobject interface, jint address) {
if (usbFindHelperClass(env)) {
static jmethodID method = 0;
if (findJavaStaticMethod(env, &method, usbHelperClass, "getInterfaceEndpoint",
JAVA_SIG_METHOD(JAVA_SIG_USB_ENDPOINT,
JAVA_SIG_USB_INTERFACE // interface
JAVA_SIG_INT // address
))) {
jobject endpoint = (*env)->CallStaticObjectMethod(env, usbHelperClass, method, interface, address);
if (endpoint) return endpoint;
clearJavaException(env, 1);
errno = EIO;
}
}
return NULL;
}
static jobject
usbOpenDeviceConnection (JNIEnv *env, jobject device) {
logMessage(LOG_CATEGORY(USB_IO), "opening device connection");
if (usbFindHelperClass(env)) {
static jmethodID method = 0;
if (findJavaStaticMethod(env, &method, usbHelperClass, "openDeviceConnection",
JAVA_SIG_METHOD(JAVA_SIG_USB_CONNECTION, JAVA_SIG_USB_DEVICE))) {
jobject connection = (*env)->CallStaticObjectMethod(env, usbHelperClass, method, device);
if (!clearJavaException(env, 1)) {
if (connection) {
return connection;
}
}
errno = EIO;
}
}
return NULL;
}
static int
usbFindDeviceClass (JNIEnv *env) {
return findJavaClass(env, &usbDeviceClass, JAVA_OBJ_USB_DEVICE);
}
static int
usbGetIntDeviceProperty (
JNIEnv *env, jint *value,
jobject device, const char *methodName, jmethodID *methodIdentifier
) {
if (usbFindDeviceClass(env)) {
if (findJavaInstanceMethod(env, methodIdentifier, usbDeviceClass, methodName,
JAVA_SIG_METHOD(JAVA_SIG_INT, ))) {
if (!clearJavaException(env, 1)) {
*value = (*env)->CallIntMethod(env, device, *methodIdentifier);
return 1;
}
errno = EIO;
}
}
return 0;
}
static int
usbGetDeviceVendor (JNIEnv *env, jobject device, UsbDeviceDescriptor *descriptor) {
static jmethodID method = 0;
jint vendor;
int ok = usbGetIntDeviceProperty(env, &vendor, device, "getVendorId", &method);
if (ok) putLittleEndian16(&descriptor->idVendor, vendor);
return ok;
}
static int
usbGetDeviceProduct (JNIEnv *env, jobject device, UsbDeviceDescriptor *descriptor) {
static jmethodID method = 0;
jint product;
int ok = usbGetIntDeviceProperty(env, &product, device, "getProductId", &method);
if (ok) putLittleEndian16(&descriptor->idProduct, product);
return ok;
}
static int
usbGetDeviceClass (JNIEnv *env, jobject device, UsbDeviceDescriptor *descriptor) {
static jmethodID method = 0;
jint class;
int ok = usbGetIntDeviceProperty(env, &class, device, "getDeviceClass", &method);
if (ok) descriptor->bDeviceClass = class;
return ok;
}
static int
usbGetDeviceSubclass (JNIEnv *env, jobject device, UsbDeviceDescriptor *descriptor) {
static jmethodID method = 0;
jint subclass;
int ok = usbGetIntDeviceProperty(env, &subclass, device, "getDeviceSubclass", &method);
if (ok) descriptor->bDeviceSubClass = subclass;
return ok;
}
static int
usbGetDeviceProtocol (JNIEnv *env, jobject device, UsbDeviceDescriptor *descriptor) {
static jmethodID method = 0;
jint protocol;
int ok = usbGetIntDeviceProperty(env, &protocol, device, "getDeviceProtocol", &method);
if (ok) descriptor->bDeviceProtocol = protocol;
return ok;
}
static int
usbFindInterfaceClass (JNIEnv *env) {
return findJavaClass(env, &usbInterfaceClass, JAVA_OBJ_USB_INTERFACE);
}
static int
usbGetInterfaceIdentifier (JNIEnv *env, uint8_t *identifier, jobject interface) {
if (usbFindInterfaceClass(env)) {
static jmethodID method = 0;
if (findJavaInstanceMethod(env, &method, usbInterfaceClass, "getId",
JAVA_SIG_METHOD(JAVA_SIG_INT,
))) {
jint result = (*env)->CallIntMethod(env, interface, method);
if (!clearJavaException(env, 1)) {
*identifier = result;
return 1;
}
errno = EIO;
}
}
return 0;
}
static int
usbFindConnectionClass (JNIEnv *env) {
return findJavaClass(env, &usbConnectionClass, JAVA_OBJ_USB_CONNECTION);
}
static int
usbDoClaimInterface (JNIEnv *env, jobject connection, jobject interface) {
if (usbFindConnectionClass(env)) {
static jmethodID method = 0;
if (findJavaInstanceMethod(env, &method, usbConnectionClass, "claimInterface",
JAVA_SIG_METHOD(JAVA_SIG_BOOLEAN,
JAVA_SIG_USB_INTERFACE // interface
JAVA_SIG_BOOLEAN // force
))) {
jboolean result = (*env)->CallBooleanMethod(env, connection, method, interface, JNI_TRUE);
if (clearJavaException(env, 1)) {
errno = EIO;
} else if (result) {
return 1;
} else {
logSystemError("USB claim interface");
}
}
}
return 0;
}
static int
usbDoReleaseInterface (JNIEnv *env, jobject connection, jobject interface) {
if (usbFindConnectionClass(env)) {
static jmethodID method = 0;
if (findJavaInstanceMethod(env, &method, usbConnectionClass, "releaseInterface",
JAVA_SIG_METHOD(JAVA_SIG_BOOLEAN,
JAVA_SIG_USB_INTERFACE // interface
))) {
jboolean result = (*env)->CallBooleanMethod(env, connection, method, interface);
if (clearJavaException(env, 1)) {
errno = EIO;
} else if (result) {
return 1;
} else {
logSystemError("USB release interface");
}
}
}
return 0;
}
static int
usbDoControlTransfer (
JNIEnv *env, jobject connection,
int type, int request, int value, int index,
jbyteArray buffer, int length, int timeout
) {
if (usbFindConnectionClass(env)) {
static jmethodID method = 0;
if (findJavaInstanceMethod(env, &method, usbConnectionClass, "controlTransfer",
JAVA_SIG_METHOD(JAVA_SIG_INT,
JAVA_SIG_INT // type
JAVA_SIG_INT // request
JAVA_SIG_INT // value
JAVA_SIG_INT // index
JAVA_SIG_ARRAY(JAVA_SIG_BYTE) // buffer
JAVA_SIG_INT // length
JAVA_SIG_INT // timeout
))) {
jint result = (*env)->CallIntMethod(env, connection, method,
type, request, value, index,
buffer, length, timeout);
if (!clearJavaException(env, 1)) return result;
errno = EIO;
}
}
return -1;
}
static int
usbDoBulkTransfer (
JNIEnv *env, jobject connection, jobject endpoint,
jbyteArray buffer, int length, int timeout
) {
if (usbFindConnectionClass(env)) {
static jmethodID method = 0;
if (findJavaInstanceMethod(env, &method, usbConnectionClass, "bulkTransfer",
JAVA_SIG_METHOD(JAVA_SIG_INT,
JAVA_SIG_USB_ENDPOINT // endpoint
JAVA_SIG_ARRAY(JAVA_SIG_BYTE) // buffer
JAVA_SIG_INT // length
JAVA_SIG_INT // timeout
))) {
jint result = (*env)->CallIntMethod(env, connection, method,
endpoint, buffer, length, timeout);
if (!clearJavaException(env, 1)) return result;
errno = EIO;
}
}
return -1;
}
static void
usbCloseDeviceConnection (JNIEnv *env, jobject connection) {
if (usbFindConnectionClass(env)) {
static jmethodID method = 0;
if (findJavaInstanceMethod(env, &method, usbConnectionClass, "close",
JAVA_SIG_METHOD(JAVA_SIG_VOID, ))) {
(*env)->CallVoidMethod(env, connection, method);
clearJavaException(env, 1);
}
}
}
static int
usbOpenConnection (UsbDeviceExtension *devx) {
if (devx->connection) return 1;
{
const UsbHostDevice *host = devx->host;
jobject connection = usbOpenDeviceConnection(host->env, host->device);
if (connection) {
devx->connection = (*host->env)->NewGlobalRef(host->env, connection);
(*host->env)->DeleteLocalRef(host->env, connection);
connection = NULL;
if (devx->connection) return 1;
logMallocError();
clearJavaException(host->env, 0);
}
}
return 0;
}
static int
usbGetFileDescriptor (UsbDeviceExtension *devx) {
if (devx->fileDescriptor != INVALID_FILE_DESCRIPTOR) return 1;
JNIEnv *env = devx->host->env;
if (usbFindConnectionClass(env)) {
static jmethodID method = 0;
if (findJavaInstanceMethod(env, &method, usbConnectionClass, "getFileDescriptor",
JAVA_SIG_METHOD(JAVA_SIG_INT, ))) {
jint fileDescriptor = (*env)->CallIntMethod(env, devx->connection, method);
if (!clearJavaException(env, 1)) {
devx->fileDescriptor = fileDescriptor;
return 1;
}
}
}
errno = EIO;
return 0;
}
static void
usbUnsetInterface (UsbDeviceExtension *devx) {
if (devx->interface) {
JNIEnv *env = devx->host->env;
(*env)->DeleteGlobalRef(env, devx->interface);
devx->interface = NULL;
}
}
static int
usbSetInterface (UsbDeviceExtension *devx, uint8_t identifier) {
JNIEnv *env = devx->host->env;
if (devx->interface) {
uint8_t id;
if (!usbGetInterfaceIdentifier(env, &id, devx->interface)) return 0;
if (id == identifier) return 1;
}
{
jobject interface = usbGetDeviceInterface(env, devx->host->device, identifier);
if (interface) {
usbUnsetInterface(devx);
devx->interface = (*env)->NewGlobalRef(env, interface);
(*env)->DeleteLocalRef(env, interface);
interface = NULL;
if (devx->interface) return 1;
logMallocError();
}
}
return 0;
}
int
usbDisableAutosuspend (UsbDevice *device) {
logUnsupportedFunction();
return 0;
}
int
usbSetConfiguration (UsbDevice *device, unsigned char configuration) {
logMessage(LOG_CATEGORY(USB_IO), "setting configuration: %u", configuration);
if (configuration == 1) return 1;
logUnsupportedFunction();
return 0;
}
int
usbClaimInterface (UsbDevice *device, unsigned char interface) {
UsbDeviceExtension *devx = device->extension;
logMessage(LOG_CATEGORY(USB_IO), "claiming interface: %u", interface);
if (usbSetInterface(devx, interface)) {
if (usbOpenConnection(devx)) {
if (usbDoClaimInterface(devx->host->env, devx->connection, devx->interface)) {
return 1;
}
}
}
return 0;
}
int
usbReleaseInterface (UsbDevice *device, unsigned char interface) {
UsbDeviceExtension *devx = device->extension;
logMessage(LOG_CATEGORY(USB_IO), "releasing interface: %u", interface);
if (usbSetInterface(devx, interface)) {
if (usbOpenConnection(devx)) {
if (usbDoReleaseInterface(devx->host->env, devx->connection, devx->interface)) {
return 1;
}
}
}
return 0;
}
int
usbSetAlternative (
UsbDevice *device,
unsigned char interface,
unsigned char alternative
) {
logMessage(LOG_CATEGORY(USB_IO), "setting alternative: %u[%u]", interface, alternative);
if (alternative == 0) return 1;
logUnsupportedFunction();
return 0;
}
int
usbResetDevice (UsbDevice *device) {
UsbDeviceExtension *devx = device->extension;
logMessage(LOG_CATEGORY(USB_IO), "reset device");
if (usbGetFileDescriptor(devx)) {
unsigned int arg = 0;
if (ioctl(devx->fileDescriptor, USBDEVFS_RESET, &arg) != -1) return 1;
logSystemError("USB device reset");
}
return 0;
}
int
usbClearHalt (UsbDevice *device, unsigned char endpointAddress) {
UsbDeviceExtension *devx = device->extension;
logMessage(LOG_CATEGORY(USB_IO), "clear halt: %02X", endpointAddress);
if (usbGetFileDescriptor(devx)) {
unsigned int arg = endpointAddress;
if (ioctl(devx->fileDescriptor, USBDEVFS_CLEAR_HALT, &arg) != -1) return 1;
logSystemError("USB endpoint clear");
}
return 0;
}
ssize_t
usbControlTransfer (
UsbDevice *device,
uint8_t direction,
uint8_t recipient,
uint8_t type,
uint8_t request,
uint16_t value,
uint16_t index,
void *buffer,
uint16_t length,
int timeout
) {
ssize_t result = -1;
UsbDeviceExtension *devx = device->extension;
if (usbOpenConnection(devx)) {
const UsbHostDevice *host = devx->host;
jbyteArray bytes = (*host->env)->NewByteArray(host->env, length);
if (bytes) {
if (direction == UsbControlDirection_Output) {
(*host->env)->SetByteArrayRegion(host->env, bytes, 0, length, buffer);
if (length) logBytes(LOG_CATEGORY(USB_IO), "control output", buffer, length);
}
result = usbDoControlTransfer(host->env, devx->connection,
direction | recipient | type,
request, value, index,
bytes, length, timeout);
if (direction == UsbControlDirection_Input) {
if (result > 0) {
(*host->env)->GetByteArrayRegion(host->env, bytes, 0, result, buffer);
logBytes(LOG_CATEGORY(USB_IO), "control input", buffer, result);
}
}
(*host->env)->DeleteLocalRef(host->env, bytes);
} else {
logMallocError();
}
}
if (result == -1) logSystemError("USB control transfer");
return result;
}
void *
usbSubmitRequest (
UsbDevice *device,
unsigned char endpointAddress,
void *buffer,
size_t length,
void *context
) {
logUnsupportedFunction();
return NULL;
}
int
usbCancelRequest (UsbDevice *device, void *request) {
logUnsupportedFunction();
return 0;
}
void *
usbReapResponse (
UsbDevice *device,
unsigned char endpointAddress,
UsbResponse *response,
int wait
) {
logUnsupportedFunction();
return NULL;
}
int
usbMonitorInputEndpoint (
UsbDevice *device, unsigned char endpointNumber,
AsyncMonitorCallback *callback, void *data
) {
return 0;
}
ssize_t
usbReadEndpoint (
UsbDevice *device,
unsigned char endpointNumber,
void *buffer,
size_t length,
int timeout
) {
ssize_t result = -1;
UsbEndpoint *endpoint = usbGetInputEndpoint(device, endpointNumber);
logMessage(LOG_CATEGORY(USB_IO), "reading endpoint: %u", endpointNumber);
if (endpoint) {
UsbDeviceExtension *devx = device->extension;
if (usbOpenConnection(devx)) {
const UsbHostDevice *host = devx->host;
JNIEnv *env = host->env;
jbyteArray bytes = (*env)->NewByteArray(env, length);
if (bytes) {
UsbEndpointExtension *eptx = endpoint->extension;
result = usbDoBulkTransfer(env, devx->connection, eptx->object, bytes, length, timeout);
if (result > 0) (*env)->GetByteArrayRegion(env, bytes, 0, result, buffer);
(*env)->DeleteLocalRef(env, bytes);
} else {
logMallocError();
clearJavaException(env, 0);
}
}
}
if (result >= 0) {
if (!usbApplyInputFilters(endpoint, buffer, length, &result)) {
errno = EIO;
result = -1;
}
}
if (result == -1) {
if (errno == ETIMEDOUT) errno = EAGAIN;
if (errno != EAGAIN) logSystemError("USB bulk read");
}
return result;
}
ssize_t
usbWriteEndpoint (
UsbDevice *device,
unsigned char endpointNumber,
const void *buffer,
size_t length,
int timeout
) {
ssize_t result = -1;
UsbEndpoint *endpoint = usbGetOutputEndpoint(device, endpointNumber);
if (endpoint) {
UsbDeviceExtension *devx = device->extension;
usbLogEndpointData(endpoint, "output", buffer, length);
if (usbOpenConnection(devx)) {
const UsbHostDevice *host = devx->host;
JNIEnv *env = host->env;
jbyteArray bytes = (*env)->NewByteArray(env, length);
if (bytes) {
UsbEndpointExtension *eptx = endpoint->extension;
(*env)->SetByteArrayRegion(env, bytes, 0, length, buffer);
result = usbDoBulkTransfer(env, devx->connection, eptx->object, bytes, length, timeout);
(*env)->DeleteLocalRef(env, bytes);
} else {
logMallocError();
clearJavaException(env, 0);
}
}
}
if (result == -1) logSystemError("USB bulk write");
return result;
}
int
usbReadDeviceDescriptor (UsbDevice *device) {
device->descriptor = device->extension->host->descriptor;
return 1;
}
int
usbAllocateEndpointExtension (UsbEndpoint *endpoint) {
UsbDevice *device = endpoint->device;
const UsbInterfaceDescriptor *interface = endpoint->interface;
UsbDeviceExtension *devx = device->extension;
const UsbHostDevice *host = devx->host;
JNIEnv *env = host->env;
if (usbSetInterface(devx, interface->bInterfaceNumber)) {
jobject localReference = usbGetInterfaceEndpoint(env, devx->interface, endpoint->descriptor->bEndpointAddress);
if (localReference) {
jobject globalReference = (*env)->NewGlobalRef(env, localReference);
(*env)->DeleteLocalRef(env, localReference);
localReference = NULL;
if (globalReference) {
UsbEndpointExtension *eptx = malloc(sizeof(*eptx));
if (eptx) {
memset(eptx, 0, sizeof(*eptx));
eptx->endpoint = endpoint;
eptx->object = globalReference;
endpoint->extension = eptx;
return 1;
} else {
logMallocError();
}
(*env)->DeleteGlobalRef(env, globalReference);
globalReference = NULL;
} else {
logMallocError();
clearJavaException(env, 0);
}
} else {
logMessage(LOG_ERR, "couldn't get endpoint object");
errno = EIO;
}
} else {
errno = ENOSYS;
}
return 0;
}
void
usbDeallocateEndpointExtension (UsbEndpointExtension *eptx) {
UsbEndpoint *endpoint = eptx->endpoint;
UsbDevice *device = endpoint->device;
UsbDeviceExtension *devx = device->extension;
const UsbHostDevice *host = devx->host;
JNIEnv *env = host->env;
if (eptx->object) {
(*env)->DeleteGlobalRef(env, eptx->object);
eptx->object = NULL;
}
free(eptx);
}
void
usbDeallocateDeviceExtension (UsbDeviceExtension *devx) {
usbUnsetInterface(devx);
if (devx->connection) {
const UsbHostDevice *host = devx->host;
usbCloseDeviceConnection(host->env, devx->connection);
(*host->env)->DeleteGlobalRef(host->env, devx->connection);
devx->connection = NULL;
}
free(devx);
}
static void
usbDeallocateHostDevice (void *item, void *data) {
UsbHostDevice *host = item;
(*host->env)->DeleteGlobalRef(host->env, host->device);
free(host);
}
static int
usbAddHostDevice (JNIEnv *env, jobject device) {
UsbHostDevice *host;
if ((host = malloc(sizeof(*host)))) {
memset(host, 0, sizeof(*host));
host->env = env;
host->descriptor.bLength = UsbDescriptorSize_Device;
host->descriptor.bDescriptorType = UsbDescriptorType_Device;
host->descriptor.bNumConfigurations = 1;
if ((host->device = (*host->env)->NewGlobalRef(host->env, device))) {
if (usbGetDeviceVendor(host->env, host->device, &host->descriptor)) {
if (usbGetDeviceProduct(host->env, host->device, &host->descriptor)) {
if (usbGetDeviceClass(host->env, host->device, &host->descriptor)) {
if (usbGetDeviceSubclass(host->env, host->device, &host->descriptor)) {
if (usbGetDeviceProtocol(host->env, host->device, &host->descriptor)) {
if (enqueueItem(usbHostDevices, host)) {
return 1;
}
}
}
}
}
}
(*host->env)->DeleteGlobalRef(host->env, host->device);
}
free(host);
} else {
logMallocError();
}
return 0;
}
typedef struct {
UsbDeviceChooser *chooser;
void *data;
UsbDevice *device;
} UsbTestHostDeviceData;
static int
usbTestHostDevice (void *item, void *data) {
const UsbHostDevice *host = item;
UsbTestHostDeviceData *test = data;
UsbDeviceExtension *devx;
if ((devx = malloc(sizeof(*devx)))) {
memset(devx, 0, sizeof(*devx));
devx->host = host;
devx->connection = NULL;
devx->interface = NULL;
devx->fileDescriptor = INVALID_FILE_DESCRIPTOR;
if ((test->device = usbTestDevice(devx, test->chooser, test->data))) return 1;
usbDeallocateDeviceExtension(devx);
} else {
logMallocError();
}
return 0;
}
UsbDevice *
usbFindDevice (UsbDeviceChooser *chooser, UsbChooseChannelData *data) {
if (!usbHostDevices) {
int ok = 0;
if ((usbHostDevices = newQueue(usbDeallocateHostDevice, NULL))) {
JNIEnv *env = getJavaNativeInterface();
if (env) {
jobject iterator = usbGetDeviceIterator(env);
if (iterator) {
jobject device;
ok = 1;
while ((device = usbGetNextDevice(env, iterator))) {
int added = usbAddHostDevice(env, device);
(*env)->DeleteLocalRef(env, device);
if (!added) {
ok = 0;
break;
}
}
(*env)->DeleteLocalRef(env, iterator);
}
}
if (!ok) {
deallocateQueue(usbHostDevices);
usbHostDevices = NULL;
}
}
}
if (usbHostDevices) {
UsbTestHostDeviceData test = {
.chooser = chooser,
.data = data,
.device = NULL
};
if (processQueue(usbHostDevices, usbTestHostDevice, &test)) return test.device;
}
return NULL;
}
void
usbForgetDevices (void) {
if (usbHostDevices) {
deallocateQueue(usbHostDevices);
usbHostDevices = NULL;
}
}