3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver.scsi.commands;
19 |
20 | import java.nio.ByteBuffer;
21 |
22 | /**
23 | * Represents the command to read the capacity from the mass storage device.
24 | *
25 | * The data is transferred in the data phase.
26 | *
27 | * @author mjahnen
28 | * @see com.github.mjdev.libaums.driver.scsi.commands.ScsiReadCapacityResponse
29 | */
30 | public class ScsiReadCapacity extends CommandBlockWrapper {
31 |
32 | private static final int RESPONSE_LENGTH = 0x8;
33 | private static final byte LENGTH = 0x10;
34 | private static final byte OPCODE = 0x25;
35 |
36 | public ScsiReadCapacity() {
37 | super(RESPONSE_LENGTH, Direction.IN, (byte) 0, LENGTH);
38 | }
39 |
40 | @Override
41 | public void serialize(ByteBuffer buffer) {
42 | super.serialize(buffer);
43 | buffer.put(OPCODE);
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/jwoolston/libusb/UsbEndpoint.java:
--------------------------------------------------------------------------------
1 | package com.jwoolston.libusb;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | public class UsbEndpoint extends BaseUsbEndpoint implements Parcelable {
7 |
8 | public static final Parcelable.Creator CREATOR =
9 | new Parcelable.Creator() {
10 | public UsbEndpoint createFromParcel(Parcel in) {
11 | int address = in.readInt();
12 | int attributes = in.readInt();
13 | int maxPacketSize = in.readInt();
14 | int interval = in.readInt();
15 | return new UsbEndpoint(address, attributes, maxPacketSize, interval);
16 | }
17 |
18 | public UsbEndpoint[] newArray(int size) {
19 | return new UsbEndpoint[size];
20 | }
21 | };
22 |
23 | /**
24 | * UsbEndpoint should only be instantiated by UsbService implementation
25 | *
26 | * @param address
27 | * @param attributes
28 | * @param maxPacketSize
29 | * @param interval
30 | */
31 | UsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
32 | super(address, attributes, maxPacketSize, interval);
33 | }
34 |
35 | public int describeContents() {
36 | return 0;
37 | }
38 |
39 | public void writeToParcel(Parcel parcel, int flags) {
40 | parcel.writeInt(address);
41 | parcel.writeInt(attributes);
42 | parcel.writeInt(maxPacketSize);
43 | parcel.writeInt(interval);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | defaults: &defaults
2 | working_directory: ~/repo
3 | docker:
4 | - image: jwoolston/java-android-docker:latest
5 | environment:
6 | # Customize the JVM maximum heap limit
7 | JVM_OPTS: -Xmx3200m
8 | TERM: dumb
9 |
10 | version: 2
11 | jobs:
12 | build:
13 | <<: *defaults
14 | steps:
15 | - checkout
16 | - run:
17 | name: Git Submodules
18 | command: |
19 | git submodule sync
20 | git submodule update --init
21 | - run:
22 | name: Execute Gradle
23 | command: |
24 | echo "org.gradle.daemon=false" >> gradle.properties
25 | gpg --version
26 | if [ "master" = "$CIRCLE_BRANCH" ] || [ ! -z "$CIRCLE_TAG" ]; then
27 | echo "signing.keyId=${SIGNING_KEY}" >> "gradle.properties"
28 | echo "signing.password=${SIGNING_PASSWORD}" >> "gradle.properties"
29 | echo "signing.secretKeyRingFile=/root/repo/secret.gpg" >> "gradle.properties"
30 | gpg --cipher-algo AES256 --yes --batch --passphrase=$ENC_FILE_KEY secret.gpg.gpg
31 | ./gradlew build publish
32 | else
33 | ./gradlew build
34 | fi
35 | - run:
36 | name: Code Coverage
37 | command: |
38 | bash <(wget -q https://codecov.io/bash)
39 | bash <(wget -q https://codecov.io/bash) -f "./library/integration.xml" -F integration
40 |
41 | workflows:
42 | version: 2
43 | build:
44 | jobs:
45 | - build:
46 | context: Sonatype
47 | filters:
48 | tags:
49 | only: /.*/
50 |
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/scsi/commands/ScsiRequestSense.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver.scsi.commands;
19 |
20 | import java.nio.ByteBuffer;
21 |
22 | /**
23 | * This class is used to issue a SCSI request sense when a command has failed.
24 | *
25 | * @author mjahnen
26 | * @see com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper
27 | * #getbCswStatus()
28 | */
29 | public class ScsiRequestSense extends CommandBlockWrapper {
30 | private static final byte OPCODE = 0x3;
31 | private static final byte LENGTH = 0x6;
32 |
33 | private byte allocationLength;
34 |
35 | public ScsiRequestSense(byte allocationLength) {
36 | super(0, Direction.NONE, (byte) 0, LENGTH);
37 | this.allocationLength = allocationLength;
38 | }
39 |
40 | @Override
41 | public void serialize(ByteBuffer buffer) {
42 | super.serialize(buffer);
43 | buffer.put(OPCODE);
44 | buffer.put((byte) 0);
45 | buffer.put((byte) 0);
46 | buffer.put((byte) 0);
47 | buffer.put(allocationLength);
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/scsi/commands/ScsiTestUnitReady.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver.scsi.commands;
19 |
20 | import java.nio.ByteBuffer;
21 |
22 | /**
23 | * This command is used to determine if the logical unit of the mass storage
24 | * device is ready. Sometimes this command fails even if the unit can process
25 | * all commands successfully. Thus this command issues only a warning in the
26 | * {@link com.github.mjdev.libaums.driver.scsi.ScsiBlockDevice}.
27 | *
28 | * This command has no data phase, the result is determined by
29 | * {@link com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper #getbCswStatus()}.
30 | *
31 | * @author mjahnen
32 | *
33 | */
34 | public class ScsiTestUnitReady extends CommandBlockWrapper {
35 |
36 | private static final byte LENGTH = 0x6;
37 | private static final byte OPCODE = 0x0;
38 |
39 | public ScsiTestUnitReady() {
40 | super(0, Direction.NONE, (byte) 0, LENGTH);
41 | }
42 |
43 | @Override
44 | public void serialize(ByteBuffer buffer) {
45 | super.serialize(buffer);
46 | buffer.put(OPCODE);
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/scsi/commands/ScsiInquiry.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver.scsi.commands;
19 |
20 | import java.nio.ByteBuffer;
21 |
22 | /**
23 | * This class represents a SCSI Inquiry command. It is used to get important
24 | * information about the connected mass storage device. This information include
25 | * the supported SCSI commands.
26 | *
27 | * The response is sent in the data phase.
28 | *
29 | * @author mjahnen
30 | * @see com.github.mjdev.libaums.driver.scsi.commands.ScsiInquiryResponse
31 | */
32 | public class ScsiInquiry extends CommandBlockWrapper {
33 |
34 | private static final byte LENGTH = 0x6;
35 | private static final byte OPCODE = 0x12;
36 |
37 | private byte allocationLength;
38 |
39 | public ScsiInquiry(byte allocationLength) {
40 | super(allocationLength, Direction.IN, (byte) 0, LENGTH);
41 |
42 | this.allocationLength = allocationLength;
43 | }
44 |
45 | @Override
46 | public void serialize(ByteBuffer buffer) {
47 | super.serialize(buffer);
48 | buffer.put(OPCODE);
49 | buffer.put((byte) 0);
50 | buffer.put((byte) 0);
51 | buffer.put((byte) 0);
52 | buffer.put(allocationLength);
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/BlockDeviceDriverFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver;
19 |
20 | import com.jwoolston.android.libusb.msc_test_core.driver.scsi.AsyncScsiBlockDevice;
21 | import com.jwoolston.android.libusb.msc_test_core.driver.scsi.ScsiBlockDevice;
22 | import com.jwoolston.android.libusb.msc_test_core.usb.UsbCommunication;
23 |
24 | /**
25 | * A helper class to create different
26 | * {@link com.github.mjdev.libaums.driver.BlockDeviceDriver}s.
27 | *
28 | * @author mjahnen
29 | *
30 | */
31 | public class BlockDeviceDriverFactory {
32 | /**
33 | * This method creates a
34 | * {@link com.github.mjdev.libaums.driver.BlockDeviceDriver} which is
35 | * suitable for the underlying mass storage device.
36 | *
37 | * @param usbCommunication
38 | * The underlying USB communication.
39 | * @return A driver which can handle the USB mass storage device.
40 | */
41 | public static BlockDeviceDriver createBlockDevice(UsbCommunication usbCommunication, boolean async) {
42 | // we currently only support scsi transparent command set
43 | if (async) {
44 | return new AsyncScsiBlockDevice(usbCommunication);
45 | } else {
46 | return new ScsiBlockDevice(usbCommunication);
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/jwoolston/libusb/UsbManager.java:
--------------------------------------------------------------------------------
1 | package com.jwoolston.libusb;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 |
6 | import com.toxicbakery.logging.Arbor;
7 |
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public class UsbManager extends BaseUsbManager {
11 |
12 | private final Context context;
13 | private final android.hardware.usb.UsbManager androidUsbManager;
14 |
15 | public UsbManager(@NotNull Context context) {
16 | super();
17 | this.context = context.getApplicationContext();
18 | androidUsbManager = (android.hardware.usb.UsbManager) context.getSystemService(Context.USB_SERVICE);
19 | }
20 |
21 | @NonNull
22 | public UsbDeviceConnection registerDevice(@NonNull android.hardware.usb.UsbDevice device) throws
23 | DevicePermissionDenied {
24 | synchronized (cacheLock) {
25 | final String key = device.getDeviceName();
26 | if (localConnectionCache.containsKey(key)) {
27 | // We have already dealt with this device, do nothing
28 | Arbor.d("returning cached device.");
29 | return (UsbDeviceConnection) localConnectionCache.get(key);
30 | } else {
31 | android.hardware.usb.UsbDeviceConnection connection = androidUsbManager.openDevice(device);
32 | if (connection == null) {
33 | throw new DevicePermissionDenied(device);
34 | }
35 | final UsbDevice usbDevice = UsbDevice.fromAndroidDevice(libUsbContext, device, connection);
36 | final UsbDeviceConnection usbConnection = UsbDeviceConnection.fromAndroidConnection(context, this, usbDevice);
37 | localDeviceCache.put(key, usbDevice);
38 | localConnectionCache.put(key, usbConnection);
39 |
40 | usbDevice.populate();
41 | return usbConnection;
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/jvm/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'maven-publish'
3 | id 'signing'
4 | }
5 |
6 | apply plugin: 'java-library'
7 |
8 | dependencies {
9 | implementation fileTree(dir: 'libs', include: ['*.jar'])
10 |
11 | api project(':library')
12 |
13 | // Utils
14 | implementation 'org.jetbrains:annotations:16.0.1'
15 | }
16 |
17 | sourceCompatibility = "8"
18 | targetCompatibility = "8"
19 |
20 | publishing {
21 | publications {
22 | all(MavenPublication) { publication ->
23 | artifact jar
24 | pom {
25 | name = POM_NAME
26 | description = POM_DESCRIPTION
27 | url = POM_URL
28 | scm {
29 | url = POM_SCM_URL
30 | connection = POM_SCM_CONNECTION
31 | developerConnection = POM_SCM_DEV_CONNECTION
32 | }
33 | licenses {
34 | license {
35 | name = POM_LICENCE_NAME
36 | url = POM_LICENCE_URL
37 | distribution = POM_LICENCE_DIST
38 | }
39 | }
40 | developers {
41 | developer {
42 | id = POM_DEVELOPER_ID
43 | name = POM_DEVELOPER_NAME
44 | email = POM_DEVELOPER_EMAIL
45 | organization = POM_DEVELOPER_ORGANIZATION
46 | organizationUrl = POM_DEVELOPER_ORGANIZATION_URL
47 | }
48 | }
49 | }
50 | }
51 | }
52 | repositories {
53 | maven {
54 | url !version.contains("SNAPSHOT") ? getReleaseUrl() : getSnapshotUrl()
55 | credentials {
56 | username = getRepoUsername()
57 | password = getRepoPassword()
58 | }
59 | }
60 | }
61 | }
62 |
63 | signing {
64 | required isCi()
65 | sign publishing.publications
66 | }
--------------------------------------------------------------------------------
/jni/usb_configuration.c:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jared Woolston (Jared.Woolston@gmail.com)
3 | //
4 |
5 | #include
6 | #include
7 |
8 | #define LOG_TAG "UsbConfiguration-Native"
9 |
10 | JNIEXPORT jobject JNICALL
11 | Java_com_jwoolston_libusb_BaseUsbConfiguration_nativeGetConfiguration(JNIEnv *env, jclass type, jobject device,
12 | jint configuration) {
13 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *) (*env)->GetDirectBufferAddress(env,
14 | device);
15 | struct libusb_config_descriptor *config;
16 | int retval = libusb_get_config_descriptor(deviceHandle->dev, (uint8_t) (0xFF & configuration), &config);
17 | if (retval) {
18 | LOGE("Error fetching configuration descriptor: %s", libusb_strerror(retval));
19 | return NULL;
20 | }
21 |
22 | return ((*env)->NewDirectByteBuffer(env, (void *) config, sizeof(struct libusb_config_descriptor)));
23 | }
24 |
25 | JNIEXPORT jobject JNICALL
26 | Java_com_jwoolston_libusb_BaseUsbConfiguration_nativeGetInterface(JNIEnv *env, jclass type, jobject nativeObject,
27 | jint interfaceIndex) {
28 | struct libusb_config_descriptor *config = (struct libusb_config_descriptor *)
29 | (*env)->GetDirectBufferAddress(env, nativeObject);
30 |
31 | return ((*env)->NewDirectByteBuffer(env, (void *) (config->interface + interfaceIndex), sizeof(struct
32 | libusb_interface)));
33 | }
34 |
35 | JNIEXPORT void JNICALL
36 | Java_com_jwoolston_libusb_BaseUsbConfiguration_nativeDestroy(JNIEnv *env, jclass type, jobject nativeObject) {
37 | struct libusb_config_descriptor *config = (struct libusb_config_descriptor *)
38 | (*env)->GetDirectBufferAddress(env, nativeObject);
39 | libusb_free_config_descriptor(config);
40 | }
41 |
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/library/src/main/java/com/jwoolston/libusb/LibusbError.java:
--------------------------------------------------------------------------------
1 | package com.jwoolston.libusb;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | /**
6 | * Error codes. Most libusb functions return 0 on success or one of these codes on failure.
7 | *
8 | * @author Jared Woolston (Jared.Woolston@gmail.com)
9 | */
10 | public enum LibusbError {
11 |
12 | /** Success (no error) */
13 | LIBUSB_SUCCESS(0),
14 |
15 | /** Input/output error */
16 | LIBUSB_ERROR_IO(-1),
17 |
18 | /** Invalid parameter */
19 | LIBUSB_ERROR_INVALID_PARAM(-2),
20 |
21 | /** Access denied (insufficient permissions) */
22 | LIBUSB_ERROR_ACCESS(-3),
23 |
24 | /** No such device (it may have been disconnected) */
25 | LIBUSB_ERROR_NO_DEVICE(-4),
26 |
27 | /** Entity not found */
28 | LIBUSB_ERROR_NOT_FOUND(-5),
29 |
30 | /** Resource busy */
31 | LIBUSB_ERROR_BUSY(-6),
32 |
33 | /** Operation timed out */
34 | LIBUSB_ERROR_TIMEOUT(-7),
35 |
36 | /** Overflow */
37 | LIBUSB_ERROR_OVERFLOW(-8),
38 |
39 | /** Pipe error */
40 | LIBUSB_ERROR_PIPE(-9),
41 |
42 | /** System call interrupted (perhaps due to signal) */
43 | LIBUSB_ERROR_INTERRUPTED(-10),
44 |
45 | /** Insufficient memory */
46 | LIBUSB_ERROR_NO_MEM(-11),
47 |
48 | /** Operation not supported or unimplemented on this platform */
49 | LIBUSB_ERROR_NOT_SUPPORTED(-12),
50 |
51 | /** Other error */
52 | LIBUSB_ERROR_OTHER(-99);
53 |
54 | private final int code;
55 |
56 | LibusbError(int code) {
57 | this.code = code;
58 | }
59 |
60 | @Override
61 | public String toString() {
62 | return getDescriptionString(code);
63 | }
64 |
65 | @NotNull
66 | public static LibusbError fromNative(int code) {
67 | for (LibusbError error : values()) {
68 | if (error.code == code) {
69 | return error;
70 | }
71 | }
72 | return LIBUSB_ERROR_OTHER;
73 | }
74 |
75 | private static native String getDescriptionString(int code);
76 | }
77 |
--------------------------------------------------------------------------------
/jvm/src/main/java/com/jwoolston/libusb/UsbDevice.java:
--------------------------------------------------------------------------------
1 | package com.jwoolston.libusb;
2 |
3 | import com.jwoolston.libusb.util.Preconditions;
4 |
5 | import org.jetbrains.annotations.NotNull;
6 | import org.jetbrains.annotations.Nullable;
7 |
8 | import java.nio.ByteBuffer;
9 |
10 | public class UsbDevice extends BaseUsbDevice {
11 |
12 | /*private UsbDevice(@NotNull android.hardware.usb.UsbDeviceConnection connection,
13 | @NotNull android.hardware.usb.UsbDevice device, @Nullable ByteBuffer nativeObject) {
14 |
15 |
16 | serialNumber = connection.getSerial();
17 |
18 |
19 | fileDescriptor = connection.getFileDescriptor();
20 | }*/
21 |
22 | private UsbDevice(long nativePointer) {
23 | Preconditions.checkArgument(nativePointer != 0, "BaseUsbDevice initialization failed.");
24 | LibUsbDeviceDescriptor descriptor = LibUsbDeviceDescriptor.getDeviceDescriptor(nativePointer);
25 | initFromDescriptor(descriptor);
26 | }
27 |
28 | private UsbDevice(@Nullable ByteBuffer nativeObject) {
29 | LibUsbDeviceDescriptor descriptor = LibUsbDeviceDescriptor.getDeviceDescriptor(this);
30 | initFromDescriptor(descriptor);
31 | }
32 |
33 | @Override
34 | UsbConfiguration createConfiguration(int id, @Nullable String name, int attributes, int maxPower) {
35 | return new UsbConfiguration(id, name, attributes, maxPower);
36 | }
37 |
38 | @Override
39 | UsbInterface createInterface(int id, int alternateSetting, @Nullable String name,
40 | int interfaceClass, int subClass, int protocol) {
41 | return new UsbInterface(id, alternateSetting, name, interfaceClass, subClass, protocol);
42 | }
43 |
44 | @Override
45 | UsbEndpoint createEndpoint(int address, int attributes, int maxPacketSize, int interval) {
46 | return new UsbEndpoint(address, attributes, maxPacketSize, interval);
47 | }
48 |
49 | @Override
50 | @NotNull
51 | public UsbInterface getInterface(int index) {
52 | return (UsbInterface) super.getInterface(index);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/jni/isochronous_async_transfer.c:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jared Woolston (Jared.Woolston@gmail.com)
3 | //
4 |
5 | #include
6 |
7 | #define LOG_TAG "IsochronousAsyncTransfer-Native"
8 |
9 | JNIEXPORT jobject JNICALL
10 | Java_com_jwoolston_libusb_async_IsochronousAsyncTransfer_nativeAllocate(JNIEnv *env, jobject instance,
11 | jint numberPackets) {
12 | struct libusb_transfer *transfer = libusb_alloc_transfer(numberPackets);
13 | transfer->num_iso_packets = numberPackets;
14 | transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
15 | return ((*env)->NewDirectByteBuffer(env, (void *) transfer, sizeof(struct libusb_transfer)));
16 | }
17 |
18 | JNIEXPORT jint JNICALL
19 | Java_com_jwoolston_libusb_async_IsochronousAsyncTransfer_nativeSetupPackets(JNIEnv *env, jobject instance,
20 | jobject device, jobject nativeObject,
21 | jint endpoint, jint packetSize) {
22 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *) (*env)->GetDirectBufferAddress(env,
23 | device);
24 | //int size = libusb_get_max_iso_packet_size(deviceHandle->dev, endpoint);
25 | //TODO: Libusb currently does not use the current alternate setting for this
26 | //LOGV("ISO Packet Size: %i", packetSize);
27 |
28 | // Check for error condition
29 | /*if (packetSize < 0) {
30 | return packetSize;
31 | }*/
32 |
33 | struct libusb_transfer *transfer = (struct libusb_transfer *) (*env)->GetDirectBufferAddress(env, nativeObject);
34 | libusb_set_iso_packet_lengths(transfer, (unsigned int) packetSize);
35 |
36 | //return size;
37 | }
38 |
39 | JNIEXPORT void JNICALL
40 | Java_com_jwoolston_libusb_async_IsochronousAsyncTransfer_nativeDestroy(JNIEnv *env, jobject instance,
41 | jobject nativeObject) {
42 |
43 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/jwoolston/libusb/UsbConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.jwoolston.libusb;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import org.jetbrains.annotations.NotNull;
7 | import org.jetbrains.annotations.Nullable;
8 |
9 | public class UsbConfiguration extends BaseUsbConfiguration implements Parcelable {
10 |
11 | public static final Parcelable.Creator CREATOR =
12 | new Parcelable.Creator() {
13 | public UsbConfiguration createFromParcel(Parcel in) {
14 | int id = in.readInt();
15 | String name = in.readString();
16 | int attributes = in.readInt();
17 | int maxPower = in.readInt();
18 | UsbInterface[] interfaces = (UsbInterface[]) in.readParcelableArray(UsbInterface.class.getClassLoader());
19 | UsbConfiguration configuration = new UsbConfiguration(id, name, attributes, maxPower);
20 | configuration.setInterfaces(interfaces);
21 | return configuration;
22 | }
23 |
24 | public UsbConfiguration[] newArray(int size) {
25 | return new UsbConfiguration[size];
26 | }
27 | };
28 |
29 | /**
30 | * UsbConfiguration should only be instantiated by UsbService implementation
31 | *
32 | * @param id
33 | * @param name
34 | * @param attributes
35 | * @param maxPower
36 | */
37 | public UsbConfiguration(int id, @Nullable String name, int attributes, int maxPower) {
38 | super(id, name, attributes, maxPower);
39 | }
40 |
41 | public int describeContents() {
42 | return 0;
43 | }
44 |
45 | public void writeToParcel(Parcel parcel, int flags) {
46 | parcel.writeInt(id);
47 | parcel.writeString(name);
48 | parcel.writeInt(attributes);
49 | parcel.writeInt(maxPower);
50 | parcel.writeParcelableArray((Parcelable[]) interfaces, 0);
51 | }
52 |
53 | @Override
54 | @NotNull
55 | public UsbInterface getInterface(int index) {
56 | return (UsbInterface) super.getInterface(index);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/android/src/main/java/com/jwoolston/libusb/UsbInterface.java:
--------------------------------------------------------------------------------
1 | package com.jwoolston.libusb;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import org.jetbrains.annotations.Nullable;
7 |
8 | public class UsbInterface extends BaseUsbInterface implements Parcelable {
9 |
10 | public static final Parcelable.Creator CREATOR =
11 | new Parcelable.Creator() {
12 | public UsbInterface createFromParcel(Parcel in) {
13 | int id = in.readInt();
14 | int alternateSetting = in.readInt();
15 | String name = in.readString();
16 | int Class = in.readInt();
17 | int subClass = in.readInt();
18 | int protocol = in.readInt();
19 | Parcelable[] endpoints = in.readParcelableArray(UsbInterface.class.getClassLoader());
20 | UsbInterface intf = new UsbInterface(id, alternateSetting, name, Class, subClass, protocol);
21 | intf.setEndpoints((UsbEndpoint[]) endpoints);
22 | return intf;
23 | }
24 |
25 | public UsbInterface[] newArray(int size) {
26 | return new UsbInterface[size];
27 | }
28 | };
29 |
30 | /**
31 | * UsbInterface should only be instantiated by BaseUsbManager implementation
32 | *
33 | * @param id
34 | * @param alternateSetting
35 | * @param name
36 | * @param interfaceClass
37 | * @param subClass
38 | * @param protocol
39 | */
40 | UsbInterface(int id, int alternateSetting, @Nullable String name, int interfaceClass, int subClass, int protocol) {
41 | super(id, alternateSetting, name, interfaceClass, subClass, protocol);
42 | }
43 |
44 | public int describeContents() {
45 | return 0;
46 | }
47 |
48 | public void writeToParcel(Parcel parcel, int flags) {
49 | parcel.writeInt(id);
50 | parcel.writeInt(alternateSetting);
51 | parcel.writeString(name);
52 | parcel.writeInt(interfaceClass);
53 | parcel.writeInt(subclass);
54 | parcel.writeInt(protocol);
55 | parcel.writeParcelableArray((Parcelable[]) endpoints, 0);
56 | }
57 |
58 | @Override
59 | public UsbEndpoint getEndpoint(int index) {
60 | return (UsbEndpoint) super.getEndpoint(index);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/scsi/commands/ScsiReadCapacityResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver.scsi.commands;
19 |
20 | import java.nio.ByteBuffer;
21 | import java.nio.ByteOrder;
22 |
23 | /**
24 | * Represents the response of a read capacity request.
25 | *
26 | * The response data is received in the data phase
27 | *
28 | * @author mjahnen
29 | * @see com.github.mjdev.libaums.driver.scsi.commands.ScsiReadCapacity
30 | */
31 | public class ScsiReadCapacityResponse {
32 |
33 | private int logicalBlockAddress;
34 | private int blockLength;
35 |
36 | private ScsiReadCapacityResponse() {
37 |
38 | }
39 |
40 | /**
41 | * Constructs a new object with the given data.
42 | *
43 | * @param buffer
44 | * The data where the {@link #ScsiReadCapacityResponse()} is
45 | * located.
46 | * @return The parsed {@link #ScsiReadCapacityResponse()}.
47 | */
48 | public static ScsiReadCapacityResponse read(ByteBuffer buffer) {
49 | buffer.order(ByteOrder.BIG_ENDIAN);
50 | ScsiReadCapacityResponse res = new ScsiReadCapacityResponse();
51 | res.logicalBlockAddress = buffer.getInt();
52 | res.blockLength = buffer.getInt();
53 | return res;
54 | }
55 |
56 | /**
57 | * Returns the address of the last accessible block on the block device.
58 | *
59 | * The size of the device is then last accessible block + 0!
60 | *
61 | * @return The last block address.
62 | */
63 | public int getLogicalBlockAddress() {
64 | return logicalBlockAddress;
65 | }
66 |
67 | /**
68 | * Returns the size of each block in the block device.
69 | *
70 | * @return The block size in bytes.
71 | */
72 | public int getBlockLength() {
73 | return blockLength;
74 | }
75 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/jni/device_list.c:
--------------------------------------------------------------------------------
1 | //
2 | // Created by jared on 6/26/2019.
3 | //
4 | #include
5 |
6 | #define LOG_TAG "DeviceList-Native"
7 |
8 | jobject listClass;
9 | jobject usbDeviceClass;
10 | jmethodID listAdd;
11 | jmethodID usbDeviceConstructor;
12 |
13 | JNIEXPORT jboolean JNICALL
14 | Java_com_jwoolston_libusb_DeviceList_nativeInitialize(JNIEnv *env, jclass type) {
15 | // Find the classes
16 | jclass localClass = (*env)->FindClass(env, "java/util/List");
17 | listClass = (jclass) (*env)->NewGlobalRef(env, localClass);
18 | localClass = (*env)->FindClass(env, "com/jwoolston/libusb/UsbDevice");
19 | usbDeviceClass = (jclass) (*env)->NewGlobalRef(env, localClass);
20 |
21 | // Find the methods
22 | listAdd = (*env)->GetMethodID(env, listClass, "add", "(Ljava/lang/Object;)Z");
23 | usbDeviceConstructor = (*env)->GetMethodID(env, usbDeviceClass, "", "(J)V");
24 |
25 | //TODO: JNI Error checking
26 |
27 | return JNI_TRUE;
28 | }
29 |
30 | JNIEXPORT jlong JNICALL
31 | Java_com_jwoolston_libusb_DeviceList_nativeGetDeviceList(JNIEnv *env, jclass type,
32 | jobject nativeContext) {
33 |
34 | struct libusb_context *ctx
35 | = (libusb_context *) (*env)->GetDirectBufferAddress(env, nativeContext);
36 | // Discover devices
37 | libusb_device **list;
38 | ssize_t count = libusb_get_device_list(ctx, &list);
39 | if (count < 0) {
40 | LOGE("Failed to retrieve USB device list. Error: %i", count);
41 | return NULL;
42 | }
43 | return (jlong) (void *) list;
44 | }
45 |
46 | JNIEXPORT void JNICALL
47 | Java_com_jwoolston_libusb_DeviceList_nativePopulateDeviceList(JNIEnv *env, jclass type,
48 | jlong nativeObject, jobject devices) {
49 | if (nativeObject == NULL) {
50 | return;
51 | }
52 |
53 | libusb_device **list = (libusb_device **) nativeObject;
54 | int index = 0;
55 | libusb_device *device = list[index];
56 | while (device != NULL) {
57 | jobject usbDevice = (*env)->NewObject(env, usbDeviceClass, usbDeviceConstructor,
58 | (void *) device);
59 | (*env)->CallBooleanMethod(env, devices, listAdd, usbDevice);
60 | device = list[++index];
61 | }
62 | }
63 |
64 | JNIEXPORT void JNICALL
65 | Java_com_jwoolston_libusb_DeviceList_nativeRelease(JNIEnv *env, jobject instance,
66 | jlong nativeObject) {
67 | if (nativeObject == NULL) {
68 | return;
69 | }
70 |
71 | libusb_device **list = (libusb_device **) nativeObject;
72 | libusb_free_device_list(list, 1);
73 | }
74 |
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/usb/UsbCommunication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.usb;
19 |
20 | import com.jwoolston.libusb.LibusbError;
21 | import com.jwoolston.libusb.async.BulkTransferCallback;
22 |
23 | import java.io.IOException;
24 | import java.nio.ByteBuffer;
25 |
26 | /**
27 | * This Interface describes a low level device to perform USB transfers. At the
28 | * moment only bulk IN and OUT transfer are supported. Every class that follows
29 | * {@link com.github.mjdev.libaums.driver.BlockDeviceDriver} can use this to
30 | * communicate with the underlying USB stack.
31 | *
32 | * @author mjahnen
33 | *
34 | */
35 | public interface UsbCommunication {
36 | int TRANSFER_TIMEOUT = 5000;
37 |
38 | /**
39 | * Performs a bulk out transfer beginning at the offset specified in the
40 | * buffer of length buffer#remaining().
41 | *
42 | * @param src
43 | * The data to transfer.
44 | * @return Bytes transmitted if successful.
45 | */
46 | int bulkOutTransfer(ByteBuffer src) throws IOException;
47 |
48 | /**
49 | * Performs a bulk in transfer beginning at offset zero in the
50 | * buffer of length buffer#remaining().
51 | *
52 | * @param dest
53 | * The buffer where data should be transferred.
54 | * @return Bytes read if successful.
55 | */
56 | int bulkInTransfer(ByteBuffer dest) throws IOException;
57 |
58 | /**
59 | * Performs a bulk out transfer beginning at the offset specified in the
60 | * buffer of length buffer#remaining().
61 | *
62 | * @param src The data to transfer.
63 | *
64 | * @return Bytes transmitted if successful.
65 | */
66 | LibusbError asyncBulkOutTransfer(BulkTransferCallback callback, ByteBuffer src) throws IOException;
67 |
68 | /**
69 | * Performs a bulk in transfer beginning at offset zero in the
70 | * buffer of length buffer#remaining().
71 | *
72 | * @param dest The buffer where data should be transferred.
73 | *
74 | * @return Bytes read if successful.
75 | */
76 | LibusbError asyncBulkInTransfer(BulkTransferCallback callback, ByteBuffer dest) throws IOException;
77 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/jwoolston/libusb/LibUsbDeviceDescriptor.java:
--------------------------------------------------------------------------------
1 | package com.jwoolston.libusb;
2 |
3 | import com.jwoolston.libusb.util.Preconditions;
4 |
5 | import org.jetbrains.annotations.NotNull;
6 | import org.jetbrains.annotations.Nullable;
7 |
8 | import java.nio.ByteBuffer;
9 |
10 | /**
11 | * @author Jared Woolston (Jared.Woolston@gmail.com)
12 | */
13 | public class LibUsbDeviceDescriptor {
14 |
15 | private final ByteBuffer nativeObject;
16 |
17 | private boolean isValid = true;
18 |
19 | @NotNull
20 | static LibUsbDeviceDescriptor getDeviceDescriptor(@NotNull BaseUsbDevice device) {
21 | return new LibUsbDeviceDescriptor(nativeGetDeviceDescriptorFromHandle(device.getNativeObject()));
22 | }
23 |
24 | @NotNull
25 | static LibUsbDeviceDescriptor getDeviceDescriptor(long nativePointer) {
26 | return new LibUsbDeviceDescriptor(nativeGetDeviceDescriptorFromDevice(nativePointer));
27 | }
28 |
29 | private LibUsbDeviceDescriptor(ByteBuffer nativeObject) {
30 | Preconditions.checkNotNull(nativeObject, "LibUsbDeviceDescriptor Initialization failed.");
31 | this.nativeObject = nativeObject;
32 | }
33 |
34 | @NotNull
35 | ByteBuffer getNativeObject() {
36 | if (isValid) {
37 | return nativeObject;
38 | } else {
39 | throw new IllegalStateException("Descriptor is no longer valid.");
40 | }
41 | }
42 |
43 | public void destroy() {
44 | nativeDestroy(nativeObject);
45 | isValid = false;
46 | }
47 |
48 | @Override
49 | protected void finalize() throws Throwable {
50 | if (isValid) {
51 | destroy();
52 | }
53 | super.finalize();
54 | }
55 |
56 | public int getVendorId() {
57 | return nativeGetVendorId(getNativeObject());
58 | }
59 |
60 | public int getProductId() {
61 | return nativeGetProductId(getNativeObject());
62 | }
63 |
64 | public int getDeviceClass() {
65 | return nativeGetDeviceClass(getNativeObject());
66 | }
67 |
68 | public int getDeviceSubclass() {
69 | return nativeGetDeviceSubclass(getNativeObject());
70 | }
71 |
72 | public int getDeviceProtocol() {
73 | return nativeGetDeviceProtocol(getNativeObject());
74 | }
75 |
76 | @Nullable
77 | private static native ByteBuffer nativeGetDeviceDescriptorFromHandle(@NotNull ByteBuffer device);
78 |
79 | @Nullable
80 | private static native ByteBuffer nativeGetDeviceDescriptorFromDevice(long nativePointer);
81 |
82 | private static native void nativeDestroy(@NotNull ByteBuffer descriptor);
83 |
84 | private static native int nativeGetVendorId(@NotNull ByteBuffer descriptor);
85 |
86 | private static native int nativeGetProductId(@NotNull ByteBuffer descriptor);
87 |
88 | private static native int nativeGetDeviceClass(@NotNull ByteBuffer descriptor);
89 |
90 | private static native int nativeGetDeviceSubclass(@NotNull ByteBuffer descriptor);
91 |
92 | private static native int nativeGetDeviceProtocol(@NotNull ByteBuffer descriptor);
93 | }
94 |
--------------------------------------------------------------------------------
/android/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # For more information about using CMake with Android Studio, read the
2 | # documentation: https://d.android.com/studio/projects/add-native-code.html
3 |
4 | # Sets the minimum version of CMake required to build the native library.
5 |
6 | cmake_minimum_required(VERSION 3.4.1)
7 |
8 | # Creates and names a library, sets it as either STATIC
9 | # or SHARED, and provides the relative paths to its source code.
10 | # You can define multiple libraries, and CMake builds them for you.
11 | # Gradle automatically packages shared libraries with your APK.
12 |
13 | add_definitions(-DJNI_WRAPPER)
14 |
15 | include_directories(
16 | ../library/libusb/android
17 | ../library/libusb/libusb
18 | ../library/libusb/libusb/os
19 | ../jni)
20 |
21 | # Defines the source code for the library
22 | set(libusb_SRCS
23 | ../library/libusb/libusb/core.c
24 | ../library/libusb/libusb/descriptor.c
25 | ../library/libusb/libusb/hotplug.c
26 | ../library/libusb/libusb/io.c
27 | ../library/libusb/libusb/sync.c
28 | ../library/libusb/libusb/strerror.c
29 | ../library/libusb/libusb/os/linux_usbfs.c
30 | ../library/libusb/libusb/os/poll_posix.c
31 | ../library/libusb/libusb/os/threads_posix.c
32 | ../library/libusb/libusb/os/linux_netlink.c
33 | )
34 |
35 | add_library(
36 | # Sets the name of the library.
37 | wrapper_libusb
38 |
39 | # Sets the library as a shared library.
40 | SHARED
41 |
42 | # Provides a relative path to your source file(s).
43 | ../jni/async_transfer.c
44 | ../jni/async_usb_thread.c
45 | ../jni/device_list.c
46 | ../jni/common.h
47 | ../jni/isochronous_async_transfer.c
48 | ../jni/libusb_device_descriptor.c
49 | ../jni/libusb_error_enum.c
50 | ../jni/logging.c
51 | ../jni/usb_configuration.c
52 | ../jni/usb_device.c
53 | ../jni/usb_device_connection.c
54 | ../jni/usb_interface.c
55 | ../jni/usb_manager.c
56 | ${libusb_SRCS}
57 | )
58 |
59 | #set_target_properties(wrapper_libusb PROPERTIES LINKER_LANGUAGE C)
60 |
61 | # Searches for a specified prebuilt library and stores the path as a
62 | # variable. Because CMake includes system libraries in the search path by
63 | # default, you only need to specify the name of the public NDK library
64 | # you want to add. CMake verifies that the library exists before
65 | # completing its build.
66 |
67 | find_library( # Sets the name of the path variable.
68 | log-lib
69 |
70 | # Specifies the name of the NDK library that
71 | # you want CMake to locate.
72 | log )
73 |
74 | # Specifies libraries CMake should link to your target library. You
75 | # can link multiple libraries, such as libraries you define in this
76 | # build script, prebuilt third-party libraries, or system libraries.
77 |
78 | target_link_libraries( # Specifies the target library.
79 | wrapper_libusb
80 |
81 | # Links the target library to the log library
82 | # included in the NDK.
83 | ${log-lib} )
--------------------------------------------------------------------------------
/library/src/main/java/com/jwoolston/libusb/async/IsochronousAsyncTransfer.java:
--------------------------------------------------------------------------------
1 | package com.jwoolston.libusb.async;
2 |
3 | import com.jwoolston.libusb.BaseUsbDeviceConnection;
4 | import com.jwoolston.libusb.BaseUsbEndpoint;
5 | import com.jwoolston.libusb.LibusbError;
6 |
7 | import org.jetbrains.annotations.NotNull;
8 | import org.jetbrains.annotations.Nullable;
9 |
10 | import java.io.IOException;
11 | import java.nio.ByteBuffer;
12 |
13 | /**
14 | * @author Jared Woolston (Jared.Woolston@gmail.com)
15 | */
16 | public class IsochronousAsyncTransfer extends AsyncTransfer {
17 |
18 | private static final String TAG = "IsochronousAsyncTransfer";
19 |
20 | private final IsochronousTransferCallback callback;
21 | private final BaseUsbDeviceConnection connection;
22 | private final int packetCount;
23 | private final int packetSize;
24 |
25 | public IsochronousAsyncTransfer(@NotNull IsochronousTransferCallback callback, @NotNull BaseUsbEndpoint endpoint,
26 | @NotNull BaseUsbDeviceConnection connection, int packetSize, int packetCount)
27 | throws IOException {
28 | super(endpoint);
29 | this.callback = callback;
30 | this.connection = connection;
31 | setNativeObject(nativeAllocate(packetCount));
32 | int size = nativeSetupPackets(connection.getDevice().getNativeObject(), getNativeObject(),
33 | endpoint.getAddress(), packetSize);
34 | LibusbError result = size > 0 ? LibusbError.LIBUSB_SUCCESS : LibusbError.fromNative(size);
35 | if (result != LibusbError.LIBUSB_SUCCESS) {
36 | throw new IOException("Failed to setup packets: " + result);
37 | }
38 | this.packetCount = packetCount;
39 | this.packetSize = packetSize;
40 | }
41 |
42 | public void submit(@NotNull ByteBuffer buffer, int timeout) throws IllegalStateException {
43 | if (!buffer.isDirect()) {
44 | throw new IllegalArgumentException("ByteBuffers passed to this method must be direct allocations.");
45 | }
46 |
47 | if ((packetCount * packetSize) > buffer.capacity()) {
48 | throw new IllegalArgumentException("The provided byte buffer is of insufficient capacity. Required: "
49 | + (packetSize * packetCount) + " Bytes. Provided: " + buffer.capacity()
50 | + " Bytes.");
51 | }
52 |
53 | LibusbError result = LibusbError.fromNative(connection.isochronousTransfer(callback, this, getEndpoint(),
54 | buffer, timeout));
55 | if (result != LibusbError.LIBUSB_SUCCESS) {
56 | throw new IllegalStateException("Failed to submit isochronous transfer: " + result);
57 | }
58 | }
59 |
60 | @Nullable
61 | private native ByteBuffer nativeAllocate(int numberPackets);
62 |
63 | private native int nativeSetupPackets(@NotNull ByteBuffer nativeDevice, @NotNull ByteBuffer nativeObject,
64 | int endpoint, int packetSize);
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/BlockDeviceDriver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver;
19 |
20 | import java.io.IOException;
21 | import java.nio.ByteBuffer;
22 |
23 | /**
24 | * This interface describes a simple block device with a certain block size and
25 | * the ability to read and write at a certain device offset.
26 | *
27 | * @author mjahnen
28 | *
29 | */
30 | public interface BlockDeviceDriver {
31 | /**
32 | * Initializes the block device for further use. This method should be
33 | * called before doing anything else on the block device.
34 | *
35 | * @throws IOException
36 | * If initializing fails
37 | */
38 | public void init() throws IOException;
39 |
40 | /**
41 | * Reads from the block device at a certain offset into the given buffer.
42 | * The amount of bytes to be read are determined by
43 | * {@link java.nio.ByteBuffer#remaining()}.
44 | *
45 | * The deviceOffset can either be the amount of bytes or a logical block
46 | * addressing using the block size. To get the bytes in the last case you
47 | * have to multiply the lba with the block size (offset *
48 | * {@link #getBlockSize()}).
49 | *
50 | * @param deviceOffset
51 | * The offset where the reading should begin.
52 | * @param buffer
53 | * The buffer where the data should be read into.
54 | * @throws IOException
55 | * If reading fails.
56 | */
57 | public void read(long deviceOffset, ByteBuffer buffer) throws IOException;
58 |
59 | /**
60 | * Writes to the block device at a certain offset from the given buffer. The
61 | * amount of bytes to be written are determined by
62 | * {@link java.nio.ByteBuffer#remaining()}.
63 | *
64 | * The deviceOffset can either be the amount of bytes or a logical block
65 | * addressing using the block size. To get the bytes in the last case you
66 | * have to multiply the lba with the block size (offset *
67 | * {@link #getBlockSize()}).
68 | *
69 | * @param deviceOffset
70 | * The offset where the writing should begin.
71 | * @param buffer
72 | * The buffer with the data to be transferred.
73 | * @throws IOException
74 | * If writing fails.
75 | */
76 | public void write(long deviceOffset, ByteBuffer buffer) throws IOException;
77 |
78 | /**
79 | * Returns the block size of the block device. Every block device can only
80 | * read and store bytes in a specific block with a certain size.
81 | *
82 | * That means that it is only possible to read or write hole blocks!
83 | *
84 | * @return The block size in bytes, mostly 512 bytes.
85 | */
86 | public int getBlockSize();
87 | }
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/scsi/commands/ScsiRead10.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver.scsi.commands;
19 |
20 | import java.nio.ByteBuffer;
21 | import java.nio.ByteOrder;
22 |
23 | /**
24 | * SCSI command to read from the mass storage device. The 10 means that the
25 | * transfer length is two byte and the logical block address field is four byte.
26 | * Thus the hole command takes 10 byte when serialized.
27 | *
28 | * The actual data is transferred in the data phase.
29 | *
30 | * @author mjahnen
31 | *
32 | */
33 | public class ScsiRead10 extends CommandBlockWrapper {
34 |
35 | // private static final String TAG = ScsiRead10.class.getSimpleName();
36 | private static final byte LENGTH = 10;
37 | private static final byte OPCODE = 0x28;
38 |
39 | private int blockAddress;
40 | private int transferBytes;
41 | private int blockSize;
42 | private short transferBlocks;
43 |
44 | /**
45 | * Constructs a new read command without any information.
46 | * Be sure to call {@link #init(int, int, int)} before transfering command to device.
47 | */
48 | public ScsiRead10() {
49 | super(0, Direction.IN, (byte) 0, LENGTH);
50 | }
51 |
52 | /**
53 | * Constructs a new read command with the given information.
54 | *
55 | * @param blockAddress
56 | * The logical block address the read should start.
57 | * @param transferBytes
58 | * The bytes which should be transferred.
59 | * @param blockSize
60 | * The block size of the mass storage device.
61 | */
62 | public ScsiRead10(int blockAddress, int transferBytes, int blockSize) {
63 | super(transferBytes, Direction.IN, (byte) 0, LENGTH);
64 | init(blockAddress, transferBytes, blockSize);
65 | }
66 |
67 | public void init(int blockAddress, int transferBytes, int blockSize) {
68 | super.dCbwDataTransferLength = transferBytes;
69 | this.blockAddress = blockAddress;
70 | this.transferBytes = transferBytes;
71 | this.blockSize = blockSize;
72 | short transferBlocks = (short) (transferBytes / blockSize);
73 | if (transferBytes % blockSize != 0) {
74 | throw new IllegalArgumentException("transfer bytes is not a multiple of block size");
75 | }
76 | this.transferBlocks = transferBlocks;
77 | }
78 |
79 | @Override
80 | public void serialize(ByteBuffer buffer) {
81 | super.serialize(buffer);
82 | buffer.order(ByteOrder.BIG_ENDIAN);
83 | buffer.put(OPCODE);
84 | buffer.put((byte) 0);
85 | buffer.putInt(blockAddress);
86 | buffer.put((byte) 0);
87 | buffer.putShort(transferBlocks);
88 | }
89 |
90 | @Override
91 | public String toString() {
92 | return "ScsiRead10 [blockAddress=" + blockAddress + ", transferBytes=" + transferBytes
93 | + ", blockSize=" + blockSize + ", transferBlocks=" + transferBlocks
94 | + ", getdCbwDataTransferLength()=" + getdCbwDataTransferLength() + "]";
95 | }
96 |
97 | }
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java-library'
3 | }
4 |
5 | sourceCompatibility = "7"
6 | targetCompatibility = "7"
7 |
8 | dependencies {
9 | implementation fileTree(dir: 'libs', include: ['*.jar'])
10 |
11 | // Logging
12 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
13 | implementation("com.ToxicBakery.logging:arbor-jvm:$arbor_version")
14 |
15 | // Utils
16 | implementation 'org.jetbrains:annotations:16.0.1'
17 | }
18 |
19 | //dokka {
20 | // noStdlibLink = false
21 | // includeNonPublic = false
22 | // skipEmptyPackages = true
23 | // outputFormat = 'html'
24 | // outputDirectory = "$buildDir/javadoc"
25 | //}
26 | //
27 | //afterEvaluate { project ->
28 | // task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaAndroidTask) {
29 | // outputFormat = 'javadoc'
30 | // outputDirectory = "$buildDir/javadoc"
31 | // inputs.dir 'src/main/java'
32 | // }
33 | //
34 | // task javadocJar(type: Jar, dependsOn: dokkaJavadoc) {
35 | // classifier = 'javadoc'
36 | // from "$buildDir/javadoc"
37 | // }
38 | //
39 | // task sourcesJar(type: Jar) {
40 | // classifier = 'sources'
41 | // from android.sourceSets.main.java.srcDirs
42 | // }
43 | //
44 | // artifacts {
45 | // archives sourcesJar
46 | // archives javadocJar
47 | // }
48 | //}
49 | //
50 | //task jacocoUnitTestReport(type: JacocoReport, group: 'verification',
51 | // dependsOn: ['testDebugUnitTest']) {
52 | //
53 | // reports {
54 | // xml.enabled = true
55 | // html.enabled = true
56 | // }
57 | //
58 | // def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
59 | // def debugTree = fileTree(dir: "$project.buildDir/intermediates/classes/debug", excludes: fileFilter)
60 | // def mainSrc = "$project.projectDir/src/main/java"
61 | //
62 | // sourceDirectories = files([mainSrc])
63 | // classDirectories = files([debugTree])
64 | // executionData = fileTree(dir: project.buildDir, includes: [
65 | // 'jacoco/testDebugUnitTest.exec', 'outputs/code-coverage/connected/*coverage.ec'
66 | // ])
67 | //}
68 | //
69 | //task jacocoFullTestReport(type: JacocoReport, group: 'verification',
70 | // dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {
71 | //
72 | // reports {
73 | // xml.enabled = true
74 | // html.enabled = true
75 | // }
76 | //
77 | // def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
78 | // def debugTree = fileTree(dir: "$project.buildDir/intermediates/classes/debug", excludes: fileFilter)
79 | // def mainSrc = "$project.projectDir/src/main/java"
80 | //
81 | // sourceDirectories = files([mainSrc])
82 | // classDirectories = files([debugTree])
83 | // executionData = fileTree(dir: project.buildDir, includes: [
84 | // 'jacoco/testDebugUnitTest.exec', 'outputs/code-coverage/connected/*coverage.ec'
85 | // ])
86 | //}
87 | //
88 | //task copyAndroidCoverage(type: Copy) {
89 | // /*afterEvaluate {
90 | // dependsOn jacocoFullTestReport
91 | // }*/
92 | // from 'build/reports/jacoco/jacocoFullTestReport/jacocoFullTestReport.xml'
93 | // into './'
94 | // rename { String fileName ->
95 | // fileName = "integration.xml"
96 | // }
97 | //}
98 | //
99 | //task prepareForCommit(dependsOn: [copyAndroidCoverage]) {
100 | //
101 | //}
102 |
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/scsi/commands/ScsiWrite10.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver.scsi.commands;
19 |
20 | import java.nio.ByteBuffer;
21 | import java.nio.ByteOrder;
22 |
23 | /**
24 | * SCSI command to write to the mass storage device. The 10 means that the
25 | * transfer length is two byte and the logical block address field is four byte.
26 | * Thus the hole command takes 10 byte when serialized.
27 | *
28 | * The actual data is transferred in the data phase.
29 | *
30 | * @author mjahnen
31 | *
32 | */
33 | public class ScsiWrite10 extends CommandBlockWrapper {
34 |
35 | // private static final String TAG = ScsiWrite10.class.getSimpleName();
36 | private static final byte LENGTH = 10;
37 | private static final byte OPCODE = 0x2a;
38 |
39 | private int blockAddress;
40 | private int transferBytes;
41 | private int blockSize;
42 | private short transferBlocks;
43 |
44 | /**
45 | * Constructs a new write command without any information.
46 | * Be sure to call {@link #init(int, int, int)} before transfering command to device.
47 | */
48 | public ScsiWrite10() {
49 | super(0, Direction.OUT, (byte) 0, LENGTH);
50 | }
51 |
52 | /**
53 | * Constructs a new write command with the given information.
54 | *
55 | * @param blockAddress
56 | * The logical block address the write should start.
57 | * @param transferBytes
58 | * The bytes which should be transferred.
59 | * @param blockSize
60 | * The block size of the mass storage device.
61 | */
62 | public ScsiWrite10(int blockAddress, int transferBytes, int blockSize) {
63 | super(transferBytes, Direction.OUT, (byte) 0, LENGTH);
64 | init(blockAddress, transferBytes, blockSize);
65 | }
66 |
67 | public void init(int blockAddress, int transferBytes, int blockSize) {
68 | super.dCbwDataTransferLength = transferBytes;
69 | this.blockAddress = blockAddress;
70 | this.transferBytes = transferBytes;
71 | this.blockSize = blockSize;
72 | short transferBlocks = (short) (transferBytes / blockSize);
73 | if (transferBytes % blockSize != 0) {
74 | throw new IllegalArgumentException("transfer bytes is not a multiple of block size");
75 | }
76 | this.transferBlocks = transferBlocks;
77 | }
78 |
79 | @Override
80 | public void serialize(ByteBuffer buffer) {
81 | super.serialize(buffer);
82 | buffer.order(ByteOrder.BIG_ENDIAN);
83 | buffer.put(OPCODE);
84 | buffer.put((byte) 0);
85 | buffer.putInt(blockAddress);
86 | buffer.put((byte) 0);
87 | buffer.putShort(transferBlocks);
88 | }
89 |
90 | @Override
91 | public String toString() {
92 | return "ScsiWrite10 [blockAddress=" + blockAddress + ", transferBytes=" + transferBytes
93 | + ", blockSize=" + blockSize + ", transferBlocks=" + transferBlocks
94 | + ", getdCbwDataTransferLength()=" + getdCbwDataTransferLength() + "]";
95 | }
96 |
97 | }
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/scsi/commands/ScsiInquiryResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver.scsi.commands;
19 |
20 | import java.nio.ByteBuffer;
21 | import java.nio.ByteOrder;
22 |
23 | /**
24 | * This class represents the response of a SCSI Inquiry. It holds various
25 | * information about the mass storage device.
26 | *
27 | * This response is received in the data phase.
28 | *
29 | * @author mjahnen
30 | * @see com.github.mjdev.libaums.driver.scsi.commands.ScsiInquiry
31 | */
32 | public class ScsiInquiryResponse {
33 |
34 | private byte peripheralQualifier;
35 | private byte peripheralDeviceType;
36 | boolean removableMedia;
37 | byte spcVersion;
38 | byte responseDataFormat;
39 |
40 | private ScsiInquiryResponse() {
41 |
42 | }
43 |
44 | /**
45 | * Constructs a new object with the given data.
46 | *
47 | * @param buffer
48 | * The data where the {@link #ScsiInquiryResponse()} is located.
49 | * @return The parsed {@link #ScsiInquiryResponse()}.
50 | */
51 | public static ScsiInquiryResponse read(ByteBuffer buffer) {
52 | ScsiInquiryResponse response = new ScsiInquiryResponse();
53 | buffer.order(ByteOrder.LITTLE_ENDIAN);
54 | byte b = buffer.get();
55 | response.peripheralQualifier = (byte) (b & (byte) 0xe0);
56 | response.peripheralDeviceType = (byte) (b & (byte) 0x1f);
57 | response.removableMedia = buffer.get() == 0x80;
58 | response.spcVersion = buffer.get();
59 | response.responseDataFormat = (byte) (buffer.get() & (byte) 0x7);
60 | return response;
61 | }
62 |
63 | /**
64 | *
65 | * @return Zero if a device is connected to the unit.
66 | */
67 | public byte getPeripheralQualifier() {
68 | return peripheralQualifier;
69 | }
70 |
71 | /**
72 | * The type of the mass storage device.
73 | *
74 | * @return Zero for a direct access block device.
75 | */
76 | public byte getPeripheralDeviceType() {
77 | return peripheralDeviceType;
78 | }
79 |
80 | /**
81 | *
82 | * @return True if the media can be removed (eg. card reader).
83 | */
84 | public boolean isRemovableMedia() {
85 | return removableMedia;
86 | }
87 |
88 | /**
89 | * This method returns the version of the SCSI Primary Commands (SPC)
90 | * standard the device supports.
91 | *
92 | * @return Version of the SPC standard
93 | */
94 | public byte getSpcVersion() {
95 | return spcVersion;
96 | }
97 |
98 | public byte getResponseDataFormat() {
99 | return responseDataFormat;
100 | }
101 |
102 | @Override
103 | public String toString() {
104 | return "ScsiInquiryResponse [peripheralQualifier=" + peripheralQualifier
105 | + ", peripheralDeviceType=" + peripheralDeviceType + ", removableMedia="
106 | + removableMedia + ", spcVersion=" + spcVersion + ", responseDataFormat="
107 | + responseDataFormat + "]";
108 | }
109 | }
--------------------------------------------------------------------------------
/jni/libusb_device_descriptor.c:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jared Woolston (Jared.Woolston@gmail.com)
3 | //
4 |
5 | #include
6 |
7 | JNIEXPORT jobject JNICALL
8 | Java_com_jwoolston_libusb_LibUsbDeviceDescriptor_nativeGetDeviceDescriptorFromHandle(JNIEnv *env, jclass type,
9 | jobject handle) {
10 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *)
11 | (*env)->GetDirectBufferAddress(env, handle);
12 |
13 | // The descriptor is cached in memory so we don't need to allocate memory for it
14 | struct libusb_device_descriptor descriptor;
15 | libusb_get_device_descriptor(deviceHandle->dev, &descriptor);
16 | return ((*env)->NewDirectByteBuffer(env, (void *) &descriptor, sizeof(struct libusb_device_descriptor)));
17 | }
18 |
19 | JNIEXPORT jobject JNICALL
20 | Java_com_jwoolston_libusb_LibUsbDeviceDescriptor_nativeGetDeviceDescriptorFromDevice(JNIEnv *env, jclass type,
21 | jlong device) {
22 | struct libusb_device *devicePtr = (struct libusb_device *) device;
23 |
24 | // The descriptor is cached in memory so we don't need to allocate memory for it
25 | struct libusb_device_descriptor descriptor;
26 | libusb_get_device_descriptor(devicePtr, &descriptor);
27 | return ((*env)->NewDirectByteBuffer(env, (void *) &descriptor, sizeof(struct libusb_device_descriptor)));
28 | }
29 |
30 | JNIEXPORT void JNICALL
31 | Java_com_jwoolston_libusb_LibUsbDeviceDescriptor_nativeDestroy(JNIEnv *env, jclass type, jobject descriptor) {
32 | struct libusb_device_descriptor *deviceDescriptor = (struct libusb_device_descriptor *)
33 | (*env)->GetDirectBufferAddress(env, descriptor);
34 | free(deviceDescriptor);
35 | }
36 |
37 | JNIEXPORT jint JNICALL
38 | Java_com_jwoolston_libusb_LibUsbDeviceDescriptor_nativeGetVendorId(JNIEnv *env, jclass type, jobject descriptor) {
39 | struct libusb_device_descriptor *deviceDescriptor = (struct libusb_device_descriptor *)
40 | (*env)->GetDirectBufferAddress(env, descriptor);
41 | return deviceDescriptor->idVendor;
42 | }
43 |
44 | JNIEXPORT jint JNICALL
45 | Java_com_jwoolston_libusb_LibUsbDeviceDescriptor_nativeGetProductId(JNIEnv *env, jclass type, jobject descriptor) {
46 | struct libusb_device_descriptor *deviceDescriptor = (struct libusb_device_descriptor *)
47 | (*env)->GetDirectBufferAddress(env, descriptor);
48 | return deviceDescriptor->idProduct;
49 | }
50 |
51 | JNIEXPORT jint JNICALL
52 | Java_com_jwoolston_libusb_LibUsbDeviceDescriptor_nativeGetDeviceClass(JNIEnv *env, jclass type, jobject descriptor) {
53 | struct libusb_device_descriptor *deviceDescriptor = (struct libusb_device_descriptor *)
54 | (*env)->GetDirectBufferAddress(env, descriptor);
55 | return deviceDescriptor->bDeviceClass;
56 | }
57 |
58 | JNIEXPORT jint JNICALL
59 | Java_com_jwoolston_libusb_LibUsbDeviceDescriptor_nativeGetDeviceSubclass(JNIEnv *env, jclass type, jobject descriptor) {
60 | struct libusb_device_descriptor *deviceDescriptor = (struct libusb_device_descriptor *)
61 | (*env)->GetDirectBufferAddress(env, descriptor);
62 | return deviceDescriptor->bDeviceSubClass;
63 | }
64 |
65 | JNIEXPORT jint JNICALL
66 | Java_com_jwoolston_libusb_LibUsbDeviceDescriptor_nativeGetDeviceProtocol(JNIEnv *env, jclass type, jobject descriptor) {
67 | struct libusb_device_descriptor *deviceDescriptor = (struct libusb_device_descriptor *)
68 | (*env)->GetDirectBufferAddress(env, descriptor);
69 | return deviceDescriptor->bDeviceProtocol;
70 | }
--------------------------------------------------------------------------------
/mobile/src/main/java/com/jwoolston/android/libusb/MSCCommunication.java:
--------------------------------------------------------------------------------
1 | package com.jwoolston.android.libusb;
2 |
3 | import com.jwoolston.libusb.LibusbError;
4 | import com.jwoolston.libusb.UsbDeviceConnection;
5 | import com.jwoolston.libusb.UsbEndpoint;
6 | import com.jwoolston.libusb.async.BulkTransferCallback;
7 | import com.jwoolston.android.libusb.msc_test_core.usb.UsbCommunication;
8 | import java.io.IOException;
9 | import java.nio.ByteBuffer;
10 |
11 | /**
12 | * @author Jared Woolston (Jared.Woolston@gmail.com)
13 | */
14 | public class MSCCommunication implements UsbCommunication {
15 |
16 | private UsbDeviceConnection deviceConnection;
17 | private UsbEndpoint outEndpoint;
18 | private UsbEndpoint inEndpoint;
19 |
20 | MSCCommunication(UsbDeviceConnection deviceConnection, UsbEndpoint outEndpoint, UsbEndpoint inEndpoint) {
21 | this.deviceConnection = deviceConnection;
22 | this.outEndpoint = outEndpoint;
23 | this.inEndpoint = inEndpoint;
24 | }
25 |
26 | @Override
27 | public int bulkOutTransfer(ByteBuffer src) throws IOException {
28 | int result = deviceConnection.bulkTransfer(outEndpoint,
29 | src.array(), src.position(), src.remaining(), TRANSFER_TIMEOUT);
30 |
31 | if (result < 0) {
32 | throw new IOException("Could not write to device, result == " + LibusbError.fromNative(result));
33 | }
34 |
35 | src.position(src.position() + result);
36 | return result;
37 | }
38 |
39 | @Override
40 | public int bulkInTransfer(ByteBuffer dest) throws IOException {
41 | int result = deviceConnection.bulkTransfer(inEndpoint,
42 | dest.array(), dest.position(), dest.remaining(), TRANSFER_TIMEOUT);
43 |
44 | if (result < 0) {
45 | throw new IOException("Could not read from device, result == " + LibusbError.fromNative(result));
46 | }
47 |
48 | dest.position(dest.position() + result);
49 | return result;
50 | }
51 |
52 | /**
53 | * Performs a bulk out transfer beginning at the offset specified in the
54 | * buffer of length buffer#remaining().
55 | *
56 | * @param src The data to transfer.
57 | *
58 | * @return Bytes transmitted if successful.
59 | */
60 | @Override
61 | public LibusbError asyncBulkOutTransfer(BulkTransferCallback callback, ByteBuffer src) throws IOException {
62 | LibusbError result = deviceConnection.bulkTransferAsync(callback, outEndpoint, src.array(), src.position(), src.remaining(), TRANSFER_TIMEOUT);
63 |
64 | if (result != LibusbError.LIBUSB_SUCCESS) {
65 | throw new IOException("Could not write to device, result == " + result);
66 | }
67 |
68 | return result;
69 | }
70 |
71 | /**
72 | * Performs a bulk in transfer beginning at offset zero in the
73 | * buffer of length buffer#remaining().
74 | *
75 | * @param dest The buffer where data should be transferred.
76 | *
77 | * @return Bytes read if successful.
78 | */
79 | @Override
80 | public LibusbError asyncBulkInTransfer(BulkTransferCallback callback, ByteBuffer dest) throws IOException {
81 | LibusbError result = deviceConnection.bulkTransferAsync(callback, inEndpoint, dest.array(), dest.position(), dest.remaining(), TRANSFER_TIMEOUT);
82 |
83 | if (result != LibusbError.LIBUSB_SUCCESS) {
84 | throw new IOException("Could not read from device, result == " + result);
85 | }
86 |
87 | return result;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/publish.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven'
2 | apply plugin: 'signing'
3 |
4 | static String getTagName() { return System.getenv('CIRCLE_TAG') ?: "" }
5 |
6 | boolean isSnapshot() { return version.contains("SNAPSHOT") }
7 |
8 | static boolean isTag() { return !getTagName().isEmpty() }
9 |
10 | static boolean isCircle() { return System.getenv('CIRCLECI') ? true : false }
11 |
12 | static String buildNumber() { return System.getenv('CIRCLE_BUILD_NUM') ?: "0" }
13 |
14 | static String getRepositoryUsername() { return System.getenv('SONATYPE_USERNAME') ?: "" }
15 |
16 | static String getRepositoryPassword() { return System.getenv('SONATYPE_PASSWORD') ?: "" }
17 |
18 | static String getBranchName() { return System.getenv('CIRCLE_BRANCH') }
19 |
20 | boolean isRelease() { return isTag() && !isSnapshot() }
21 |
22 | afterEvaluate { project ->
23 |
24 | println "Tag ${getTagName()}"
25 | println "Branch ${getBranchName()}"
26 | println "Is Release ${isRelease()}"
27 | println "Is Circle ${isCircle()}"
28 | println "Has Username ${!getRepositoryUsername().empty}"
29 | println "Has Password ${!getRepositoryPassword().empty}"
30 | println "Version ${version}"
31 |
32 | uploadArchives {
33 | repositories {
34 | mavenDeployer {
35 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
36 |
37 | pom.artifactId = POM_ARTIFACT_ID
38 |
39 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
40 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
41 | }
42 |
43 | snapshotRepository(url: isCircle() ? "https://oss.sonatype.org/content/repositories/snapshots" : mavenLocal().url) {
44 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
45 | }
46 |
47 | pom.project {
48 | name POM_NAME
49 | packaging POM_PACKAGING
50 | description POM_DESCRIPTION
51 | url POM_URL
52 |
53 | scm {
54 | url POM_SCM_URL
55 | connection POM_SCM_CONNECTION
56 | developerConnection POM_SCM_DEV_CONNECTION
57 | }
58 |
59 | licenses {
60 | license {
61 | name POM_LICENCE_NAME
62 | url POM_LICENCE_URL
63 | distribution POM_LICENCE_DIST
64 | }
65 | }
66 |
67 | developers {
68 | developer {
69 | id POM_DEVELOPER_ID
70 | name POM_DEVELOPER_NAME
71 | email POM_DEVELOPER_EMAIL
72 | organization POM_DEVELOPER_ORGANIZATION
73 | organizationUrl POM_DEVELOPER_ORGANIZATION_URL
74 | }
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
81 | signing {
82 | required false
83 | sign configurations.archives
84 | }
85 |
86 | /*task androidJavadocs(type: Javadoc) {
87 | source = android.sourceSets.main.java.srcDirs
88 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
89 | }
90 |
91 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
92 | classifier = 'javadoc'
93 | from androidJavadocs.destinationDir
94 | }
95 |
96 | task androidSourcesJar(type: Jar) {
97 | classifier = 'sources'
98 | from android.sourceSets.main.java.sourceFiles
99 | }*/
100 |
101 | }
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'maven-publish'
3 | id 'signing'
4 | }
5 |
6 | apply plugin: 'com.android.library'
7 | apply plugin: 'kotlin-android'
8 |
9 | android {
10 | compileSdkVersion 28
11 |
12 | externalNativeBuild {
13 | cmake {
14 | path "CMakeLists.txt"
15 | }
16 | }
17 |
18 | defaultConfig {
19 | minSdkVersion 14
20 | targetSdkVersion 28
21 | versionName version
22 |
23 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
24 |
25 | externalNativeBuild {
26 | cmake {
27 | cppFlags ""
28 | arguments '-DANDROID_ARM_NEON=TRUE'
29 | }
30 | }
31 | }
32 |
33 | lintOptions {
34 | disable 'InvalidPackage'
35 | abortOnError false
36 | }
37 |
38 | testOptions {
39 | execution 'ANDROID_TEST_ORCHESTRATOR'
40 | animationsDisabled true
41 | }
42 |
43 | buildTypes {
44 | release {
45 | minifyEnabled false
46 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
47 | }
48 | }
49 |
50 | compileOptions {
51 | sourceCompatibility JavaVersion.VERSION_1_7
52 | targetCompatibility JavaVersion.VERSION_1_7
53 | }
54 | }
55 |
56 | dependencies {
57 | implementation fileTree(dir: 'libs', include: ['*.jar'])
58 |
59 | api project(":library")
60 |
61 | implementation "com.ToxicBakery.logging:arbor-jvm:$arbor_version"
62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
63 |
64 | implementation "com.android.support:appcompat-v7:$support_lib_version"
65 |
66 |
67 | testImplementation 'junit:junit:4.12'
68 |
69 | androidTestUtil 'com.android.support.test:orchestrator:1.0.2'
70 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
71 | androidTestImplementation 'com.android.support.test:rules:1.0.2'
72 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
73 | androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3'
74 |
75 | androidTestImplementation project(':msc_test_core')
76 | }
77 |
78 | publishing {
79 | publications {
80 | all(MavenPublication) { publication ->
81 | pom {
82 | name = POM_NAME
83 | description = POM_DESCRIPTION
84 | url = POM_URL
85 | scm {
86 | url = POM_SCM_URL
87 | connection = POM_SCM_CONNECTION
88 | developerConnection = POM_SCM_DEV_CONNECTION
89 | }
90 | licenses {
91 | license {
92 | name = POM_LICENCE_NAME
93 | url = POM_LICENCE_URL
94 | distribution = POM_LICENCE_DIST
95 | }
96 | }
97 | developers {
98 | developer {
99 | id = POM_DEVELOPER_ID
100 | name = POM_DEVELOPER_NAME
101 | email = POM_DEVELOPER_EMAIL
102 | organization = POM_DEVELOPER_ORGANIZATION
103 | organizationUrl = POM_DEVELOPER_ORGANIZATION_URL
104 | }
105 | }
106 | }
107 | }
108 | }
109 | repositories {
110 | maven {
111 | url !version.contains("SNAPSHOT") ? getReleaseUrl() : getSnapshotUrl()
112 | credentials {
113 | username = getRepoUsername()
114 | password = getRepoPassword()
115 | }
116 | }
117 | }
118 | }
119 |
120 | signing {
121 | required isCi()
122 | sign publishing.publications
123 | }
124 |
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/scsi/commands/CommandStatusWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver.scsi.commands;
19 |
20 | import java.nio.ByteBuffer;
21 | import java.nio.ByteOrder;
22 |
23 | import android.util.Log;
24 |
25 | /**
26 | * This class represents the command status wrapper (CSW) in the SCSI
27 | * transparent command set standard, which is transmitted from the device to the
28 | * host after the data phase (if any).
29 | *
30 | * @author mjahnen
31 | *
32 | */
33 | public class CommandStatusWrapper {
34 |
35 | /**
36 | * SCSI command has successfully been executed.
37 | */
38 | public static final int COMMAND_PASSED = 0;
39 | /**
40 | * SCSI command could not be executed, host should issue an SCSI request
41 | * sense.
42 | *
43 | * @see com.github.mjdev.libaums.driver.scsi.commands.ScsiRequestSense
44 | */
45 | public static final int COMMAND_FAILED = 1;
46 | /**
47 | * SCSI command could not be executed, host should issue a mass storage
48 | * reset.
49 | */
50 | public static final int PHASE_ERROR = 2;
51 |
52 | /**
53 | * Every CSW has the same size.
54 | */
55 | public static final int SIZE = 13;
56 |
57 | private static final String TAG = CommandStatusWrapper.class.getSimpleName();
58 |
59 | private static final int D_CSW_SIGNATURE = 0x53425355;
60 |
61 | private int dCswSignature;
62 | private int dCswTag;
63 | private int dCswDataResidue;
64 | private byte bCswStatus;
65 |
66 | /**
67 | * Reads command block wrapper from the specified buffer and stores it into this object.
68 | *
69 | * @param buffer
70 | * The data where the command block wrapper is located.
71 | */
72 | public void read(ByteBuffer buffer) {
73 | buffer.order(ByteOrder.LITTLE_ENDIAN);
74 |
75 | dCswSignature = buffer.getInt();
76 | if (dCswSignature != D_CSW_SIGNATURE) {
77 | Log.e(TAG, "unexpected dCSWSignature " + dCswSignature);
78 | }
79 | dCswTag = buffer.getInt();
80 | dCswDataResidue = buffer.getInt();
81 | bCswStatus = buffer.get();
82 | }
83 |
84 | /**
85 | * Returns the tag which can be used to determine the corresponding
86 | * {@link com.github.mjdev.libaums.driver.scsi.commands.CommandBlockWrapper
87 | * CBW}.
88 | *
89 | * @return The command status wrapper tag.
90 | * @see com.github.mjdev.libaums.driver.scsi.commands.CommandBlockWrapper
91 | * #getdCswTag()
92 | */
93 | public int getdCswTag() {
94 | return dCswTag;
95 | }
96 |
97 | /**
98 | * Returns the amount of bytes which has not been processed yet in the data
99 | * phase.
100 | *
101 | * @return The amount of bytes.
102 | */
103 | public int getdCswDataResidue() {
104 | return dCswDataResidue;
105 | }
106 |
107 | /**
108 | * Returns the status of execution of the transmitted SCSI command.
109 | *
110 | * @return The status.
111 | * @see com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper
112 | * #COMMAND_PASSED
113 | * @see com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper
114 | * #COMMAND_FAILED
115 | * @see com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper
116 | * #PHASE_ERROR
117 | */
118 | public byte getbCswStatus() {
119 | return bCswStatus;
120 | }
121 | }
--------------------------------------------------------------------------------
/jni/logging.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "logging.h"
3 | #include "stdbool.h"
4 |
5 | #ifdef __ANDROID__
6 | #include "android/log.h"
7 | #endif
8 |
9 | JavaVM *javaVM;
10 | jobject arborClass;
11 | jobject branchClass;
12 | jobject objectClass;
13 | jmethodID arborTag;
14 | jmethodID branchVerbose;
15 | jmethodID branchDebug;
16 | jmethodID branchInfo;
17 | jmethodID branchWarning;
18 | jmethodID branchError;
19 | jmethodID branchWtf;
20 |
21 | #define LOG_TAG "logging-native"
22 |
23 | void initializeArbor(JNIEnv *env) {
24 | (*env)->GetJavaVM(env, &javaVM);
25 | jclass localClass = (*env)->FindClass(env, "com/toxicbakery/logging/Arbor");
26 | arborClass = (jclass) (*env)->NewGlobalRef(env, localClass);
27 | localClass = (*env)->FindClass(env, "com/toxicbakery/logging/Branch");
28 | branchClass = (jclass) (*env)->NewGlobalRef(env, localClass);
29 | localClass = (*env)->FindClass(env, "java/lang/Object");
30 | objectClass = (*env)->NewGlobalRef(env, localClass);
31 | arborTag = (*env)->GetStaticMethodID(env, arborClass, "tag", "(Ljava/lang/String;)Lcom/toxicbakery/logging/Branch;");
32 | branchVerbose = (*env)->GetMethodID(env, branchClass, "v", "(Ljava/lang/String;[Ljava/lang/Object;)V");
33 | branchDebug = (*env)->GetMethodID(env, branchClass, "d", "(Ljava/lang/String;[Ljava/lang/Object;)V");
34 | branchInfo = (*env)->GetMethodID(env, branchClass, "i", "(Ljava/lang/String;[Ljava/lang/Object;)V");
35 | branchWarning = (*env)->GetMethodID(env, branchClass, "w", "(Ljava/lang/String;[Ljava/lang/Object;)V");
36 | branchError = (*env)->GetMethodID(env, branchClass, "e", "(Ljava/lang/String;[Ljava/lang/Object;)V");
37 | branchWtf = (*env)->GetMethodID(env, branchClass, "wtf", "(Ljava/lang/String;[Ljava/lang/Object;)V");
38 | }
39 |
40 | void arborLog(jmethodID method, const char *tag, const char *fmt, va_list args) {
41 | JNIEnv *jniEnv;
42 | // double check it's all ok
43 | int getEnvStat = (*javaVM)->GetEnv(javaVM, (void **) &jniEnv, JNI_VERSION_1_6);
44 | bool didAttach = false;
45 | if (getEnvStat == JNI_EDETACHED) {
46 | if ((*javaVM)->AttachCurrentThread(javaVM, &jniEnv, NULL) == 0) {
47 | didAttach = true;
48 | }
49 | }
50 |
51 | jstring tagString = (*jniEnv)->NewStringUTF(jniEnv, tag);
52 | jobject branch = (*jniEnv)->CallStaticObjectMethod(jniEnv, arborClass, arborTag, tagString);
53 | if ((*jniEnv)->ExceptionOccurred(jniEnv)) {
54 | #ifdef __ANDROID__
55 | __android_log_print(ANDROID_LOG_ERROR, "Native Log", "Native logging failed. Tree: %p", branch);
56 | #else
57 | fprintf(stderr, "Native logging failed. Tree: %p", branch);
58 | #endif
59 | return;
60 | }
61 |
62 | const char message[500];
63 | vsnprintf(&message, 500, fmt, args);
64 | jstring messageString = (*jniEnv)->NewStringUTF(jniEnv, message);
65 |
66 | jobjectArray array;
67 | array = (*jniEnv)->NewObjectArray(jniEnv, 0, objectClass, NULL);
68 |
69 | (*jniEnv)->CallVoidMethod(jniEnv, branch, method, messageString, array);
70 |
71 | if (didAttach) {
72 | (*javaVM)->DetachCurrentThread(javaVM);
73 | }
74 | }
75 |
76 | void __arbor_verbose(const char *tag, const char *fmt, ...) {
77 | va_list localArgs;
78 | va_start(localArgs, fmt);
79 | arborLog(branchVerbose, tag, fmt, localArgs);
80 | va_end(localArgs);
81 | }
82 |
83 | void __arbor_debug(const char *tag, const char *fmt, ...) {
84 | va_list localArgs;
85 | va_start(localArgs, fmt);
86 | arborLog(branchDebug, tag, fmt, localArgs);
87 | va_end(localArgs);
88 | }
89 |
90 | void __arbor_info(const char *tag, const char *fmt, ...) {
91 | va_list localArgs;
92 | va_start(localArgs, fmt);
93 | arborLog(branchInfo, tag, fmt, localArgs);
94 | va_end(localArgs);
95 | }
96 |
97 | void __arbor_warn(const char *tag, const char *fmt, ...) {
98 | va_list localArgs;
99 | va_start(localArgs, fmt);
100 | arborLog(branchWarning, tag, fmt, localArgs);
101 | va_end(localArgs);
102 | }
103 |
104 | void __arbor_error(const char *tag, const char *fmt, ...) {
105 | va_list localArgs;
106 | va_start(localArgs, fmt);
107 | arborLog(branchError, tag, fmt, localArgs);
108 | va_end(localArgs);
109 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android-Libusb-Wrapper
2 | Java wrapper for libusb library following Android USB Host API
3 |
4 | ## Status
5 | ### Master
6 | [](https://circleci.com/gh/jwoolston/Java-Libusb-Wrapper/tree/master)
7 | [](https://codecov.io/gh/jwoolston/Java-Libusb-Wrapper)
8 | ### Development
9 | [](https://circleci.com/gh/jwoolston/Java-Libusb-Wrapper/tree/development)
10 | [](https://codecov.io/gh/jwoolston/Java-Libusb-Wrapper)
11 |
12 | ## Overview
13 | This is wrapper of the [libusb](https://github.com/libusb/libusb) library for Android which provides a pure Java interface. The goal of this project is to avoid the shortcomings of the Android USB Host framework which notably are:
14 | * No support for Isochronous transfers
15 | * Control transfers are always synchronous
16 | * Many useful API functions were added in several different Android versions
17 | * Function return values are extremely vague. Many are either boolean, with false being for any of the dozen or more errors possible, or -1 for the same possible error set.
18 |
19 | The Android USB Host framework is generally well formatted and convenient to use (In my opinion) and I have used it extensively. For this reason, I have chosen to emulate its API wherever possible and noticeable portions of code (and API documentation) have been copied from the [Android Open Source Project](https://android.googlesource.com/platform/) and I owe them thanks for the public visibility and permissive licensing, as well as inspiration for this API.
20 |
21 | While based on the Android API, there are some differences that are necessitated, primarily due to how the Android USB permission model functions. These differences
22 | are kept to a minimum and are clearly documented. Most notable is this libraries `UsbManager` vs. the one in the Android framework. Since this library has no support for
23 | Android Accessory mode and the permission model prevents us from managing the USB port on the phone, all API related to that and permissions is excluded.
24 |
25 | ## Main Feature Set (Planned)
26 | 1. Dependency free Mavenized Android library with Java only interface. This means the libusb .so files will come with this artifact.
27 | 2. Root access **is not** required.
28 | 2. Support for most USB Host capable Android devices. Minimum API is 14.
29 |
30 | ## Checkout
31 | This repository uses a submodule to another one containing libusb. When checking out this library, either have your Git software recurse the submodules or after checkout you can run
32 | `git submodule update --init`
33 |
34 | ## Building
35 | Due to the NDK requirement, building this project will require you to have the Android NDK on your machine. Setup of the NDK is left to the user. See [Google's NDK website](https://developer.android.com/tools/sdk/ndk/index.html) for more information on this.
36 |
37 | After setting up the NDK, you will need to reference it in your version of `local.properties` by declaring `ndk.dir`.
38 |
39 | Following this, the project should build successfully.
40 |
41 | ## Open Source Credits
42 | - Configuration of builds and deployment was done by [ToxicBakery](https://github.com/ToxicBakery). Additionally, he has provided a general sounding board and motivation to enlarge the scope of this project to provide a hopefully more useful library to the community.
43 |
44 | - The underlying libusb has been modified based on development work by another community member [vianney](https://github.com/vianney/libusb/tree/android) For convenience, I maintain my own for of libusb with his modifications and use it as the libusb source for this library. As libusb is updated, those changes will be pulled in as well.
45 |
46 | - Testing of a number of features in the `library` module is done against the USB MSC class primarily because they are _relatively_ simple and provide a large data source/sink with reasonable speed capabilities. To save the effort of developing the boiler plate code needed to issue the SCSI commands to these devices, a module (`msc_test_core`) is included which contains a modified copy of of [libaums](https://github.com/magnusja/libaums) by GitHub user [magnusja](https://github.com/magnusja). This library was not used as is primarily because it utilizes the Android USB APIs internally and for testing we want to use the APIs provided by this library. The change in package name is exclusively because I do not wish to in any way conflict with his namespace, either intentionally or accidentally.
47 |
--------------------------------------------------------------------------------
/library/src/main/java/com/jwoolston/libusb/BaseUsbManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.jwoolston.libusb;
17 |
18 | import com.toxicbakery.logging.Arbor;
19 |
20 | import org.jetbrains.annotations.NotNull;
21 | import org.jetbrains.annotations.Nullable;
22 |
23 | import java.nio.ByteBuffer;
24 | import java.util.HashMap;
25 | import java.util.Map.Entry;
26 |
27 | /**
28 | * This class allows you to access the state of USB and communicate with USB devices.
29 | * Currently only host mode is supported in the public API.
30 | *
31 | * This class API is based on the Android {@code android.hardware.usb.BaseUsbManager} class.
32 | *
33 | * @author Jared Woolston (Jared.Woolston@gmail.com)
34 | */
35 | public class BaseUsbManager {
36 |
37 | static {
38 | System.loadLibrary("wrapper_libusb");
39 | }
40 |
41 | final Object cacheLock = new Object();
42 |
43 | final HashMap localDeviceCache = new HashMap<>();
44 | final HashMap localConnectionCache = new HashMap<>();
45 | final LibUsbContext libUsbContext;
46 |
47 | private volatile AsyncUSBThread asyncUsbThread;
48 |
49 | private DeviceList nativeDeviceList;
50 |
51 | @Nullable
52 | private native ByteBuffer nativeInitialize();
53 |
54 | private native void nativeSetLoggingLevel(@NotNull ByteBuffer nativeContext, int level);
55 |
56 | private native void nativeDestroy(@NotNull ByteBuffer context);
57 |
58 | public BaseUsbManager() {
59 | libUsbContext = new LibUsbContext(nativeInitialize());
60 | BaseUsbDeviceConnection.initialize();
61 | }
62 |
63 | public void setNativeLogLevel(@NotNull LoggingLevel level) {
64 | nativeSetLoggingLevel(libUsbContext.getNativeObject(), level.ordinal());
65 | }
66 |
67 | public void destroy() {
68 | if (libUsbContext != null) {
69 | nativeDestroy(libUsbContext.getNativeObject());
70 | }
71 | }
72 |
73 | void unregisterDevice(@NotNull BaseUsbDevice device) {
74 | synchronized (cacheLock) {
75 | final String key = device.getDeviceName();
76 | localConnectionCache.remove(key);
77 | localDeviceCache.remove(key);
78 | onDeviceClosed();
79 | }
80 | }
81 |
82 | /**
83 | * Returns a {@link HashMap} containing all USB devices currently attached. USB device name is
84 | * the key for the returned {@link HashMap}. The result will be empty if no devices are
85 | * attached, or if USB host mode is inactive or unsupported.
86 | *
87 | * @return {@link HashMap} containing all connected USB devices.
88 | */
89 | public HashMap getConnectedDeviceList() {
90 | synchronized (cacheLock) {
91 | final HashMap map = new HashMap<>();
92 | for (Entry entry : localDeviceCache.entrySet()) {
93 | map.put(entry.getKey(), entry.getValue());
94 | }
95 | return map;
96 | }
97 | }
98 |
99 | void onClosingDevice() {
100 | synchronized (cacheLock) {
101 | if (localConnectionCache.size() == 1) {
102 | // We need to shutdown the async communication thread if it is running
103 | if (asyncUsbThread != null) {
104 | asyncUsbThread.shutdown();
105 | }
106 | }
107 | }
108 | }
109 |
110 | void onDeviceClosed() {
111 | synchronized (cacheLock) {
112 | if (localConnectionCache.size() == 0) {
113 | try {
114 | asyncUsbThread.join();
115 | asyncUsbThread = null;
116 | } catch (InterruptedException e) {
117 | e.printStackTrace();
118 | }
119 | }
120 | }
121 | }
122 |
123 | void startAsyncIfNeeded() {
124 | if (asyncUsbThread == null) {
125 | Arbor.d("Starting async usb thread.");
126 | asyncUsbThread = new AsyncUSBThread(libUsbContext);
127 | asyncUsbThread.start();
128 | }
129 | }
130 |
131 | public static enum LoggingLevel {
132 | NONE,
133 | ERROR,
134 | WARNING,
135 | INFO,
136 | DEBUG
137 | }
138 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/jwoolston/libusb/util/Hexdump.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2006 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.jwoolston.libusb.util;
18 |
19 | /**
20 | * Clone of Android's HexDump class, for use in debugging. Cosmetic changes
21 | * only.
22 | *
23 | * @author Jared Woolston (Jared.Woolston@gmail.com)
24 | */
25 | public class Hexdump {
26 | private final static char[] HEX_DIGITS = {
27 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
28 | };
29 |
30 | public static String dumpHexString(byte[] array) {
31 | return dumpHexString(array, 0, array.length);
32 | }
33 |
34 | public static String dumpHexString(byte[] array, int offset, int length) {
35 | StringBuilder result = new StringBuilder();
36 |
37 | byte[] line = new byte[16];
38 | int lineIndex = 0;
39 |
40 | result.append("\n0x");
41 | result.append(toHexString(offset));
42 |
43 | for (int i = offset; i < offset + length; i++) {
44 | if (lineIndex == 16) {
45 | result.append(" ");
46 |
47 | for (int j = 0; j < 16; j++) {
48 | if (line[j] > ' ' && line[j] < '~') {
49 | result.append(new String(line, j, 1));
50 | } else {
51 | result.append(".");
52 | }
53 | }
54 |
55 | result.append("\n0x");
56 | result.append(toHexString(i));
57 | lineIndex = 0;
58 | }
59 |
60 | byte b = array[i];
61 | result.append(" ");
62 | result.append(HEX_DIGITS[(b >>> 4) & 0x0F]);
63 | result.append(HEX_DIGITS[b & 0x0F]);
64 |
65 | line[lineIndex++] = b;
66 | }
67 |
68 | if (lineIndex != 16) {
69 | int count = (16 - lineIndex) * 3;
70 | count++;
71 | for (int i = 0; i < count; i++) {
72 | result.append(" ");
73 | }
74 |
75 | for (int i = 0; i < lineIndex; i++) {
76 | if (line[i] > ' ' && line[i] < '~') {
77 | result.append(new String(line, i, 1));
78 | } else {
79 | result.append(".");
80 | }
81 | }
82 | }
83 |
84 | return result.toString();
85 | }
86 |
87 | public static String toHexString(byte b) {
88 | return toHexString(toByteArray(b));
89 | }
90 |
91 | public static String toHexString(byte[] array) {
92 | return toHexString(array, 0, array.length);
93 | }
94 |
95 | public static String toHexString(byte[] array, int offset, int length) {
96 | char[] buf = new char[length * 2];
97 |
98 | int bufIndex = 0;
99 | for (int i = offset; i < offset + length; i++) {
100 | byte b = array[i];
101 | buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
102 | buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
103 | }
104 |
105 | return new String(buf);
106 | }
107 |
108 | public static String toHexString(int i) {
109 | return toHexString(toByteArray(i));
110 | }
111 |
112 | public static byte[] toByteArray(byte b) {
113 | byte[] array = new byte[1];
114 | array[0] = b;
115 | return array;
116 | }
117 |
118 | public static byte[] toByteArray(int i) {
119 | byte[] array = new byte[4];
120 |
121 | array[3] = (byte) (i & 0xFF);
122 | array[2] = (byte) ((i >> 8) & 0xFF);
123 | array[1] = (byte) ((i >> 16) & 0xFF);
124 | array[0] = (byte) ((i >> 24) & 0xFF);
125 |
126 | return array;
127 | }
128 |
129 | private static int toByte(char c) {
130 | if (c >= '0' && c <= '9')
131 | return (c - '0');
132 | if (c >= 'A' && c <= 'F')
133 | return (c - 'A' + 10);
134 | if (c >= 'a' && c <= 'f')
135 | return (c - 'a' + 10);
136 |
137 | throw new RuntimeException("Invalid hex char '" + c + "'");
138 | }
139 |
140 | public static byte[] hexStringToByteArray(String hexString) {
141 | int length = hexString.length();
142 | byte[] buffer = new byte[length / 2];
143 |
144 | for (int i = 0; i < length; i += 2) {
145 | buffer[i / 2] = (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(hexString
146 | .charAt(i + 1)));
147 | }
148 |
149 | return buffer;
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
22 |
23 |
33 |
34 |
44 |
45 |
58 |
59 |
69 |
70 |
80 |
81 |
90 |
91 |
104 |
105 |
113 |
114 |
122 |
--------------------------------------------------------------------------------
/msc_test_core/src/main/java/com/jwoolston/android/libusb/msc_test_core/driver/scsi/commands/CommandBlockWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 mjahnen
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | package com.jwoolston.android.libusb.msc_test_core.driver.scsi.commands;
19 |
20 | import java.nio.ByteBuffer;
21 | import java.nio.ByteOrder;
22 |
23 | /**
24 | * This class represents the command block wrapper (CBW) which is always wrapped
25 | * around a specific SCSI command in the SCSI transparent command set standard.
26 | *
27 | * Every SCSI command shall extend this class, call the constructor
28 | * {@link #CommandBlockWrapper(int, Direction, byte, byte)} with the desired
29 | * information. When transmitting the command, the
30 | * {@link #serialize(ByteBuffer)} method has to be called!
31 | *
32 | * @author mjahnen
33 | *
34 | */
35 | public abstract class CommandBlockWrapper {
36 |
37 | /**
38 | * The direction of the data phase of the SCSI command.
39 | *
40 | * @author mjahnen
41 | *
42 | */
43 | public enum Direction {
44 | /**
45 | * Means from device to host (Android).
46 | */
47 | IN,
48 | /**
49 | * Means from host (Android) to device.
50 | */
51 | OUT,
52 | /**
53 | * There is no data phase
54 | */
55 | NONE
56 | }
57 |
58 | private static final int D_CBW_SIGNATURE = 0x43425355;
59 |
60 | private int dCbwTag;
61 | protected int dCbwDataTransferLength;
62 | private byte bmCbwFlags;
63 | private byte bCbwLun;
64 | private byte bCbwcbLength;
65 | private Direction direction;
66 |
67 | /**
68 | * Constructs a new command block wrapper with the given information which
69 | * can than easily be serialized with {@link #serialize(ByteBuffer)}.
70 | *
71 | * @param transferLength
72 | * The bytes which should be transferred in the following data
73 | * phase (Zero if no data phase).
74 | * @param direction
75 | * The direction the data shall be transferred in the data phase.
76 | * If there is no data phase it should be
77 | * {@link com.github.mjdev.libaums.driver.scsi.commands.CommandBlockWrapper.Direction #NONE
78 | * NONE}
79 | * @param lun
80 | * The logical unit number the command is directed to.
81 | * @param cbwcbLength
82 | * The length in bytes of the scsi command.
83 | */
84 | protected CommandBlockWrapper(int transferLength, Direction direction, byte lun,
85 | byte cbwcbLength) {
86 | dCbwDataTransferLength = transferLength;
87 | this.direction = direction;
88 | if (direction == Direction.IN)
89 | bmCbwFlags = (byte) 0x80;
90 | bCbwLun = lun;
91 | bCbwcbLength = cbwcbLength;
92 | }
93 |
94 | /**
95 | * Serializes the command block wrapper for transmission.
96 | *
97 | * This method should be called in every subclass right before the specific
98 | * SCSI command serializes itself to the buffer!
99 | *
100 | * @param buffer
101 | * The buffer were the serialized data should be copied to.
102 | */
103 | public void serialize(ByteBuffer buffer) {
104 | buffer.order(ByteOrder.LITTLE_ENDIAN);
105 | buffer.putInt(D_CBW_SIGNATURE);
106 | buffer.putInt(dCbwTag);
107 | buffer.putInt(dCbwDataTransferLength);
108 | buffer.put(bmCbwFlags);
109 | buffer.put(bCbwLun);
110 | buffer.put(bCbwcbLength);
111 | }
112 |
113 | /**
114 | * Returns the tag which can be used to determine the corresponding
115 | * {@link com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper
116 | * CBW}.
117 | *
118 | * @return The command block wrapper tag.
119 | * @see com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper
120 | * #getdCswTag()
121 | */
122 | public int getdCbwTag() {
123 | return dCbwTag;
124 | }
125 |
126 | /**
127 | * Sets the tag which can be used to determine the corresponding
128 | * {@link com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper
129 | * CBW}.
130 | *
131 | * @param dCbwTag The command block wrapper tag
132 | * @see com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper
133 | * #getdCswTag()
134 | */
135 | public void setdCbwTag(int dCbwTag) {
136 | this.dCbwTag = dCbwTag;
137 | }
138 |
139 | /**
140 | * Returns the amount of bytes which should be transmitted in the data
141 | * phase.
142 | *
143 | * @return The length in bytes.
144 | */
145 | public int getdCbwDataTransferLength() {
146 | return dCbwDataTransferLength;
147 | }
148 |
149 | /**
150 | * Returns the direction in the data phase.
151 | *
152 | * @return The direction.
153 | * @see com.github.mjdev.libaums.driver.scsi.commands.CommandBlockWrapper.Direction
154 | * Direction
155 | */
156 | public Direction getDirection() {
157 | return direction;
158 | }
159 |
160 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/jwoolston/libusb/BaseUsbEndpoint.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.jwoolston.libusb;
17 |
18 | import org.jetbrains.annotations.NotNull;
19 |
20 | import java.nio.ByteBuffer;
21 |
22 | /**
23 | * A class representing an endpoint on a {@link BaseUsbInterface}. Endpoints are the channels for sending and receiving
24 | * data over USB. Typically bulk endpoints are used for sending non-trivial amounts of data. Interrupt endpoints are
25 | * used for sending small amounts of data, typically events, separately from the main data streams. The endpoint zero
26 | * is a special endpoint for control messages sent from the host to device.
27 | */
28 | public class BaseUsbEndpoint {
29 |
30 | final int address;
31 | final int attributes;
32 | final int maxPacketSize;
33 | final int interval;
34 |
35 | /**
36 | * BaseUsbEndpoint should only be instantiated by UsbService implementation
37 | */
38 | BaseUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
39 | this.address = address;
40 | this.attributes = attributes;
41 | this.maxPacketSize = maxPacketSize;
42 | this.interval = interval;
43 | }
44 |
45 | /**
46 | * Returns the endpoint's address field. The address is a bitfield containing both the endpoint number as well as
47 | * the data direction of the endpoint. the endpoint number and direction can also be accessed via
48 | * {@link #getEndpointNumber} and {@link #getDirection}.
49 | *
50 | * @return the endpoint's address
51 | */
52 | public int getAddress() {
53 | return address;
54 | }
55 |
56 | /**
57 | * Extracts the endpoint's endpoint number from its address
58 | *
59 | * @return the endpoint's endpoint number
60 | */
61 | public int getEndpointNumber() {
62 | return address & UsbConstants.USB_ENDPOINT_NUMBER_MASK;
63 | }
64 |
65 | /**
66 | * Returns the endpoint's direction. Returns {@link UsbConstants#USB_DIR_OUT} if the direction is host to device,
67 | * and {@link UsbConstants#USB_DIR_IN} if the direction is device to host.
68 | *
69 | * @return the endpoint's direction
70 | * @see UsbConstants#USB_DIR_IN
71 | * @see UsbConstants#USB_DIR_OUT
72 | */
73 | public int getDirection() {
74 | return address & UsbConstants.USB_ENDPOINT_DIR_MASK;
75 | }
76 |
77 | /**
78 | * Returns the endpoint's attributes field.
79 | *
80 | * @return the endpoint's attributes
81 | */
82 | public int getAttributes() {
83 | return attributes;
84 | }
85 |
86 | /**
87 | * Returns the endpoint's type. Possible results are:
88 | *
89 | * - {@link UsbConstants#USB_ENDPOINT_XFER_CONTROL} (endpoint zero)
90 | *
- {@link UsbConstants#USB_ENDPOINT_XFER_ISOC} (isochronous endpoint)
91 | *
- {@link UsbConstants#USB_ENDPOINT_XFER_BULK} (bulk endpoint)
92 | *
- {@link UsbConstants#USB_ENDPOINT_XFER_INT} (interrupt endpoint)
93 | *
94 | *
95 | * @return the endpoint's type
96 | */
97 | public int getType() {
98 | return attributes & UsbConstants.USB_ENDPOINT_XFERTYPE_MASK;
99 | }
100 |
101 | /**
102 | * Returns the endpoint's maximum packet size.
103 | *
104 | * @return the endpoint's maximum packet size
105 | */
106 | public int getMaxPacketSize() {
107 | return maxPacketSize;
108 | }
109 |
110 | /**
111 | * Returns the endpoint's interval field.
112 | *
113 | * @return the endpoint's interval
114 | */
115 | public int getInterval() {
116 | return interval;
117 | }
118 |
119 | @Override
120 | public String toString() {
121 | return "BaseUsbEndpoint[address=" + address + ",attributes=" + attributes +
122 | ",maxPacketSize=" + maxPacketSize + ",interval=" + interval + "]";
123 | }
124 |
125 | private static final int INDEX_ADDRESS = 2;
126 | private static final int INDEX_ATTRIBUTES = 3;
127 | private static final int INDEX_MAX_PACKET_SIZE = 4;
128 | private static final int INDEX_INTERVAL = 6;
129 |
130 | static BaseUsbEndpoint fromNativeObject(@NotNull BaseUsbDevice device, @NotNull ByteBuffer nativeObject) {
131 | final int address = 0xFF & nativeObject.get(INDEX_ADDRESS);
132 | final int attributes = 0xFF & nativeObject.get(INDEX_ATTRIBUTES);
133 | final int maxPacketSize = (0xFF & nativeObject.get(INDEX_MAX_PACKET_SIZE))
134 | | ((0xFF & nativeObject.get(INDEX_MAX_PACKET_SIZE + 1)) << 8);
135 | final int interval = 0xFF & nativeObject.get(INDEX_INTERVAL);
136 | return device.createEndpoint(address, attributes, maxPacketSize, interval);
137 | }
138 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/jwoolston/libusb/UsbDevice.java:
--------------------------------------------------------------------------------
1 | package com.jwoolston.libusb;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 | import android.support.annotation.NonNull;
6 |
7 | import com.jwoolston.libusb.util.Preconditions;
8 |
9 | import org.jetbrains.annotations.NotNull;
10 | import org.jetbrains.annotations.Nullable;
11 |
12 | import java.nio.ByteBuffer;
13 |
14 | public class UsbDevice extends BaseUsbDevice implements Parcelable {
15 |
16 | @NotNull
17 | private final android.hardware.usb.UsbDevice device;
18 |
19 | @NotNull
20 | public android.hardware.usb.UsbDevice getAndroidDevice() {
21 | return device;
22 | }
23 |
24 | /**
25 | * Returns a unique integer ID for the device. This is a convenience for clients that want to
26 | * use an integer to represent the device, rather than the device name. IDs are not persistent
27 | * across USB disconnects.
28 | *
29 | * @return the device ID
30 | */
31 | public int getDeviceId() {
32 | return device.getDeviceId();
33 | }
34 |
35 | @Override
36 | public int describeContents() {
37 | return 0;
38 | }
39 |
40 | @Override
41 | public void writeToParcel(Parcel dest, int flags) {
42 | dest.writeLong(nativeGetPointerFromNativeObject(getNativeObject()));
43 | dest.writeParcelable(device, flags);
44 | dest.writeString(name);
45 | dest.writeString(manufacturerName);
46 | dest.writeString(productName);
47 | dest.writeString(version);
48 | dest.writeString(serialNumber);
49 | dest.writeInt(speed.code);
50 | dest.writeInt(vendorId);
51 | dest.writeInt(productId);
52 | dest.writeInt(deviceClass);
53 | dest.writeInt(subclass);
54 | dest.writeInt(protocol);
55 | dest.writeInt(fileDescriptor);
56 | dest.writeTypedArray((UsbConfiguration[]) configurations, flags);
57 | dest.writeTypedArray((UsbInterface[]) interfaces, flags);
58 | }
59 |
60 | public static final Creator CREATOR = new Creator() {
61 | @Override
62 | public UsbDevice createFromParcel(Parcel in) {
63 | return new UsbDevice(in);
64 | }
65 |
66 | @Override
67 | public UsbDevice[] newArray(int size) {
68 | return new UsbDevice[size];
69 | }
70 | };
71 |
72 | @NonNull
73 | static UsbDevice fromAndroidDevice(@NotNull LibUsbContext context, @NotNull android.hardware.usb.UsbDevice device,
74 | @NotNull android.hardware.usb.UsbDeviceConnection connection) {
75 | return new UsbDevice(connection, device, wrapDevice(context.getNativeObject(), connection.getFileDescriptor()));
76 | }
77 |
78 | private UsbDevice(@NotNull android.hardware.usb.UsbDeviceConnection connection,
79 | @NotNull android.hardware.usb.UsbDevice device, @Nullable ByteBuffer nativeObject) {
80 | Preconditions.checkNotNull(nativeObject, "UsbDevice initialization failed.");
81 | this.nativeObject = nativeObject;
82 | this.device = device;
83 | name = device.getDeviceName();
84 |
85 | LibUsbDeviceDescriptor descriptor = LibUsbDeviceDescriptor.getDeviceDescriptor(this);
86 | initFromDescriptor(descriptor);
87 |
88 | serialNumber = connection.getSerial();
89 | fileDescriptor = connection.getFileDescriptor();
90 | }
91 |
92 | protected UsbDevice(Parcel in) {
93 | final ByteBuffer buffer = nativeGetNativeObjectFromPointer(in.readLong());
94 | if (buffer == null) {
95 | throw new IllegalStateException("Received a null reference for the native object. Creation from "
96 | + "parcel failed.");
97 | }
98 | nativeObject = buffer;
99 | device = in.readParcelable(android.hardware.usb.UsbDevice.class.getClassLoader());
100 | name = in.readString();
101 | manufacturerName = in.readString();
102 | productName = in.readString();
103 | version = in.readString();
104 | serialNumber = in.readString();
105 | speed = LibusbSpeed.fromNative(in.readInt());
106 | vendorId = in.readInt();
107 | productId = in.readInt();
108 | deviceClass = in.readInt();
109 | subclass = in.readInt();
110 | protocol = in.readInt();
111 | fileDescriptor = in.readInt();
112 | configurations = in.createTypedArray(UsbConfiguration.CREATOR);
113 | interfaces = in.createTypedArray(UsbInterface.CREATOR);
114 | }
115 |
116 | @Override
117 | UsbConfiguration createConfiguration(int id, @Nullable String name, int attributes, int maxPower) {
118 | return new UsbConfiguration(id, name, attributes, maxPower);
119 | }
120 |
121 | @Override
122 | UsbInterface createInterface(int id, int alternateSetting, @Nullable String name,
123 | int interfaceClass, int subClass, int protocol) {
124 | return new UsbInterface(id, alternateSetting, name, interfaceClass, subClass, protocol);
125 | }
126 |
127 | @Override
128 | UsbEndpoint createEndpoint(int address, int attributes, int maxPacketSize, int interval) {
129 | return new UsbEndpoint(address, attributes, maxPacketSize, interval);
130 | }
131 |
132 | @Override
133 | @NotNull
134 | public UsbInterface getInterface(int index) {
135 | return (UsbInterface) super.getInterface(index);
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/jvm/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # For more information about using CMake with Android Studio, read the
2 | # documentation: https://d.android.com/studio/projects/add-native-code.html
3 |
4 | # Sets the minimum version of CMake required to build the native library.
5 |
6 | cmake_minimum_required(VERSION 3.4.1)
7 |
8 | MESSAGE(STATUS "CMAKE_SYSTEM_NAME is ${CMAKE_SYSTEM_NAME}")
9 |
10 | find_package(JNI)
11 |
12 | SET(CMAKE_CXX_STANDARD 11)
13 |
14 | # Creates and names a library, sets it as either STATIC
15 | # or SHARED, and provides the relative paths to its source code.
16 | # You can define multiple libraries, and CMake builds them for you.
17 | # Gradle automatically packages shared libraries with your APK.
18 |
19 | add_definitions(-DJNI_WRAPPER)
20 |
21 | set(includeDIRS
22 | ../library/libusb/libusb
23 | ../library/libusb/libusb/os
24 | ../library/jni
25 | ${JAVA_INCLUDE_PATH})
26 |
27 | if(WIN32)
28 | set(includeDIRS
29 | ${includeDIRS}
30 | ${JAVA_INCLUDE_PATH}/Win32
31 | ../library/libusb/msvc)
32 | endif(WIN32)
33 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
34 | set(includeDIRS
35 | ${includeDIRS}
36 | ${JAVA_INCLUDE_PATH}/linux
37 | ../library/libusb)
38 | endif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
39 |
40 | include_directories(${includeDIRS})
41 |
42 | set(POSIX_POLL_SRC
43 | ../library/libusb/libusb/os/poll_posix.h
44 | ../library/libusb/libusb/os/poll_posix.c)
45 |
46 | set(POSIX_THREADS_SRC
47 | ../library/libusb/libusb/os/threads_posix.h
48 | ../library/libusb/libusb/os/threads_posix.c)
49 |
50 | set(WINDOWS_POLL_SRC
51 | ../library/libusb/libusb/os/poll_windows.h
52 | ../library/libusb/libusb/os/poll_windows.c)
53 |
54 | set(WINDOWS_THREADS_SRC
55 | ../library/libusb/libusb/os/threads_windows.h
56 | ../library/libusb/libusb/os/threads_windows.c)
57 |
58 | set(LINUX_USBFS_SRC
59 | ../library/libusb/libusb/os/linux_usbfs.h
60 | ../library/libusb/libusb/os/linux_usbfs.c)
61 |
62 | set(DARWIN_USB_SRC
63 | ../library/libusb/libusb/os/darwin_usb.h
64 | ../library/libusb/libusb/os/darwin_usb.c)
65 |
66 | set(OPENBSD_USB_SRC
67 | ../library/libusb/libusb/os/openbsd_usb.c)
68 |
69 | set(NETBSD_USB_SRC
70 | ../library/libusb/libusb/os/netbsd_usb.c)
71 |
72 | set(SUNOS_USB_SRC
73 | ../library/libusb/libusb/os/sunos_usb.c
74 | ../library/libusb/libusb/os/sunos_usb.h)
75 |
76 | set(WINDOWS_USB_SRC
77 | ../library/libusb/libusb/libusb-1.0.def
78 | ../library/libusb/libusb/libusb-1.0.rc
79 | ../library/libusb/libusb/os/windows_common.h
80 | ../library/libusb/libusb/os/windows_nt_common.h
81 | ../library/libusb/libusb/os/windows_nt_common.c
82 | ../library/libusb/libusb/os/windows_nt_shared_types.h
83 | ../library/libusb/libusb/os/windows_usbdk.h
84 | ../library/libusb/libusb/os/windows_usbdk.c
85 | ../library/libusb/libusb/os/windows_winusb.h
86 | ../library/libusb/libusb/os/windows_winusb.c)
87 |
88 | set(WINCE_USB_SRC
89 | ../library/libusb/libusb/os/wince_usb.h
90 | ../library/libusb/libusb/os/wince_usb.c)
91 |
92 | set(HAIKU_USB_SRC
93 | ../library/libusb/libusb/os/haiku_usb.h
94 | ../library/libusb/libusb/os/haiku_usb_backend.cpp
95 | ../library/libusb/libusb/os/haiku_usb_raw.h
96 | ../library/libusb/libusb/os/haiku_usb_raw.cpp
97 | ../library/libusb/libusb/os/haiku_pollfs.cpp)
98 |
99 | set(EXTRA_DIST
100 | ${POSIX_POLL_SRC}
101 | ${POSIX_THREADS_SRC}
102 | ${WINDOWS_POLL_SRC}
103 | ${WINDOWS_THREADS_SRC}
104 | ${LINUX_USBFS_SRC}
105 | ${DARWIN_USB_SRC}
106 | ${OPENBSD_USB_SRC}
107 | ${NETBSD_USB_SRC}
108 | ${WINDOWS_USB_SRC}
109 | ${WINCE_USB_SRC}
110 | ${HAIKU_USB_SRC}
111 | ../library/libusb/libusb/os/linux_udev.c
112 | ../library/libusb/libusb/os/linux_netlink.c)
113 |
114 | # Defines the source code for the library
115 | set(libusb_SRCS
116 | ../library/libusb/libusb/core.c
117 | ../library/libusb/libusb/descriptor.c
118 | ../library/libusb/libusb/hotplug.c
119 | ../library/libusb/libusb/io.c
120 | ../library/libusb/libusb/sync.c
121 | ../library/libusb/libusb/strerror.c)
122 |
123 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
124 | if(USE_UDEV)
125 | set(OS_SRC
126 | ${LINUX_USBFS_SRC}
127 | ${POSIX_POLL_SRC}
128 | ${POSIX_THREADS_SRC}
129 | ../library/libusb/libusb/os/linux_udev.c)
130 | else(USE_UDEV)
131 | set(OS_SRC
132 | ${LINUX_USBFS_SRC}
133 | ${POSIX_POLL_SRC}
134 | ${POSIX_THREADS_SRC}
135 | ../library/libusb/libusb/os/linux_netlink.c)
136 | endif(USE_UDEV)
137 | endif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
138 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
139 | set(OS_SRC
140 | ${DARWIN_USB_SRC}
141 | ${POSIX_POLL_SRC}
142 | ${POSIX_THREADS_SRC})
143 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -no-cpp-precomp")
144 | endif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
145 | if(WIN32)
146 | set(OS_SRC
147 | ${WINDOWS_USB_SRC}
148 | ${WINDOWS_POLL_SRC}
149 | ${WINDOWS_THREADS_SRC})
150 | endif(WIN32)
151 |
152 | add_library(
153 | # Sets the name of the library.
154 | wrapper_libusb
155 |
156 | # Sets the library as a shared library.
157 | SHARED
158 |
159 | # Provides a relative path to your source file(s).
160 | ../library/jni/async_transfer.c
161 | ../library/jni/async_usb_thread.c
162 | ../library/jni/common.h
163 | ../library/jni/isochronous_async_transfer.c
164 | ../library/jni/libusb_device_descriptor.c
165 | ../library/jni/libusb_error_enum.c
166 | ../library/jni/logging.c
167 | ../library/jni/usb_configuration.c
168 | ../library/jni/usb_device.c
169 | ../library/jni/usb_device_connection.c
170 | ../library/jni/usb_interface.c
171 | ../library/jni/usb_manager.c
172 | ${libusb_SRCS}
173 | ${OS_SRC}
174 | )
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/library/src/main/java/com/jwoolston/libusb/UsbConstants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.jwoolston.libusb;
17 |
18 | /**
19 | * Contains constants for the USB protocol.
20 | * These constants correspond to definitions in linux/usb/ch9.h in the linux kernel.
21 | */
22 | public final class UsbConstants {
23 | /**
24 | * Bitmask used for extracting the {@link BaseUsbEndpoint} direction from its address field.
25 | *
26 | * @see BaseUsbEndpoint#getAddress
27 | * @see BaseUsbEndpoint#getDirection
28 | * @see #USB_DIR_OUT
29 | * @see #USB_DIR_IN
30 | */
31 | public static final int USB_ENDPOINT_DIR_MASK = 0x80;
32 | /**
33 | * Used to signify direction of data for a {@link BaseUsbEndpoint} is OUT (host to device)
34 | *
35 | * @see BaseUsbEndpoint#getDirection
36 | */
37 | public static final int USB_DIR_OUT = 0;
38 | /**
39 | * Used to signify direction of data for a {@link BaseUsbEndpoint} is IN (device to host)
40 | *
41 | * @see BaseUsbEndpoint#getDirection
42 | */
43 | public static final int USB_DIR_IN = 0x80;
44 | /**
45 | * Bitmask used for extracting the {@link BaseUsbEndpoint} number its address field.
46 | *
47 | * @see BaseUsbEndpoint#getAddress
48 | * @see BaseUsbEndpoint#getEndpointNumber
49 | */
50 | public static final int USB_ENDPOINT_NUMBER_MASK = 0x0f;
51 | /**
52 | * Bitmask used for extracting the {@link BaseUsbEndpoint} type from its address field.
53 | *
54 | * @see BaseUsbEndpoint#getAddress
55 | * @see BaseUsbEndpoint#getType
56 | * @see #USB_ENDPOINT_XFER_CONTROL
57 | * @see #USB_ENDPOINT_XFER_ISOC
58 | * @see #USB_ENDPOINT_XFER_BULK
59 | * @see #USB_ENDPOINT_XFER_INT
60 | */
61 | public static final int USB_ENDPOINT_XFERTYPE_MASK = 0x03;
62 | /**
63 | * Control endpoint type (endpoint zero)
64 | *
65 | * @see BaseUsbEndpoint#getType
66 | */
67 | public static final int USB_ENDPOINT_XFER_CONTROL = 0;
68 | /**
69 | * Isochronous endpoint type (currently not supported)
70 | *
71 | * @see BaseUsbEndpoint#getType
72 | */
73 | public static final int USB_ENDPOINT_XFER_ISOC = 1;
74 | /**
75 | * Bulk endpoint type
76 | *
77 | * @see BaseUsbEndpoint#getType
78 | */
79 | public static final int USB_ENDPOINT_XFER_BULK = 2;
80 | /**
81 | * Interrupt endpoint type
82 | *
83 | * @see BaseUsbEndpoint#getType
84 | */
85 | public static final int USB_ENDPOINT_XFER_INT = 3;
86 | /**
87 | * Bitmask used for encoding the request type for a control request on endpoint zero.
88 | */
89 | public static final int USB_TYPE_MASK = (0x03 << 5);
90 | /**
91 | * Used to specify that an endpoint zero control request is a standard request.
92 | */
93 | public static final int USB_TYPE_STANDARD = (0x00 << 5);
94 | /**
95 | * Used to specify that an endpoint zero control request is a class specific request.
96 | */
97 | public static final int USB_TYPE_CLASS = (0x01 << 5);
98 | /**
99 | * Used to specify that an endpoint zero control request is a vendor specific request.
100 | */
101 | public static final int USB_TYPE_VENDOR = (0x02 << 5);
102 | /**
103 | * Reserved endpoint zero control request type (currently unused).
104 | */
105 | public static final int USB_TYPE_RESERVED = (0x03 << 5);
106 | /**
107 | * USB class indicating that the class is determined on a per-interface basis.
108 | */
109 | public static final int USB_CLASS_PER_INTERFACE = 0;
110 | /**
111 | * USB class for audio devices.
112 | */
113 | public static final int USB_CLASS_AUDIO = 1;
114 | /**
115 | * USB class for communication devices.
116 | */
117 | public static final int USB_CLASS_COMM = 2;
118 | /**
119 | * USB class for human interface devices (for example, mice and keyboards).
120 | */
121 | public static final int USB_CLASS_HID = 3;
122 | /**
123 | * USB class for physical devices.
124 | */
125 | public static final int USB_CLASS_PHYSICA = 5;
126 | /**
127 | * USB class for still image devices (digital cameras).
128 | */
129 | public static final int USB_CLASS_STILL_IMAGE = 6;
130 | /**
131 | * USB class for printers.
132 | */
133 | public static final int USB_CLASS_PRINTER = 7;
134 | /**
135 | * USB class for mass storage devices.
136 | */
137 | public static final int USB_CLASS_MASS_STORAGE = 8;
138 | /**
139 | * USB class for USB hubs.
140 | */
141 | public static final int USB_CLASS_HUB = 9;
142 | /**
143 | * USB class for CDC devices (communications device class).
144 | */
145 | public static final int USB_CLASS_CDC_DATA = 0x0a;
146 | /**
147 | * USB class for content smart card devices.
148 | */
149 | public static final int USB_CLASS_CSCID = 0x0b;
150 | /**
151 | * USB class for content security devices.
152 | */
153 | public static final int USB_CLASS_CONTENT_SEC = 0x0d;
154 | /**
155 | * USB class for video devices.
156 | */
157 | public static final int USB_CLASS_VIDEO = 0x0e;
158 | /**
159 | * USB class for wireless controller devices.
160 | */
161 | public static final int USB_CLASS_WIRELESS_CONTROLLER = 0xe0;
162 | /**
163 | * USB class for wireless miscellaneous devices.
164 | */
165 | public static final int USB_CLASS_MISC = 0xef;
166 | /**
167 | * Application specific USB class.
168 | */
169 | public static final int USB_CLASS_APP_SPEC = 0xfe;
170 | /**
171 | * Vendor specific USB class.
172 | */
173 | public static final int USB_CLASS_VENDOR_SPEC = 0xff;
174 | /**
175 | * Boot subclass for HID devices.
176 | */
177 | public static final int USB_INTERFACE_SUBCLASS_BOOT = 1;
178 | /**
179 | * Vendor specific USB subclass.
180 | */
181 | public static final int USB_SUBCLASS_VENDOR_SPEC = 0xff;
182 | }
--------------------------------------------------------------------------------
/jni/usb_device.c:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jared Woolston (Jared.Woolston@gmail.com)
3 | //
4 |
5 | #include
6 |
7 | #pragma clang diagnostic push
8 | #pragma clang diagnostic ignored "-Wunused-parameter"
9 | #define LOG_TAG "UsbDevice-Native"
10 |
11 | #define STR_ALLOC_LENGTH 50
12 | #define SIGN_MASK 0xFF
13 |
14 | JNIEXPORT jstring JNICALL
15 | Java_com_jwoolston_libusb_BaseUsbDevice_nativeGetStringDescriptor(JNIEnv *env, jclass type, jobject device,
16 | jint index) {
17 | if (index == 0) {
18 | return NULL;
19 | }
20 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *) (*env)->GetDirectBufferAddress(env,
21 | device);
22 | size_t length = STR_ALLOC_LENGTH * sizeof(unsigned char);
23 | unsigned char *name = malloc(length);
24 | libusb_get_string_descriptor_ascii(deviceHandle, (uint8_t) (SIGN_MASK & index), name, (int) length);
25 | jstring retval = (*env)->NewStringUTF(env, (const char *) name);
26 | free(name);
27 | return retval;
28 | }
29 |
30 | JNIEXPORT jobject JNICALL
31 | Java_com_jwoolston_libusb_BaseUsbDevice_wrapDevice(JNIEnv *env, jclass type, jobject context, jint fd) {
32 | LOGD("Wrapping USB Device Handle.");
33 | struct libusb_device_handle *deviceHandle;
34 |
35 | struct libusb_context *ctx = (struct libusb_context *) (*env)->GetDirectBufferAddress(env, context);
36 | int ret = libusb_wrap_sys_device(ctx, fd, &deviceHandle);
37 |
38 | if (deviceHandle == NULL) {
39 | LOGE("Failed to wrap usb device file descriptor. Error: %s", libusb_strerror((enum libusb_error) ret));
40 | return NULL;
41 | }
42 |
43 | return ((*env)->NewDirectByteBuffer(env, (void *) deviceHandle, sizeof(struct libusb_device_handle)));
44 | }
45 |
46 | JNIEXPORT jstring JNICALL
47 | Java_com_jwoolston_libusb_BaseUsbDevice_nativeGetManufacturerString(JNIEnv *env, jobject instance, jobject device,
48 | jobject descriptor) {
49 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *) (*env)->GetDirectBufferAddress(env,
50 | device);
51 | struct libusb_device_descriptor *deviceDescriptor = (struct libusb_device_descriptor *)
52 | (*env)->GetDirectBufferAddress(env, descriptor);
53 |
54 | size_t length = STR_ALLOC_LENGTH * sizeof(unsigned char);
55 | unsigned char *name = malloc(length);
56 | libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor->iManufacturer, name, (int) length);
57 | jstring retval = (*env)->NewStringUTF(env, (const char *) name);
58 | free(name);
59 | return retval;
60 | }
61 |
62 | JNIEXPORT jstring JNICALL
63 | Java_com_jwoolston_libusb_BaseUsbDevice_nativeGetSerialString(JNIEnv *env, jobject instance, jobject device,
64 | jobject descriptor) {
65 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *) (*env)->GetDirectBufferAddress(env,
66 | device);
67 | struct libusb_device_descriptor *deviceDescriptor = (struct libusb_device_descriptor *)
68 | (*env)->GetDirectBufferAddress(env, descriptor);
69 |
70 | size_t length = STR_ALLOC_LENGTH * sizeof(unsigned char);
71 | unsigned char *serial = malloc(length);
72 | libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor->iSerialNumber, serial, (int) length);
73 | jstring retval = (*env)->NewStringUTF(env, (const char *) serial);
74 | free(serial);
75 | return retval;
76 | }
77 |
78 | JNIEXPORT jstring JNICALL
79 | Java_com_jwoolston_libusb_BaseUsbDevice_nativeGetProductNameString(JNIEnv *env, jobject instance, jobject device,
80 | jobject descriptor) {
81 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *) (*env)->GetDirectBufferAddress(env,
82 | device);
83 | struct libusb_device_descriptor *deviceDescriptor = (struct libusb_device_descriptor *)
84 | (*env)->GetDirectBufferAddress(env, descriptor);
85 |
86 | size_t length = STR_ALLOC_LENGTH * sizeof(unsigned char);
87 | unsigned char *name = malloc(length);
88 | libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor->iProduct, name, (int) length);
89 | jstring retval = (*env)->NewStringUTF(env, (const char *) name);
90 | free(name);
91 | return retval;
92 | }
93 |
94 | JNIEXPORT jstring JNICALL
95 | Java_com_jwoolston_libusb_BaseUsbDevice_nativeGetDeviceVersion(JNIEnv *env, jobject instance, jobject descriptor) {
96 | struct libusb_device_descriptor *deviceDescriptor = (struct libusb_device_descriptor *)
97 | (*env)->GetDirectBufferAddress(env, descriptor);
98 | uint16_t bcdDevice = deviceDescriptor->bcdDevice;
99 | size_t length = 4 * sizeof(unsigned char);
100 | char *version = malloc(length);
101 | snprintf(version, length, "%i.%i", SIGN_MASK & (bcdDevice >> 8), SIGN_MASK & bcdDevice);
102 | jstring retval = (*env)->NewStringUTF(env, (const char *) version);
103 | free(version);
104 | return retval;
105 | }
106 |
107 | JNIEXPORT jint JNICALL
108 | Java_com_jwoolston_libusb_BaseUsbDevice_nativeGetDeviceSpeed(JNIEnv *env, jobject instance, jobject device,
109 | jobject descriptor) {
110 |
111 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *) (*env)->GetDirectBufferAddress(env,
112 | device);
113 | return libusb_get_device_speed(deviceHandle->dev);
114 | }
115 |
116 | JNIEXPORT jint JNICALL
117 | Java_com_jwoolston_libusb_BaseUsbDevice_nativeGetConfigurationCount(JNIEnv *env, jobject instance, jobject device) {
118 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *) (*env)->GetDirectBufferAddress(env,
119 | device);
120 | return deviceHandle->dev->num_configurations;
121 | }
122 |
123 | JNIEXPORT jlong JNICALL
124 | Java_com_jwoolston_libusb_BaseUsbDevice_nativeGetPointerFromNativeObject(JNIEnv *env, jobject instance,
125 | jobject device) {
126 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *) (*env)->GetDirectBufferAddress(env,
127 | device);
128 | return (jlong) ((void *) deviceHandle);
129 |
130 | }
131 |
132 | JNIEXPORT jobject JNICALL
133 | Java_com_jwoolston_libusb_BaseUsbDevice_nativeGetNativeObjectFromPointer(JNIEnv *env, jobject instance,
134 | jlong pointer) {
135 | struct libusb_device_handle *deviceHandle = (struct libusb_device_handle *) ((void *) pointer);
136 |
137 | if (deviceHandle == NULL) {
138 | return NULL;
139 | }
140 |
141 | return ((*env)->NewDirectByteBuffer(env, (void *) deviceHandle, sizeof(struct libusb_device_handle)));
142 | }
143 |
144 | #pragma clang diagnostic pop
--------------------------------------------------------------------------------
/library/src/main/java/com/jwoolston/libusb/BaseUsbConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.jwoolston.libusb;
17 |
18 | import com.jwoolston.libusb.util.Preconditions;
19 |
20 | import org.jetbrains.annotations.NotNull;
21 | import org.jetbrains.annotations.Nullable;
22 |
23 | import java.nio.ByteBuffer;
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | /**
28 | * A class representing a configuration on a {@link BaseUsbDevice}. A USB configuration can have one or more interfaces,
29 | * each one providing a different piece of functionality, separate from the other interfaces. An interface will have
30 | * one or more {@link BaseUsbEndpoint}s, which are the channels by which the host transfers data with the device.
31 | */
32 | public class BaseUsbConfiguration {
33 |
34 | /**
35 | * Mask for "self-powered" bit in the configuration's attributes.
36 | */
37 | private static final int ATTR_SELF_POWERED = 1 << 6;
38 |
39 | /**
40 | * Mask for "remote wakeup" bit in the configuration's attributes.
41 | */
42 | private static final int ATTR_REMOTE_WAKEUP = 1 << 5;
43 |
44 | final int id;
45 | @Nullable
46 | final String name;
47 | final int attributes;
48 | final int maxPower;
49 |
50 | /**
51 | * All interfaces for this config, only null during creation
52 | */
53 | @Nullable
54 | BaseUsbInterface[] interfaces;
55 |
56 | /**
57 | * BaseUsbConfiguration should only be instantiated by UsbService implementation
58 | */
59 | public BaseUsbConfiguration(int id, @Nullable String name, int attributes, int maxPower) {
60 | this.id = id;
61 | this.name = name;
62 | this.attributes = attributes;
63 | this.maxPower = maxPower;
64 | }
65 |
66 | /**
67 | * Returns the configuration's ID field.
68 | * This is an integer that uniquely identifies the configuration on the device.
69 | *
70 | * @return the configuration's ID
71 | */
72 | public int getId() {
73 | return id;
74 | }
75 |
76 | /**
77 | * Returns the configuration's name.
78 | *
79 | * @return the configuration's name, or {@code null} if the property could not be read
80 | */
81 | public @Nullable
82 | String getName() {
83 | return name;
84 | }
85 |
86 | /**
87 | * Returns the self-powered attribute value configuration's attributes field.
88 | * This attribute indicates that the device has a power source other than the USB connection.
89 | *
90 | * @return the configuration's self-powered attribute
91 | */
92 | public boolean isSelfPowered() {
93 | return (attributes & ATTR_SELF_POWERED) != 0;
94 | }
95 |
96 | /**
97 | * Returns the remote-wakeup attribute value configuration's attributes field.
98 | * This attributes that the device may signal the host to wake from suspend.
99 | *
100 | * @return the configuration's remote-wakeup attribute
101 | */
102 | public boolean isRemoteWakeup() {
103 | return (attributes & ATTR_REMOTE_WAKEUP) != 0;
104 | }
105 |
106 | /**
107 | * Returns the configuration's max power consumption, in milliamps.
108 | *
109 | * @return the configuration's max power
110 | */
111 | public int getMaxPower() {
112 | return maxPower * 2;
113 | }
114 |
115 | /**
116 | * Returns the number of {@link BaseUsbInterface}s this configuration contains.
117 | *
118 | * @return the number of endpoints
119 | */
120 | public int getInterfaceCount() {
121 | return interfaces.length;
122 | }
123 |
124 | /**
125 | * Returns the {@link BaseUsbInterface} at the given index.
126 | *
127 | * @return the interface
128 | */
129 | public @NotNull
130 | BaseUsbInterface getInterface(int index) {
131 | return interfaces[index];
132 | }
133 |
134 | /**
135 | * Only used by UsbService implementation
136 | */
137 | public void setInterfaces(@NotNull BaseUsbInterface[] interfaces) {
138 | this.interfaces = Preconditions.checkArrayElementsNotNull(interfaces, "interfaces");
139 | }
140 |
141 | @Override
142 | public String toString() {
143 | StringBuilder builder = new StringBuilder("BaseUsbConfiguration[id=" + id +
144 | ",name=" + name + ",attributes=" + attributes +
145 | ",maxPower=" + maxPower + ",interfaces=[");
146 | for (int i = 0; i < interfaces.length; i++) {
147 | builder.append("\n");
148 | builder.append(interfaces[i].toString());
149 | }
150 | builder.append("]");
151 | return builder.toString();
152 | }
153 |
154 | private static final int INDEX_NUMBER_INTERFACES = 4;
155 | private static final int INDEX_CONFIGURATION_VALUE = 5;
156 | private static final int INDEX_CONFIGURATION_STRING_INDEX = 6;
157 | private static final int INDEX_ATTRIBUTES = 7;
158 | private static final int INDEX_MAX_POWER = 8;
159 |
160 | @NotNull
161 | static BaseUsbConfiguration fromNativeObject(@NotNull BaseUsbDevice device, int configuration) {
162 | // Get the native configuration object. Make sure you free it!
163 | final ByteBuffer nativeObject = nativeGetConfiguration(device.getNativeObject(), configuration);
164 | final int numberInterfaces = 0xFF & nativeObject.get(INDEX_NUMBER_INTERFACES);
165 | final int id = 0xFF & nativeObject.get(INDEX_CONFIGURATION_VALUE);
166 | final int stringIndex = 0xFF & nativeObject.get(INDEX_CONFIGURATION_STRING_INDEX);
167 | final int attributes = 0xFF & nativeObject.get(INDEX_ATTRIBUTES);
168 | final int maxPower = 0xFF & nativeObject.get(INDEX_MAX_POWER);
169 | final String name = BaseUsbDevice.nativeGetStringDescriptor(device.getNativeObject(), stringIndex);
170 |
171 | final BaseUsbConfiguration usbConfiguration = device.createConfiguration(id, name, attributes, maxPower);
172 | final List usbInterfaces = new ArrayList<>();
173 | for (int i = 0; i < numberInterfaces; ++i) {
174 | // This is of type struct libusb_interface
175 | final ByteBuffer nativeInterface = nativeGetInterface(nativeObject, i);
176 | List usbInterface = BaseUsbInterface.fromNativeObject(device, nativeInterface);
177 | usbInterfaces.addAll(usbInterface);
178 | }
179 | usbConfiguration.setInterfaces(usbInterfaces.toArray(new BaseUsbInterface[0]));
180 |
181 | // Destroy the native configuration object
182 | nativeDestroy(nativeObject);
183 | return usbConfiguration;
184 | }
185 |
186 | private static native ByteBuffer nativeGetConfiguration(@NotNull ByteBuffer device, int configuration);
187 |
188 | /**
189 | *
190 | * @param nativeObject {@link ByteBuffer} wrapper to native stuct. Expected to be a libusb_config_descriptor.
191 | * @param interfaceIndex
192 | * @return
193 | */
194 | private static native ByteBuffer nativeGetInterface(@NotNull ByteBuffer nativeObject, int interfaceIndex);
195 |
196 | private static native void nativeDestroy(@NotNull ByteBuffer nativeObject);
197 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/jwoolston/libusb/BaseUsbInterface.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.jwoolston.libusb;
17 |
18 | import com.jwoolston.libusb.util.Preconditions;
19 |
20 | import org.jetbrains.annotations.NotNull;
21 | import org.jetbrains.annotations.Nullable;
22 |
23 | import java.nio.ByteBuffer;
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | /**
28 | * A class representing an interface on a {@link BaseUsbDevice}. USB devices can have one or more interfaces, each one
29 | * providing a different piece of functionality, separate from the other interfaces. An interface will have one or
30 | * more {@link BaseUsbEndpoint}s, which are the channels by which the host transfers data with the device.
31 | */
32 | public class BaseUsbInterface {
33 |
34 | final int id;
35 | final int alternateSetting;
36 | @Nullable
37 | final String name;
38 | final int interfaceClass;
39 | final int subclass;
40 | final int protocol;
41 |
42 | /** All endpoints of this interface, only null during creation */
43 | BaseUsbEndpoint[] endpoints;
44 |
45 | /**
46 | * BaseUsbInterface should only be instantiated by BaseUsbManager implementation
47 | */
48 | BaseUsbInterface(int id, int alternateSetting, @Nullable String name, int interfaceClass, int subClass, int protocol) {
49 | this.id = id;
50 | this.alternateSetting = alternateSetting;
51 | this.name = name;
52 | this.interfaceClass = interfaceClass;
53 | subclass = subClass;
54 | this.protocol = protocol;
55 | }
56 |
57 | /**
58 | * Returns the interface's bInterfaceNumber field. This is an integer that along with the alternate setting
59 | * uniquely identifies the interface on the device.
60 | *
61 | * @return the interface's ID
62 | */
63 | public int getId() {
64 | return id;
65 | }
66 |
67 | /**
68 | * Returns the interface's bAlternateSetting field. This is an integer that along with the ID uniquely identifies
69 | * the interface on the device. {@link BaseUsbDeviceConnection#setInterface} can be used to switch between
70 | * two interfaces with the same ID but different alternate setting.
71 | *
72 | * @return the interface's alternate setting
73 | */
74 | public int getAlternateSetting() {
75 | return alternateSetting;
76 | }
77 |
78 | /**
79 | * Returns the interface's name.
80 | *
81 | * @return the interface's name, or {@code null} if the property could not be read
82 | */
83 | public @Nullable
84 | String getName() {
85 | return name;
86 | }
87 |
88 | /**
89 | * Returns the interface's class field. Some useful constants for USB classes can be found in {@link UsbConstants}
90 | *
91 | * @return the interface's class
92 | */
93 | public int getInterfaceClass() {
94 | return interfaceClass;
95 | }
96 |
97 | /**
98 | * Returns the interface's subclass field.
99 | *
100 | * @return the interface's subclass
101 | */
102 | public int getInterfaceSubclass() {
103 | return subclass;
104 | }
105 |
106 | /**
107 | * Returns the interface's protocol field.
108 | *
109 | * @return the interface's protocol
110 | */
111 | public int getInterfaceProtocol() {
112 | return protocol;
113 | }
114 |
115 | /**
116 | * Returns the number of endpoints this interface contains.
117 | *
118 | * @return the number of endpoints
119 | */
120 | public int getEndpointCount() {
121 | return endpoints.length;
122 | }
123 |
124 | /**
125 | * Returns the endpoint at the given index.
126 | *
127 | * @return the endpoint
128 | */
129 | public BaseUsbEndpoint getEndpoint(int index) {
130 | return endpoints[index];
131 | }
132 |
133 | /**
134 | * Only used by BaseUsbManager implementation
135 | */
136 | void setEndpoints(BaseUsbEndpoint[] endpoints) {
137 | this.endpoints = Preconditions.checkArrayElementsNotNull(endpoints, "endpoints");
138 | }
139 |
140 | @Override
141 | public String toString() {
142 | StringBuilder builder = new StringBuilder("BaseUsbInterface[id=" + id +
143 | ",alternateSetting=" + alternateSetting +
144 | ",name=" + name + ",interfaceClass=" + interfaceClass +
145 | ",subclass=" + subclass + ",protocol=" + protocol +
146 | ",endpoints=[");
147 | if (endpoints != null) {
148 | for (int i = 0; i < endpoints.length; i++) {
149 | builder.append("\n");
150 | builder.append(endpoints[i].toString());
151 | }
152 | builder.append("]");
153 | }
154 | return builder.toString();
155 | }
156 |
157 | private static final int INDEX_INTERFACE_ID = 2;
158 | private static final int INDEX_ALTERNATE_SETTING = 3;
159 | private static final int INDEX_NUM_ENDPOINTS = 4;
160 | private static final int INDEX_INTERFACE_CLASS = 5;
161 | private static final int INDEX_INTERFACE_SUBCLASS = 6;
162 | private static final int INDEX_INTERFACE_PROTOCOL = 7;
163 | private static final int INDEX_INTERFACE_STRING_INDEX = 8;
164 |
165 | @NotNull
166 | static List fromNativeObject(@NotNull BaseUsbDevice device, @NotNull ByteBuffer nativeInterface) {
167 | final List interfaces = new ArrayList<>();
168 | BaseUsbInterface usbInterface = null;
169 | do {
170 | usbInterface = fromNativeDescriptor(device, nativeInterface, interfaces.size());
171 | if (usbInterface != null) {
172 | interfaces.add(usbInterface);
173 | }
174 | } while (usbInterface != null);
175 | return interfaces;
176 | }
177 |
178 | @Nullable
179 | private static BaseUsbInterface fromNativeDescriptor(@NotNull BaseUsbDevice device, @NotNull ByteBuffer nativeObject,
180 | int index) {
181 | final ByteBuffer nativeDescriptor = nativeGetInterfaceDescriptor(nativeObject, index);
182 |
183 | if (nativeDescriptor == null) {
184 | return null;
185 | }
186 | nativeDescriptor.isDirect();
187 |
188 | final int id = 0xFF & nativeDescriptor.get(INDEX_INTERFACE_ID);
189 | final int alternateSetting = 0xFF & nativeDescriptor.get(INDEX_ALTERNATE_SETTING);
190 | final int numEndpoints = 0xFF & nativeDescriptor.get(INDEX_NUM_ENDPOINTS);
191 | final int interfaceClass = 0xFF & nativeDescriptor.get(INDEX_INTERFACE_CLASS);
192 | final int subclass = 0xFF & nativeDescriptor.get(INDEX_INTERFACE_SUBCLASS);
193 | final int protocol = 0xFF & nativeDescriptor.get(INDEX_INTERFACE_PROTOCOL);
194 | final int stringIndex = 0xFF & nativeDescriptor.get(INDEX_INTERFACE_STRING_INDEX);
195 | final String name = BaseUsbDevice.nativeGetStringDescriptor(device.getNativeObject(), stringIndex);
196 | final BaseUsbInterface usbInterface = device.createInterface(id, alternateSetting, name,
197 | interfaceClass, subclass, protocol);
198 | final BaseUsbEndpoint[] endpoints = new BaseUsbEndpoint[numEndpoints];
199 | for (int i = 0; i < numEndpoints; ++i) {
200 | final ByteBuffer nativeEndpoint = nativeGetEndpoint(nativeDescriptor, i);
201 | if (nativeEndpoint != null) {
202 | endpoints[i] = BaseUsbEndpoint.fromNativeObject(device, nativeEndpoint);
203 | } else {
204 | throw new IllegalStateException("Received a null endpoint when one was expected. Expected index: " +
205 | i + " Expected total: " + numEndpoints);
206 | }
207 | }
208 | usbInterface.setEndpoints(endpoints);
209 | return usbInterface;
210 | }
211 |
212 | @Nullable
213 | private static native ByteBuffer nativeGetInterfaceDescriptor(@NotNull ByteBuffer nativeObject, int index);
214 |
215 | @Nullable
216 | private static native ByteBuffer nativeGetEndpoint(@NotNull ByteBuffer nativeDescriptor, int index);
217 | }
--------------------------------------------------------------------------------