Skip to content

Commit

Permalink
Merge pull request #372 from magnusja/develop
Browse files Browse the repository at this point in the history
Release core v0.9.3 libusbcommunication v0.2.4
  • Loading branch information
magnusja authored Mar 10, 2023
2 parents c25e643 + 2587e0a commit ea6c0f8
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 38 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A library to access USB mass storage devices (pen drives, external HDDs, card re
The library can be included into your project like this:

```ruby
implementation 'me.jahnen.libaums:core:0.9.2'
implementation 'me.jahnen.libaums:core:0.9.3'
```

If you need the HTTP or the storage provider module:
Expand Down
2 changes: 1 addition & 1 deletion libaums/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jacoco {

ext {
PUBLISH_GROUP_ID = 'me.jahnen.libaums'
PUBLISH_VERSION = '0.9.2'
PUBLISH_VERSION = '0.9.3'
PUBLISH_ARTIFACT_ID = 'core'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import me.jahnen.libaums.core.driver.scsi.commands.sense.*
import me.jahnen.libaums.core.usb.PipeException
import me.jahnen.libaums.core.usb.UsbCommunication
import java.io.IOException
import java.lang.IllegalStateException
import java.nio.ByteBuffer
import java.util.*

Expand Down Expand Up @@ -78,19 +77,25 @@ class ScsiBlockDevice(private val usbCommunication: UsbCommunication, private va
*/
@Throws(IOException::class)
override fun init() {
var lastException: Exception? = null
for(i in 0..MAX_RECOVERY_ATTEMPTS) {
try {
initAttempt()
return
} catch(e: InitRequired) {
} catch (e: InitRequired) {
Log.i(TAG, e.message ?: "Reinitializing device")
lastException = e
} catch (e: NotReadyTryAgain) {
Log.i(TAG, e.message ?: "Reinitializing device")
lastException = e
}
Thread.sleep(100)
}

throw IOException("MAX_RECOVERY_ATTEMPTS Exceeded while trying to init communication with USB device, please reattach device and try again")
throw IOException(
"MAX_RECOVERY_ATTEMPTS Exceeded while trying to init communication with USB device, please reattach device and try again",
lastException
)
}

@Throws(IOException::class)
Expand Down Expand Up @@ -143,6 +148,7 @@ class ScsiBlockDevice(private val usbCommunication: UsbCommunication, private va
*/
@Throws(IOException::class)
private fun transferCommand(command: CommandBlockWrapper, inBuffer: ByteBuffer) {
var lastException: Exception? = null
for(i in 0..MAX_RECOVERY_ATTEMPTS) {
try {
val result = transferOneCommand(command, inBuffer)
Expand All @@ -167,18 +173,24 @@ class ScsiBlockDevice(private val usbCommunication: UsbCommunication, private va
is NotReadyTryAgain -> {} // try again
else -> throw e
}
lastException = e
} catch(e: PipeException) {
Log.w(TAG, (e.message ?: "PipeException") + ", try bulk storage reset and retry")
bulkOnlyMassStorageReset()
lastException = e
} catch (e: IOException) {
// Retry
Log.w(TAG, (e.message ?: "IOException") + ", retrying...")
lastException = e
}

Thread.sleep(100)
}

throw IOException("MAX_RECOVERY_ATTEMPTS Exceeded while trying to transfer command to device, please reattach device and try again")
throw IOException(
"MAX_RECOVERY_ATTEMPTS Exceeded while trying to transfer command to device, please reattach device and try again",
lastException
)
}

@Throws(IOException::class)
Expand Down Expand Up @@ -344,7 +356,7 @@ class ScsiBlockDevice(private val usbCommunication: UsbCommunication, private va
}

companion object {
private const val MAX_RECOVERY_ATTEMPTS = 20
private const val MAX_RECOVERY_ATTEMPTS = 5
private val TAG = ScsiBlockDevice::class.java.simpleName
}
}
2 changes: 1 addition & 1 deletion libusbcommunication/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Refer to the following blog where someone claims that LPGL cannot be used in clo
#### Inclusion in your build.gradle

```ruby
implementation 'me.jahnen.libaums:libusbcommunication:0.2.3'
implementation 'me.jahnen.libaums:libusbcommunication:0.2.4'
```

### Activate libusb communication
Expand Down
2 changes: 1 addition & 1 deletion libusbcommunication/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ String libusbDir = props['libusb.dir']

ext {
PUBLISH_GROUP_ID = 'me.jahnen.libaums'
PUBLISH_VERSION = '0.2.3'
PUBLISH_VERSION = '0.2.4'
PUBLISH_ARTIFACT_ID = 'libusbcommunication'
}

Expand Down
12 changes: 6 additions & 6 deletions libusbcommunication/src/c/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#define TAG "native_libusbcom"

JNIEXPORT jboolean JNICALL
JNIEXPORT jint JNICALL
Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeInit(JNIEnv *env, jobject thiz, jint fd, jlongArray handle) {
LOG_D(TAG, "init native libusb");
int ret;
Expand All @@ -18,33 +18,33 @@ Java_me_jahnen_libaums_libusbcommunication_LibusbCommunication_nativeInit(JNIEnv
ret = libusb_set_option(NULL, LIBUSB_OPTION_WEAK_AUTHORITY);
if (ret != 0) {
LOG_E(TAG, "libusb_set_option returned %d, %s", ret, libusb_strerror(ret));
return (jboolean) JNI_FALSE;
return ret;
}
#endif

ret = libusb_init(NULL);
if (ret != 0) {
LOG_E(TAG, "libusb_init returned %d, %s", ret, libusb_strerror(ret));
return (jboolean) JNI_FALSE;
return ret;
}

libusb_device_handle *devh = NULL;
ret = libusb_wrap_sys_device(NULL, fd, &devh);
if (ret != 0) {
LOG_E(TAG, "libusb_wrap_sys_device returned %d, %s", ret, libusb_strerror(ret));
return (jboolean) JNI_FALSE;
return ret;
}
if (devh == NULL) {
LOG_E(TAG, "libusb_wrap_sys_device device handle, %s NULL", libusb_strerror(ret));
return (jboolean) JNI_FALSE;
return LIBUSB_ERROR_OTHER;
}

jlong *body = (*env)->GetLongArrayElements(env, handle, NULL);
// cache heap address in java class object
body[0] = (jlong)devh;
(*env)->ReleaseLongArrayElements(env, handle, body, NULL);

return (jboolean) JNI_TRUE;
return 0;
}

JNIEXPORT void JNICALL
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package me.jahnen.libaums.libusbcommunication

import me.jahnen.libaums.core.ErrNo
import java.io.IOException

/**
* IOException that captures the errno and errstr of the current thread.
*/
open class ErrNoIOException(message: String, cause: Throwable? = null) : IOException(message, cause) {
val errno = ErrNo.errno
val errstr = ErrNo.errstr
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package me.jahnen.libaums.libusbcommunication

import android.hardware.usb.*
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbEndpoint
import android.hardware.usb.UsbInterface
import android.hardware.usb.UsbManager
import android.util.Log
import me.jahnen.libaums.core.ErrNo
import me.jahnen.libaums.core.usb.PipeException
Expand All @@ -12,11 +16,11 @@ import java.nio.ByteBuffer


class LibusbCommunication(
usbManager: UsbManager,
usbDevice: UsbDevice,
override val usbInterface: UsbInterface,
override val outEndpoint: UsbEndpoint,
override val inEndpoint: UsbEndpoint
usbManager: UsbManager,
usbDevice: UsbDevice,
override val usbInterface: UsbInterface,
override val outEndpoint: UsbEndpoint,
override val inEndpoint: UsbEndpoint
) : UsbCommunication {

// used to save heap address of libusb device handle
Expand All @@ -29,23 +33,24 @@ class LibusbCommunication(
System.loadLibrary("libusbcom")

deviceConnection = usbManager.openDevice(usbDevice)
?: throw IOException("deviceConnection is null!")
?: throw IOException("deviceConnection is null!")

if(!nativeInit(deviceConnection!!.fileDescriptor, libUsbHandleArray)) {
throw IOException("libusb init failed")
val res = nativeInit(deviceConnection!!.fileDescriptor, libUsbHandleArray)
if (res != 0) {
throw LibusbException("libusb init failed", LibusbError.fromCode(res))
}

val claim = deviceConnection!!.claimInterface(usbInterface, true)
if (!claim) {
throw IOException("could not claim interface!")
throw ErrNoIOException("could not claim interface!")
}
// val ret = nativeClaimInterface(libUsbHandle, usbInterface.id)
// if (ret < 0) {
// throw IOException("libusb returned $ret in claim interface")
// }
}

private external fun nativeInit(fd: Int, handle: LongArray): Boolean
private external fun nativeInit(fd: Int, handle: LongArray): Int
private external fun nativeClaimInterface(handle: Long, interfaceNumber: Int): Int
private external fun nativeClose(handle: Long, interfaceNumber: Int)
private external fun nativeReset(handle: Long): Int
Expand All @@ -54,20 +59,30 @@ class LibusbCommunication(
private external fun nativeControlTransfer(handle: Long, requestType: Int, request: Int, value: Int, index: Int, buffer: ByteArray, length: Int, timeout: Int): Int

override fun bulkOutTransfer(src: ByteBuffer): Int {
val transferred = nativeBulkTransfer(libUsbHandle, outEndpoint.address, src.array(), src.position(), src.remaining(), TRANSFER_TIMEOUT)
val transferred = nativeBulkTransfer(
libUsbHandle, outEndpoint.address, src.array(), src.position(), src.remaining(),
TRANSFER_TIMEOUT
)
when {
transferred == LIBUSB_EPIPE -> throw PipeException()
transferred < 0 -> throw IOException("libusb returned $transferred in control transfer")
transferred == LibusbError.PIPE.code -> throw PipeException()
transferred < 0 -> throw LibusbException(
"libusb control transfer failed", LibusbError.fromCode(transferred)
)
}
src.position(src.position() + transferred)
return transferred
}

override fun bulkInTransfer(dest: ByteBuffer): Int {
val transferred = nativeBulkTransfer(libUsbHandle, inEndpoint.address, dest.array(), dest.position(), dest.remaining(), TRANSFER_TIMEOUT)
val transferred = nativeBulkTransfer(
libUsbHandle, inEndpoint.address, dest.array(), dest.position(), dest.remaining(),
TRANSFER_TIMEOUT
)
when {
transferred == LIBUSB_EPIPE -> throw PipeException()
transferred < 0 -> throw IOException("libusb returned $transferred in control transfer")
transferred == LibusbError.PIPE.code -> throw PipeException()
transferred < 0 -> throw LibusbException(
"libusb control transfer failed", LibusbError.fromCode(transferred)
)
}
dest.position(dest.position() + transferred)
return transferred
Expand All @@ -76,7 +91,7 @@ class LibusbCommunication(
override fun controlTransfer(requestType: Int, request: Int, value: Int, index: Int, buffer: ByteArray, length: Int): Int {
val ret = nativeControlTransfer(libUsbHandle, requestType, request, value, index, buffer, length, TRANSFER_TIMEOUT)
if (ret < 0) {
throw IOException("libusb returned $ret in control transfer")
throw LibusbException("libusb control transfer failed", LibusbError.fromCode(ret))
}
return ret
}
Expand All @@ -88,12 +103,14 @@ class LibusbCommunication(

val ret = nativeReset(libUsbHandle)
// if LIBUSB_ERROR_NOT_FOUND might need reenumeration
Log.d(TAG, "libusb reset returned $ret")
Log.d(TAG, "libusb reset returned $ret: ${LibusbError.fromCode(ret).message}")

var counter = 3
while(!deviceConnection!!.claimInterface(usbInterface, true) && counter >= 0) {
while (!deviceConnection!!.claimInterface(usbInterface, true) && counter >= 0) {
if (counter == 0) {
throw IOException("Could not claim interface, errno: ${ErrNo.errno} ${ErrNo.errstr}")
throw ErrNoIOException(
"Could not claim interface, errno: ${ErrNo.errno} ${ErrNo.errstr}"
)
}
Thread.sleep(800)
counter--
Expand All @@ -102,7 +119,7 @@ class LibusbCommunication(

override fun clearFeatureHalt(endpoint: UsbEndpoint) {
val ret = nativeClearHalt(libUsbHandle, endpoint.address)
Log.d(TAG, "libusb clearFeatureHalt returned $ret")
Log.d(TAG, "libusb clearFeatureHalt returned $ret: ${LibusbError.fromCode(ret).message}")
}

override fun close() {
Expand All @@ -113,7 +130,6 @@ class LibusbCommunication(

companion object {
private val TAG = LibusbCommunication::class.java.simpleName
private val LIBUSB_EPIPE = -9
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package me.jahnen.libaums.libusbcommunication

enum class LibusbError(val code: Int, val message: String) {
SUCCESS(0, "Success (no error)"),
IO(-1, "Input/output error"),
INVALID_PARAM(-2, "Invalid parameter"),
ACCESS(-3, "Access denied (insufficient permissions)"),
NO_DEVICE(-4, "No such device (it may have been disconnected)"),
NOT_FOUND(-5, "Entity not found"),
BUSY(-6, "Resource busy"),
TIMEOUT(-7, "Operation timed out"),
OVERFLOW(-8, "Overflow"),
PIPE(-9, "Pipe error"),
INTERRUPTED(-10, "System call interrupted (perhaps due to signal)"),
NO_MEM(-11, "Insufficient memory"),
NOT_SUPPORTED(-12, "Operation not supported or unimplemented on this platform"),
OTHER(-99, "Other error");

companion object {
fun fromCode(code: Int): LibusbError {
return values().firstOrNull { it.code == code } ?: OTHER
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package me.jahnen.libaums.libusbcommunication

open class LibusbException(
message: String,
val libusbError: LibusbError,
cause: Throwable? = null
) :
ErrNoIOException("$message: ${libusbError.message} [${libusbError.code}]", cause)

0 comments on commit ea6c0f8

Please sign in to comment.