mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-04-20 22:13:48 +00:00
92 lines
3.1 KiB
Dart
92 lines
3.1 KiB
Dart
import 'dart:io';
|
|
|
|
/// Queries the macOS IOKit registry via [ioreg] to build a map of serial port
|
|
/// callout device paths to human-readable USB device names.
|
|
///
|
|
/// The [flserial] native library uses the deprecated [IOUSBDevice] IOKit class
|
|
/// to resolve device names, but macOS 10.15+ renamed it to [IOUSBHostDevice].
|
|
/// As a result flserial always returns "n/a" for USB product/vendor info on
|
|
/// modern macOS. This utility bypasses that limitation by invoking ioreg
|
|
/// directly and parsing its output.
|
|
///
|
|
/// Returns a Map of e.g. `"/dev/cu.usbmodem1101"` → `"Nordic NRF52 DK"`.
|
|
/// Devices without a USB product name are not included in the map.
|
|
Future<Map<String, String>> queryMacOsUsbDeviceNames() async {
|
|
assert(Platform.isMacOS);
|
|
try {
|
|
final result = await Process.run('ioreg', [
|
|
'-r',
|
|
'-c',
|
|
'IOUSBHostDevice',
|
|
'-l',
|
|
], stdoutEncoding: const SystemEncoding());
|
|
if (result.exitCode != 0) return const <String, String>{};
|
|
return _parseIoregOutput(result.stdout as String);
|
|
} catch (_) {
|
|
return const <String, String>{};
|
|
}
|
|
}
|
|
|
|
Map<String, String> _parseIoregOutput(String output) {
|
|
final lines = output.split('\n');
|
|
final result = <String, String>{};
|
|
|
|
// We accumulate the current device block's properties.
|
|
// A new block starts at a line beginning with "+-o " which indicates a
|
|
// top-level IOUSBHostDevice entry in the ioreg tree.
|
|
String? currentVendor;
|
|
String? currentProduct;
|
|
final List<String> currentPorts = <String>[];
|
|
|
|
void flushBlock() {
|
|
if (currentPorts.isNotEmpty &&
|
|
(currentVendor != null || currentProduct != null)) {
|
|
final parts = <String>[
|
|
if (currentVendor != null && currentVendor!.isNotEmpty) currentVendor!,
|
|
if (currentProduct != null && currentProduct!.isNotEmpty)
|
|
currentProduct!,
|
|
];
|
|
final name = parts.join(' ');
|
|
for (final port in currentPorts) {
|
|
result[port] = name;
|
|
}
|
|
}
|
|
currentVendor = null;
|
|
currentProduct = null;
|
|
currentPorts.clear();
|
|
}
|
|
|
|
for (final line in lines) {
|
|
// A new top-level device block begins here.
|
|
if (line.startsWith('+-o ')) {
|
|
flushBlock();
|
|
continue;
|
|
}
|
|
// USB Product Name (appears at multiple depths in the tree, first wins)
|
|
final productMatch = _kProductName.firstMatch(line);
|
|
if (productMatch != null && currentProduct == null) {
|
|
currentProduct = productMatch.group(1)?.trim();
|
|
continue;
|
|
}
|
|
// USB Vendor Name
|
|
final vendorMatch = _kVendorName.firstMatch(line);
|
|
if (vendorMatch != null && currentVendor == null) {
|
|
currentVendor = vendorMatch.group(1)?.trim();
|
|
continue;
|
|
}
|
|
// IOCalloutDevice — the /dev/cu.xxx path our app uses
|
|
final calloutMatch = _kCalloutDevice.firstMatch(line);
|
|
if (calloutMatch != null) {
|
|
final port = calloutMatch.group(1)?.trim();
|
|
if (port != null && port.isNotEmpty) {
|
|
currentPorts.add(port);
|
|
}
|
|
}
|
|
}
|
|
flushBlock();
|
|
return result;
|
|
}
|
|
|
|
final RegExp _kProductName = RegExp(r'"USB Product Name" = "([^"]*)"');
|
|
final RegExp _kVendorName = RegExp(r'"USB Vendor Name" = "([^"]*)"');
|
|
final RegExp _kCalloutDevice = RegExp(r'"IOCalloutDevice" = "([^"]*)"');
|