109 , datagramService_(if_datagram_service)
116 return allocate_and_call(
122 return datagramService_;
130 const NodeHandle &dst()
135 const BootloaderRequest *request()
142 if (responseDatagram_)
144 LOG_ERROR(
"Multiple response datagrams arrived from the "
146 responseDatagram_->unref();
148 responseDatagram_ = datagram;
160 if (!
message()->data()->request_reboot)
162 return call_immediately(
STATE(send_pip_request));
169 payload.push_back(
message()->data()->memory_space);
171 message()->data()->dst, payload);
177 return wait_and_call(
STATE(reboot_dg_done));
182 uint32_t dg_result = dgClient_->
result();
183 LOG(
INFO,
"Reboot command result: %04x", dg_result);
184 return call_immediately(
STATE(send_pip_request));
189 if (
message()->data()->skip_pip) {
190 LOG(
INFO,
"Skipping PIP request. Using streams.");
191 return call_immediately(
STATE(bootload_using_stream));
194 return wait_and_call(
STATE(pip_response));
199 if (pipClient_.
error_code() != PIPClient::OPERATION_SUCCESS) {
201 "PIP request failed. Error code: %" PRIx32
". Using streams.",
203 return call_immediately(
STATE(bootload_using_stream));
205 if (pipClient_.
response() & Defs::STREAM) {
206 LOG(
INFO,
"Using streams for bootloading.");
207 return call_immediately(
STATE(bootload_using_stream));
209 LOG(
INFO,
"Using datagrams for bootloading.");
210 return call_immediately(
STATE(bootload_using_datagrams));
214 Action bootload_using_stream()
221 payload.push_back(
message()->data()->offset >> 24);
222 payload.push_back(
message()->data()->offset >> 16);
223 payload.push_back(
message()->data()->offset >> 8);
224 payload.push_back(
message()->data()->offset);
225 payload.push_back(
message()->data()->memory_space);
226 localStreamId_ = allocate_local_stream_id();
227 payload.push_back(localStreamId_);
229 message()->data()->dst, payload);
233 responseDatagram_ =
nullptr;
235 register_write_response_handler();
236 return wait_and_call(
STATE(write_request_sent));
254 if (datagram->
dst != parent_->node() ||
256 parent_->dst(), datagram->
src) ||
257 datagram->
payload.size() < 6 ||
259 ((datagram->
payload[1] & 0xF4) !=
278 Action write_request_sent()
280 uint32_t dg_result = dgClient_->
result();
283 if (responseDatagram_)
286 return call_immediately(
STATE(wait_for_response_dg));
288 else if (dg_result & DatagramClient::OPERATION_SUCCESS)
291 return sleep_and_call(&timer_,
293 STATE(wait_for_response_dg));
298 string error_details;
299 if (dg_result & DatagramClient::DST_NOT_FOUND)
301 error_details =
"Destination node not found.";
303 else if (dg_result & DatagramClient::TIMEOUT)
305 error_details =
"Timeout waiting for destination node to ACK "
306 "the write request datagram.";
308 else if (dg_result & DatagramClient::PERMANENT_ERROR)
310 error_details =
"Write request: Permanent error.";
312 else if (dg_result & DatagramClient::RESEND_OK)
315 "Write request: Temporary error, please try again.";
317 return return_error(dg_result & 0xffff, error_details);
321 Action wait_for_response_dg()
324 if (!responseDatagram_)
326 return return_error(DatagramClient::RESEND_OK,
327 "Timed out waiting for response datagram.");
329 unregister_write_response_handler();
330 const auto &payload = responseDatagram_->data()->payload;
331 if ((payload[1] & 0xFC) ==
335 uint16_t error_code = DatagramClient::PERMANENT_ERROR;
336 unsigned error_ofs = 6;
341 LOG(
WARNING,
"payload length %" PRIdPTR
" error offset %u data %02x %02x",
342 payload.size(), error_ofs, payload[error_ofs],
343 payload[error_ofs + 1]);
345 (payload[error_ofs] << 8) | ((uint8_t)payload[error_ofs + 1]);
348 error_code,
"Write rejected " + payload.substr(error_ofs));
350 else if ((payload[1] & 0xFC) ==
354 responseDatagram_->unref();
355 responseDatagram_ =
nullptr;
356 return call_immediately(
STATE(initiate_stream));
362 DatagramClient::PERMANENT_ERROR,
"internal inconsistency.");
366 uint8_t allocate_local_stream_id()
371 Action return_error(uint16_t error_code,
const string &error_details)
373 unregister_write_response_handler();
374 message()->data()->response->error_code = error_code;
375 message()->data()->response->error_details = error_details;
376 if (responseDatagram_)
378 responseDatagram_->unref();
379 responseDatagram_ =
nullptr;
381 return release_and_exit();
384 void register_write_response_handler()
388 writeResponseRegistered_ =
true;
391 void unregister_write_response_handler()
393 if (writeResponseRegistered_)
395 writeResponseRegistered_ =
false;
403 return allocate_and_call(
405 STATE(send_init_stream));
410 auto *b = get_allocation_result(
422 STATE(received_init_stream));
427 if (
message->data()->dstNode != node_ ||
433 const auto &payload =
message->data()->payload;
434 if (payload.size() < 6 || payload[4] != localStreamId_)
439 remoteStreamId_ = payload[5];
440 streamFlags_ = payload[2];
441 streamAdditionalFlags_ = payload[3];
442 maxBufferSize_ = (payload[0] << 8) | payload[1];
444 if (
message->data()->src.alias)
446 this->
message()->data()->dst.alias = message->
data()->src.alias;
452 Action received_init_stream()
458 if (!(streamFlags_ & StreamDefs::FLAG_ACCEPT))
464 "Stream initiate request was denied (permanent error).");
469 Defs::ERROR_TEMPORARY | streamAdditionalFlags_,
470 "Stream initiate request was denied (temporary error).");
476 "Inconsistency: zero buffer length but "
477 "accepted stream request.");
479 availableBufferSize_ = maxBufferSize_;
482 lastMeasurementOffset_ = 0;
486 return call_immediately(
STATE(send_stream_data));
491 if (bufferOffset_ >=
message()->data()->data.size())
493 return call_immediately(
STATE(close_stream));
495 return allocate_and_call(
499 Action fill_outgoing_stream_frame()
508 auto *frame = b->
data()->mutable_frame();
509 SET_CAN_FRAME_ID_EFF(*frame, can_id);
511 std::min(
size_t(7),
message()->data()->data.size() - bufferOffset_);
512 if (availableBufferSize_ < len)
514 len = availableBufferSize_;
516 frame->can_dlc = len + 1;
517 frame->data[0] = remoteStreamId_;
518 memcpy(&frame->data[1], &
message()->data()->data[bufferOffset_], len);
519 bufferOffset_ += len;
520 availableBufferSize_ -= len;
525 if (availableBufferSize_)
527 return wait_and_call(
STATE(send_stream_data));
531 return wait_and_call(
STATE(wait_for_stream_proceed));
535 Action wait_for_stream_proceed()
537 if (availableBufferSize_)
550 if (
message->data()->dstNode != node_ ||
556 size_t bytes_sent = bufferOffset_ - lastMeasurementOffset_;
558 float new_speed = next_time - lastMeasurementTimeNsec_;
559 new_speed = float(bytes_sent) * 1e9 / new_speed;
560 if (!lastMeasurementOffset_)
566 speed_ = speed_ * 0.8 + new_speed * 0.2;
569 clock_gettime(CLOCK_REALTIME, &ts);
570 if (request()->progress_callback)
572 float ofs = bufferOffset_;
573 ofs /= request()->
data.size();
577 "%02ld.%06ld stream offset: %" PRIdPTR
"; wrote %.0lld usec slept "
578 "%.0lld usec, speed=%.0f bytes/sec",
579 ts.tv_sec % 60, ts.tv_nsec / 1000, bufferOffset_,
580 (sleepStartTimeNsec_ - lastMeasurementTimeNsec_) / 1000,
581 (next_time - sleepStartTimeNsec_) / 1000, speed_);
582 lastMeasurementOffset_ = bufferOffset_;
583 lastMeasurementTimeNsec_ = next_time;
585 const auto &payload =
message->data()->payload;
586 if (payload.size() < 2 || payload[0] != localStreamId_)
591 availableBufferSize_ += maxBufferSize_;
602 if (!availableBufferSize_)
606 return return_error(Defs::ERROR_TEMPORARY,
607 "Times out waiting for stream proceed message.");
608 return call_immediately(
STATE(close_stream));
610 return call_immediately(
STATE(send_stream_data));
617 return allocate_and_call(
619 STATE(send_close_stream));
622 Action send_close_stream()
624 auto *b = get_allocation_result(
631 return sleep_and_call(
635 Action send_reboot_request()
637 if (
message()->data()->request_reboot_after) {
638 return allocate_and_call(
641 return return_error(0,
"Remote node left in bootloader.");
645 Action bootload_using_datagrams()
657 unsigned len =
message()->data()->data.size() - bufferOffset_;
658 if (len > 64) len = 64;
659 payload.append(&
message()->data()->data[bufferOffset_], len);
662 message()->data()->dst, payload);
665 responseDatagram_ =
nullptr;
668 return wait_and_call(
STATE(dg_write_request_sent));
671 Action dg_write_request_sent()
674 dgClient_->
result() & DatagramClient::RESPONSE_CODE_MASK;
675 if (dg_result != DatagramClient::OPERATION_SUCCESS) {
677 return return_error(dg_result,
"Write rejected.");
680 if (dgClient_->
result() & DatagramClient::OK_REPLY_PENDING) {
681 DIE(
"Write datagram results with reply pending not supported for "
685 unsigned len =
message()->data()->data.size() - bufferOffset_;
686 if (len > 64) len = 64;
687 bufferOffset_ += len;
689 if ((bufferOffset_ & ~0xFF) != ((bufferOffset_ - len) & ~0xFF)) {
691 LOG(
INFO,
"write offset: %" PRIdPTR
"; speed=%.0f bytes/sec",
692 bufferOffset_, speedAvg_.
avg());
693 if (request()->progress_callback)
695 float ofs = bufferOffset_;
696 ofs /= request()->
data.size();
701 if (bufferOffset_ <
message()->data()->data.size()) {
704 if (
message()->data()->request_reboot_after) {
705 return call_immediately(
STATE(reboot_with_dg_client));
707 return return_error(0,
"Remote node left in bootloader.");
715 return reboot_with_dg_client();
718 Action reboot_with_dg_client()
725 payload.push_back(
message()->data()->memory_space);
727 message()->data()->dst, payload);
730 return wait_and_call(
STATE(finish));
735 auto result = dgClient_->
result();
736 result &= DatagramClient::RESPONSE_CODE_MASK;
737 if (result == DatagramClient::DST_REBOOT ||
738 result == DatagramClient::OPERATION_SUCCESS)
743 uint16_t olcb_error = result & 0xffff;
744 if (olcb_error == FirmwareUpgradeDefs::ERROR_INCOMPATIBLE_FIRMWARE)
746 return return_error(olcb_error,
747 "The firmware data is incompatible with this hardware.");
749 if (olcb_error == FirmwareUpgradeDefs::ERROR_CORRUPTED_DATA)
752 olcb_error,
"The firmware data is invalid or corrupted.");
754 if (olcb_error == FirmwareUpgradeDefs::ERROR_WRITE_CHECKSUM_FAILED)
756 return return_error(olcb_error,
757 "The firmware written has failed checksum. Try again.");
759 if (olcb_error & DatagramClient::PERMANENT_ERROR)
761 return return_error(result & 0xffff,
"");
765 return return_error(0,
"");
770 DatagramService *datagramService_;
772 DatagramClient *dgClient_ =
nullptr;
774 uint8_t localStreamId_;
775 uint8_t remoteStreamId_;
777 uint16_t maxBufferSize_;
779 uint8_t streamFlags_;
781 uint8_t streamAdditionalFlags_;
785 uint32_t availableBufferSize_;
787 size_t bufferOffset_;
793 size_t lastMeasurementOffset_;
795 long long lastMeasurementTimeNsec_;
798 long long sleepStartTimeNsec_;
800 WriteResponseHandler writeResponseHandler_{
this};
801 bool writeResponseRegistered_ =
false;
802 MessageHandler::GenericHandler streamInitiateReplyHandler_{
803 this, &BootloaderClient::stream_initiate_replied};
804 MessageHandler::GenericHandler streamProceedHandler_{
805 this, &BootloaderClient::stream_proceed_received};
806 StateFlowTimer timer_{
this};
809 bool sleeping_ =
false;
811 PIPClient pipClient_{ifCan_};