Skip to content

Commit

Permalink
libusb auto-detect
Browse files Browse the repository at this point in the history
  • Loading branch information
ctacke committed Sep 29, 2023
1 parent 9efb3f6 commit 5f1963d
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 36 deletions.
109 changes: 96 additions & 13 deletions Source/v2/Meadow.Cli/Commands/Current/Firmware/FirmwareWriteCommand.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using CliFx.Attributes;
using CliFx.Infrastructure;
using Meadow.Cli;
using Meadow.CLI.Core.Internals.Dfu;
using Meadow.Hcom;
using Meadow.LibUsb;
using Meadow.Software;
using Microsoft.Extensions.Logging;

Expand All @@ -26,11 +29,15 @@ public class FirmwareWriteCommand : BaseDeviceCommand<FirmwareWriteCommand>
public FirmwareType[]? Files { get; set; } = default!;

private FileManager FileManager { get; }
private ISettingsManager Settings { get; }

public FirmwareWriteCommand(FileManager fileManager, MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory)
private ILibUsbDevice? _libUsbDevice;

public FirmwareWriteCommand(ISettingsManager settingsManager, FileManager fileManager, MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory)
: base(connectionManager, loggerFactory)
{
FileManager = fileManager;
Settings = settingsManager;
}

public override async ValueTask ExecuteAsync(IConsole console)
Expand Down Expand Up @@ -67,35 +74,112 @@ public override async ValueTask ExecuteAsync(IConsole console)

if (UseDfu && Files.Contains(FirmwareType.OS))
{
// get a list of ports - it will not have our meadow in it (since it should be in DFU mode)
var initialPorts = await MeadowConnectionManager.GetSerialPorts();

// get the device's serial number via DFU - we'll need it to find the device after it resets
try
{
_libUsbDevice = GetLibUsbDeviceForCurrentEnvironment();
}
catch (Exception ex)
{
Logger.LogError(ex.Message);
return;
}

var serial = _libUsbDevice.GetDeviceSerialNumber();

// no connection is required here - in fact one won't exist
// unless maybe we add a "DFUConnection"?
await WriteOsWithDfu(package.GetFullyQualifiedPath(package.OSWithBootloader));

// TODO: if the user requested flashing more than the OS, we have to wait for a connection and then proceed with that
if (Files.Any(f => f != FirmwareType.OS))
try
{
await WriteOsWithDfu(package.GetFullyQualifiedPath(package.OSWithBootloader), serial);
}
catch (Exception ex)
{
var connection = ConnectionManager.GetCurrentConnection();
if (connection == null)
// TODO: scope this to the right exception type for Win 10 access violation thing
// TODO: catch the Win10 DFU error here and change the global provider configuration to "classic"
Settings.SaveSetting(SettingsManager.PublicSettings.LibUsb, "classic");
}

// now wait for a new serial port to appear
var ports = await MeadowConnectionManager.GetSerialPorts();
var retryCount = 0;

var newPort = ports.Except(initialPorts).FirstOrDefault();
while (newPort == null)
{
if (retryCount++ > 10)
{
Logger.LogError($"No connection path is defined");
return;
throw new Exception("New meadow device not found");
}
await Task.Delay(500);
ports = await MeadowConnectionManager.GetSerialPorts();
}

// configure the route to that port for the user
Settings.SaveSetting(SettingsManager.PublicSettings.Route, newPort);

var connection = ConnectionManager.GetCurrentConnection();
if (connection == null)
{
Logger.LogError($"No connection path is defined");
return;
}

var cancellationToken = console.RegisterCancellationHandler();

if (Files.Any(f => f != FirmwareType.OS))
{
await connection.WaitForMeadowAttach();

var cancellationToken = console.RegisterCancellationHandler();
await ExecuteCommand(connection, connection.Device, cancellationToken);
}
Logger.LogInformation($"Done.");

var deviceInfo = connection.Device.GetDeviceInfo(cancellationToken);

if (deviceInfo != null)
{
Logger.LogInformation($"Done.");
Logger.LogInformation(deviceInfo.ToString());
}
}
else
{
await base.ExecuteAsync(console);
}
}

private ILibUsbDevice GetLibUsbDeviceForCurrentEnvironment()
{
ILibUsbProvider provider;

// TODO: read the settings manager to decide which provider to use (default to non-classic)
var setting = Settings.GetAppSetting(SettingsManager.PublicSettings.LibUsb);
if (setting == "classic")
{
provider = new ClassicLibUsbProvider();
}
else
{
provider = new LibUsbProvider();
}

var devices = provider.GetDevicesInBootloaderMode();

switch (devices.Count)
{
case 0:
throw new Exception("No device found in bootloader mode");
case 1:
return devices[0];
default:
throw new Exception("Multiple devices found in bootloader mode. Disconnect all but one");
}
}

private async Task<FirmwarePackage?> GetSelectedPackage()
{
await FileManager.Refresh();
Expand Down Expand Up @@ -209,14 +293,13 @@ protected override async ValueTask ExecuteCommand(IMeadowConnection connection,
// TODO: if we're an F7 device, we need to reset
}

private async Task WriteOsWithDfu(string osFile)
private async Task WriteOsWithDfu(string osFile, string serialNumber)
{
/*
await DfuUtils.FlashFile(
osFile,
serialNumber,
logger: Logger,
format: DfuUtils.DfuFlashFormat.ConsoleOut);
*/
}
}

24 changes: 10 additions & 14 deletions Source/v2/Meadow.Cli/DFU/DfuUtils.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//using LibUsbDotNet.LibUsb;
using Meadow.Hcom;
using Meadow.LibUsb;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System.ComponentModel;
Expand All @@ -14,7 +13,7 @@ public static class DfuUtils
{
private static int _osAddress = 0x08000000;

public static string LastSerialNumber { get; private set; } = "";
// public static string LastSerialNumber { get; private set; } = "";

public enum DfuFlashFormat
{
Expand Down Expand Up @@ -64,7 +63,7 @@ private static void FormatDfuOutput(string logLine, ILogger? logger, DfuFlashFor
}
}

public static async Task<bool> FlashFile(string fileName, ILibUsbDevice device, ILogger? logger = null, DfuFlashFormat format = DfuFlashFormat.Percent)
public static async Task<bool> FlashFile(string fileName, string dfuSerialNumber, ILogger? logger = null, DfuFlashFormat format = DfuFlashFormat.Percent)
{
logger ??= NullLogger.Instance;

Expand All @@ -76,12 +75,10 @@ public static async Task<bool> FlashFile(string fileName, ILibUsbDevice device,

logger.LogInformation($"Flashing OS with {fileName}");

LastSerialNumber = device.GetDeviceSerialNumber();

var dfuUtilVersion = GetDfuUtilVersion();
var dfuUtilVersion = new System.Version(GetDfuUtilVersion());
logger.LogDebug("Detected OS: {os}", RuntimeInformation.OSDescription);

if (string.IsNullOrEmpty(dfuUtilVersion))
if (dfuUtilVersion == null)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Expand All @@ -93,24 +90,23 @@ public static async Task<bool> FlashFile(string fileName, ILibUsbDevice device,
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
logger.LogError("dfu-util not found - install using package manager, for example: `apt install dfu-util`");
logger.LogError("dfu-util not found - install using package manager, for example: `apt install dfu-util` or the equivalent for your Linux distribution");
}
return false;
}
else if (dfuUtilVersion != "0.10")
else if (dfuUtilVersion.CompareTo(new System.Version("0.11")) < 0)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
logger.LogError("dfu-util update required. To install, run in administrator mode: meadow install dfu-util");
logger.LogError("dfu-util update required. To update, run in administrator mode: `meadow install dfu-util`");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
logger.LogError("dfu-util update required. To install, run: brew upgrade dfu-util");
logger.LogError("dfu-util update required. To update, run: `brew upgrade dfu-util`");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
if (dfuUtilVersion != "0.9")
return false;
logger.LogError("dfu-util update required. To update , run: `apt upgrade dfu-util` or the equivalent for your Linux distribution");
}
else
{
Expand All @@ -120,7 +116,7 @@ public static async Task<bool> FlashFile(string fileName, ILibUsbDevice device,

try
{
var args = $"-a 0 -S {LastSerialNumber} -D \"{fileName}\" -s {_osAddress}:leave";
var args = $"-a 0 -S {dfuSerialNumber} -D \"{fileName}\" -s {_osAddress}:leave";

await RunDfuUtil(args, logger, format);
}
Expand Down
2 changes: 1 addition & 1 deletion Source/v2/Meadow.Cli/ISettingsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
public interface ISettingsManager
{
void DeleteSetting(string setting);
string? GetAppSetting(string name);
string? GetAppSetting(string name, string? defaultValue = null);
Dictionary<string, string> GetPublicSettings();
string? GetSetting(string setting);
void SaveSetting(string setting, string value);
Expand Down
9 changes: 4 additions & 5 deletions Source/v2/Meadow.Cli/SettingsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ private class Settings
public static class PublicSettings
{
public const string Route = "route";
public const string LibUsb = "libusb";
}

private readonly string Path = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WildernessLabs", "cli.settings");
Expand Down Expand Up @@ -91,16 +92,14 @@ public void SaveSetting(string setting, string value)
File.WriteAllText(Path, json);
}

public string? GetAppSetting(string name)
public string? GetAppSetting(string name, string? defaultValue = null)
{
if (ConfigurationManager.AppSettings.AllKeys.Contains(name))
{
return ConfigurationManager.AppSettings[name];
}
else
{
throw new ArgumentException($"{name} setting not found.");
}

return defaultValue;
}

private Settings GetSettings()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void DeleteSetting(string setting)
}
}

public string? GetAppSetting(string name)
public string? GetAppSetting(string name, string? defaultValue = null)
{
throw new NotImplementedException();
}
Expand Down
2 changes: 1 addition & 1 deletion Source/v2/Meadow.UsbLib/LibUsbDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Meadow.LibUsb;

public class UsbLibProvider : ILibUsbProvider
public class LibUsbProvider : ILibUsbProvider
{
private const int _osAddress = 0x08000000;
// public const string UsbStmName = "STM32 BOOTLOADER";
Expand Down
2 changes: 1 addition & 1 deletion Source/v2/Meadow.UsbLibClassic/ClassicLibUsbDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Meadow.LibUsb;

public class ClassicUsbLibProvider : ILibUsbProvider
public class ClassicLibUsbProvider : ILibUsbProvider
{
// private const int _osAddress = 0x08000000;
private const string UsbStmName = "STM32 BOOTLOADER";
Expand Down

0 comments on commit 5f1963d

Please sign in to comment.