private static async Task PerformFirmwareUpdate()
        {
            // Notify clients that we are now installing a firmware update
            using (await Model.Provider.AccessReadWriteAsync())
            {
                Model.Provider.Get.State.Status = MachineStatus.Updating;
            }

            // Get the CRC16 checksum of the firmware binary
            byte[] firmwareBlob = new byte[_firmwareStream.Length];
            await _firmwareStream.ReadAsync(firmwareBlob, 0, (int)_firmwareStream.Length);

            ushort crc16 = CRC16.Calculate(firmwareBlob);

            // Send the IAP binary to the firmware
            Console.Write("[info] Flashing IAP binary");
            bool dataSent;

            do
            {
                dataSent = DataTransfer.WriteIapSegment(_iapStream);
                DataTransfer.PerformFullTransfer();
                Console.Write('.');
            }while (dataSent);
            Console.WriteLine();

            _iapStream.Close();
            _iapStream = null;

            // Start the IAP binary
            DataTransfer.StartIap();

            // Send the firmware binary to the IAP program
            int numRetries = 0;

            do
            {
                if (numRetries != 0)
                {
                    Console.WriteLine("Error");
                }

                Console.Write("[info] Flashing RepRapFirmware");
                _firmwareStream.Seek(0, SeekOrigin.Begin);
                while (DataTransfer.FlashFirmwareSegment(_firmwareStream))
                {
                    Console.Write('.');
                }
                Console.WriteLine();

                Console.Write("[info] Verifying checksum... ");
            }while (++numRetries < 3 && !DataTransfer.VerifyFirmwareChecksum(_firmwareStream.Length, crc16));

            if (numRetries == 3)
            {
                Console.WriteLine("Error");

                // Failed to flash the firmware
                await Utility.Logger.LogOutput(MessageType.Error, "Could not flash the firmware binary after 3 attempts. Please install it manually via bossac.");

                Program.CancelSource.Cancel();
            }
            else
            {
                Console.WriteLine("OK");

                // Wait for the IAP binary to restart the controller
                DataTransfer.WaitForIapReset();
                Console.WriteLine("[info] Firmware update successful!");
            }

            _firmwareStream.Close();
            _firmwareStream = null;

            using (_firmwareUpdateLock.Lock())
            {
                _firmwareUpdateRequest.SetResult(null);
                _firmwareUpdateRequest = null;
            }
        }
        /// <summary>
        /// Perform a full data transfer synchronously
        /// </summary>
        /// <param name="mustSucceed">Keep retrying until the transfer succeeds</param>
        /// <returns>Whether new data could be transferred</returns>
        public static bool PerformFullTransfer(bool mustSucceed = true)
        {
            _lastTransferNumber = _rxHeader.SequenceNumber;

            // Reset RX transfer header
            _rxHeader.FormatCode      = Communication.Consts.InvalidFormatCode;
            _rxHeader.NumPackets      = 0;
            _rxHeader.ProtocolVersion = 0;
            _rxHeader.DataLength      = 0;
            _rxHeader.ChecksumData    = 0;
            _rxHeader.ChecksumHeader  = 0;

            // Set up TX transfer header
            _txHeader.NumPackets = _packetId;
            _txHeader.SequenceNumber++;
            _txHeader.DataLength   = (ushort)_txPointer;
            _txHeader.ChecksumData = CRC16.Calculate(_txBuffers[_txBufferIndex].Slice(0, _txPointer).Span);
            MemoryMarshal.Write(_txHeaderBuffer.Span, ref _txHeader);
            _txHeader.ChecksumHeader = CRC16.Calculate(_txHeaderBuffer.Slice(0, Marshal.SizeOf(_txHeader) - Marshal.SizeOf(typeof(ushort))).Span);
            MemoryMarshal.Write(_txHeaderBuffer.Span, ref _txHeader);

            do
            {
                try
                {
                    // Exchange transfer headers. This also deals with transfer responses
                    if (!ExchangeHeader())
                    {
                        continue;
                    }

                    // Exchange data if there is anything to transfer
                    if ((_rxHeader.DataLength != 0 || _txPointer != 0) && !ExchangeData())
                    {
                        continue;
                    }

                    // Deal with timeouts
                    if (_hadTimeout)
                    {
                        using (Model.Provider.AccessReadWrite())
                        {
                            if (Model.Provider.Get.State.Status == MachineStatus.Off)
                            {
                                Model.Provider.Get.State.Status = MachineStatus.Idle;
                            }
                            Model.Provider.Get.Messages.Add(new Message(MessageType.Success, "Connection to Duet established"));
                            Console.WriteLine("[info] Connection to Duet established");
                        }
                        _hadTimeout = _resetting = false;
                    }

                    // Deal with the first transmission
                    if (!_started)
                    {
                        _lastTransferNumber = (ushort)(_rxHeader.SequenceNumber - 1);
                        _started            = true;
                    }

                    // Transfer OK
                    Interlocked.Increment(ref _numMeasuredTransfers);
                    _txBufferIndex = (_txBufferIndex == 0) ? 1 : 0;
                    _rxPointer     = _txPointer = 0;
                    _packetId      = 0;

                    // Deal with reset requests
                    if (_resetting)
                    {
                        _waitingForFirstTransfer = _hadTimeout = true;
                        return(PerformFullTransfer(mustSucceed));
                    }
                    return(true);
                }
                catch (OperationCanceledException e)
                {
                    if (!Program.CancelSource.IsCancellationRequested && !_hadTimeout && _started)
                    {
                        // If this is the first unexpected timeout event, report it unless the firmware is being updated
                        using (Model.Provider.AccessReadWrite())
                        {
                            if (Model.Provider.Get.State.Status == MachineStatus.Updating)
                            {
                                _waitingForFirstTransfer        = _hadTimeout = true;
                                Model.Provider.Get.State.Status = MachineStatus.Off;
                                Model.Provider.Get.Messages.Add(new Message(MessageType.Warning, $"Lost connection to Duet ({e.Message})"));
                                Console.WriteLine($"[warn] Lost connection to Duet ({e.Message})");
                            }
                        }
                    }
                }
            } while (mustSucceed && !Program.CancelSource.IsCancellationRequested);

            return(false);
        }
        /// <summary>
        /// Perform the firmware update internally
        /// </summary>
        /// <returns>Asynchronous task</returns>
        private static async Task PerformFirmwareUpdate()
        {
            using (await Model.Provider.AccessReadWriteAsync())
            {
                Model.Provider.Get.State.Status = MachineStatus.Updating;
            }
            DataTransfer.Updating = true;

            try
            {
                // Get the CRC16 checksum of the firmware binary
                byte[] firmwareBlob = new byte[_firmwareStream.Length];
                await _firmwareStream.ReadAsync(firmwareBlob, 0, (int)_firmwareStream.Length);

                ushort crc16 = CRC16.Calculate(firmwareBlob);

                // Send the IAP binary to the firmware
                _logger.Info("Flashing IAP binary");
                bool dataSent;
                do
                {
                    dataSent = DataTransfer.WriteIapSegment(_iapStream);
                    DataTransfer.PerformFullTransfer();
                    if (_logger.IsDebugEnabled)
                    {
                        Console.Write('.');
                    }
                }while (dataSent);
                if (_logger.IsDebugEnabled)
                {
                    Console.WriteLine();
                }

                // Start the IAP binary
                DataTransfer.StartIap();

                // Send the firmware binary to the IAP program
                int numRetries = 0;
                do
                {
                    if (numRetries != 0)
                    {
                        _logger.Error("Firmware checksum verification failed");
                    }

                    _logger.Info("Flashing RepRapFirmware");
                    _firmwareStream.Seek(0, SeekOrigin.Begin);
                    while (DataTransfer.FlashFirmwareSegment(_firmwareStream))
                    {
                        if (_logger.IsDebugEnabled)
                        {
                            Console.Write('.');
                        }
                    }
                    if (_logger.IsDebugEnabled)
                    {
                        Console.WriteLine();
                    }

                    _logger.Info("Verifying checksum");
                }while (++numRetries < 3 && !DataTransfer.VerifyFirmwareChecksum(_firmwareStream.Length, crc16));

                if (numRetries == 3)
                {
                    // Failed to flash the firmware
                    await Logger.LogOutput(MessageType.Error, "Could not flash the firmware binary after 3 attempts. Please install it manually via bossac.");

                    Program.CancelSource.Cancel();
                }
                else
                {
                    // Wait for the IAP binary to restart the controller
                    await DataTransfer.WaitForIapReset();

                    _logger.Info("Firmware update successful");
                }
            }
            finally
            {
                DataTransfer.Updating = false;
                // Machine state is reset when the next status response is processed
            }
        }