/// <summary> /// Attempts to deploy an SREC (.hex) file to the connected nanoFramework device. /// </summary> /// <param name="srecFile">Storage file with the SREC (.hex) file</param> /// <param name="entrypoint">Out parameter that is set to the entry point address for the given SREC file</param> /// <returns>Returns false if the deployment fails, true otherwise /// Possible exceptions: MFFileNotFoundException, MFDeviceNoResponseException, MFUserExitException /// </returns> public async Task <Tuple <uint, bool> > DeployAsync(StorageFile srecFile, CancellationToken cancellationToken, IProgress <ProgressReport> progress = null) { uint entryPoint = 0; List <SRecordFile.Block> blocks = new List <SRecordFile.Block>(); if (!srecFile.IsAvailable) { throw new FileNotFoundException(srecFile.Path); } if (DebugEngine == null) { throw new NanoFrameworkDeviceNoResponseException(); } // make sure we know who we are talking to if (await CheckForMicroBooterAsync(cancellationToken)) { var reply = await DeploySRECAsync(srecFile, cancellationToken); // check if request was successful if (reply.Item2) { entryPoint = reply.Item1; return(new Tuple <uint, bool>(entryPoint, true)); } else { return(new Tuple <uint, bool>(0, false)); } } await DebugEngine.ConnectAsync(1000, false, ConnectionSource.Unknown); var parseResult = await SRecordFile.ParseAsync(srecFile); entryPoint = parseResult.Item1; blocks = parseResult.Item2; if (blocks.Count > 0) { long total = 0; long value = 0; for (int i = 0; i < blocks.Count; i++) { total += (blocks[i] as SRecordFile.Block).data.Length; } await PrepareForDeployAsync(blocks, cancellationToken, progress); foreach (SRecordFile.Block block in blocks) { long len = block.data.Length; uint addr = block.address; // check if cancellation was requested if (cancellationToken.IsCancellationRequested) { throw new NanoUserExitException(); } block.data.Seek(0, SeekOrigin.Begin); progress?.Report(new ProgressReport(0, total, string.Format("Erasing sector 0x{0:x08}", block.address))); // the clr requires erase before writing var eraseResult = DebugEngine.EraseMemory(block.address, (uint)len); if (!eraseResult.Success) { return(new Tuple <uint, bool>(0, false)); } while (len > 0) { // check if cancellation was requested if (cancellationToken.IsCancellationRequested) { throw new NanoUserExitException(); } uint buflen = len > DebugEngine.WireProtocolPacketSize ? DebugEngine.WireProtocolPacketSize : (uint)len; byte[] data = new byte[buflen]; if (block.data.Read(data, 0, (int)buflen) <= 0) { return(new Tuple <uint, bool>(0, false)); } var writeResult = DebugEngine.WriteMemory(addr, data); if (writeResult.Success == false) { return(new Tuple <uint, bool>(0, false)); } value += buflen; addr += (uint)buflen; len -= buflen; progress?.Report(new ProgressReport(value, total, string.Format("Deploying {0}...", srecFile.Name))); } } } return(new Tuple <uint, bool>(entryPoint, true)); }
/// <summary> /// Erases the deployment sectors of the connected .Net Micro Framework device /// </summary> /// <param name="options">Identifies which areas are to be erased</param> /// <param name="cancellationToken">Cancellation token to allow caller to cancel task</param> /// <param name="progress">Progress report of execution</param> /// <returns>Returns false if the erase fails, true otherwise /// Possible exceptions: MFUserExitException, MFDeviceNoResponseException /// </returns> public async Task <bool> EraseAsync(EraseOptions options, CancellationToken cancellationToken, IProgress <ProgressReport> progress = null) { bool ret = false; bool fReset = false; if (DebugEngine == null) { throw new NanoFrameworkDeviceNoResponseException(); } if (!await DebugEngine.ConnectAsync(500, true)) { throw new NanoFrameworkDeviceNoResponseException(); } if (!IsClrDebuggerEnabled || 0 != (options & EraseOptions.Firmware)) { fReset = (Ping() == PingConnectionType.nanoCLR); if (!await ConnectToNanoBooterAsync(cancellationToken)) { throw new NanoBooterConnectionFailureException(); } } var reply = DebugEngine.GetFlashSectorMap(); if (reply == null) { throw new NanoFrameworkDeviceNoResponseException(); } Commands.Monitor_Ping.Reply ping = DebugEngine.GetConnectionSource(); ret = true; long total = 0; long value = 0; bool isConnectedToCLR = ((ping != null) && (ping.m_source == Commands.Monitor_Ping.c_Ping_Source_NanoCLR)); if (isConnectedToCLR) { DebugEngine.PauseExecution(); } List <Commands.Monitor_FlashSectorMap.FlashSectorData> eraseSectors = new List <Commands.Monitor_FlashSectorMap.FlashSectorData>(); foreach (Commands.Monitor_FlashSectorMap.FlashSectorData flashSectorData in reply) { if (cancellationToken.IsCancellationRequested) { throw new NanoUserExitException(); } switch (flashSectorData.m_flags & Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_MASK) { case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_DEPLOYMENT: if (EraseOptions.Deployment == (options & EraseOptions.Deployment)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_UPDATE: if (EraseOptions.UpdateStorage == (options & EraseOptions.UpdateStorage)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_SIMPLE_A: case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_SIMPLE_B: if (EraseOptions.SimpleStorage == (options & EraseOptions.SimpleStorage)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_STORAGE_A: case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_STORAGE_B: if (EraseOptions.UserStorage == (options & EraseOptions.UserStorage)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_FS: if (EraseOptions.FileSystem == (options & EraseOptions.FileSystem)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_CONFIG: if (EraseOptions.Configuration == (options & EraseOptions.Configuration)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_CODE: if (EraseOptions.Firmware == (options & EraseOptions.Firmware)) { eraseSectors.Add(flashSectorData); total++; } break; } } foreach (Commands.Monitor_FlashSectorMap.FlashSectorData flashSectorData in eraseSectors) { progress?.Report(new ProgressReport(value, total, string.Format("Erasing sector 0x{0:x08}", flashSectorData.m_StartAddress))); var eraseResult = DebugEngine.EraseMemory(flashSectorData.m_StartAddress, (flashSectorData.m_NumBlocks * flashSectorData.m_BytesPerBlock)); ret &= eraseResult.Success; value++; } // reset if we specifically entered nanoBooter to erase if (fReset) { DebugEngine.ExecuteMemory(0); } // reboot if we are talking to the CLR if (isConnectedToCLR) { progress?.Report(new ProgressReport(0, 0, "Rebooting...")); var rebootOptions = RebootOptions.ClrOnly; // if we've just erase the deployment area there is no more app so the execution engine can't be gracefully stopped if ((options & EraseOptions.Deployment) == EraseOptions.Deployment) { rebootOptions |= RebootOptions.NoShutdown; } DebugEngine.RebootDevice(rebootOptions); } return(ret); }
/// <summary> /// Attempts to deploy an SREC (.hex) file to the connected .Net Micro Framework device. The /// signatureFile is used to validate the image once it has been deployed to the device. If /// the signature does not match the image is erased. /// </summary> /// <param name="srecFile">Storage file with the SREC (.hex) file</param> /// <param name="signatureFile">Storage file with the signature file (.sig) for the corresponding SREC file in the srecFile parameter</param> /// <param name="entrypoint">Out parameter that is set to the entry point address for the given SREC file</param> /// <returns>Returns false if the deployment fails, true otherwise /// Possible exceptions: MFFileNotFoundException, MFDeviceNoResponseException, MFUserExitException /// </returns> public async Task <Tuple <uint, bool> > DeployAsync(StorageFile srecFile, StorageFile signatureFile, CancellationToken cancellationToken, IProgress <ProgressReport> progress = null) { uint entryPoint = 0; if (!srecFile.IsAvailable) { throw new FileNotFoundException(srecFile.Path); } if (DebugEngine == null) { throw new MFDeviceNoResponseException(); } // make sure we know who we are talking to if (await CheckForMicroBooterAsync(cancellationToken)) { var reply = await DeploySRECAsync(srecFile, cancellationToken); // check if request was successful if (reply.Item2) { entryPoint = reply.Item1; return(new Tuple <uint, bool>(entryPoint, true)); } else { return(new Tuple <uint, bool>(0, false)); } } await DebugEngine.ConnectAsync(1, 1000, false, ConnectionSource.Unknown); List <SRecordFile.Block> blocks = new List <SRecordFile.Block>(); entryPoint = await SRecordFile.ParseAsync(srecFile, blocks, signatureFile != null?signatureFile : null); if (blocks.Count > 0) { long total = 0; long value = 0; for (int i = 0; i < blocks.Count; i++) { total += (blocks[i] as SRecordFile.Block).data.Length; } await PrepareForDeployAsync(blocks, cancellationToken, progress); foreach (SRecordFile.Block block in blocks) { long len = block.data.Length; uint addr = block.address; // check if cancellation was requested if (cancellationToken.IsCancellationRequested) { throw new MFUserExitException(); } block.data.Seek(0, SeekOrigin.Begin); progress?.Report(new ProgressReport(0, total, string.Format("Erasing sector 0x{0:x08}", block.address))); // the clr requires erase before writing if (!await DebugEngine.EraseMemoryAsync(block.address, (uint)len)) { return(new Tuple <uint, bool>(0, false)); } while (len > 0) { // check if cancellation was requested if (cancellationToken.IsCancellationRequested) { throw new MFUserExitException(); } int buflen = len > 1024 ? 1024 : (int)len; byte[] data = new byte[buflen]; if (block.data.Read(data, 0, buflen) <= 0) { return(new Tuple <uint, bool>(0, false)); } if (!await DebugEngine.WriteMemoryAsync(addr, data)) { return(new Tuple <uint, bool>(0, false)); } value += buflen; addr += (uint)buflen; len -= buflen; progress?.Report(new ProgressReport(value, total, string.Format("Deploying {0}...", srecFile.Name))); } if (DebugEngine.ConnectionSource != ConnectionSource.TinyCLR) { byte[] emptySig = new byte[128]; progress?.Report(new ProgressReport(value, total, "Checking Signature...")); if (!await DebugEngine.CheckSignatureAsync(((block.signature == null || block.signature.Length == 0) ? emptySig : block.signature), 0)) { throw new MFSignatureFailureException(signatureFile); } } } } return(new Tuple <uint, bool>(entryPoint, true)); }
/// <summary> /// Attempt to establish a connection with nanoBooter (with reboot if necessary) /// </summary> /// <returns>true connection was made, false otherwise</returns> public async Task <bool> ConnectToNanoBooterAsync(CancellationToken cancellationToken) { bool ret = false; if (!await DebugEngine.ConnectAsync(1000, true)) { return(false); } if (DebugEngine != null) { if (DebugEngine.ConnectionSource == ConnectionSource.nanoBooter) { return(true); } DebugEngine.RebootDevice(RebootOptions.EnterBootloader); ///////////////////////////////////////// // FIXME ///////////////////////////////////////// //// nanoBooter is only com port so //if (Transport == TransportType.TcpIp) //{ // _DBG.PortDefinition pdTmp = m_port; // Disconnect(); // try // { // m_port = m_portNanoBooter; // // digi takes forever to reset // if (!Connect(60000, true)) // { // Console.WriteLine(Properties.Resources.ErrorUnableToConnectToNanoBooterSerial); // return false; // } // } // finally // { // m_port = pdTmp; // } //} bool fConnected = false; for (int i = 0; i < 40; i++) { // check if cancellation was requested if (cancellationToken.IsCancellationRequested) { return(false); } if (fConnected = await DebugEngine.ConnectAsync(1000, true, ConnectionSource.Unknown)) { Commands.Monitor_Ping.Reply reply = DebugEngine.GetConnectionSource(); ret = (reply.m_source == Commands.Monitor_Ping.c_Ping_Source_NanoBooter); break; } } if (!fConnected) { //Debug.WriteLine("Unable to connect to NanoBooter"); } } return(ret); }
/// <summary> /// Erases the deployment sectors of the connected .Net Micro Framework device /// </summary> /// <param name="options">Identifies which areas are to be erased</param> /// <param name="cancellationToken">Cancellation token to allow caller to cancel task</param> /// <param name="progress">Progress report of execution</param> /// <returns>Returns false if the erase fails, true otherwise /// Possible exceptions: MFUserExitException, MFDeviceNoResponseException /// </returns> public async Task <bool> EraseAsync(EraseOptions options, CancellationToken cancellationToken, IProgress <ProgressReport> progress = null) { bool ret = false; bool fReset = false; if (DebugEngine == null) { throw new MFDeviceNoResponseException(); } if (!await DebugEngine.ConnectAsync(2, 500, true)) { throw new MFDeviceNoResponseException(); } if (!IsClrDebuggerEnabled || 0 != (options & EraseOptions.Firmware)) { fReset = (await PingAsync() == PingConnectionType.TinyCLR); if (!await ConnectToTinyBooterAsync(cancellationToken)) { throw new MFTinyBooterConnectionFailureException(); } } var reply = await DebugEngine.GetFlashSectorMapAsync(); if (reply == null) { throw new MFDeviceNoResponseException(); } Commands.Monitor_Ping.Reply ping = await DebugEngine.GetConnectionSourceAsync(); ret = true; long total = 0; long value = 0; bool isConnectedToCLR = ((ping != null) && (ping.m_source == Commands.Monitor_Ping.c_Ping_Source_TinyCLR)); if (isConnectedToCLR) { await DebugEngine.PauseExecutionAsync(); } List <Commands.Monitor_FlashSectorMap.FlashSectorData> eraseSectors = new List <Commands.Monitor_FlashSectorMap.FlashSectorData>(); foreach (Commands.Monitor_FlashSectorMap.FlashSectorData flashSectorData in reply) { if (cancellationToken.IsCancellationRequested) { throw new MFUserExitException(); } switch (flashSectorData.m_flags & Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_MASK) { case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_DEPLOYMENT: if (EraseOptions.Deployment == (options & EraseOptions.Deployment)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_UPDATE: if (EraseOptions.UpdateStorage == (options & EraseOptions.UpdateStorage)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_SIMPLE_A: case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_SIMPLE_B: if (EraseOptions.SimpleStorage == (options & EraseOptions.SimpleStorage)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_STORAGE_A: case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_STORAGE_B: if (EraseOptions.UserStorage == (options & EraseOptions.UserStorage)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_FS: if (EraseOptions.FileSystem == (options & EraseOptions.FileSystem)) { eraseSectors.Add(flashSectorData); total++; } break; case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_CONFIG: case Commands.Monitor_FlashSectorMap.c_MEMORY_USAGE_CODE: if (EraseOptions.Firmware == (options & EraseOptions.Firmware)) { eraseSectors.Add(flashSectorData); total++; } break; } } foreach (Commands.Monitor_FlashSectorMap.FlashSectorData flashSectorData in eraseSectors) { progress?.Report(new ProgressReport(value, total, string.Format("Erasing sector 0x{0:x08}", flashSectorData.m_address))); ret &= await DebugEngine.EraseMemoryAsync(flashSectorData.m_address, flashSectorData.m_size); value++; } // reset if we specifically entered tinybooter for the erase if (fReset) { await DebugEngine.ExecuteMemoryAsync(0); } // reboot if we are talking to the clr if (isConnectedToCLR) { progress?.Report(new ProgressReport(0, 0, "Rebooting...")); await DebugEngine.RebootDeviceAsync(RebootOption.RebootClrOnly); } return(ret); }