From ee46d980669000d31cc7933bfda7723214ebe87d Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Tue, 5 Mar 2024 18:18:16 +0100 Subject: [PATCH 01/26] - updated dependencies - added get/setFeature method for all platforms --- hid/example/pubspec.yaml | 7 +- .../rustui/hid_android/HidAndroidPlugin.kt | 57 +++++- hid_android/lib/hid_android.dart | 18 +- hid_android/pubspec.yaml | 10 +- hid_linux/lib/generated_bindings.dart | 177 ++++++------------ hid_linux/pubspec.yaml | 12 +- hid_macos/lib/generated_bindings.dart | 177 ++++++------------ hid_macos/lib/hid_macos.dart | 51 ++++- hid_macos/pubspec.yaml | 12 +- hid_platform_interface/lib/device.dart | 8 + hid_platform_interface/pubspec.yaml | 8 +- hid_windows/lib/generated_bindings.dart | 177 ++++++------------ hid_windows/lib/hid_windows.dart | 43 ++++- hid_windows/pubspec.yaml | 12 +- 14 files changed, 345 insertions(+), 424 deletions(-) diff --git a/hid/example/pubspec.yaml b/hid/example/pubspec.yaml index daaea53..b64ba3b 100644 --- a/hid/example/pubspec.yaml +++ b/hid/example/pubspec.yaml @@ -3,7 +3,8 @@ description: Demonstrates how to use the hid plugin. publish_to: "none" # Remove this line if you wish to publish to pub.dev environment: - sdk: ">=2.15.1 <3.0.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: @@ -14,7 +15,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 # From a real application you should not have this dependency_overrides: @@ -28,6 +29,8 @@ dependency_overrides: path: ../../hid_macos hid_windows: path: ../../hid_windows + hid_platform_interface: + path: ../../hid_platform_interface flutter: uses-material-design: true diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 787dfa9..f762bf1 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -26,8 +26,10 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { private val gson = Gson() private var connection: UsbDeviceConnection? = null private var device: UsbDevice? = null - private var interfaceIndex: Int? = null - private var endpointIndex: Int? = null + private var readInterfaceIndex: Int? = null + private var readEndpointIndex: Int? = null + private var writeInterfaceIndex: Int? = null + private var writeEndpointIndex: Int? = null override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "hid_android") @@ -69,13 +71,17 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { "open" -> { device = usbManager.deviceList[call.argument("deviceName")]!! connection = usbManager.openDevice(device) - (interfaceIndex, endpointIndex) = getReadIndices(device!!)!! - result.success( - connection!!.claimInterface( - device!!.getInterface(interfaceIndex!!), - true - ) - ) + var pair = getReadIndices(device!!)!! + readInterfaceIndex = pair.first + readEndpointIndex = pair.second + pair = getWriteIndices(device!!)!! + writeInterfaceIndex = pair.first + writeEndpointIndex = pair.second + + val success : Boolean = connection!!.claimInterface(device!!.getInterface(readInterfaceIndex!!), true) && + ((readInterfaceIndex == writeInterfaceIndex) || connection!!.claimInterface(device!!.getInterface(writeInterfaceIndex!!), true)) + + result.success( success ) } "read" -> { if (connection != null) { @@ -85,7 +91,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { kotlin.run { val array = ByteArray(length) connection!!.bulkTransfer( - device!!.getInterface(i).getEndpoint(j), + device!!.getInterface(readInterfaceIndex!!).getEndpoint(readEndpointIndex!!), array, length, duration @@ -97,6 +103,24 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { result.error("error", "error", "error") } } + "write" -> { + if (connection != null) { + val bytes: ByteArray = call.argument("bytes")!! + Thread { + kotlin.run { + connection!!.bulkTransfer( + device!!.getInterface(writeInterfaceIndex!!).getEndpoint(writeEndpointIndex!!), + bytes, + bytes.size, + 1000 + ) + result.success(0) + } + }.start() + } else { + result.error("error", "error", "error") + } + } "close" -> { connection?.close() connection = null @@ -122,4 +146,17 @@ fun getReadIndices(device: UsbDevice): Pair? { } } return null +} + +fun getWriteIndices(device: UsbDevice): Pair? { + for (i in 0 until device.interfaceCount) { + val inter = device.getInterface(i) + for (j in 0 until inter.endpointCount) { + val endpoint = inter.getEndpoint(j) + if (endpoint.type == UsbConstants.USB_ENDPOINT_XFER_INT && endpoint.direction == UsbConstants.USB_DIR_OUT) { + return Pair(i, j) + } + } + } + return null } \ No newline at end of file diff --git a/hid_android/lib/hid_android.dart b/hid_android/lib/hid_android.dart index 460b0da..d6526fd 100644 --- a/hid_android/lib/hid_android.dart +++ b/hid_android/lib/hid_android.dart @@ -57,8 +57,7 @@ class UsbDevice extends Device { Stream> read(int length, int duration) async* { while (isOpen) { final start = DateTime.now(); - final List array = await _channel.invokeMethod( - 'read', {'length': length, 'duration': duration}); + final List array = await _channel.invokeMethod('read', {'length': length, 'duration': duration}); yield array.map((e) => e! as int).toList(); var t = DateTime.now().difference(start).inMilliseconds; t = min(max(0, t), duration); @@ -66,6 +65,21 @@ class UsbDevice extends Device { } } + @override + Future write(Uint8List bytes) async { + await _channel.invokeMethod('write', {'bytes': bytes}); + } + + @override + Future setFeature(Uint8List bytes) async { + await _channel.invokeMethod('setFeature', {'bytes': bytes}); + } + + @override + Future getFeature(Uint8List bytes) async { + await _channel.invokeMethod('getFeature', {'bytes': bytes}); + } + @override Future close() async { isOpen = false; diff --git a/hid_android/pubspec.yaml b/hid_android/pubspec.yaml index c5a3064..7720499 100644 --- a/hid_android/pubspec.yaml +++ b/hid_android/pubspec.yaml @@ -1,21 +1,21 @@ name: hid_android description: Android implementation of the hid plugin. -version: 0.1.3 +version: 0.1.4 homepage: https://github.com/rustui/hid/tree/main/hid_android environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: sdk: flutter - hid_platform_interface: ^0.0.8 + hid_platform_interface: ^0.0.9 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/hid_linux/lib/generated_bindings.dart b/hid_linux/lib/generated_bindings.dart index 5a51438..d4e1169 100644 --- a/hid_linux/lib/generated_bindings.dart +++ b/hid_linux/lib/generated_bindings.dart @@ -5,17 +5,13 @@ import 'dart:ffi' as ffi; class Api { /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; + final ffi.Pointer Function(String symbolName) _lookup; /// The symbols are looked up in [dynamicLibrary]. Api(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; /// The symbols are looked up with [lookup]. - Api.fromLookup( - ffi.Pointer Function(String symbolName) - lookup) - : _lookup = lookup; + Api.fromLookup(ffi.Pointer Function(String symbolName) lookup) : _lookup = lookup; /// @brief Initialize the HIDAPI library. /// @@ -34,8 +30,7 @@ class Api { return _init(); } - late final _initPtr = - _lookup>('hid_init'); + late final _initPtr = _lookup>('hid_init'); late final _init = _initPtr.asFunction(); /// @brief Finalize the HIDAPI library. @@ -52,8 +47,7 @@ class Api { return _exit(); } - late final _exitPtr = - _lookup>('hid_exit'); + late final _exitPtr = _lookup>('hid_exit'); late final _exit = _exitPtr.asFunction(); /// @brief Enumerate the HID Devices. @@ -86,12 +80,8 @@ class Api { ); } - late final _enumeratePtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); - late final _enumerate = _enumeratePtr - .asFunction Function(int, int)>(); + late final _enumeratePtr = _lookup Function(ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); + late final _enumerate = _enumeratePtr.asFunction Function(int, int)>(); /// @brief Free an enumeration Linked List /// @@ -108,11 +98,8 @@ class Api { ); } - late final _free_enumerationPtr = _lookup< - ffi.NativeFunction)>>( - 'hid_free_enumeration'); - late final _free_enumeration = _free_enumerationPtr - .asFunction)>(); + late final _free_enumerationPtr = _lookup)>>('hid_free_enumeration'); + late final _free_enumeration = _free_enumerationPtr.asFunction)>(); /// @brief Open a HID device using a Vendor ID (VID), Product ID /// (PID) and optionally a serial number. @@ -143,12 +130,8 @@ class Api { ); } - late final _openPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); - late final _open = _openPtr.asFunction< - ffi.Pointer Function(int, int, ffi.Pointer)>(); + late final _openPtr = _lookup Function(ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); + late final _open = _openPtr.asFunction Function(int, int, ffi.Pointer)>(); /// @brief Open a HID device by its path name. /// @@ -172,12 +155,8 @@ class Api { ); } - late final _open_pathPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer)>>('hid_open_path'); - late final _open_path = _open_pathPtr - .asFunction Function(ffi.Pointer)>(); + late final _open_pathPtr = _lookup Function(ffi.Pointer)>>('hid_open_path'); + late final _open_path = _open_pathPtr.asFunction Function(ffi.Pointer)>(); /// @brief Write an Output report to a HID device. /// @@ -218,12 +197,8 @@ class Api { ); } - late final _writePtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_write'); - late final _write = _writePtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _writePtr = _lookup, ffi.Pointer, size_t)>>('hid_write'); + late final _write = _writePtr.asFunction, ffi.Pointer, int)>(); /// @brief Read an Input report from a HID device with timeout. /// @@ -259,13 +234,9 @@ class Api { ); } - late final _read_timeoutPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t, ffi.Int32)>>('hid_read_timeout'); - late final _read_timeout = _read_timeoutPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer, int, int)>(); + late final _read_timeoutPtr = + _lookup, ffi.Pointer, size_t, ffi.Int32)>>('hid_read_timeout'); + late final _read_timeout = _read_timeoutPtr.asFunction, ffi.Pointer, int, int)>(); /// @brief Read an Input report from a HID device. /// @@ -298,12 +269,8 @@ class Api { ); } - late final _readPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_read'); - late final _read = _readPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _readPtr = _lookup, ffi.Pointer, size_t)>>('hid_read'); + late final _read = _readPtr.asFunction, ffi.Pointer, int)>(); /// @brief Set the device handle to be non-blocking. /// @@ -332,12 +299,8 @@ class Api { ); } - late final _set_nonblockingPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function( - ffi.Pointer, ffi.Int32)>>('hid_set_nonblocking'); - late final _set_nonblocking = _set_nonblockingPtr - .asFunction, int)>(); + late final _set_nonblockingPtr = _lookup, ffi.Int32)>>('hid_set_nonblocking'); + late final _set_nonblocking = _set_nonblockingPtr.asFunction, int)>(); /// @brief Send a Feature report to the device. /// @@ -378,12 +341,9 @@ class Api { ); } - late final _send_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_send_feature_report'); - late final _send_feature_report = _send_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _send_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_send_feature_report'); + late final _send_feature_report = _send_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a feature report from a HID device. /// @@ -421,12 +381,9 @@ class Api { ); } - late final _get_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_feature_report'); - late final _get_feature_report = _get_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_feature_report'); + late final _get_feature_report = _get_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a input report from a HID device. /// @@ -462,12 +419,9 @@ class Api { ); } - late final _get_input_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_input_report'); - late final _get_input_report = _get_input_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_input_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_input_report'); + late final _get_input_report = _get_input_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Close a HID device. /// @@ -483,11 +437,8 @@ class Api { ); } - late final _closePtr = - _lookup)>>( - 'hid_close'); - late final _close = - _closePtr.asFunction)>(); + late final _closePtr = _lookup)>>('hid_close'); + late final _close = _closePtr.asFunction)>(); /// @brief Get The Manufacturer String from a HID device. /// @@ -510,12 +461,9 @@ class Api { ); } - late final _get_manufacturer_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_manufacturer_string'); - late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_manufacturer_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_manufacturer_string'); + late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Product String from a HID device. /// @@ -538,12 +486,9 @@ class Api { ); } - late final _get_product_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_product_string'); - late final _get_product_string = _get_product_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_product_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_product_string'); + late final _get_product_string = _get_product_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Serial Number String from a HID device. /// @@ -566,13 +511,9 @@ class Api { ); } - late final _get_serial_number_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_serial_number_string'); - late final _get_serial_number_string = - _get_serial_number_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_serial_number_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_serial_number_string'); + late final _get_serial_number_string = _get_serial_number_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a string from a HID device, based on its string index. /// @@ -598,12 +539,9 @@ class Api { ); } - late final _get_indexed_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Int32, - ffi.Pointer, size_t)>>('hid_get_indexed_string'); - late final _get_indexed_string = _get_indexed_stringPtr.asFunction< - int Function(ffi.Pointer, int, ffi.Pointer, int)>(); + late final _get_indexed_stringPtr = + _lookup, ffi.Int32, ffi.Pointer, size_t)>>('hid_get_indexed_string'); + late final _get_indexed_string = _get_indexed_stringPtr.asFunction, int, ffi.Pointer, int)>(); /// @brief Get a string describing the last error which occurred. /// @@ -631,11 +569,8 @@ class Api { ); } - late final _errorPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer)>>('hid_error'); - late final _error = _errorPtr - .asFunction Function(ffi.Pointer)>(); + late final _errorPtr = _lookup Function(ffi.Pointer)>>('hid_error'); + late final _error = _errorPtr.asFunction Function(ffi.Pointer)>(); /// @brief Get a runtime version of the library. /// @@ -647,11 +582,8 @@ class Api { return _version(); } - late final _versionPtr = - _lookup Function()>>( - 'hid_version'); - late final _version = - _versionPtr.asFunction Function()>(); + late final _versionPtr = _lookup Function()>>('hid_version'); + late final _version = _versionPtr.asFunction Function()>(); /// @brief Get a runtime version string of the library. /// @@ -663,14 +595,11 @@ class Api { return _version_str(); } - late final _version_strPtr = - _lookup Function()>>( - 'hid_version_str'); - late final _version_str = - _version_strPtr.asFunction Function()>(); + late final _version_strPtr = _lookup Function()>>('hid_version_str'); + late final _version_str = _version_strPtr.asFunction Function()>(); } -class hid_api_version extends ffi.Struct { +base class hid_api_version extends ffi.Struct { @ffi.Int32() external int major; @@ -681,10 +610,10 @@ class hid_api_version extends ffi.Struct { external int patch; } -class hid_device_ extends ffi.Opaque {} +base class hid_device_ extends ffi.Opaque {} /// hidapi info structure -class hid_device_info extends ffi.Struct { +base class hid_device_info extends ffi.Struct { /// Platform-specific device path external ffi.Pointer path; diff --git a/hid_linux/pubspec.yaml b/hid_linux/pubspec.yaml index a879f7e..d3ddb3f 100644 --- a/hid_linux/pubspec.yaml +++ b/hid_linux/pubspec.yaml @@ -1,22 +1,22 @@ name: hid_linux description: Linux implementation of the hid plugin. -version: 0.1.0 +version: 0.1.1 repository: https://github.com/rustui/hid/tree/main/hid_linux environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: sdk: flutter - ffi: ^1.1.2 - hid_platform_interface: ^0.0.8 + hid_platform_interface: ^0.0.9 + ffi: ^2.1.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 flutter: plugin: diff --git a/hid_macos/lib/generated_bindings.dart b/hid_macos/lib/generated_bindings.dart index 5a51438..d4e1169 100644 --- a/hid_macos/lib/generated_bindings.dart +++ b/hid_macos/lib/generated_bindings.dart @@ -5,17 +5,13 @@ import 'dart:ffi' as ffi; class Api { /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; + final ffi.Pointer Function(String symbolName) _lookup; /// The symbols are looked up in [dynamicLibrary]. Api(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; /// The symbols are looked up with [lookup]. - Api.fromLookup( - ffi.Pointer Function(String symbolName) - lookup) - : _lookup = lookup; + Api.fromLookup(ffi.Pointer Function(String symbolName) lookup) : _lookup = lookup; /// @brief Initialize the HIDAPI library. /// @@ -34,8 +30,7 @@ class Api { return _init(); } - late final _initPtr = - _lookup>('hid_init'); + late final _initPtr = _lookup>('hid_init'); late final _init = _initPtr.asFunction(); /// @brief Finalize the HIDAPI library. @@ -52,8 +47,7 @@ class Api { return _exit(); } - late final _exitPtr = - _lookup>('hid_exit'); + late final _exitPtr = _lookup>('hid_exit'); late final _exit = _exitPtr.asFunction(); /// @brief Enumerate the HID Devices. @@ -86,12 +80,8 @@ class Api { ); } - late final _enumeratePtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); - late final _enumerate = _enumeratePtr - .asFunction Function(int, int)>(); + late final _enumeratePtr = _lookup Function(ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); + late final _enumerate = _enumeratePtr.asFunction Function(int, int)>(); /// @brief Free an enumeration Linked List /// @@ -108,11 +98,8 @@ class Api { ); } - late final _free_enumerationPtr = _lookup< - ffi.NativeFunction)>>( - 'hid_free_enumeration'); - late final _free_enumeration = _free_enumerationPtr - .asFunction)>(); + late final _free_enumerationPtr = _lookup)>>('hid_free_enumeration'); + late final _free_enumeration = _free_enumerationPtr.asFunction)>(); /// @brief Open a HID device using a Vendor ID (VID), Product ID /// (PID) and optionally a serial number. @@ -143,12 +130,8 @@ class Api { ); } - late final _openPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); - late final _open = _openPtr.asFunction< - ffi.Pointer Function(int, int, ffi.Pointer)>(); + late final _openPtr = _lookup Function(ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); + late final _open = _openPtr.asFunction Function(int, int, ffi.Pointer)>(); /// @brief Open a HID device by its path name. /// @@ -172,12 +155,8 @@ class Api { ); } - late final _open_pathPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer)>>('hid_open_path'); - late final _open_path = _open_pathPtr - .asFunction Function(ffi.Pointer)>(); + late final _open_pathPtr = _lookup Function(ffi.Pointer)>>('hid_open_path'); + late final _open_path = _open_pathPtr.asFunction Function(ffi.Pointer)>(); /// @brief Write an Output report to a HID device. /// @@ -218,12 +197,8 @@ class Api { ); } - late final _writePtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_write'); - late final _write = _writePtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _writePtr = _lookup, ffi.Pointer, size_t)>>('hid_write'); + late final _write = _writePtr.asFunction, ffi.Pointer, int)>(); /// @brief Read an Input report from a HID device with timeout. /// @@ -259,13 +234,9 @@ class Api { ); } - late final _read_timeoutPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t, ffi.Int32)>>('hid_read_timeout'); - late final _read_timeout = _read_timeoutPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer, int, int)>(); + late final _read_timeoutPtr = + _lookup, ffi.Pointer, size_t, ffi.Int32)>>('hid_read_timeout'); + late final _read_timeout = _read_timeoutPtr.asFunction, ffi.Pointer, int, int)>(); /// @brief Read an Input report from a HID device. /// @@ -298,12 +269,8 @@ class Api { ); } - late final _readPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_read'); - late final _read = _readPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _readPtr = _lookup, ffi.Pointer, size_t)>>('hid_read'); + late final _read = _readPtr.asFunction, ffi.Pointer, int)>(); /// @brief Set the device handle to be non-blocking. /// @@ -332,12 +299,8 @@ class Api { ); } - late final _set_nonblockingPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function( - ffi.Pointer, ffi.Int32)>>('hid_set_nonblocking'); - late final _set_nonblocking = _set_nonblockingPtr - .asFunction, int)>(); + late final _set_nonblockingPtr = _lookup, ffi.Int32)>>('hid_set_nonblocking'); + late final _set_nonblocking = _set_nonblockingPtr.asFunction, int)>(); /// @brief Send a Feature report to the device. /// @@ -378,12 +341,9 @@ class Api { ); } - late final _send_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_send_feature_report'); - late final _send_feature_report = _send_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _send_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_send_feature_report'); + late final _send_feature_report = _send_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a feature report from a HID device. /// @@ -421,12 +381,9 @@ class Api { ); } - late final _get_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_feature_report'); - late final _get_feature_report = _get_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_feature_report'); + late final _get_feature_report = _get_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a input report from a HID device. /// @@ -462,12 +419,9 @@ class Api { ); } - late final _get_input_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_input_report'); - late final _get_input_report = _get_input_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_input_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_input_report'); + late final _get_input_report = _get_input_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Close a HID device. /// @@ -483,11 +437,8 @@ class Api { ); } - late final _closePtr = - _lookup)>>( - 'hid_close'); - late final _close = - _closePtr.asFunction)>(); + late final _closePtr = _lookup)>>('hid_close'); + late final _close = _closePtr.asFunction)>(); /// @brief Get The Manufacturer String from a HID device. /// @@ -510,12 +461,9 @@ class Api { ); } - late final _get_manufacturer_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_manufacturer_string'); - late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_manufacturer_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_manufacturer_string'); + late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Product String from a HID device. /// @@ -538,12 +486,9 @@ class Api { ); } - late final _get_product_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_product_string'); - late final _get_product_string = _get_product_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_product_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_product_string'); + late final _get_product_string = _get_product_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Serial Number String from a HID device. /// @@ -566,13 +511,9 @@ class Api { ); } - late final _get_serial_number_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_serial_number_string'); - late final _get_serial_number_string = - _get_serial_number_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_serial_number_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_serial_number_string'); + late final _get_serial_number_string = _get_serial_number_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a string from a HID device, based on its string index. /// @@ -598,12 +539,9 @@ class Api { ); } - late final _get_indexed_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Int32, - ffi.Pointer, size_t)>>('hid_get_indexed_string'); - late final _get_indexed_string = _get_indexed_stringPtr.asFunction< - int Function(ffi.Pointer, int, ffi.Pointer, int)>(); + late final _get_indexed_stringPtr = + _lookup, ffi.Int32, ffi.Pointer, size_t)>>('hid_get_indexed_string'); + late final _get_indexed_string = _get_indexed_stringPtr.asFunction, int, ffi.Pointer, int)>(); /// @brief Get a string describing the last error which occurred. /// @@ -631,11 +569,8 @@ class Api { ); } - late final _errorPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer)>>('hid_error'); - late final _error = _errorPtr - .asFunction Function(ffi.Pointer)>(); + late final _errorPtr = _lookup Function(ffi.Pointer)>>('hid_error'); + late final _error = _errorPtr.asFunction Function(ffi.Pointer)>(); /// @brief Get a runtime version of the library. /// @@ -647,11 +582,8 @@ class Api { return _version(); } - late final _versionPtr = - _lookup Function()>>( - 'hid_version'); - late final _version = - _versionPtr.asFunction Function()>(); + late final _versionPtr = _lookup Function()>>('hid_version'); + late final _version = _versionPtr.asFunction Function()>(); /// @brief Get a runtime version string of the library. /// @@ -663,14 +595,11 @@ class Api { return _version_str(); } - late final _version_strPtr = - _lookup Function()>>( - 'hid_version_str'); - late final _version_str = - _version_strPtr.asFunction Function()>(); + late final _version_strPtr = _lookup Function()>>('hid_version_str'); + late final _version_str = _version_strPtr.asFunction Function()>(); } -class hid_api_version extends ffi.Struct { +base class hid_api_version extends ffi.Struct { @ffi.Int32() external int major; @@ -681,10 +610,10 @@ class hid_api_version extends ffi.Struct { external int patch; } -class hid_device_ extends ffi.Opaque {} +base class hid_device_ extends ffi.Opaque {} /// hidapi info structure -class hid_device_info extends ffi.Struct { +base class hid_device_info extends ffi.Struct { /// Platform-specific device path external ffi.Pointer path; diff --git a/hid_macos/lib/hid_macos.dart b/hid_macos/lib/hid_macos.dart index 3b2b172..6a81cff 100644 --- a/hid_macos/lib/hid_macos.dart +++ b/hid_macos/lib/hid_macos.dart @@ -45,13 +45,7 @@ class UsbDevice extends Device { required String productName, required int usagePage, required int usage, - }) : super( - vendorId: vendorId, - productId: productId, - serialNumber: serialNumber, - productName: productName, - usagePage: usagePage, - usage: usage); + }) : super(vendorId: vendorId, productId: productId, serialNumber: serialNumber, productName: productName, usagePage: usagePage, usage: usage); @override Future open() async { @@ -100,8 +94,7 @@ class UsbDevice extends Device { _buf.setRange(0, bytes.lengthInBytes, bytes); var offset = 0; while (isOpen && bytes.lengthInBytes - offset > 0) { - final count = - _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + final count = _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); if (count == -1) { break; } else { @@ -110,6 +103,46 @@ class UsbDevice extends Device { } calloc.free(buf); } + + @override + Future setFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw Exception(); + final buf = calloc(bytes.lengthInBytes); + final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); + _buf.setRange(0, bytes.lengthInBytes, bytes); + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { + final count = _api.send_feature_report(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + if (count == -1) { + break; + } else { + offset += count; + } + } + calloc.free(buf); + } + + @override + Future getFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw Exception(); + final buf = calloc(bytes.lengthInBytes); + var count = 0; + var pos = 0; + while (isOpen) { + count = _api.get_feature_report(raw, buf, bytes.lengthInBytes); + if (count == -1) { + break; + } else if (count > 0) { + final res = buf.asTypedList(count); + for (var idx = 0; idx < count; idx++) { + bytes[pos++] = res[idx]; + } + } + } + calloc.free(buf); + } } extension PointerToString on Pointer { diff --git a/hid_macos/pubspec.yaml b/hid_macos/pubspec.yaml index 8148cf0..b06a5ef 100644 --- a/hid_macos/pubspec.yaml +++ b/hid_macos/pubspec.yaml @@ -1,22 +1,22 @@ name: hid_macos description: macOS implementation of the hid plugin. -version: 0.1.1 +version: 0.1.2 repository: https://github.com/rustui/hid/tree/main/hid_macos environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: sdk: flutter - ffi: ^1.1.2 - hid_platform_interface: ^0.0.8 + ffi: ^2.1.0 + hid_platform_interface: ^0.0.9 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 flutter: plugin: diff --git a/hid_platform_interface/lib/device.dart b/hid_platform_interface/lib/device.dart index d941f88..a533990 100644 --- a/hid_platform_interface/lib/device.dart +++ b/hid_platform_interface/lib/device.dart @@ -30,4 +30,12 @@ abstract class Device { Future write(Uint8List bytes) { throw UnimplementedError(); } + + Future getFeature(Uint8List bytes) { + throw UnimplementedError(); + } + + Future setFeature(Uint8List bytes) { + throw UnimplementedError(); + } } diff --git a/hid_platform_interface/pubspec.yaml b/hid_platform_interface/pubspec.yaml index f31d630..ed9e19c 100644 --- a/hid_platform_interface/pubspec.yaml +++ b/hid_platform_interface/pubspec.yaml @@ -1,11 +1,11 @@ name: hid_platform_interface description: A common platform interface for the hid plugin. -version: 0.0.8 +version: 0.0.9 repository: https://github.com/rustui/hid/tree/main/hid_platform_interface environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: @@ -15,7 +15,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/hid_windows/lib/generated_bindings.dart b/hid_windows/lib/generated_bindings.dart index e5d29b2..c0e1cf6 100644 --- a/hid_windows/lib/generated_bindings.dart +++ b/hid_windows/lib/generated_bindings.dart @@ -5,17 +5,13 @@ import 'dart:ffi' as ffi; class Api { /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; + final ffi.Pointer Function(String symbolName) _lookup; /// The symbols are looked up in [dynamicLibrary]. Api(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; /// The symbols are looked up with [lookup]. - Api.fromLookup( - ffi.Pointer Function(String symbolName) - lookup) - : _lookup = lookup; + Api.fromLookup(ffi.Pointer Function(String symbolName) lookup) : _lookup = lookup; /// @brief Initialize the HIDAPI library. /// @@ -34,8 +30,7 @@ class Api { return _init(); } - late final _initPtr = - _lookup>('hid_init'); + late final _initPtr = _lookup>('hid_init'); late final _init = _initPtr.asFunction(); /// @brief Finalize the HIDAPI library. @@ -52,8 +47,7 @@ class Api { return _exit(); } - late final _exitPtr = - _lookup>('hid_exit'); + late final _exitPtr = _lookup>('hid_exit'); late final _exit = _exitPtr.asFunction(); /// @brief Enumerate the HID Devices. @@ -86,12 +80,8 @@ class Api { ); } - late final _enumeratePtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); - late final _enumerate = _enumeratePtr - .asFunction Function(int, int)>(); + late final _enumeratePtr = _lookup Function(ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); + late final _enumerate = _enumeratePtr.asFunction Function(int, int)>(); /// @brief Free an enumeration Linked List /// @@ -108,11 +98,8 @@ class Api { ); } - late final _free_enumerationPtr = _lookup< - ffi.NativeFunction)>>( - 'hid_free_enumeration'); - late final _free_enumeration = _free_enumerationPtr - .asFunction)>(); + late final _free_enumerationPtr = _lookup)>>('hid_free_enumeration'); + late final _free_enumeration = _free_enumerationPtr.asFunction)>(); /// @brief Open a HID device using a Vendor ID (VID), Product ID /// (PID) and optionally a serial number. @@ -143,12 +130,8 @@ class Api { ); } - late final _openPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); - late final _open = _openPtr.asFunction< - ffi.Pointer Function(int, int, ffi.Pointer)>(); + late final _openPtr = _lookup Function(ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); + late final _open = _openPtr.asFunction Function(int, int, ffi.Pointer)>(); /// @brief Open a HID device by its path name. /// @@ -172,12 +155,8 @@ class Api { ); } - late final _open_pathPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer)>>('hid_open_path'); - late final _open_path = _open_pathPtr - .asFunction Function(ffi.Pointer)>(); + late final _open_pathPtr = _lookup Function(ffi.Pointer)>>('hid_open_path'); + late final _open_path = _open_pathPtr.asFunction Function(ffi.Pointer)>(); /// @brief Write an Output report to a HID device. /// @@ -218,12 +197,8 @@ class Api { ); } - late final _writePtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_write'); - late final _write = _writePtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _writePtr = _lookup, ffi.Pointer, size_t)>>('hid_write'); + late final _write = _writePtr.asFunction, ffi.Pointer, int)>(); /// @brief Read an Input report from a HID device with timeout. /// @@ -259,13 +234,9 @@ class Api { ); } - late final _read_timeoutPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t, ffi.Int32)>>('hid_read_timeout'); - late final _read_timeout = _read_timeoutPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer, int, int)>(); + late final _read_timeoutPtr = + _lookup, ffi.Pointer, size_t, ffi.Int32)>>('hid_read_timeout'); + late final _read_timeout = _read_timeoutPtr.asFunction, ffi.Pointer, int, int)>(); /// @brief Read an Input report from a HID device. /// @@ -298,12 +269,8 @@ class Api { ); } - late final _readPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_read'); - late final _read = _readPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _readPtr = _lookup, ffi.Pointer, size_t)>>('hid_read'); + late final _read = _readPtr.asFunction, ffi.Pointer, int)>(); /// @brief Set the device handle to be non-blocking. /// @@ -332,12 +299,8 @@ class Api { ); } - late final _set_nonblockingPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function( - ffi.Pointer, ffi.Int32)>>('hid_set_nonblocking'); - late final _set_nonblocking = _set_nonblockingPtr - .asFunction, int)>(); + late final _set_nonblockingPtr = _lookup, ffi.Int32)>>('hid_set_nonblocking'); + late final _set_nonblocking = _set_nonblockingPtr.asFunction, int)>(); /// @brief Send a Feature report to the device. /// @@ -378,12 +341,9 @@ class Api { ); } - late final _send_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_send_feature_report'); - late final _send_feature_report = _send_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _send_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_send_feature_report'); + late final _send_feature_report = _send_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a feature report from a HID device. /// @@ -421,12 +381,9 @@ class Api { ); } - late final _get_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_feature_report'); - late final _get_feature_report = _get_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_feature_report'); + late final _get_feature_report = _get_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a input report from a HID device. /// @@ -462,12 +419,9 @@ class Api { ); } - late final _get_input_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_input_report'); - late final _get_input_report = _get_input_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_input_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_input_report'); + late final _get_input_report = _get_input_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Close a HID device. /// @@ -483,11 +437,8 @@ class Api { ); } - late final _closePtr = - _lookup)>>( - 'hid_close'); - late final _close = - _closePtr.asFunction)>(); + late final _closePtr = _lookup)>>('hid_close'); + late final _close = _closePtr.asFunction)>(); /// @brief Get The Manufacturer String from a HID device. /// @@ -510,12 +461,9 @@ class Api { ); } - late final _get_manufacturer_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_manufacturer_string'); - late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_manufacturer_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_manufacturer_string'); + late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Product String from a HID device. /// @@ -538,12 +486,9 @@ class Api { ); } - late final _get_product_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_product_string'); - late final _get_product_string = _get_product_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_product_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_product_string'); + late final _get_product_string = _get_product_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Serial Number String from a HID device. /// @@ -566,13 +511,9 @@ class Api { ); } - late final _get_serial_number_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_serial_number_string'); - late final _get_serial_number_string = - _get_serial_number_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_serial_number_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_serial_number_string'); + late final _get_serial_number_string = _get_serial_number_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a string from a HID device, based on its string index. /// @@ -598,12 +539,9 @@ class Api { ); } - late final _get_indexed_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Int32, - ffi.Pointer, size_t)>>('hid_get_indexed_string'); - late final _get_indexed_string = _get_indexed_stringPtr.asFunction< - int Function(ffi.Pointer, int, ffi.Pointer, int)>(); + late final _get_indexed_stringPtr = + _lookup, ffi.Int32, ffi.Pointer, size_t)>>('hid_get_indexed_string'); + late final _get_indexed_string = _get_indexed_stringPtr.asFunction, int, ffi.Pointer, int)>(); /// @brief Get a string describing the last error which occurred. /// @@ -631,11 +569,8 @@ class Api { ); } - late final _errorPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer)>>('hid_error'); - late final _error = _errorPtr - .asFunction Function(ffi.Pointer)>(); + late final _errorPtr = _lookup Function(ffi.Pointer)>>('hid_error'); + late final _error = _errorPtr.asFunction Function(ffi.Pointer)>(); /// @brief Get a runtime version of the library. /// @@ -647,11 +582,8 @@ class Api { return _version(); } - late final _versionPtr = - _lookup Function()>>( - 'hid_version'); - late final _version = - _versionPtr.asFunction Function()>(); + late final _versionPtr = _lookup Function()>>('hid_version'); + late final _version = _versionPtr.asFunction Function()>(); /// @brief Get a runtime version string of the library. /// @@ -663,14 +595,11 @@ class Api { return _version_str(); } - late final _version_strPtr = - _lookup Function()>>( - 'hid_version_str'); - late final _version_str = - _version_strPtr.asFunction Function()>(); + late final _version_strPtr = _lookup Function()>>('hid_version_str'); + late final _version_str = _version_strPtr.asFunction Function()>(); } -class hid_api_version extends ffi.Struct { +base class hid_api_version extends ffi.Struct { @ffi.Int32() external int major; @@ -681,10 +610,10 @@ class hid_api_version extends ffi.Struct { external int patch; } -class hid_device_ extends ffi.Opaque {} +base class hid_device_ extends ffi.Opaque {} /// hidapi info structure -class hid_device_info extends ffi.Struct { +base class hid_device_info extends ffi.Struct { /// Platform-specific device path external ffi.Pointer path; diff --git a/hid_windows/lib/hid_windows.dart b/hid_windows/lib/hid_windows.dart index 8a9497d..5880728 100644 --- a/hid_windows/lib/hid_windows.dart +++ b/hid_windows/lib/hid_windows.dart @@ -100,8 +100,7 @@ class UsbDevice extends Device { _buf.setRange(0, bytes.lengthInBytes, bytes); var offset = 0; while (isOpen && bytes.lengthInBytes - offset > 0) { - final count = - _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + final count = _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); if (count == -1) { break; } else { @@ -110,6 +109,46 @@ class UsbDevice extends Device { } calloc.free(buf); } + + @override + Future setFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw Exception(); + final buf = calloc(bytes.lengthInBytes); + final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); + _buf.setRange(0, bytes.lengthInBytes, bytes); + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { + final count = _api.send_feature_report(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + if (count == -1) { + break; + } else { + offset += count; + } + } + calloc.free(buf); + } + + @override + Future getFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw Exception(); + final buf = calloc(bytes.lengthInBytes); + var count = 0; + var pos = 0; + while (isOpen) { + count = _api.get_feature_report(raw, buf, bytes.lengthInBytes); + if (count == -1) { + break; + } else if (count > 0) { + final res = buf.asTypedList(count); + for (var idx = 0; idx < count; idx++) { + bytes[pos++] = res[idx]; + } + } + } + calloc.free(buf); + } } extension PointerToString on Pointer { diff --git a/hid_windows/pubspec.yaml b/hid_windows/pubspec.yaml index 4e177ed..cc1f4f4 100644 --- a/hid_windows/pubspec.yaml +++ b/hid_windows/pubspec.yaml @@ -1,22 +1,22 @@ name: hid_windows description: Windows implementation of the hid plugin. -version: 0.1.0 +version: 0.1.1 repository: https://github.com/rustui/hid/tree/main/hid_windows environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: - ffi: ^1.1.2 + ffi: ^2.1.0 flutter: sdk: flutter - hid_platform_interface: ^0.0.8 + hid_platform_interface: ^0.0.9 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 9b4e73500b989bed5fbf12166da87b29e22e01ed Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Tue, 5 Mar 2024 18:33:24 +0100 Subject: [PATCH 02/26] added missing pubspec --- hid/pubspec.yaml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/hid/pubspec.yaml b/hid/pubspec.yaml index c672e55..d4ccdfa 100644 --- a/hid/pubspec.yaml +++ b/hid/pubspec.yaml @@ -1,25 +1,26 @@ name: hid description: A multi-platform plugin which allows an application to interface with USB HID-Class devices on Linux, Windows, Android, and macOS. -version: 0.1.6 +version: 0.1.7 repository: https://github.com/rustui/hid/tree/main/hid environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: sdk: flutter - hid_platform_interface: ^0.0.8 - hid_windows: ^0.1.0 - hid_macos: ^0.1.1 - hid_linux: ^0.1.0 - hid_android: ^0.1.3 + ffi: ^2.1.0 + hid_platform_interface: ^0.0.9 + hid_windows: ^0.1.1 + hid_macos: ^0.1.2 + hid_linux: ^0.1.1 + hid_android: ^0.1.4 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 18fbecb637870a9feeb486c9dd2aec16ca531af7 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Tue, 5 Mar 2024 19:26:17 +0100 Subject: [PATCH 03/26] added missing methods --- .../rustui/hid_android/HidAndroidPlugin.kt | 45 ++++++++++++++++++- hid_linux/lib/hid_linux.dart | 40 +++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index f762bf1..5f9fd8a 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -79,7 +79,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { writeEndpointIndex = pair.second val success : Boolean = connection!!.claimInterface(device!!.getInterface(readInterfaceIndex!!), true) && - ((readInterfaceIndex == writeInterfaceIndex) || connection!!.claimInterface(device!!.getInterface(writeInterfaceIndex!!), true)) + ((readInterfaceIndex == writeInterfaceIndex) || connection!!.claimInterface(device!!.getInterface(writeInterfaceIndex!!), true)) result.success( success ) } @@ -121,7 +121,48 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { result.error("error", "error", "error") } } - "close" -> { + "setFeature" -> { + if (connection != null) { + val bytes: ByteArray = call.argument("bytes")!! + Thread { + kotlin.run { + connection!!.controlTransfer( + 33, // (0x01 << 5)|0x01|0x00 + 9, + 768 | bytes[0], + 1, + bytes.size - 1, + bytes, + 1000 + ) + result.success(0) + } + }.start() + } else { + result.error("error", "error", "error") + } + } + "getFeature" -> { + if (connection != null) { + val bytes: ByteArray = call.argument("bytes")!! + Thread { + kotlin.run { + connection!!.controlTransfer( + 161, //(0x01 << 5)|0x01|0x80 + 1, + 768 | bytes[0], + 1, + bytes.size - 1, + bytes, + 1000 + ) + result.success(0) + } + }.start() + } else { + result.error("error", "error", "error") + } + } "close" -> { connection?.close() connection = null device = null diff --git a/hid_linux/lib/hid_linux.dart b/hid_linux/lib/hid_linux.dart index 179194b..cbf6952 100644 --- a/hid_linux/lib/hid_linux.dart +++ b/hid_linux/lib/hid_linux.dart @@ -111,6 +111,46 @@ class UsbDevice extends Device { } calloc.free(buf); } + + @override + Future setFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw Exception(); + final buf = calloc(bytes.lengthInBytes); + final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); + _buf.setRange(0, bytes.lengthInBytes, bytes); + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { + final count = _api.send_feature_report(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + if (count == -1) { + break; + } else { + offset += count; + } + } + calloc.free(buf); + } + + @override + Future getFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw Exception(); + final buf = calloc(bytes.lengthInBytes); + var count = 0; + var pos = 0; + while (isOpen) { + count = _api.get_feature_report(raw, buf, bytes.lengthInBytes); + if (count == -1) { + break; + } else if (count > 0) { + final res = buf.asTypedList(count); + for (var idx = 0; idx < count; idx++) { + bytes[pos++] = res[idx]; + } + } + } + calloc.free(buf); + } } extension PointerToString on Pointer { From 35bbbb55877d68d7f262bdf47a17c5717d300fed Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Wed, 6 Mar 2024 11:39:55 +0100 Subject: [PATCH 04/26] android plugin fix --- .../main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 5f9fd8a..5c01a35 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -129,7 +129,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { connection!!.controlTransfer( 33, // (0x01 << 5)|0x01|0x00 9, - 768 | bytes[0], + 768 + (bytes[0] & 255), 1, bytes.size - 1, bytes, @@ -150,7 +150,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { connection!!.controlTransfer( 161, //(0x01 << 5)|0x01|0x80 1, - 768 | bytes[0], + 768 + (bytes[0] & 255), 1, bytes.size - 1, bytes, @@ -162,7 +162,8 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { } else { result.error("error", "error", "error") } - } "close" -> { + } + "close" -> { connection?.close() connection = null device = null From 4f1fe41185c80cf5ae631fa69c181243834d60ba Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Wed, 6 Mar 2024 11:55:35 +0100 Subject: [PATCH 05/26] android plugin fix --- .../main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 5c01a35..cf8bedb 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -129,7 +129,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { connection!!.controlTransfer( 33, // (0x01 << 5)|0x01|0x00 9, - 768 + (bytes[0] & 255), + 768 or (bytes[0] and 255), 1, bytes.size - 1, bytes, @@ -150,7 +150,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { connection!!.controlTransfer( 161, //(0x01 << 5)|0x01|0x80 1, - 768 + (bytes[0] & 255), + 768 or (bytes[0] and 255), 1, bytes.size - 1, bytes, From 6db0b202a452848350d29a473dff935718ec1925 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Wed, 6 Mar 2024 12:02:26 +0100 Subject: [PATCH 06/26] android --- .../main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index cf8bedb..040cc57 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -129,7 +129,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { connection!!.controlTransfer( 33, // (0x01 << 5)|0x01|0x00 9, - 768 or (bytes[0] and 255), + 768 or (bytes.get(0) and 255), 1, bytes.size - 1, bytes, @@ -150,7 +150,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { connection!!.controlTransfer( 161, //(0x01 << 5)|0x01|0x80 1, - 768 or (bytes[0] and 255), + 768 or (bytes.get(0) and 255), 1, bytes.size - 1, bytes, From 6e50b17bb47ddad660d95a63ed0c78dce58e417e Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Wed, 6 Mar 2024 12:05:54 +0100 Subject: [PATCH 07/26] kotlin typos --- .../main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 040cc57..91325c5 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -129,7 +129,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { connection!!.controlTransfer( 33, // (0x01 << 5)|0x01|0x00 9, - 768 or (bytes.get(0) and 255), + bytes.get(0), // 768 | 1, bytes.size - 1, bytes, @@ -150,7 +150,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { connection!!.controlTransfer( 161, //(0x01 << 5)|0x01|0x80 1, - 768 or (bytes.get(0) and 255), + bytes.get(0), // 768 | 1, bytes.size - 1, bytes, From 11bda5b59af0f04ae40007a4df6e3dbb17051130 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Wed, 6 Mar 2024 13:24:42 +0100 Subject: [PATCH 08/26] kotlin fixes get/set feature --- .../rustui/hid_android/HidAndroidPlugin.kt | 63 +++++++++++++++---- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 91325c5..9c907b3 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -30,6 +30,15 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { private var readEndpointIndex: Int? = null private var writeInterfaceIndex: Int? = null private var writeEndpointIndex: Int? = null + private var hidInterfaceIndex: Int? = null + private var hidEndpointIndex: Int? = null + + private const val REQUEST_GET_REPORT = 0x01; + private const val REQUEST_SET_REPORT = 0x09; + private const val REPORT_TYPE_INPUT = 0x0100; + private const val REPORT_TYPE_OUTPUT = 0x0200; + private const val REPORT_TYPE_FEATURE = 0x0300; + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "hid_android") @@ -77,9 +86,17 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { pair = getWriteIndices(device!!)!! writeInterfaceIndex = pair.first writeEndpointIndex = pair.second + pair = getHIDIndices(device!!)!! + hidInterfaceIndex = pair.first + hidEndpointIndex = pair.second - val success : Boolean = connection!!.claimInterface(device!!.getInterface(readInterfaceIndex!!), true) && - ((readInterfaceIndex == writeInterfaceIndex) || connection!!.claimInterface(device!!.getInterface(writeInterfaceIndex!!), true)) + var success : Boolean = connection!!.claimInterface(device!!.getInterface(readInterfaceIndex!!), true); + if (writeInterfaceIndex != readInterfaceIndex) { + success = success and connection!!.claimInterface(device!!.getInterface(writeInterfaceIndex!!), true) + } + if (hidInterfaceIndex != readInterfaceIndex and hidInterfaceIndex != writeInterfaceIndex) { + success = success and connection!!.claimInterface(device!!.getInterface(hidInterfaceIndex!!), true) + } result.success( success ) } @@ -126,14 +143,17 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { val bytes: ByteArray = call.argument("bytes")!! Thread { kotlin.run { + val reportId = bytes.get(0) and 0xff + connection!!.controlTransfer( - 33, // (0x01 << 5)|0x01|0x00 - 9, - bytes.get(0), // 768 | + UsbConstants.USB_DIR_OUT or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, + REQUEST_SET_REPORT, + reportId or REPORT_TYPE_OUTPUT, + hidEndpointIndex, + bytes, 1, bytes.size - 1, - bytes, - 1000 + 0 ) result.success(0) } @@ -147,14 +167,16 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { val bytes: ByteArray = call.argument("bytes")!! Thread { kotlin.run { + val reportId = bytes.get(0) and 0xff + connection!!.controlTransfer( - 161, //(0x01 << 5)|0x01|0x80 - 1, - bytes.get(0), // 768 | - 1, - bytes.size - 1, + UsbConstants.USB_DIR_IN or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, + REQUEST_GET_REPORT, + reportId or REPORT_TYPE_INPUT, + hidEndpointIndex, bytes, - 1000 + bytes.size, + 0 ) result.success(0) } @@ -201,4 +223,19 @@ fun getWriteIndices(device: UsbDevice): Pair? { } } return null +} + +fun getHIDIndices(device: UsbDevice): Pair? { + for (i in 0 until device.interfaceCount) { + val inter = device.getInterface(i) + if (inter.getInterfaceClass() == UsbConstants.USB_CLASS_HID) { + for (j in 0 until inter.endpointCount) { + val endpoint = inter.getEndpoint(j) + if (endpoint.type == UsbConstants.USB_ENDPOINT_XFER_INT && endpoint.direction == UsbConstants.USB_IN) { + return Pair(i, j) + } + } + } + } + return null } \ No newline at end of file From 01296a0f3f58623cb818d4cc78aefa3c4d256268 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Wed, 6 Mar 2024 13:32:17 +0100 Subject: [PATCH 09/26] kotlin --- .../com/rustui/hid_android/HidAndroidPlugin.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 9c907b3..b5c21bd 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -17,6 +17,12 @@ import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result +private const val REQUEST_GET_REPORT = 0x01; +private const val REQUEST_SET_REPORT = 0x09; +private const val REPORT_TYPE_INPUT = 0x0100; +private const val REPORT_TYPE_OUTPUT = 0x0200; +private const val REPORT_TYPE_FEATURE = 0x0300; + /** HidAndroidPlugin */ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { private lateinit var channel: MethodChannel @@ -33,13 +39,6 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { private var hidInterfaceIndex: Int? = null private var hidEndpointIndex: Int? = null - private const val REQUEST_GET_REPORT = 0x01; - private const val REQUEST_SET_REPORT = 0x09; - private const val REPORT_TYPE_INPUT = 0x0100; - private const val REPORT_TYPE_OUTPUT = 0x0200; - private const val REPORT_TYPE_FEATURE = 0x0300; - - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "hid_android") channel.setMethodCallHandler(this) From 2c3d613964dd344fd8479d40acd5d0dc8233b105 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Wed, 6 Mar 2024 14:00:17 +0100 Subject: [PATCH 10/26] kotlin --- .../com/rustui/hid_android/HidAndroidPlugin.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index b5c21bd..bdd1dc0 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -16,6 +16,7 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result +import kotlin.experimental.and private const val REQUEST_GET_REPORT = 0x01; private const val REQUEST_SET_REPORT = 0x09; @@ -93,7 +94,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { if (writeInterfaceIndex != readInterfaceIndex) { success = success and connection!!.claimInterface(device!!.getInterface(writeInterfaceIndex!!), true) } - if (hidInterfaceIndex != readInterfaceIndex and hidInterfaceIndex != writeInterfaceIndex) { + if (hidInterfaceIndex != readInterfaceIndex && hidInterfaceIndex != writeInterfaceIndex) { success = success and connection!!.claimInterface(device!!.getInterface(hidInterfaceIndex!!), true) } @@ -142,13 +143,13 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { val bytes: ByteArray = call.argument("bytes")!! Thread { kotlin.run { - val reportId = bytes.get(0) and 0xff + val reportId = bytes.get(0).toInt() and 0xff connection!!.controlTransfer( UsbConstants.USB_DIR_OUT or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, REQUEST_SET_REPORT, reportId or REPORT_TYPE_OUTPUT, - hidEndpointIndex, + hidEndpointIndex ?: 0, bytes, 1, bytes.size - 1, @@ -166,13 +167,13 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { val bytes: ByteArray = call.argument("bytes")!! Thread { kotlin.run { - val reportId = bytes.get(0) and 0xff + val reportId = bytes.get(0).toInt() and 0xff connection!!.controlTransfer( UsbConstants.USB_DIR_IN or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, REQUEST_GET_REPORT, reportId or REPORT_TYPE_INPUT, - hidEndpointIndex, + hidEndpointIndex ?: 0, bytes, bytes.size, 0 @@ -230,7 +231,7 @@ fun getHIDIndices(device: UsbDevice): Pair? { if (inter.getInterfaceClass() == UsbConstants.USB_CLASS_HID) { for (j in 0 until inter.endpointCount) { val endpoint = inter.getEndpoint(j) - if (endpoint.type == UsbConstants.USB_ENDPOINT_XFER_INT && endpoint.direction == UsbConstants.USB_IN) { + if (endpoint.type == UsbConstants.USB_ENDPOINT_XFER_INT && endpoint.direction == UsbConstants.USB_DIR_IN) { return Pair(i, j) } } From 386dc4121e87ee04febfb6f4cacfefeca8cfce0c Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Wed, 6 Mar 2024 14:59:06 +0100 Subject: [PATCH 11/26] setFeature --- .../rustui/hid_android/HidAndroidPlugin.kt | 171 ++++++++++-------- 1 file changed, 96 insertions(+), 75 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index bdd1dc0..619cc51 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -33,12 +33,6 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { private val gson = Gson() private var connection: UsbDeviceConnection? = null private var device: UsbDevice? = null - private var readInterfaceIndex: Int? = null - private var readEndpointIndex: Int? = null - private var writeInterfaceIndex: Int? = null - private var writeEndpointIndex: Int? = null - private var hidInterfaceIndex: Int? = null - private var hidEndpointIndex: Int? = null override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "hid_android") @@ -80,42 +74,36 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { "open" -> { device = usbManager.deviceList[call.argument("deviceName")]!! connection = usbManager.openDevice(device) - var pair = getReadIndices(device!!)!! - readInterfaceIndex = pair.first - readEndpointIndex = pair.second - pair = getWriteIndices(device!!)!! - writeInterfaceIndex = pair.first - writeEndpointIndex = pair.second - pair = getHIDIndices(device!!)!! - hidInterfaceIndex = pair.first - hidEndpointIndex = pair.second - - var success : Boolean = connection!!.claimInterface(device!!.getInterface(readInterfaceIndex!!), true); - if (writeInterfaceIndex != readInterfaceIndex) { - success = success and connection!!.claimInterface(device!!.getInterface(writeInterfaceIndex!!), true) - } - if (hidInterfaceIndex != readInterfaceIndex && hidInterfaceIndex != writeInterfaceIndex) { - success = success and connection!!.claimInterface(device!!.getInterface(hidInterfaceIndex!!), true) - } - - result.success( success ) + + result.success( true ) } "read" -> { if (connection != null) { val length: Int = call.argument("length")!! val duration: Int = call.argument("duration")!! - Thread { - kotlin.run { - val array = ByteArray(length) - connection!!.bulkTransfer( - device!!.getInterface(readInterfaceIndex!!).getEndpoint(readEndpointIndex!!), - array, - length, - duration - ) - result.success(array.map { it.toUByte().toInt() }) + val pair = getReadIndices(device!!) + if (pair == null) { + result.error("error", "error", "error") + } else { + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "error", "error") + } else { + Thread { + kotlin.run { + val array = ByteArray(length) + connection!!.bulkTransfer( + device!!.getInterface(interfaceIndex!!).getEndpoint(endpointIndex!!), + array, + length, + duration + ) + result.success(array.map { it.toUByte().toInt() }) + } + }.start() } - }.start() + } } else { result.error("error", "error", "error") } @@ -123,17 +111,28 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { "write" -> { if (connection != null) { val bytes: ByteArray = call.argument("bytes")!! - Thread { - kotlin.run { - connection!!.bulkTransfer( - device!!.getInterface(writeInterfaceIndex!!).getEndpoint(writeEndpointIndex!!), - bytes, - bytes.size, - 1000 - ) - result.success(0) + val pair = getWriteIndices(device!!) + if (pair == null) { + result.error("error", "error", "error") + } else { + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "error", "error") + } else { + Thread { + kotlin.run { + connection!!.bulkTransfer( + device!!.getInterface(interfaceIndex!!).getEndpoint(endpointIndex!!), + bytes, + bytes.size, + 1000 + ) + result.success(0) + } + }.start() } - }.start() + } } else { result.error("error", "error", "error") } @@ -141,23 +140,34 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { "setFeature" -> { if (connection != null) { val bytes: ByteArray = call.argument("bytes")!! - Thread { - kotlin.run { - val reportId = bytes.get(0).toInt() and 0xff + val pair = getHIDIndices(device!!) + if (pair == null) { + result.error("error", "error", "error") + } else { + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "error", "error") + } else { + Thread { + kotlin.run { + val reportId = bytes.get(0).toInt() and 0xff - connection!!.controlTransfer( - UsbConstants.USB_DIR_OUT or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, - REQUEST_SET_REPORT, - reportId or REPORT_TYPE_OUTPUT, - hidEndpointIndex ?: 0, - bytes, - 1, - bytes.size - 1, - 0 - ) - result.success(0) + connection!!.controlTransfer( + UsbConstants.USB_DIR_OUT or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, + REQUEST_SET_REPORT, + reportId or REPORT_TYPE_OUTPUT, + interfaceIndex ?: 0, + bytes, + 1, + bytes.size - 1, + 0 + ) + result.success(0) + } + }.start() } - }.start() + } } else { result.error("error", "error", "error") } @@ -165,22 +175,33 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { "getFeature" -> { if (connection != null) { val bytes: ByteArray = call.argument("bytes")!! - Thread { - kotlin.run { - val reportId = bytes.get(0).toInt() and 0xff + val pair = getHIDIndices(device!!) + if (pair == null) { + result.error("error", "error", "error") + } else { + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "error", "error") + } else { + Thread { + kotlin.run { + val reportId = bytes.get(0).toInt() and 0xff - connection!!.controlTransfer( - UsbConstants.USB_DIR_IN or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, - REQUEST_GET_REPORT, - reportId or REPORT_TYPE_INPUT, - hidEndpointIndex ?: 0, - bytes, - bytes.size, - 0 - ) - result.success(0) + connection!!.controlTransfer( + UsbConstants.USB_DIR_IN or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, + REQUEST_GET_REPORT, + reportId or REPORT_TYPE_INPUT, + interfaceIndex ?: 0, + bytes, + bytes.size, + 0 + ) + result.success(0) + } + }.start() } - }.start() + } } else { result.error("error", "error", "error") } From 8d930fddf3ff7b6baab82ac67aab5185ad43669b Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Wed, 6 Mar 2024 17:22:50 +0100 Subject: [PATCH 12/26] fixed close method --- .../src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 619cc51..16b7aa9 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -210,6 +210,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { connection?.close() connection = null device = null + result.success(0) } else -> result.notImplemented() } From d3926a75d72717fb863c03b7f058daed1827fdcd Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Thu, 7 Mar 2024 12:32:18 +0100 Subject: [PATCH 13/26] fixed permission --- .../src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 16b7aa9..6b569a1 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -64,7 +64,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { context, 0, Intent("ACTION_USB_PERMISSION"), - 0 + PendingIntent.FLAG_IMMUTABLE ) usbManager.requestPermission(device, permissionIntent) } From 091d46c1eac84d2b33f4922a8c4d519e506c21b1 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Thu, 7 Mar 2024 14:34:17 +0100 Subject: [PATCH 14/26] added productID / vendorID filter --- hid/lib/hid.dart | 4 ++-- .../rustui/hid_android/HidAndroidPlugin.kt | 23 +++++++++++-------- hid_android/lib/hid_android.dart | 20 ++++++---------- hid_linux/lib/hid_linux.dart | 15 ++++-------- hid_macos/lib/hid_macos.dart | 4 ++-- .../lib/hid_platform_interface.dart | 2 +- hid_windows/lib/hid_windows.dart | 12 +++------- 7 files changed, 33 insertions(+), 47 deletions(-) diff --git a/hid/lib/hid.dart b/hid/lib/hid.dart index 7bddad6..a779f15 100644 --- a/hid/lib/hid.dart +++ b/hid/lib/hid.dart @@ -3,6 +3,6 @@ import 'package:hid_platform_interface/hid_platform_interface.dart'; export 'package:hid_platform_interface/device.dart'; HidPlatform get _platform => HidPlatform.instance; -Future> getDeviceList() { - return _platform.getDeviceList(); +Future> getDeviceList({int? vendorId, int? productId}) { + return _platform.getDeviceList(vendorId: vendorId, productId: productId); } diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 6b569a1..74b0afe 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -45,19 +45,24 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { when (call.method) { "getDeviceList" -> { + val vendorId: Int? = call.argument("vendorId") + val productId: Int? = call.argument("productId") val devices: MutableList = mutableListOf() for (device in usbManager.deviceList.values) { try { - val json = gson.toJson( - HidDevice( - device.vendorId, - device.productId, - device.serialNumber ?: "", - device.productName ?: "", - device.deviceName + if (((vendorId == null) or (device.getVendorId() == (vendorId ?: 0))) and + ((productId == null) or (device.getProductId() == (productId ?: 0)))) { + val json = gson.toJson( + HidDevice( + device.vendorId, + device.productId, + device.serialNumber ?: "", + device.productName ?: "", + device.deviceName + ) ) - ) - devices.add(json) + devices.add(json) + } } catch (e: Exception) { val permissionIntent = PendingIntent.getBroadcast( diff --git a/hid_android/lib/hid_android.dart b/hid_android/lib/hid_android.dart index d6526fd..0a72608 100644 --- a/hid_android/lib/hid_android.dart +++ b/hid_android/lib/hid_android.dart @@ -12,9 +12,12 @@ class HidAndroid extends HidPlatform { } @override - Future> getDeviceList() async { + Future> getDeviceList({int? vendorId, int? productId}) async { final List list = []; - final List devices = await _channel.invokeMethod('getDeviceList'); + final List devices = await _channel.invokeMethod('getDeviceList', { + 'vendorId': vendorId, + 'productId': productId, + }); for (var deviceObject in devices) { final rawDevice = deviceObject! as String; final device = jsonDecode(rawDevice); @@ -32,17 +35,8 @@ class HidAndroid extends HidPlatform { class UsbDevice extends Device { bool isOpen = false; String deviceName; - UsbDevice( - {required int vendorId, - required int productId, - required String serialNumber, - required String productName, - required this.deviceName}) - : super( - vendorId: vendorId, - productId: productId, - serialNumber: serialNumber, - productName: productName); + UsbDevice({required int vendorId, required int productId, required String serialNumber, required String productName, required this.deviceName}) + : super(vendorId: vendorId, productId: productId, serialNumber: serialNumber, productName: productName); @override Future open() async { diff --git a/hid_linux/lib/hid_linux.dart b/hid_linux/lib/hid_linux.dart index cbf6952..e250de2 100644 --- a/hid_linux/lib/hid_linux.dart +++ b/hid_linux/lib/hid_linux.dart @@ -14,9 +14,9 @@ class HidPluginLinux extends HidPlatform { } @override - Future> getDeviceList() async { + Future> getDeviceList({int? vendorId, int? productId}) async { List devices = []; - final pointer = _api.enumerate(0, 0); + final pointer = _api.enumerate(vendorId ?? 0, productId ?? 0); var current = pointer; while (current.address != nullptr.address) { final ref = current.ref; @@ -46,13 +46,7 @@ class UsbDevice extends Device { required String productName, required int usagePage, required int usage, - }) : super( - vendorId: vendorId, - productId: productId, - serialNumber: serialNumber, - productName: productName, - usagePage: usagePage, - usage: usage); + }) : super(vendorId: vendorId, productId: productId, serialNumber: serialNumber, productName: productName, usagePage: usagePage, usage: usage); @override Future open() async { @@ -101,8 +95,7 @@ class UsbDevice extends Device { _buf.setRange(0, bytes.lengthInBytes, bytes); var offset = 0; while (isOpen && bytes.lengthInBytes - offset > 0) { - final count = - _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + final count = _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); if (count == -1) { break; } else { diff --git a/hid_macos/lib/hid_macos.dart b/hid_macos/lib/hid_macos.dart index 6a81cff..d2aee62 100644 --- a/hid_macos/lib/hid_macos.dart +++ b/hid_macos/lib/hid_macos.dart @@ -13,9 +13,9 @@ class HidPluginMacOS extends HidPlatform { } @override - Future> getDeviceList() async { + Future> getDeviceList({int? vendorId, int? productId}) async { List devices = []; - final pointer = _api.enumerate(0, 0); + final pointer = _api.enumerate(vendorId ?? 0, productId ?? 0); var current = pointer; while (current.address != nullptr.address) { final ref = current.ref; diff --git a/hid_platform_interface/lib/hid_platform_interface.dart b/hid_platform_interface/lib/hid_platform_interface.dart index 0029b98..a5d9c84 100644 --- a/hid_platform_interface/lib/hid_platform_interface.dart +++ b/hid_platform_interface/lib/hid_platform_interface.dart @@ -16,7 +16,7 @@ abstract class HidPlatform extends PlatformInterface { _instance = instance; } - Future> getDeviceList() { + Future> getDeviceList({int? vendorId, int? productId}) { throw UnimplementedError(); } } diff --git a/hid_windows/lib/hid_windows.dart b/hid_windows/lib/hid_windows.dart index 5880728..44283a5 100644 --- a/hid_windows/lib/hid_windows.dart +++ b/hid_windows/lib/hid_windows.dart @@ -13,9 +13,9 @@ class HidPluginWindows extends HidPlatform { } @override - Future> getDeviceList() async { + Future> getDeviceList({int? vendorId, int? productId}) async { List devices = []; - final pointer = _api.enumerate(0, 0); + final pointer = _api.enumerate(vendorId ?? 0, productId ?? 0); var current = pointer; while (current.address != nullptr.address) { final ref = current.ref; @@ -45,13 +45,7 @@ class UsbDevice extends Device { required String productName, required int usagePage, required int usage, - }) : super( - vendorId: vendorId, - productId: productId, - serialNumber: serialNumber, - productName: productName, - usagePage: usagePage, - usage: usage); + }) : super(vendorId: vendorId, productId: productId, serialNumber: serialNumber, productName: productName, usagePage: usagePage, usage: usage); @override Future open() async { From 32d7ffab179fdb27c4da5b1ee1843e77abec8a24 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Thu, 21 Mar 2024 14:39:06 +0100 Subject: [PATCH 15/26] android getFeature data was not returned back to dart --- .../kotlin/com/rustui/hid_android/HidAndroidPlugin.kt | 8 +++++--- hid_android/lib/hid_android.dart | 8 +++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 74b0afe..47b902e 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -193,16 +193,18 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { kotlin.run { val reportId = bytes.get(0).toInt() and 0xff + val array = ByteArray(bytes.size - 1) + connection!!.controlTransfer( UsbConstants.USB_DIR_IN or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, REQUEST_GET_REPORT, reportId or REPORT_TYPE_INPUT, interfaceIndex ?: 0, - bytes, - bytes.size, + array, + bytes.size - 1, 0 ) - result.success(0) + result.success(array.map { it.toUByte().toInt() }) } }.start() } diff --git a/hid_android/lib/hid_android.dart b/hid_android/lib/hid_android.dart index 0a72608..7c4fa74 100644 --- a/hid_android/lib/hid_android.dart +++ b/hid_android/lib/hid_android.dart @@ -71,7 +71,13 @@ class UsbDevice extends Device { @override Future getFeature(Uint8List bytes) async { - await _channel.invokeMethod('getFeature', {'bytes': bytes}); + final List array = await _channel.invokeMethod('getFeature', {'bytes': bytes}); + final res = array.map((e) => e! as int).toList(); + final count = min(res.length, bytes.length - 1); + var pos = 1; // keep the first byte in place + for (var idx = 0; idx < count; idx++) { + bytes[pos++] = res[idx]; + } } @override From 9056e1c758a50d917bd64d2e3d604b930ea66b5d Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Thu, 21 Mar 2024 15:47:45 +0100 Subject: [PATCH 16/26] do not crash on invalid device --- hid_android/lib/hid_android.dart | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/hid_android/lib/hid_android.dart b/hid_android/lib/hid_android.dart index 7c4fa74..34ed269 100644 --- a/hid_android/lib/hid_android.dart +++ b/hid_android/lib/hid_android.dart @@ -19,14 +19,17 @@ class HidAndroid extends HidPlatform { 'productId': productId, }); for (var deviceObject in devices) { - final rawDevice = deviceObject! as String; - final device = jsonDecode(rawDevice); - list.add(UsbDevice( - vendorId: device['vendorId'], - productId: device['productId'], - serialNumber: device['serialNumber'], - productName: device['productName'], - deviceName: device['deviceName'])); + final rawDevice = deviceObject as String?; + if (rawDevice != null) { + final json = jsonDecode(rawDevice); + final device = UsbDevice( + vendorId: json['vendorId'], + productId: json['productId'], + serialNumber: json['serialNumber'] ?? '', + productName: json['productName'] ?? '', + deviceName: json['deviceName'] ?? ''); + list.add(device); + } } return list; } From 7cebae46f048e1973840cbb1fb24c17cb4956b5b Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Thu, 21 Mar 2024 16:13:48 +0100 Subject: [PATCH 17/26] still crashes on Android in release mode, another blind fix --- hid/example/lib/main.dart | 73 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/hid/example/lib/main.dart b/hid/example/lib/main.dart index c55c50b..42e8b18 100644 --- a/hid/example/lib/main.dart +++ b/hid/example/lib/main.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'dart:typed_data'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:hid/hid.dart'; @@ -20,7 +22,9 @@ class _MyAppState extends State { @override void initState() { super.initState(); - _listDevices(); + Future.delayed(const Duration(milliseconds: 100), () { + _listDevices(); + }); } // Platform messages are asynchronous, so we initialize in an async method. @@ -29,7 +33,7 @@ class _MyAppState extends State { _hidDevices = null; }); - final hidDevices = await getDeviceList(); + final hidDevices = await getDeviceList(vendorId: null, productId: null); hidDevices.sort((a, b) => a.usage?.compareTo(b.usage ?? 0) ?? 0); hidDevices.sort((a, b) => a.usagePage?.compareTo(b.usagePage ?? 0) ?? 0); hidDevices.sort((a, b) => a.productId.compareTo(b.productId)); @@ -41,11 +45,73 @@ class _MyAppState extends State { // setState to update our non-existent appearance. if (!mounted) return; + _operateTheRelay(); + setState(() { _hidDevices = hidDevices; }); } + Future _operateTheRelay() async { + if (!await _openTheRelay(true, 0)) { + return false; + } + await _openTheRelay(true, 1); + + var states = await _getPortStates(); + + await Future.delayed(const Duration(seconds: 2)); + await _openTheRelay(false, 0); + await _openTheRelay(false, 1); + + states = await _getPortStates(); + + return true; + } + + Future _openTheRelay(bool switchOn, int portNo) async { + final relayBoard = (await getDeviceList(vendorId: 5824, productId: 1503)).firstOrNull; + + if (relayBoard == null) { + return false; + } + + await relayBoard.open(); + final bytes = Uint8List.fromList([0x00, switchOn ? 0xff : 0xfd, portNo + 1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + + await relayBoard.setFeature(bytes); + await relayBoard.close(); + return true; + } + + Future> _getPortStates() async { + final relayBoard = (await getDeviceList(vendorId: 5824, productId: 1503)).firstOrNull; + + final result = []; + + if (relayBoard == null) { + return result; + } + + try { + final portCount = int.tryParse(relayBoard.productName.replaceAll("USBRelay", "")) ?? 0; + + await relayBoard.open(); + + var bytes = Uint8List.fromList([1, 0, 0, 0, 0, 0, 0, 0, 0]); + await relayBoard.getFeature(bytes); + + for (var portNo = 0; portNo < portCount; portNo++) { + final state = (bytes[8] & (1 << portNo)) != 0; + result.add(state); + } + + return result; + } finally { + await relayBoard.close(); + } + } + _getUsagePageIcon(int? usagePage, int? usage) { switch (usagePage) { case 0x01: @@ -92,8 +158,7 @@ class _MyAppState extends State { : ListView.builder( itemCount: dev.length, itemBuilder: (context, index) => ListTile( - leading: Icon(_getUsagePageIcon( - dev[index].usagePage, dev[index].usage)), + leading: Icon(_getUsagePageIcon(dev[index].usagePage, dev[index].usage)), title: Text(dev[index].productName), subtitle: Text( '${dev[index].vendorId.toRadixString(16).padLeft(4, '0')}:${dev[index].productId.toRadixString(16).padLeft(4, '0')} ${dev[index].serialNumber}'), From 50031d0c8c7447c65b240ee37b9fc72ac3213224 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Thu, 21 Mar 2024 16:37:23 +0100 Subject: [PATCH 18/26] reverted wrongly submitted main, added android code --- hid/example/lib/main.dart | 73 ++------------------------------ hid_android/lib/hid_android.dart | 20 +++++---- 2 files changed, 16 insertions(+), 77 deletions(-) diff --git a/hid/example/lib/main.dart b/hid/example/lib/main.dart index 42e8b18..c55c50b 100644 --- a/hid/example/lib/main.dart +++ b/hid/example/lib/main.dart @@ -1,7 +1,5 @@ import 'dart:async'; -import 'dart:typed_data'; -import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:hid/hid.dart'; @@ -22,9 +20,7 @@ class _MyAppState extends State { @override void initState() { super.initState(); - Future.delayed(const Duration(milliseconds: 100), () { - _listDevices(); - }); + _listDevices(); } // Platform messages are asynchronous, so we initialize in an async method. @@ -33,7 +29,7 @@ class _MyAppState extends State { _hidDevices = null; }); - final hidDevices = await getDeviceList(vendorId: null, productId: null); + final hidDevices = await getDeviceList(); hidDevices.sort((a, b) => a.usage?.compareTo(b.usage ?? 0) ?? 0); hidDevices.sort((a, b) => a.usagePage?.compareTo(b.usagePage ?? 0) ?? 0); hidDevices.sort((a, b) => a.productId.compareTo(b.productId)); @@ -45,73 +41,11 @@ class _MyAppState extends State { // setState to update our non-existent appearance. if (!mounted) return; - _operateTheRelay(); - setState(() { _hidDevices = hidDevices; }); } - Future _operateTheRelay() async { - if (!await _openTheRelay(true, 0)) { - return false; - } - await _openTheRelay(true, 1); - - var states = await _getPortStates(); - - await Future.delayed(const Duration(seconds: 2)); - await _openTheRelay(false, 0); - await _openTheRelay(false, 1); - - states = await _getPortStates(); - - return true; - } - - Future _openTheRelay(bool switchOn, int portNo) async { - final relayBoard = (await getDeviceList(vendorId: 5824, productId: 1503)).firstOrNull; - - if (relayBoard == null) { - return false; - } - - await relayBoard.open(); - final bytes = Uint8List.fromList([0x00, switchOn ? 0xff : 0xfd, portNo + 1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - - await relayBoard.setFeature(bytes); - await relayBoard.close(); - return true; - } - - Future> _getPortStates() async { - final relayBoard = (await getDeviceList(vendorId: 5824, productId: 1503)).firstOrNull; - - final result = []; - - if (relayBoard == null) { - return result; - } - - try { - final portCount = int.tryParse(relayBoard.productName.replaceAll("USBRelay", "")) ?? 0; - - await relayBoard.open(); - - var bytes = Uint8List.fromList([1, 0, 0, 0, 0, 0, 0, 0, 0]); - await relayBoard.getFeature(bytes); - - for (var portNo = 0; portNo < portCount; portNo++) { - final state = (bytes[8] & (1 << portNo)) != 0; - result.add(state); - } - - return result; - } finally { - await relayBoard.close(); - } - } - _getUsagePageIcon(int? usagePage, int? usage) { switch (usagePage) { case 0x01: @@ -158,7 +92,8 @@ class _MyAppState extends State { : ListView.builder( itemCount: dev.length, itemBuilder: (context, index) => ListTile( - leading: Icon(_getUsagePageIcon(dev[index].usagePage, dev[index].usage)), + leading: Icon(_getUsagePageIcon( + dev[index].usagePage, dev[index].usage)), title: Text(dev[index].productName), subtitle: Text( '${dev[index].vendorId.toRadixString(16).padLeft(4, '0')}:${dev[index].productId.toRadixString(16).padLeft(4, '0')} ${dev[index].serialNumber}'), diff --git a/hid_android/lib/hid_android.dart b/hid_android/lib/hid_android.dart index 34ed269..745721f 100644 --- a/hid_android/lib/hid_android.dart +++ b/hid_android/lib/hid_android.dart @@ -21,14 +21,18 @@ class HidAndroid extends HidPlatform { for (var deviceObject in devices) { final rawDevice = deviceObject as String?; if (rawDevice != null) { - final json = jsonDecode(rawDevice); - final device = UsbDevice( - vendorId: json['vendorId'], - productId: json['productId'], - serialNumber: json['serialNumber'] ?? '', - productName: json['productName'] ?? '', - deviceName: json['deviceName'] ?? ''); - list.add(device); + try { + final json = jsonDecode(rawDevice); + final device = UsbDevice( + vendorId: json['vendorId'], + productId: json['productId'], + serialNumber: json['serialNumber'] ?? '', + productName: json['productName'] ?? '', + deviceName: json['deviceName'] ?? ''); + list.add(device); + } catch (e) { + // ignored + } } } return list; From d1f9d60f0ab94f4150e059fdd8f74e762a3a3d6b Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Thu, 21 Mar 2024 17:11:48 +0100 Subject: [PATCH 19/26] allow the gson work even when obfuscated --- .../src/main/kotlin/com/rustui/hid_android/HidDevice.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidDevice.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidDevice.kt index 921e987..820dae7 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidDevice.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidDevice.kt @@ -1,10 +1,17 @@ package com.rustui.hid_android +import com.google.gson.annotations.SerializedName + class HidDevice( + @SerializedName("vendorId") val vendorId: Int, + @SerializedName("productId") val productId: Int, + @SerializedName("serialNumber") val serialNumber: String, + @SerializedName("productName") val productName: String, + @SerializedName("deviceName") val deviceName: String ) { } \ No newline at end of file From a5fa274e3e8b5a5d5d200102ca33fe3e96b214b0 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Thu, 21 Mar 2024 17:26:48 +0100 Subject: [PATCH 20/26] added try/catch blocks to avoid unhandled exceptions on Java side --- .../rustui/hid_android/HidAndroidPlugin.kt | 230 ++++++++++-------- 1 file changed, 127 insertions(+), 103 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 47b902e..bb87722 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -77,147 +77,171 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { result.success(devices) } "open" -> { - device = usbManager.deviceList[call.argument("deviceName")]!! - connection = usbManager.openDevice(device) + try { + device = usbManager.deviceList[call.argument("deviceName")]!! + connection = usbManager.openDevice(device) - result.success( true ) + result.success( true ) + } catch (e: Exception) { + result.error("error", "error", "error") + } } "read" -> { - if (connection != null) { - val length: Int = call.argument("length")!! - val duration: Int = call.argument("duration")!! - val pair = getReadIndices(device!!) - if (pair == null) { - result.error("error", "error", "error") - } else { - val interfaceIndex = pair.first; - val endpointIndex = pair.second; - if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + try { + if (connection != null) { + val length: Int = call.argument("length")!! + val duration: Int = call.argument("duration")!! + val pair = getReadIndices(device!!) + if (pair == null) { result.error("error", "error", "error") } else { - Thread { - kotlin.run { - val array = ByteArray(length) - connection!!.bulkTransfer( - device!!.getInterface(interfaceIndex!!).getEndpoint(endpointIndex!!), - array, - length, - duration - ) - result.success(array.map { it.toUByte().toInt() }) - } - }.start() + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "error", "error") + } else { + Thread { + kotlin.run { + val array = ByteArray(length) + connection!!.bulkTransfer( + device!!.getInterface(interfaceIndex!!).getEndpoint(endpointIndex!!), + array, + length, + duration + ) + result.success(array.map { it.toUByte().toInt() }) + } + }.start() + } } + } else { + result.error("error", "error", "error") } - } else { + } catch (e: Exception) { result.error("error", "error", "error") } } "write" -> { - if (connection != null) { - val bytes: ByteArray = call.argument("bytes")!! - val pair = getWriteIndices(device!!) - if (pair == null) { - result.error("error", "error", "error") - } else { - val interfaceIndex = pair.first; - val endpointIndex = pair.second; - if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + try { + if (connection != null) { + val bytes: ByteArray = call.argument("bytes")!! + val pair = getWriteIndices(device!!) + if (pair == null) { result.error("error", "error", "error") } else { - Thread { - kotlin.run { - connection!!.bulkTransfer( - device!!.getInterface(interfaceIndex!!).getEndpoint(endpointIndex!!), - bytes, - bytes.size, - 1000 - ) - result.success(0) - } - }.start() + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "error", "error") + } else { + Thread { + kotlin.run { + connection!!.bulkTransfer( + device!!.getInterface(interfaceIndex!!).getEndpoint(endpointIndex!!), + bytes, + bytes.size, + 1000 + ) + result.success(0) + } + }.start() + } } + } else { + result.error("error", "error", "error") } - } else { + } catch (e: Exception) { result.error("error", "error", "error") } } "setFeature" -> { - if (connection != null) { - val bytes: ByteArray = call.argument("bytes")!! - val pair = getHIDIndices(device!!) - if (pair == null) { - result.error("error", "error", "error") - } else { - val interfaceIndex = pair.first; - val endpointIndex = pair.second; - if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + try { + if (connection != null) { + val bytes: ByteArray = call.argument("bytes")!! + val pair = getHIDIndices(device!!) + if (pair == null) { result.error("error", "error", "error") } else { - Thread { - kotlin.run { - val reportId = bytes.get(0).toInt() and 0xff + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "error", "error") + } else { + Thread { + kotlin.run { + val reportId = bytes.get(0).toInt() and 0xff - connection!!.controlTransfer( - UsbConstants.USB_DIR_OUT or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, - REQUEST_SET_REPORT, - reportId or REPORT_TYPE_OUTPUT, - interfaceIndex ?: 0, - bytes, - 1, - bytes.size - 1, - 0 - ) - result.success(0) - } - }.start() + connection!!.controlTransfer( + UsbConstants.USB_DIR_OUT or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, + REQUEST_SET_REPORT, + reportId or REPORT_TYPE_OUTPUT, + interfaceIndex ?: 0, + bytes, + 1, + bytes.size - 1, + 0 + ) + result.success(0) + } + }.start() + } } + } else { + result.error("error", "error", "error") } - } else { + } catch (e: Exception) { result.error("error", "error", "error") } } "getFeature" -> { - if (connection != null) { - val bytes: ByteArray = call.argument("bytes")!! - val pair = getHIDIndices(device!!) - if (pair == null) { - result.error("error", "error", "error") - } else { - val interfaceIndex = pair.first; - val endpointIndex = pair.second; - if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + try { + if (connection != null) { + val bytes: ByteArray = call.argument("bytes")!! + val pair = getHIDIndices(device!!) + if (pair == null) { result.error("error", "error", "error") } else { - Thread { - kotlin.run { - val reportId = bytes.get(0).toInt() and 0xff + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "error", "error") + } else { + Thread { + kotlin.run { + val reportId = bytes.get(0).toInt() and 0xff - val array = ByteArray(bytes.size - 1) + val array = ByteArray(bytes.size - 1) - connection!!.controlTransfer( - UsbConstants.USB_DIR_IN or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, - REQUEST_GET_REPORT, - reportId or REPORT_TYPE_INPUT, - interfaceIndex ?: 0, - array, - bytes.size - 1, - 0 - ) - result.success(array.map { it.toUByte().toInt() }) - } - }.start() + connection!!.controlTransfer( + UsbConstants.USB_DIR_IN or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, + REQUEST_GET_REPORT, + reportId or REPORT_TYPE_INPUT, + interfaceIndex ?: 0, + array, + bytes.size - 1, + 0 + ) + result.success(array.map { it.toUByte().toInt() }) + } + }.start() + } } + } else { + result.error("error", "error", "error") } - } else { + } catch (e: Exception) { result.error("error", "error", "error") } } "close" -> { - connection?.close() - connection = null - device = null - result.success(0) + try { + connection?.close() + connection = null + device = null + result.success(0) + } catch (e: Exception) { + result.error("error", "error", "error") + } } else -> result.notImplemented() } From 622c01fb02736be30754e62febad81e967838fd9 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Fri, 22 Mar 2024 13:43:40 +0100 Subject: [PATCH 21/26] more try/catch calls for android not crash on USB errors --- .../rustui/hid_android/HidAndroidPlugin.kt | 74 +++++++++++-------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index bb87722..a7d8ec3 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -136,13 +136,17 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { } else { Thread { kotlin.run { - connection!!.bulkTransfer( - device!!.getInterface(interfaceIndex!!).getEndpoint(endpointIndex!!), - bytes, - bytes.size, - 1000 - ) - result.success(0) + try { + connection!!.bulkTransfer( + device!!.getInterface(interfaceIndex!!).getEndpoint(endpointIndex!!), + bytes, + bytes.size, + 1000 + ) + result.success(0) + } catch (e: Exception) { + result.error("error", "error", "error") + } } }.start() } @@ -169,19 +173,23 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { } else { Thread { kotlin.run { - val reportId = bytes.get(0).toInt() and 0xff + try { + val reportId = bytes.get(0).toInt() and 0xff - connection!!.controlTransfer( - UsbConstants.USB_DIR_OUT or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, - REQUEST_SET_REPORT, - reportId or REPORT_TYPE_OUTPUT, - interfaceIndex ?: 0, - bytes, - 1, - bytes.size - 1, - 0 - ) - result.success(0) + connection!!.controlTransfer( + UsbConstants.USB_DIR_OUT or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, + REQUEST_SET_REPORT, + reportId or REPORT_TYPE_OUTPUT, + interfaceIndex ?: 0, + bytes, + 1, + bytes.size - 1, + 0 + ) + result.success(0) + } catch (e: Exception) { + result.error("error", "error", "error") + } } }.start() } @@ -208,20 +216,24 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { } else { Thread { kotlin.run { - val reportId = bytes.get(0).toInt() and 0xff + try { + val reportId = bytes.get(0).toInt() and 0xff - val array = ByteArray(bytes.size - 1) + val array = ByteArray(bytes.size - 1) - connection!!.controlTransfer( - UsbConstants.USB_DIR_IN or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, - REQUEST_GET_REPORT, - reportId or REPORT_TYPE_INPUT, - interfaceIndex ?: 0, - array, - bytes.size - 1, - 0 - ) - result.success(array.map { it.toUByte().toInt() }) + connection!!.controlTransfer( + UsbConstants.USB_DIR_IN or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, + REQUEST_GET_REPORT, + reportId or REPORT_TYPE_INPUT, + interfaceIndex ?: 0, + array, + bytes.size - 1, + 0 + ) + result.success(array.map { it.toUByte().toInt() }) + } catch (e: Exception) { + result.error("error", "error", "error") + } } }.start() } From e13e8021e4cb66145362fe3f9da58db222f14d74 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Fri, 22 Mar 2024 14:19:03 +0100 Subject: [PATCH 22/26] exceptiom messages --- .../rustui/hid_android/HidAndroidPlugin.kt | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index a7d8ec3..92f026f 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -83,7 +83,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { result.success( true ) } catch (e: Exception) { - result.error("error", "error", "error") + result.error("error", e.message, e.getStackTrace()) } } "read" -> { @@ -93,12 +93,12 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { val duration: Int = call.argument("duration")!! val pair = getReadIndices(device!!) if (pair == null) { - result.error("error", "error", "error") + result.error("error", "Could not find interface and endpoint for read operation", call.method) } else { val interfaceIndex = pair.first; val endpointIndex = pair.second; if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { - result.error("error", "error", "error") + result.error("error", "Could not claim interface for read operation", call.method) } else { Thread { kotlin.run { @@ -115,10 +115,10 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { } } } else { - result.error("error", "error", "error") + result.error("error", "device connection is null", call.method) } } catch (e: Exception) { - result.error("error", "error", "error") + result.error("error", e.message, e.getStackTrace()) } } "write" -> { @@ -127,12 +127,12 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { val bytes: ByteArray = call.argument("bytes")!! val pair = getWriteIndices(device!!) if (pair == null) { - result.error("error", "error", "error") + result.error("error", "Could not find interface and endpoint for write operation", call.method) } else { val interfaceIndex = pair.first; val endpointIndex = pair.second; if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { - result.error("error", "error", "error") + result.error("error", "Could not claim interface for write operation", call.method) } else { Thread { kotlin.run { @@ -145,17 +145,17 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { ) result.success(0) } catch (e: Exception) { - result.error("error", "error", "error") + result.error("error", e.message, e.getStackTrace()) } } }.start() } } } else { - result.error("error", "error", "error") + result.error("error", "device connection is null", call.method) } } catch (e: Exception) { - result.error("error", "error", "error") + result.error("error", e.message, e.getStackTrace()) } } "setFeature" -> { @@ -164,12 +164,12 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { val bytes: ByteArray = call.argument("bytes")!! val pair = getHIDIndices(device!!) if (pair == null) { - result.error("error", "error", "error") + result.error("error", "Could not find interface and endpoint for HID operation", call.method) } else { val interfaceIndex = pair.first; val endpointIndex = pair.second; if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { - result.error("error", "error", "error") + result.error("error", "Could not claim interface for HID operation", call.method) } else { Thread { kotlin.run { @@ -188,17 +188,17 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { ) result.success(0) } catch (e: Exception) { - result.error("error", "error", "error") + result.error("error", e.message, e.getStackTrace()) } } }.start() } } } else { - result.error("error", "error", "error") + result.error("error", "device connection is null", call.method) } } catch (e: Exception) { - result.error("error", "error", "error") + result.error("error", e.message, e.getStackTrace()) } } "getFeature" -> { @@ -207,12 +207,12 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { val bytes: ByteArray = call.argument("bytes")!! val pair = getHIDIndices(device!!) if (pair == null) { - result.error("error", "error", "error") + result.error("error", "Could not find interface and endpoint for HID operation", call.method) } else { val interfaceIndex = pair.first; val endpointIndex = pair.second; if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { - result.error("error", "error", "error") + result.error("error", "Could not claim interface for HID operation", call.method) } else { Thread { kotlin.run { @@ -232,17 +232,17 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { ) result.success(array.map { it.toUByte().toInt() }) } catch (e: Exception) { - result.error("error", "error", "error") + result.error("error", e.message, e.getStackTrace()) } } }.start() } } } else { - result.error("error", "error", "error") + result.error("error", "device connection is null", call.method) } } catch (e: Exception) { - result.error("error", "error", "error") + result.error("error", e.message, e.getStackTrace()) } } "close" -> { @@ -252,7 +252,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { device = null result.success(0) } catch (e: Exception) { - result.error("error", "error", "error") + result.error("error", e.message, e.getStackTrace()) } } else -> result.notImplemented() From 69fbcdb9678bc30ac4fa9c56cf137e8197d200b3 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Fri, 22 Mar 2024 16:57:36 +0100 Subject: [PATCH 23/26] fixed crash with stacktrace --- .../com/rustui/hid_android/HidAndroidPlugin.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 92f026f..55f7306 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -83,7 +83,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { result.success( true ) } catch (e: Exception) { - result.error("error", e.message, e.getStackTrace()) + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) } } "read" -> { @@ -118,7 +118,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { result.error("error", "device connection is null", call.method) } } catch (e: Exception) { - result.error("error", e.message, e.getStackTrace()) + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) } } "write" -> { @@ -145,7 +145,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { ) result.success(0) } catch (e: Exception) { - result.error("error", e.message, e.getStackTrace()) + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) } } }.start() @@ -155,7 +155,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { result.error("error", "device connection is null", call.method) } } catch (e: Exception) { - result.error("error", e.message, e.getStackTrace()) + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) } } "setFeature" -> { @@ -188,7 +188,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { ) result.success(0) } catch (e: Exception) { - result.error("error", e.message, e.getStackTrace()) + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) } } }.start() @@ -198,7 +198,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { result.error("error", "device connection is null", call.method) } } catch (e: Exception) { - result.error("error", e.message, e.getStackTrace()) + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) } } "getFeature" -> { @@ -232,7 +232,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { ) result.success(array.map { it.toUByte().toInt() }) } catch (e: Exception) { - result.error("error", e.message, e.getStackTrace()) + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) } } }.start() @@ -242,7 +242,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { result.error("error", "device connection is null", call.method) } } catch (e: Exception) { - result.error("error", e.message, e.getStackTrace()) + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) } } "close" -> { @@ -252,7 +252,7 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { device = null result.success(0) } catch (e: Exception) { - result.error("error", e.message, e.getStackTrace()) + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) } } else -> result.notImplemented() From 8444bafad22789393f3245781b772fbdc278143e Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Mon, 25 Mar 2024 13:42:48 +0100 Subject: [PATCH 24/26] getGeature fix for linux, windows and macos --- hid_linux/lib/hid_linux.dart | 6 +++--- hid_macos/lib/hid_macos.dart | 6 +++--- hid_windows/lib/hid_windows.dart | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hid_linux/lib/hid_linux.dart b/hid_linux/lib/hid_linux.dart index e250de2..61a7b88 100644 --- a/hid_linux/lib/hid_linux.dart +++ b/hid_linux/lib/hid_linux.dart @@ -130,15 +130,15 @@ class UsbDevice extends Device { if (raw == null) throw Exception(); final buf = calloc(bytes.lengthInBytes); var count = 0; - var pos = 0; - while (isOpen) { + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { count = _api.get_feature_report(raw, buf, bytes.lengthInBytes); if (count == -1) { break; } else if (count > 0) { final res = buf.asTypedList(count); for (var idx = 0; idx < count; idx++) { - bytes[pos++] = res[idx]; + bytes[offset++] = res[idx]; } } } diff --git a/hid_macos/lib/hid_macos.dart b/hid_macos/lib/hid_macos.dart index d2aee62..aeb7008 100644 --- a/hid_macos/lib/hid_macos.dart +++ b/hid_macos/lib/hid_macos.dart @@ -129,15 +129,15 @@ class UsbDevice extends Device { if (raw == null) throw Exception(); final buf = calloc(bytes.lengthInBytes); var count = 0; - var pos = 0; - while (isOpen) { + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { count = _api.get_feature_report(raw, buf, bytes.lengthInBytes); if (count == -1) { break; } else if (count > 0) { final res = buf.asTypedList(count); for (var idx = 0; idx < count; idx++) { - bytes[pos++] = res[idx]; + bytes[offset++] = res[idx]; } } } diff --git a/hid_windows/lib/hid_windows.dart b/hid_windows/lib/hid_windows.dart index 44283a5..1d34b27 100644 --- a/hid_windows/lib/hid_windows.dart +++ b/hid_windows/lib/hid_windows.dart @@ -129,15 +129,15 @@ class UsbDevice extends Device { if (raw == null) throw Exception(); final buf = calloc(bytes.lengthInBytes); var count = 0; - var pos = 0; - while (isOpen) { + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { count = _api.get_feature_report(raw, buf, bytes.lengthInBytes); if (count == -1) { break; } else if (count > 0) { final res = buf.asTypedList(count); for (var idx = 0; idx < count; idx++) { - bytes[pos++] = res[idx]; + bytes[offset++] = res[idx]; } } } From ebaa1fbc6545b8cfcc9421abb4d51a194de6607f Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Mon, 25 Mar 2024 14:04:48 +0100 Subject: [PATCH 25/26] fix for closing the device multiple times --- hid_linux/lib/hid_linux.dart | 4 +++- hid_macos/lib/hid_macos.dart | 11 +++++++---- hid_windows/lib/hid_windows.dart | 11 +++++++---- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/hid_linux/lib/hid_linux.dart b/hid_linux/lib/hid_linux.dart index 61a7b88..a01e13c 100644 --- a/hid_linux/lib/hid_linux.dart +++ b/hid_linux/lib/hid_linux.dart @@ -2,6 +2,7 @@ import 'dart:ffi'; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; +import 'package:flutter/services.dart'; import 'package:hid_platform_interface/hid_platform_interface.dart'; import 'generated_bindings.dart'; @@ -65,13 +66,14 @@ class UsbDevice extends Device { final raw = _raw; if (raw != null) { _api.close(raw); + _raw = null; } } @override Stream read(int length, int duration) async* { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to clal open?"); final buf = calloc(length); var count = 0; while (isOpen) { diff --git a/hid_macos/lib/hid_macos.dart b/hid_macos/lib/hid_macos.dart index aeb7008..e7cd0c7 100644 --- a/hid_macos/lib/hid_macos.dart +++ b/hid_macos/lib/hid_macos.dart @@ -1,6 +1,7 @@ import 'dart:ffi'; import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:hid_platform_interface/hid_platform_interface.dart'; import 'package:ffi/ffi.dart'; import 'generated_bindings.dart'; @@ -49,6 +50,7 @@ class UsbDevice extends Device { @override Future open() async { + if (_raw != null) throw PlatformException(code: "rawDeviceAlreadyOpen", message: "Pointer to the USB device is not null, device already open"); final pointer = _api.open(vendorId, productId, serialNumber.toPointer()); if (pointer.address == nullptr.address) return false; final result = _api.set_nonblocking(pointer, 1); @@ -64,13 +66,14 @@ class UsbDevice extends Device { final raw = _raw; if (raw != null) { _api.close(raw); + _raw = null; } } @override Stream read(int length, int duration) async* { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(length); var count = 0; while (isOpen) { @@ -88,7 +91,7 @@ class UsbDevice extends Device { @override Future write(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); @@ -107,7 +110,7 @@ class UsbDevice extends Device { @override Future setFeature(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); @@ -126,7 +129,7 @@ class UsbDevice extends Device { @override Future getFeature(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); var count = 0; var offset = 0; diff --git a/hid_windows/lib/hid_windows.dart b/hid_windows/lib/hid_windows.dart index 1d34b27..848f042 100644 --- a/hid_windows/lib/hid_windows.dart +++ b/hid_windows/lib/hid_windows.dart @@ -1,6 +1,7 @@ import 'dart:ffi'; import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:hid_platform_interface/hid_platform_interface.dart'; import 'package:ffi/ffi.dart'; import 'generated_bindings.dart'; @@ -49,6 +50,7 @@ class UsbDevice extends Device { @override Future open() async { + if (_raw != null) throw PlatformException(code: "rawDeviceAlreadyOpen", message: "Pointer to the USB device is not null, device already open"); final pointer = _api.open(vendorId, productId, serialNumber.toPointer()); if (pointer.address == nullptr.address) return false; final result = _api.set_nonblocking(pointer, 1); @@ -64,13 +66,14 @@ class UsbDevice extends Device { final raw = _raw; if (raw != null) { _api.close(raw); + _raw = null; } } @override Stream read(int length, int duration) async* { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(length); var count = 0; while (isOpen) { @@ -88,7 +91,7 @@ class UsbDevice extends Device { @override Future write(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); @@ -107,7 +110,7 @@ class UsbDevice extends Device { @override Future setFeature(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); @@ -126,7 +129,7 @@ class UsbDevice extends Device { @override Future getFeature(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); var count = 0; var offset = 0; From 9eb6e5a4f7a9c6c492459d633de14ab2f5944891 Mon Sep 17 00:00:00 2001 From: Honza Filipsky Date: Mon, 25 Mar 2024 14:04:48 +0100 Subject: [PATCH 26/26] fix for closing the device multiple times --- hid_linux/lib/hid_linux.dart | 11 +++++++---- hid_macos/lib/hid_macos.dart | 11 +++++++---- hid_windows/lib/hid_windows.dart | 11 +++++++---- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/hid_linux/lib/hid_linux.dart b/hid_linux/lib/hid_linux.dart index 61a7b88..202ed8c 100644 --- a/hid_linux/lib/hid_linux.dart +++ b/hid_linux/lib/hid_linux.dart @@ -2,6 +2,7 @@ import 'dart:ffi'; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; +import 'package:flutter/services.dart'; import 'package:hid_platform_interface/hid_platform_interface.dart'; import 'generated_bindings.dart'; @@ -50,6 +51,7 @@ class UsbDevice extends Device { @override Future open() async { + if (_raw != null) throw PlatformException(code: "rawDeviceAlreadyOpen", message: "Pointer to the USB device is not null, device already open"); final pointer = _api.open(vendorId, productId, serialNumber.toPointer()); if (pointer.address == nullptr.address) return false; final result = _api.set_nonblocking(pointer, 1); @@ -65,13 +67,14 @@ class UsbDevice extends Device { final raw = _raw; if (raw != null) { _api.close(raw); + _raw = null; } } @override Stream read(int length, int duration) async* { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(length); var count = 0; while (isOpen) { @@ -89,7 +92,7 @@ class UsbDevice extends Device { @override Future write(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); @@ -108,7 +111,7 @@ class UsbDevice extends Device { @override Future setFeature(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); @@ -127,7 +130,7 @@ class UsbDevice extends Device { @override Future getFeature(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); var count = 0; var offset = 0; diff --git a/hid_macos/lib/hid_macos.dart b/hid_macos/lib/hid_macos.dart index aeb7008..e7cd0c7 100644 --- a/hid_macos/lib/hid_macos.dart +++ b/hid_macos/lib/hid_macos.dart @@ -1,6 +1,7 @@ import 'dart:ffi'; import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:hid_platform_interface/hid_platform_interface.dart'; import 'package:ffi/ffi.dart'; import 'generated_bindings.dart'; @@ -49,6 +50,7 @@ class UsbDevice extends Device { @override Future open() async { + if (_raw != null) throw PlatformException(code: "rawDeviceAlreadyOpen", message: "Pointer to the USB device is not null, device already open"); final pointer = _api.open(vendorId, productId, serialNumber.toPointer()); if (pointer.address == nullptr.address) return false; final result = _api.set_nonblocking(pointer, 1); @@ -64,13 +66,14 @@ class UsbDevice extends Device { final raw = _raw; if (raw != null) { _api.close(raw); + _raw = null; } } @override Stream read(int length, int duration) async* { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(length); var count = 0; while (isOpen) { @@ -88,7 +91,7 @@ class UsbDevice extends Device { @override Future write(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); @@ -107,7 +110,7 @@ class UsbDevice extends Device { @override Future setFeature(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); @@ -126,7 +129,7 @@ class UsbDevice extends Device { @override Future getFeature(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); var count = 0; var offset = 0; diff --git a/hid_windows/lib/hid_windows.dart b/hid_windows/lib/hid_windows.dart index 1d34b27..848f042 100644 --- a/hid_windows/lib/hid_windows.dart +++ b/hid_windows/lib/hid_windows.dart @@ -1,6 +1,7 @@ import 'dart:ffi'; import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:hid_platform_interface/hid_platform_interface.dart'; import 'package:ffi/ffi.dart'; import 'generated_bindings.dart'; @@ -49,6 +50,7 @@ class UsbDevice extends Device { @override Future open() async { + if (_raw != null) throw PlatformException(code: "rawDeviceAlreadyOpen", message: "Pointer to the USB device is not null, device already open"); final pointer = _api.open(vendorId, productId, serialNumber.toPointer()); if (pointer.address == nullptr.address) return false; final result = _api.set_nonblocking(pointer, 1); @@ -64,13 +66,14 @@ class UsbDevice extends Device { final raw = _raw; if (raw != null) { _api.close(raw); + _raw = null; } } @override Stream read(int length, int duration) async* { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(length); var count = 0; while (isOpen) { @@ -88,7 +91,7 @@ class UsbDevice extends Device { @override Future write(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); @@ -107,7 +110,7 @@ class UsbDevice extends Device { @override Future setFeature(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); @@ -126,7 +129,7 @@ class UsbDevice extends Device { @override Future getFeature(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); var count = 0; var offset = 0;