ONU SW upgrade API change - step 1 to allow for straightforward upgrade (with activate/commit options)

Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: I6808f8f41db40faa060ed7198025abdda8506ae7
diff --git a/VERSION b/VERSION
index 9e933da..38fe29a 100755
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.3.1-dev201
+1.3.1-dev202
diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go
index fad08fa..c4ab249 100644
--- a/internal/pkg/config/config.go
+++ b/internal/pkg/config/config.go
@@ -66,7 +66,9 @@
 	defaultMibAuditInterval   = 0
 	defaultAlarmAuditInterval = 300 * time.Second
 
-	defaultOmciTimeout = 3 * time.Second
+	defaultOmciTimeout          = 3 * time.Second
+	defaultDlToAdapterTimeout   = 10 * time.Second
+	defaultDlToOnuTimeoutPer4MB = 60 * time.Minute //assumed for 4 MB of the image
 )
 
 // AdapterFlags represents the set of configurations used by the read-write adaptercore service
@@ -105,6 +107,8 @@
 	MibAuditInterval            time.Duration
 	OmciTimeout                 time.Duration
 	AlarmAuditInterval          time.Duration
+	DownloadToAdapterTimeout    time.Duration
+	DownloadToOnuTimeout4MB     time.Duration
 }
 
 // NewAdapterFlags returns a new RWCore config
@@ -143,6 +147,8 @@
 		MibAuditInterval:            defaultMibAuditInterval,
 		AlarmAuditInterval:          defaultAlarmAuditInterval,
 		OmciTimeout:                 defaultOmciTimeout,
+		DownloadToAdapterTimeout:    defaultDlToAdapterTimeout,
+		DownloadToOnuTimeout4MB:     defaultDlToOnuTimeoutPer4MB,
 	}
 	return &adapterFlags
 }
@@ -150,104 +156,110 @@
 // ParseCommandArguments parses the arguments when running read-write adaptercore service
 func (so *AdapterFlags) ParseCommandArguments() {
 
-	help := fmt.Sprintf("Kafka - Adapter messaging address")
+	help := "Kafka - Adapter messaging address"
 	flag.StringVar(&(so.KafkaAdapterAddress), "kafka_adapter_address", defaultKafkaadapteraddress, help)
 
-	help = fmt.Sprintf("Kafka - Cluster messaging address")
+	help = "Kafka - Cluster messaging address"
 	flag.StringVar(&(so.KafkaClusterAddress), "kafka_cluster_address", defaultKafkaclusteraddress, help)
 
-	help = fmt.Sprintf("Open ONU topic")
+	help = "Open ONU topic"
 	baseAdapterTopic := flag.String("adapter_topic", defaultTopic, help)
 
-	help = fmt.Sprintf("Core topic")
+	help = "Core topic"
 	flag.StringVar(&(so.CoreTopic), "core_topic", defaultCoreTopic, help)
 
-	help = fmt.Sprintf("Event topic")
+	help = "Event topic"
 	flag.StringVar(&(so.EventTopic), "event_topic", defaultEventTopic, help)
 
-	help = fmt.Sprintf("KV store type")
+	help = "KV store type"
 	flag.StringVar(&(so.KVStoreType), "kv_store_type", defaultKvstoretype, help)
 
-	help = fmt.Sprintf("The default timeout when making a kv store request")
+	help = "The default timeout when making a kv store request"
 	flag.DurationVar(&(so.KVStoreTimeout), "kv_store_request_timeout", defaultKvstoretimeout, help)
 
-	help = fmt.Sprintf("KV store address")
+	help = "KV store address"
 	flag.StringVar(&(so.KVStoreAddress), "kv_store_address", defaultKvstoreaddress, help)
 
-	help = fmt.Sprintf("Log level")
+	help = "Log level"
 	flag.StringVar(&(so.LogLevel), "log_level", defaultLoglevel, help)
 
-	help = fmt.Sprintf("Number of ONUs")
+	help = "Number of ONUs"
 	flag.IntVar(&(so.OnuNumber), "onu_number", defaultOnunumber, help)
 
-	help = fmt.Sprintf("Show startup banner log lines")
+	help = "Show startup banner log lines"
 	flag.BoolVar(&(so.Banner), "banner", defaultBanner, help)
 
-	help = fmt.Sprintf("Show version information and exit")
+	help = "Show version information and exit"
 	flag.BoolVar(&(so.DisplayVersionOnly), "version", defaultDisplayVersionOnly, help)
 
-	help = fmt.Sprintf("Acceptance of incremental EVTOCD configuration")
+	help = "Acceptance of incremental EVTOCD configuration"
 	flag.BoolVar(&(so.AccIncrEvto), "accept_incr_evto", defaultAccIncrEvto, help)
 
-	help = fmt.Sprintf("The address on which to listen to answer liveness and readiness probe queries over HTTP.")
+	help = "The address on which to listen to answer liveness and readiness probe queries over HTTP"
 	flag.StringVar(&(so.ProbeHost), "probe_host", defaultProbeHost, help)
 
-	help = fmt.Sprintf("The port on which to listen to answer liveness and readiness probe queries over HTTP.")
+	help = "The port on which to listen to answer liveness and readiness probe queries over HTTP"
 	flag.IntVar(&(so.ProbePort), "probe_port", defaultProbePort, help)
 
-	help = fmt.Sprintf("Number of seconds for the default liveliness check")
+	help = "Number of seconds for the default liveliness check"
 	flag.DurationVar(&(so.LiveProbeInterval), "live_probe_interval", defaultLiveProbeInterval, help)
 
-	help = fmt.Sprintf("Number of seconds for liveliness check if probe is not running")
+	help = "Number of seconds for liveliness check if probe is not running"
 	flag.DurationVar(&(so.NotLiveProbeInterval), "not_live_probe_interval", defaultNotLiveProbeInterval, help)
 
-	help = fmt.Sprintf("Number of seconds for heartbeat check interval.")
+	help = "Number of seconds for heartbeat check interval"
 	flag.DurationVar(&(so.HeartbeatCheckInterval), "hearbeat_check_interval", defaultHearbeatCheckInterval, help)
 
-	help = fmt.Sprintf("Number of seconds adapter has to wait before reporting core on the hearbeat check failure.")
+	help = "Number of seconds adapter has to wait before reporting core on the hearbeat check failure"
 	flag.DurationVar(&(so.HeartbeatFailReportInterval), "hearbeat_fail_interval", defaultHearbeatFailReportInterval, help)
 
-	help = fmt.Sprintf("Number of retries to connect to Kafka.")
+	help = "Number of retries to connect to Kafka"
 	flag.IntVar(&(so.KafkaReconnectRetries), "kafka_reconnect_retries", defaultKafkaReconnectRetries, help)
 
-	help = "Replica number of this particular instance (default: %s)"
+	help = "Replica number of this particular instance"
 	flag.IntVar(&(so.CurrentReplica), "current_replica", defaultCurrentReplica, help)
 
 	help = "Total number of instances for this adapter"
 	flag.IntVar(&(so.TotalReplicas), "total_replica", defaultTotalReplicas, help)
 
-	help = fmt.Sprintf("Maximum Number of seconds for the default interadapter communication timeout")
+	help = "Maximum Number of seconds for the default interadapter communication timeout"
 	flag.DurationVar(&(so.MaxTimeoutInterAdapterComm), "max_timeout_interadapter_comm",
 		defaultMaxTimeoutInterAdapterComm, help)
 
-	help = fmt.Sprintf("Maximum Number of seconds for the default ONU reconciling timeout")
+	help = "Maximum Number of seconds for the default ONU reconciling timeout"
 	flag.DurationVar(&(so.MaxTimeoutReconciling), "max_timeout_reconciling",
 		defaultMaxTimeoutReconciling, help)
 
-	help = fmt.Sprintf("Whether to send logs to tracing agent?")
+	help = "Whether to send logs to tracing agent"
 	flag.BoolVar(&(so.TraceEnabled), "trace_enabled", defaultTraceEnabled, help)
 
-	help = fmt.Sprintf("The address of tracing agent to which span info should be sent.")
+	help = "The address of tracing agent to which span info should be sent"
 	flag.StringVar(&(so.TraceAgentAddress), "trace_agent_address", defaultTraceAgentAddress, help)
 
-	help = fmt.Sprintf("Whether to enrich log statements with fields denoting operation being executed for achieving correlation?")
+	help = "Whether to enrich log statements with fields denoting operation being executed for achieving correlation"
 	flag.BoolVar(&(so.LogCorrelationEnabled), "log_correlation_enabled", defaultLogCorrelationEnabled, help)
 
-	help = fmt.Sprintf("List of Allowed ONU Vendor Ids")
+	help = "List of Allowed ONU Vendor Ids"
 	flag.StringVar(&(so.OnuVendorIds), "allowed_onu_vendors", defaultOnuVendorIds, help)
 
-	help = fmt.Sprintf("Whether to enable metrics collection")
+	help = "Whether to enable metrics collection"
 	flag.BoolVar(&(so.MetricsEnabled), "metrics_enabled", defaultMetricsEnabled, help)
 
-	help = fmt.Sprintf("Mib Audit Interval in seconds - the value zero will disable Mib Audit")
+	help = "Mib Audit Interval in seconds - the value zero will disable Mib Audit"
 	flag.DurationVar(&(so.MibAuditInterval), "mib_audit_interval", defaultMibAuditInterval, help)
 
-	help = fmt.Sprintf("OMCI timeout duration - this timeout value is used on the OMCI channel for waiting on response from ONU")
+	help = "OMCI timeout duration - this timeout value is used on the OMCI channel for waiting on response from ONU"
 	flag.DurationVar(&(so.OmciTimeout), "omci_timeout", defaultOmciTimeout, help)
 
-	help = fmt.Sprintf("Alarm Audit Interval in seconds - the value zero will disable alarm audit")
+	help = "Alarm Audit Interval in seconds - the value zero will disable alarm audit"
 	flag.DurationVar(&(so.AlarmAuditInterval), "alarm_audit_interval", defaultAlarmAuditInterval, help)
 
+	help = "File download to adapter timeout in seconds"
+	flag.DurationVar(&(so.DownloadToAdapterTimeout), "download_to_adapter_timeout", defaultDlToAdapterTimeout, help)
+
+	help = "File download to ONU timeout in minutes for a block of 4MB"
+	flag.DurationVar(&(so.DownloadToOnuTimeout4MB), "download_to_onu_timeout_4MB", defaultDlToOnuTimeoutPer4MB, help)
+
 	flag.Parse()
 	containerName := getContainerInfo()
 	if len(containerName) > 0 {
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index 07fa67b..fc29ac2 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -1100,6 +1100,182 @@
 	return err
 }
 
+//onuSwUpgradeAfterDownload initiates the SW download transfer to the ONU with activate and commit options
+// after the OnuImage has been downloaded to the adapter, called in background
+func (dh *deviceHandler) onuSwUpgradeAfterDownload(ctx context.Context, apImageRequest *voltha.DeviceImageDownloadRequest,
+	apDownloadManager *fileDownloadManager, aImageIdentifier string) {
+
+	var err error
+	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+	if pDevEntry == nil {
+		logger.Errorw(ctx, "start Onu SW upgrade rejected: no valid OnuDevice", log.Fields{"device-id": dh.deviceID})
+		return
+	}
+
+	var inactiveImageID uint16
+	if inactiveImageID, err = pDevEntry.GetInactiveImageMeID(ctx); err == nil {
+		logger.Debugw(ctx, "onuSwUpgrade requested", log.Fields{
+			"device-id": dh.deviceID, "image-version": apImageRequest.Image.Version, "to onu-image": inactiveImageID})
+		dh.lockUpgradeFsm.Lock()
+		defer dh.lockUpgradeFsm.Unlock()
+		if dh.pOnuUpradeFsm == nil {
+			//OmciOnuSwUpgradeDone could be used to create some Kafka event with information on upgrade completion,
+			//  but none yet defined
+			err = dh.createOnuUpgradeFsm(ctx, pDevEntry, OmciOnuSwUpgradeDone)
+			if err == nil {
+				if err = dh.pOnuUpradeFsm.SetDownloadParamsAfterDownload(ctx, inactiveImageID,
+					apImageRequest, apDownloadManager, aImageIdentifier, dh.pOpenOnuAc.dlToOnuTimeout4M); err != nil {
+					logger.Errorw(ctx, "onu upgrade fsm could not set parameters", log.Fields{
+						"device-id": dh.deviceID, "error": err})
+					return
+				}
+			} else {
+				logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+					"device-id": dh.deviceID, "error": err})
+			}
+			return
+		}
+		//OnuSw upgrade already running - restart (with possible abort of running)
+		logger.Debugw(ctx, "Onu SW upgrade already running - abort", log.Fields{"device-id": dh.deviceID})
+		pUpgradeStatemachine := dh.pOnuUpradeFsm.pAdaptFsm.pFsm
+		if pUpgradeStatemachine != nil {
+			if err = pUpgradeStatemachine.Event(upgradeEvAbort); err != nil {
+				logger.Errorw(ctx, "onu upgrade fsm could not abort a running processing", log.Fields{
+					"device-id": dh.deviceID, "error": err})
+				return
+			}
+			//TODO!!!: wait for 'ready' to start and configure - see above SetDownloadParams()
+			// for now a second start of download should work again - must still be initiated by user
+		} else { //should never occur
+			logger.Errorw(ctx, "onu upgrade fsm inconsistent setup", log.Fields{
+				"device-id": dh.deviceID})
+		}
+		return
+	}
+	logger.Errorw(ctx, "start Onu SW upgrade rejected: no inactive image", log.Fields{
+		"device-id": dh.deviceID, "error": err})
+}
+
+//onuSwActivateRequest ensures activation of the requested image with commit options
+func (dh *deviceHandler) onuSwActivateRequest(ctx context.Context, aVersion string, aCommitRequest bool) {
+	var err error
+	//SW activation for the ONU image may have two use cases, one of them is selected here according to following prioritization:
+	//  1.) activation of the image for a started upgrade process (in case the running upgrade runs on the requested image)
+	//  2.) activation of the inactive image
+
+	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+	if pDevEntry == nil {
+		logger.Errorw(ctx, "Onu image activation rejected: no valid OnuDevice", log.Fields{"device-id": dh.deviceID})
+		return
+	}
+	dh.lockUpgradeFsm.RLock()
+	if dh.pOnuUpradeFsm != nil {
+		dh.lockUpgradeFsm.RUnlock()
+		onuVolthaDevice, getErr := dh.coreProxy.GetDevice(log.WithSpanFromContext(context.TODO(), ctx),
+			dh.deviceID, dh.deviceID)
+		if getErr != nil || onuVolthaDevice == nil {
+			logger.Errorw(ctx, "Failed to fetch Onu device for image activation", log.Fields{"device-id": dh.deviceID, "err": getErr})
+			return
+		}
+		//  use the OnuVendor identification from this device for the internal unique name
+		imageIdentifier := onuVolthaDevice.VendorId + aVersion //head on vendor ID of the ONU
+		// 1.) check a started upgrade process and rely the activation request to it
+		if err = dh.pOnuUpradeFsm.SetActivationParamsRunning(ctx, imageIdentifier, aCommitRequest); err != nil {
+			logger.Errorw(ctx, "onu upgrade fsm did not accept activation while running", log.Fields{
+				"device-id": dh.deviceID, "error": err})
+		} else {
+			logger.Debugw(ctx, "image activation acknowledged by onu upgrade processing", log.Fields{
+				"device-id": dh.deviceID, "image-id": imageIdentifier})
+		}
+		//if some ONU upgrade is ongoing we do not accept some explicit ONU image-version related activation
+		//   (even though parameter setting is not accepted)
+		return
+	} //else
+	dh.lockUpgradeFsm.RUnlock()
+
+	// 2.) check if requested image-version equals the inactive one and start its activation
+	//   (image version is not [yet] checked - would be possible, but with increased effort ...)
+	var inactiveImageID uint16
+	if inactiveImageID, err = pDevEntry.GetInactiveImageMeID(ctx); err != nil || inactiveImageID > 1 {
+		logger.Errorw(ctx, "get inactive image failed", log.Fields{
+			"device-id": dh.deviceID, "err": err, "image-id": inactiveImageID})
+		return
+	}
+	err = dh.createOnuUpgradeFsm(ctx, pDevEntry, OmciOnuSwUpgradeDone)
+	if err == nil {
+		if err = dh.pOnuUpradeFsm.SetActivationParamsStart(ctx, aVersion,
+			inactiveImageID, aCommitRequest); err != nil {
+			logger.Errorw(ctx, "onu upgrade fsm did not accept activation to start", log.Fields{
+				"device-id": dh.deviceID, "error": err})
+			return
+		}
+		logger.Debugw(ctx, "inactive image activation acknowledged by onu upgrade", log.Fields{
+			"device-id": dh.deviceID, "image-version": aVersion})
+		return
+	} //else
+	logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+		"device-id": dh.deviceID, "error": err})
+}
+
+//onuSwCommitRequest ensures commitment of the requested image
+func (dh *deviceHandler) onuSwCommitRequest(ctx context.Context, aVersion string) {
+	var err error
+	//SW commitment for the ONU image may have two use cases, one of them is selected here according to following prioritization:
+	//  1.) commitment of the image for a started upgrade process (in case the running upgrade runs on the requested image)
+	//  2.) commitment of the active image
+
+	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+	if pDevEntry == nil {
+		logger.Errorw(ctx, "Onu image commitment rejected: no valid OnuDevice", log.Fields{"device-id": dh.deviceID})
+		return
+	}
+	dh.lockUpgradeFsm.RLock()
+	if dh.pOnuUpradeFsm != nil {
+		dh.lockUpgradeFsm.RUnlock()
+		onuVolthaDevice, getErr := dh.coreProxy.GetDevice(log.WithSpanFromContext(context.TODO(), ctx),
+			dh.deviceID, dh.deviceID)
+		if getErr != nil || onuVolthaDevice == nil {
+			logger.Errorw(ctx, "Failed to fetch Onu device for image commitment", log.Fields{"device-id": dh.deviceID, "err": getErr})
+			return
+		}
+		//  use the OnuVendor identification from this device for the internal unique name
+		imageIdentifier := onuVolthaDevice.VendorId + aVersion //head on vendor ID of the ONU
+		// 1.) check a started upgrade process and rely the commitment request to it
+		if err = dh.pOnuUpradeFsm.SetCommitmentParamsRunning(ctx, imageIdentifier); err != nil {
+			logger.Errorw(ctx, "onu upgrade fsm did not accept commitment while running", log.Fields{
+				"device-id": dh.deviceID, "error": err})
+		} else {
+			logger.Debugw(ctx, "image commitment acknowledged by onu upgrade processing", log.Fields{
+				"device-id": dh.deviceID, "image-id": imageIdentifier})
+		}
+		//if some ONU upgrade is ongoing we do not accept some explicit ONU image-version related commitment
+		//   (even though parameter setting is not accepted)
+		return
+	} //else
+	dh.lockUpgradeFsm.RUnlock()
+
+	// 2.) check if requested image-version equals the inactive one and start its commitment
+	var activeImageID uint16
+	if activeImageID, err = pDevEntry.GetActiveImageMeID(ctx); err != nil || activeImageID > 1 {
+		logger.Errorw(ctx, "get active image failed", log.Fields{
+			"device-id": dh.deviceID, "err": err, "image-id": activeImageID})
+		return
+	}
+	err = dh.createOnuUpgradeFsm(ctx, pDevEntry, OmciOnuSwUpgradeDone)
+	if err == nil {
+		if err = dh.pOnuUpradeFsm.SetCommitmentParamsStart(ctx, aVersion, activeImageID); err != nil {
+			logger.Errorw(ctx, "onu upgrade fsm did not accept commitment to start", log.Fields{
+				"device-id": dh.deviceID, "error": err})
+			return
+		}
+		logger.Debugw(ctx, "active image commitment acknowledged by onu upgrade", log.Fields{
+			"device-id": dh.deviceID, "image-version": aVersion})
+		return
+	} //else
+	logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+		"device-id": dh.deviceID, "error": err})
+}
+
 //  deviceHandler methods that implement the adapters interface requests## end #########
 // #####################################################################################
 
@@ -1731,18 +1907,10 @@
 	}
 
 	//reset a possibly running upgrade FSM
-	// specific here: If the FSM is in upgradeStWaitForCommit, it is left there for possibly later commit
-	// this possibly also refers later to (not yet existing) upgradeStWaitForActivate (with ctl API changes)
+	//  (note the Upgrade FSM may stay alive e.g. in state upgradeStWaitForCommit to endure the ONU reboot)
 	dh.lockUpgradeFsm.RLock()
 	if dh.pOnuUpradeFsm != nil {
-		pUpgradeStatemachine := dh.pOnuUpradeFsm.pAdaptFsm.pFsm
-		if pUpgradeStatemachine != nil {
-			if pUpgradeStatemachine.Is(upgradeStWaitEndDL) {
-				dh.pOnuUpradeFsm.chReceiveExpectedResponse <- false //which aborts the FSM (activate was not yet sent)
-			}
-			_ = pUpgradeStatemachine.Event(upgradeEvReset) //anyway and for all other states
-		}
-		//else the FSM seems already to be in some released state
+		dh.pOnuUpradeFsm.CancelProcessing(ctx)
 	}
 	dh.lockUpgradeFsm.RUnlock()
 
diff --git a/internal/pkg/onuadaptercore/file_download_manager.go b/internal/pkg/onuadaptercore/file_download_manager.go
new file mode 100644
index 0000000..0658c6f
--- /dev/null
+++ b/internal/pkg/onuadaptercore/file_download_manager.go
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//Package adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"bufio"
+	"context"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"os"
+	"sync"
+	"time"
+
+	"github.com/opencord/voltha-lib-go/v4/pkg/log"
+)
+
+const cDefaultLocalDir = "/tmp" //this is the default local dir to download to
+
+type fileState uint32
+
+//nolint:varcheck, deadcode
+const (
+	cFileStateUnknown fileState = iota
+	cFileStateDlStarted
+	cFileStateDlSucceeded
+	cFileStateDlFailed
+	cFileStateDlAborted
+	cFileStateDlInvalid
+)
+
+type downloadImageParams struct {
+	downloadImageName  string
+	downloadImageState fileState
+	downloadImageLen   int64
+	downloadImageCrc   uint32
+}
+
+type requesterChannelMap map[chan<- bool]struct{} //using an empty structure map for easier (unique) element appending
+
+//fileDownloadManager structure holds information needed for downloading to and storing images within the adapter
+type fileDownloadManager struct {
+	mutexDownloadImageDsc sync.RWMutex
+	downloadImageDscSlice []downloadImageParams
+	dnldImgReadyWaiting   map[string]requesterChannelMap
+	dlToAdapterTimeout    time.Duration
+}
+
+//newFileDownloadManager constructor returns a new instance of a fileDownloadManager
+//mib_db (as well as not inluded alarm_db not really used in this code? VERIFY!!)
+func newFileDownloadManager(ctx context.Context) *fileDownloadManager {
+	logger.Debug(ctx, "init-fileDownloadManager")
+	var localDnldMgr fileDownloadManager
+	localDnldMgr.downloadImageDscSlice = make([]downloadImageParams, 0)
+	localDnldMgr.dnldImgReadyWaiting = make(map[string]requesterChannelMap)
+	localDnldMgr.dlToAdapterTimeout = 10 * time.Second //default timeout, should be overwritten immediately after start
+	return &localDnldMgr
+}
+
+//SetDownloadTimeout configures the timeout used to supervice the download of the image to the adapter (assumed in seconds)
+func (dm *fileDownloadManager) SetDownloadTimeout(ctx context.Context, aDlTimeout time.Duration) {
+	dm.mutexDownloadImageDsc.Lock()
+	defer dm.mutexDownloadImageDsc.Unlock()
+	logger.Debugw(ctx, "setting download timeout", log.Fields{"timeout": aDlTimeout})
+	dm.dlToAdapterTimeout = aDlTimeout
+}
+
+//GetDownloadTimeout delivers the timeout used to supervice the download of the image to the adapter (assumed in seconds)
+func (dm *fileDownloadManager) GetDownloadTimeout(ctx context.Context) time.Duration {
+	dm.mutexDownloadImageDsc.RLock()
+	defer dm.mutexDownloadImageDsc.RUnlock()
+	return dm.dlToAdapterTimeout
+}
+
+//ImageExists returns true if the requested image already exists within the adapter
+func (dm *fileDownloadManager) ImageExists(ctx context.Context, aImageName string) bool {
+	logger.Debugw(ctx, "checking on existence of the image", log.Fields{"image-name": aImageName})
+	dm.mutexDownloadImageDsc.RLock()
+	defer dm.mutexDownloadImageDsc.RUnlock()
+
+	for _, dnldImgDsc := range dm.downloadImageDscSlice {
+		if dnldImgDsc.downloadImageName == aImageName {
+			//image found (by name)
+			return true
+		}
+	}
+	//image not found (by name)
+	return false
+}
+
+//StartDownload returns true if the download of the requested image could be started for the given file name and URL
+func (dm *fileDownloadManager) StartDownload(ctx context.Context, aImageName string, aURLCommand string) error {
+	logger.Infow(ctx, "image download-to-adapter requested", log.Fields{
+		"image-name": aImageName, "url-command": aURLCommand})
+	loDownloadImageParams := downloadImageParams{
+		downloadImageName: aImageName, downloadImageState: cFileStateDlStarted,
+		downloadImageLen: 0, downloadImageCrc: 0}
+	dm.mutexDownloadImageDsc.Lock()
+	dm.downloadImageDscSlice = append(dm.downloadImageDscSlice, loDownloadImageParams)
+	dm.mutexDownloadImageDsc.Unlock()
+	//try to download from http
+	err := dm.downloadFile(ctx, aURLCommand, cDefaultLocalDir, aImageName)
+	//return the result of the start-request to comfort the core processing even though the complete download may go on in background
+	return err
+}
+
+//GetImageBufferLen returns the length of the specified file in bytes (file size) - as detected after download
+func (dm *fileDownloadManager) GetImageBufferLen(ctx context.Context, aFileName string) (int64, error) {
+	dm.mutexDownloadImageDsc.RLock()
+	defer dm.mutexDownloadImageDsc.RUnlock()
+	for _, dnldImgDsc := range dm.downloadImageDscSlice {
+		if dnldImgDsc.downloadImageName == aFileName && dnldImgDsc.downloadImageState == cFileStateDlSucceeded {
+			//image found (by name) and fully downloaded
+			return dnldImgDsc.downloadImageLen, nil
+		}
+	}
+	return 0, fmt.Errorf("no downloaded image found: %s", aFileName)
+}
+
+//GetDownloadImageBuffer returns the content of the requested file as byte slice
+func (dm *fileDownloadManager) GetDownloadImageBuffer(ctx context.Context, aFileName string) ([]byte, error) {
+	//nolint:gosec
+	file, err := os.Open(cDefaultLocalDir + "/" + aFileName)
+	if err != nil {
+		return nil, err
+	}
+	//nolint:errcheck
+	defer file.Close()
+
+	stats, statsErr := file.Stat()
+	if statsErr != nil {
+		return nil, statsErr
+	}
+
+	var size int64 = stats.Size()
+	bytes := make([]byte, size)
+
+	buffer := bufio.NewReader(file)
+	_, err = buffer.Read(bytes)
+
+	return bytes, err
+}
+
+//RequestDownloadReady receives a channel that has to be used to inform the requester in case the concerned file is downloaded
+func (dm *fileDownloadManager) RequestDownloadReady(ctx context.Context, aFileName string, aWaitChannel chan<- bool) {
+	if dm.imageLocallyDownloaded(ctx, aFileName) {
+		//image found (by name) and fully downloaded
+		logger.Debugw(ctx, "file ready - immediate response", log.Fields{"image-name": aFileName})
+		aWaitChannel <- true
+		return
+	}
+	//when we are here the image was not yet found or not fully downloaded -
+	//  add the device specific channel to the list of waiting requesters
+	dm.mutexDownloadImageDsc.Lock()
+	defer dm.mutexDownloadImageDsc.Unlock()
+	if loRequesterChannelMap, ok := dm.dnldImgReadyWaiting[aFileName]; ok {
+		//entry for the file name already exists
+		if _, exists := loRequesterChannelMap[aWaitChannel]; !exists {
+			// requester channel does not yet exist for the image
+			loRequesterChannelMap[aWaitChannel] = struct{}{}
+			dm.dnldImgReadyWaiting[aFileName] = loRequesterChannelMap
+			logger.Debugw(ctx, "file not ready - adding new requester", log.Fields{
+				"image-name": aFileName, "number-of-requesters": len(dm.dnldImgReadyWaiting[aFileName])})
+		}
+	} else {
+		//entry for the file name does not even exist
+		addRequesterChannelMap := make(map[chan<- bool]struct{})
+		addRequesterChannelMap[aWaitChannel] = struct{}{}
+		dm.dnldImgReadyWaiting[aFileName] = addRequesterChannelMap
+		logger.Debugw(ctx, "file not ready - setting first requester", log.Fields{
+			"image-name": aFileName})
+	}
+}
+
+//RemoveReadyRequest removes the specified channel from the requester(channel) map for the given file name
+func (dm *fileDownloadManager) RemoveReadyRequest(ctx context.Context, aFileName string, aWaitChannel chan bool) {
+	dm.mutexDownloadImageDsc.Lock()
+	defer dm.mutexDownloadImageDsc.Unlock()
+	for imageName, channelMap := range dm.dnldImgReadyWaiting {
+		if imageName == aFileName {
+			for channel := range channelMap {
+				if channel == aWaitChannel {
+					delete(dm.dnldImgReadyWaiting[imageName], channel)
+					logger.Debugw(ctx, "channel removed from the requester map", log.Fields{
+						"image-name": aFileName, "new number-of-requesters": len(dm.dnldImgReadyWaiting[aFileName])})
+					return //can leave directly
+				}
+			}
+			return //can leave directly
+		}
+	}
+}
+
+// FileDownloadManager private (unexported) methods -- start
+
+//imageLocallyDownloaded returns true if the requested image already exists within the adapter
+func (dm *fileDownloadManager) imageLocallyDownloaded(ctx context.Context, aImageName string) bool {
+	logger.Debugw(ctx, "checking if image is fully downloaded to adapter", log.Fields{"image-name": aImageName})
+	dm.mutexDownloadImageDsc.RLock()
+	defer dm.mutexDownloadImageDsc.RUnlock()
+
+	for _, dnldImgDsc := range dm.downloadImageDscSlice {
+		if dnldImgDsc.downloadImageName == aImageName {
+			//image found (by name)
+			if dnldImgDsc.downloadImageState == cFileStateDlSucceeded {
+				logger.Debugw(ctx, "image has been fully downloaded", log.Fields{"image-name": aImageName})
+				return true
+			}
+			logger.Debugw(ctx, "image not yet fully downloaded", log.Fields{"image-name": aImageName})
+			return false
+		}
+	}
+	//image not found (by name)
+	logger.Errorw(ctx, "image does not exist", log.Fields{"image-name": aImageName})
+	return false
+}
+
+//downloadFile downloads the specified file from the given http location
+func (dm *fileDownloadManager) downloadFile(ctx context.Context, aURLCommand string, aFilePath string, aFileName string) error {
+	// Get the data
+	logger.Infow(ctx, "downloading with URL", log.Fields{"url": aURLCommand, "localPath": aFilePath})
+	// verifying the complete URL by parsing it to its URL elements
+	urlBase, err1 := url.Parse(aURLCommand)
+	if err1 != nil {
+		logger.Errorw(ctx, "could not set base url command", log.Fields{"url": aURLCommand, "error": err1})
+		return fmt.Errorf("could not set base url command: %s, error: %s", aURLCommand, err1)
+	}
+	urlParams := url.Values{}
+	urlBase.RawQuery = urlParams.Encode()
+
+	//pre-check on file existence - assuming http location here
+	reqExist, errExist2 := http.NewRequest("HEAD", urlBase.String(), nil)
+	if errExist2 != nil {
+		logger.Errorw(ctx, "could not generate http head request", log.Fields{"url": urlBase.String(), "error": errExist2})
+		return fmt.Errorf("could not  generate http head request: %s, error: %s", aURLCommand, errExist2)
+	}
+	ctxExist, cancelExist := context.WithDeadline(ctx, time.Now().Add(3*time.Second)) //waiting for some fast answer
+	defer cancelExist()
+	_ = reqExist.WithContext(ctxExist)
+	respExist, errExist3 := http.DefaultClient.Do(reqExist)
+	if errExist3 != nil || respExist.StatusCode != http.StatusOK {
+		logger.Infow(ctx, "could not http head from url", log.Fields{"url": urlBase.String(),
+			"error": errExist3, "status": respExist.StatusCode})
+		//if head is not supported by server we cannot use this test and just try to continue
+		if respExist.StatusCode != http.StatusMethodNotAllowed {
+			logger.Errorw(ctx, "http head from url: file does not exist here, aborting", log.Fields{"url": urlBase.String(),
+				"error": errExist3, "status": respExist.StatusCode})
+			return fmt.Errorf("http head from url: file does not exist here, aborting: %s, error: %s, status: %d",
+				aURLCommand, errExist2, respExist.StatusCode)
+		}
+	}
+	defer func() {
+		deferredErr := respExist.Body.Close()
+		if deferredErr != nil {
+			logger.Errorw(ctx, "error at closing http head response body", log.Fields{"url": urlBase.String(), "error": deferredErr})
+		}
+	}()
+
+	//trying to download - do it in background as it may take some time ...
+	go func() {
+		req, err2 := http.NewRequest("GET", urlBase.String(), nil)
+		if err2 != nil {
+			logger.Errorw(ctx, "could not generate http request", log.Fields{"url": urlBase.String(), "error": err2})
+			return
+		}
+		ctx, cancel := context.WithDeadline(ctx, time.Now().Add(dm.dlToAdapterTimeout)) //timeout as given from SetDownloadTimeout()
+		defer cancel()
+		_ = req.WithContext(ctx)
+		resp, err3 := http.DefaultClient.Do(req)
+		if err3 != nil || respExist.StatusCode != http.StatusOK {
+			logger.Errorw(ctx, "could not http get from url", log.Fields{"url": urlBase.String(),
+				"error": err3, "status": respExist.StatusCode})
+			return
+		}
+		defer func() {
+			deferredErr := resp.Body.Close()
+			if deferredErr != nil {
+				logger.Errorw(ctx, "error at closing http get response body", log.Fields{"url": urlBase.String(), "error": deferredErr})
+			}
+		}()
+
+		// Create the file
+		aLocalPathName := aFilePath + "/" + aFileName
+		file, err := os.Create(aLocalPathName)
+		if err != nil {
+			logger.Errorw(ctx, "could not create local file", log.Fields{"path_file": aLocalPathName, "error": err})
+			return
+		}
+		defer func() {
+			deferredErr := file.Close()
+			if deferredErr != nil {
+				logger.Errorw(ctx, "error at closing new file", log.Fields{"path_file": aLocalPathName, "error": deferredErr})
+			}
+		}()
+
+		// Write the body to file
+		_, err = io.Copy(file, resp.Body)
+		if err != nil {
+			logger.Errorw(ctx, "could not copy file content", log.Fields{"url": urlBase.String(), "file": aLocalPathName, "error": err})
+			return
+		}
+
+		fileStats, statsErr := file.Stat()
+		if err != nil {
+			logger.Errorw(ctx, "created file can't be accessed", log.Fields{"file": aLocalPathName, "stat-error": statsErr})
+		}
+		fileSize := fileStats.Size()
+		logger.Infow(ctx, "written file size is", log.Fields{"file": aLocalPathName, "length": fileSize})
+
+		dm.mutexDownloadImageDsc.Lock()
+		defer dm.mutexDownloadImageDsc.Unlock()
+		for imgKey, dnldImgDsc := range dm.downloadImageDscSlice {
+			if dnldImgDsc.downloadImageName == aFileName {
+				//image found (by name) - need to write changes on the original map
+				dm.downloadImageDscSlice[imgKey].downloadImageState = cFileStateDlSucceeded
+				dm.downloadImageDscSlice[imgKey].downloadImageLen = fileSize
+				//in case upgrade process(es) was/were waiting for the file, inform them
+				for imageName, channelMap := range dm.dnldImgReadyWaiting {
+					if imageName == aFileName {
+						for channel := range channelMap {
+							// use all found channels to inform possible requesters about the existence of the file
+							channel <- true
+							delete(dm.dnldImgReadyWaiting[imageName], channel) //requester served
+						}
+						return //can leave directly
+					}
+				}
+				return //can leave directly
+			}
+		}
+		//TODO:!!! further extension could be provided here, e.g. already computing and possibly comparing the CRC, vendor check
+	}()
+	return nil
+}
diff --git a/internal/pkg/onuadaptercore/omci_cc.go b/internal/pkg/onuadaptercore/omci_cc.go
index f371379..0e3b8a5 100644
--- a/internal/pkg/onuadaptercore/omci_cc.go
+++ b/internal/pkg/onuadaptercore/omci_cc.go
@@ -492,13 +492,15 @@
 func (oo *omciCC) send(ctx context.Context, txFrame []byte, timeout int, retry int, highPrio bool,
 	receiveCallbackPair callbackPair) error {
 
-	logger.Debugw(ctx, "register-response-callback:", log.Fields{"for TansCorrId": receiveCallbackPair.cbKey})
-	// it could be checked, if the callback keay is already registered - but simply overwrite may be acceptable ...
-	oo.mutexRxSchedMap.Lock()
-	oo.rxSchedulerMap[receiveCallbackPair.cbKey] = receiveCallbackPair.cbEntry
-	printFrame := receiveCallbackPair.cbEntry.framePrint //printFrame true means debug print of frame is requested
-	oo.mutexRxSchedMap.Unlock()
+	if timeout != 0 {
+		logger.Debugw(ctx, "register-response-callback:", log.Fields{"for TansCorrId": receiveCallbackPair.cbKey})
+		oo.mutexRxSchedMap.Lock()
+		// it could be checked, if the callback key is already registered - but simply overwrite may be acceptable ...
+		oo.rxSchedulerMap[receiveCallbackPair.cbKey] = receiveCallbackPair.cbEntry
+		oo.mutexRxSchedMap.Unlock()
+	} //else timeout 0 indicates that no response is expected - fire and forget
 
+	printFrame := receiveCallbackPair.cbEntry.framePrint //printFrame true means debug print of frame is requested
 	//just use a simple list for starting - might need some more effort, especially for multi source write access
 	omciTxRequest := omciTransferStructure{
 		txFrame,
@@ -585,15 +587,6 @@
 			proxy_device_id=self._proxy_address.device_id
 		)
 		*/
-		device, err := oo.coreProxy.GetDevice(ctx,
-			oo.pBaseDeviceHandler.deviceID, oo.deviceID) //parent, child
-		if err != nil || device == nil {
-			/*TODO: needs to handle error scenarios */
-			logger.Errorw(ctx, "Failed to fetch device", log.Fields{"err": err, "ParentId": oo.pBaseDeviceHandler.deviceID,
-				"ChildId": oo.deviceID})
-			return fmt.Errorf("failed to fetch device %s", oo.deviceID)
-		}
-
 		if omciTxRequest.withFramePrint {
 			logger.Debugw(ctx, "omci-message-to-send:", log.Fields{
 				"TxOmciMessage": hex.EncodeToString(omciTxRequest.txFrame),
@@ -2706,7 +2699,7 @@
 	return nil
 }
 
-func (oo *omciCC) sendDownloadSection(ctx context.Context, timeout int, highPrio bool,
+func (oo *omciCC) sendDownloadSection(ctx context.Context, aTimeout int, highPrio bool,
 	rxChan chan Message, aImageMeID uint16, aAckRequest uint8, aDownloadSectionNo uint8, aSection []byte, aPrint bool) error {
 	tid := oo.getNextTid(highPrio)
 	logger.Debugw(ctx, "send DlSectionRequest:", log.Fields{"device-id": oo.deviceID,
@@ -2716,8 +2709,10 @@
 	//TODO!!!: don't know by now on how to generate the possibly needed AR (or enforce it to 0) with current omci-lib
 	//    by now just try to send it as defined by omci-lib
 	msgType := omci.DownloadSectionRequestType
+	var timeout int = 0 //default value for no response expected
 	if aAckRequest > 0 {
 		msgType = omci.DownloadSectionRequestWithResponseType
+		timeout = aTimeout
 	}
 	omciLayer := &omci.OMCI{
 		TransactionID: tid,
@@ -2754,6 +2749,8 @@
 	}
 
 	omciRxCallbackPair := callbackPair{cbKey: tid,
+		// the callback is set even though no response might be required here, the tid (key) setting is needed here anyway
+		//   (used to avoid retransmission of frames with the same TID)
 		cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, printFrame /*aPrint*/},
 	}
 	err = oo.send(ctx, outgoingPacket, timeout, cDefaultRetries, highPrio, omciRxCallbackPair)
@@ -3014,52 +3011,58 @@
 
 func (oo *omciCC) processRequestMonitoring(ctx context.Context, aOmciTxRequest omciTransferStructure) {
 
-	chSuccess := make(chan bool)
-	aOmciTxRequest.chSuccess = chSuccess
-
-	tid := aOmciTxRequest.cbPair.cbKey
 	timeout := aOmciTxRequest.timeout
-	retries := aOmciTxRequest.retries
-
-	oo.mutexMonReq.Lock()
-	oo.monitoredRequests[tid] = aOmciTxRequest
-	oo.mutexMonReq.Unlock()
-
-	retryCounter := 0
-loop:
-	for retryCounter <= retries {
-
+	if timeout == 0 {
+		//timeout 0 indicates that no response is expected - fire and forget
 		oo.mutexTxQueue.Lock()
 		oo.txQueue.PushBack(aOmciTxRequest) // enqueue
 		oo.mutexTxQueue.Unlock()
-
 		go oo.sendNextRequest(ctx)
+	} else {
+		chSuccess := make(chan bool)
+		aOmciTxRequest.chSuccess = chSuccess
+		tid := aOmciTxRequest.cbPair.cbKey
+		retries := aOmciTxRequest.retries
 
-		select {
-		case success := <-chSuccess:
-			if success {
-				logger.Debugw(ctx, "reqMon: response received in time",
-					log.Fields{"tid": tid, "device-id": oo.deviceID})
-			} else {
-				logger.Debugw(ctx, "reqMon: wait for response aborted",
-					log.Fields{"tid": tid, "device-id": oo.deviceID})
-			}
-			break loop
-		case <-time.After(time.Duration(timeout) * time.Second):
-			if retryCounter == retries {
-				logger.Errorw(ctx, "reqMon: timeout waiting for response - no of max retries reached!",
-					log.Fields{"tid": tid, "retries": retryCounter, "device-id": oo.deviceID})
+		oo.mutexMonReq.Lock()
+		oo.monitoredRequests[tid] = aOmciTxRequest
+		oo.mutexMonReq.Unlock()
+
+		retryCounter := 0
+	loop:
+		for retryCounter <= retries {
+
+			oo.mutexTxQueue.Lock()
+			oo.txQueue.PushBack(aOmciTxRequest) // enqueue
+			oo.mutexTxQueue.Unlock()
+			go oo.sendNextRequest(ctx)
+
+			select {
+			case success := <-chSuccess:
+				if success {
+					logger.Debugw(ctx, "reqMon: response received in time",
+						log.Fields{"tid": tid, "device-id": oo.deviceID})
+				} else {
+					logger.Debugw(ctx, "reqMon: wait for response aborted",
+						log.Fields{"tid": tid, "device-id": oo.deviceID})
+				}
 				break loop
-			} else {
-				logger.Infow(ctx, "reqMon: timeout waiting for response - retry",
-					log.Fields{"tid": tid, "retries": retryCounter, "device-id": oo.deviceID})
+			case <-time.After(time.Duration(timeout) * time.Second):
+				if retryCounter == retries {
+					logger.Errorw(ctx, "reqMon: timeout waiting for response - no of max retries reached!",
+						log.Fields{"tid": tid, "retries": retryCounter, "device-id": oo.deviceID})
+					break loop
+				} else {
+					logger.Infow(ctx, "reqMon: timeout waiting for response - retry",
+						log.Fields{"tid": tid, "retries": retryCounter, "device-id": oo.deviceID})
+				}
 			}
+			retryCounter++
 		}
-		retryCounter++
+		oo.mutexMonReq.Lock()
+		delete(oo.monitoredRequests, tid)
+		oo.mutexMonReq.Unlock()
 	}
-	oo.mutexMonReq.Lock()
-	delete(oo.monitoredRequests, tid)
-	oo.mutexMonReq.Unlock()
 }
 
 //CancelRequestMonitoring terminates monitoring of outstanding omci requests
diff --git a/internal/pkg/onuadaptercore/omci_onu_upgrade.go b/internal/pkg/onuadaptercore/omci_onu_upgrade.go
index 58e78a4..1e542ce 100644
--- a/internal/pkg/onuadaptercore/omci_onu_upgrade.go
+++ b/internal/pkg/onuadaptercore/omci_onu_upgrade.go
@@ -19,8 +19,10 @@
 
 import (
 	"context"
+	"encoding/binary"
 	"fmt"
 	"strconv"
+	"sync"
 	"time"
 
 	"github.com/boguslaw-wojcik/crc32a"
@@ -38,16 +40,17 @@
 	cOmciDownloadSectionSize     = 31 //in bytes
 	cOmciDownloadWindowSizeLimit = 31 //in sections for window offset (windowSize(32)-1)
 	//cOmciDownloadWindowRetryMax  = 2    // max attempts for a specific window
-	cOmciSectionInterleaveMilliseconds = 100 //DownloadSection interleave time in milliseconds
-	cOmciEndSwDlDelaySeconds           = 1   //End Software Download delay after last section (may be also configurable?)
-	cWaitCountEndSwDl                  = 6   //maximum number of EndSwDl requests
-	cWaitDelayEndSwDlSeconds           = 10  //duration, how long is waited before next request on EndSwDl
+	cOmciSectionInterleaveMilliseconds = 0  //DownloadSection interleave time in milliseconds (0 for no delay)
+	cOmciEndSwDlDelaySeconds           = 1  //End Software Download delay after last section (may be also configurable?)
+	cWaitCountEndSwDl                  = 6  //maximum number of EndSwDl requests
+	cWaitDelayEndSwDlSeconds           = 10 //duration, how long is waited before next request on EndSwDl
 	//cOmciDownloadCompleteTimeout = 5400 //in s for the complete timeout (may be better scale to image size/ noOfWindows)
 )
 
 const (
 	// events of config PON ANI port FSM
 	upgradeEvStart              = "upgradeEvStart"
+	upgradeEvAdapterDownload    = "upgradeEvAdapterDownload"
 	upgradeEvPrepareSwDownload  = "upgradeEvPrepareSwDownload"
 	upgradeEvRxStartSwDownload  = "upgradeEvRxStartSwDownload"
 	upgradeEvWaitWindowAck      = "upgradeEvWaitWindowAck"
@@ -55,6 +58,7 @@
 	upgradeEvEndSwDownload      = "upgradeEvEndSwDownload"
 	upgradeEvWaitEndDownload    = "upgradeEvWaitEndDownload"
 	upgradeEvContinueFinalize   = "upgradeEvContinueFinalize"
+	upgradeEvWaitForActivate    = "upgradeEvWaitForActivate"
 	upgradeEvRequestActivate    = "upgradeEvRequestActivate"
 	upgradeEvWaitForCommit      = "upgradeEvWaitForCommit"
 	upgradeEvCommitSw           = "upgradeEvCommitSw"
@@ -71,11 +75,13 @@
 	// states of config PON ANI port FSM
 	upgradeStDisabled           = "upgradeStDisabled"
 	upgradeStStarting           = "upgradeStStarting"
+	upgradeStWaitingAdapterDL   = "upgradeStWaitingAdapterDL"
 	upgradeStPreparingDL        = "upgradeStPreparingDL"
 	upgradeStDLSection          = "upgradeStDLSection"
 	upgradeStVerifyWindow       = "upgradeStVerifyWindow"
 	upgradeStFinalizeDL         = "upgradeStFinalizeDL"
 	upgradeStWaitEndDL          = "upgradeStWaitEndDL"
+	upgradeStWaitForActivate    = "upgradeStWaitForActivate"
 	upgradeStRequestingActivate = "upgradeStRequestingActivate"
 	upgradeStWaitForCommit      = "upgradeStWaitForCommit"
 	upgradeStCommitSw           = "upgradeStCommitSw"
@@ -90,32 +96,46 @@
 type OnuUpgradeFsm struct {
 	pDeviceHandler   *deviceHandler
 	pDownloadManager *adapterDownloadManager
+	pFileManager     *fileDownloadManager //used from R2.8 with new API version
 	deviceID         string
 	pOnuOmciDevice   *OnuDeviceEntry
 	pOmciCC          *omciCC
 	pOnuDB           *onuDeviceDB
 	requestEvent     OnuDeviceEvent
 	//omciMIdsResponseReceived chan bool //seperate channel needed for checking multiInstance OMCI message responses
-	pAdaptFsm                    *AdapterFsm
-	pImageDsc                    *voltha.ImageDownload
-	imageBuffer                  []byte
-	origImageLength              uint32        //as also limited by OMCI
-	imageCRC                     uint32        //as per OMCI - ITU I.363.5 crc
-	imageLength                  uint32        //including last bytes padding
-	omciDownloadWindowSizeLimit  uint8         //windowSize-1 in sections
-	omciDownloadWindowSizeLast   uint8         //number of sections in last window
-	noOfSections                 uint32        //uint32 range for sections should be sufficient for very long images
-	nextDownloadSectionsAbsolute uint32        //number of next section to download in overall image
-	nextDownloadSectionsWindow   uint8         //number of next section to download within current window
-	noOfWindows                  uint32        //uint32 range for windows should be sufficient for very long images
-	nextDownloadWindow           uint32        //number of next window to download
-	inactiveImageMeID            uint16        //ME-ID of the inactive image
-	omciSectionInterleaveDelay   time.Duration //DownloadSectionInterleave delay in milliseconds
-	delayEndSwDl                 bool          //flag to provide a delay between last section and EndSwDl
-	pLastTxMeInstance            *me.ManagedEntity
-	waitCountEndSwDl             uint8         //number, how often is waited for EndSwDl at maximum
-	waitDelayEndSwDl             time.Duration //duration, how long is waited before next request on EndSwDl
-	chReceiveExpectedResponse    chan bool
+	pAdaptFsm                        *AdapterFsm
+	pImageDsc                        *voltha.ImageDownload
+	imageBuffer                      []byte
+	origImageLength                  uint32        //as also limited by OMCI
+	imageCRC                         uint32        //as per OMCI - ITU I.363.5 crc
+	imageLength                      uint32        //including last bytes padding
+	omciDownloadWindowSizeLimit      uint8         //windowSize-1 in sections
+	omciDownloadWindowSizeLast       uint8         //number of sections in last window
+	noOfSections                     uint32        //uint32 range for sections should be sufficient for very long images
+	nextDownloadSectionsAbsolute     uint32        //number of next section to download in overall image
+	nextDownloadSectionsWindow       uint8         //number of next section to download within current window
+	noOfWindows                      uint32        //uint32 range for windows should be sufficient for very long images
+	nextDownloadWindow               uint32        //number of next window to download
+	inactiveImageMeID                uint16        //ME-ID of the inactive image
+	downloadToOnuTimeout4MB          time.Duration //timeout for downloading the image to the ONU for a 4MB image slice
+	omciSectionInterleaveDelay       time.Duration //DownloadSectionInterleave delay in milliseconds
+	delayEndSwDl                     bool          //flag to provide a delay between last section and EndSwDl
+	pLastTxMeInstance                *me.ManagedEntity
+	waitCountEndSwDl                 uint8         //number, how often is waited for EndSwDl at maximum
+	waitDelayEndSwDl                 time.Duration //duration, how long is waited before next request on EndSwDl
+	chReceiveExpectedResponse        chan bool
+	useAPIVersion43                  bool //flag for indication on which API version is used (and accordingly which specific methods)
+	mutexUpgradeParams               sync.RWMutex
+	imageVersion                     string //name of the image as used within OMCI (and on extrenal API interface)
+	imageIdentifier                  string //name of the image as used in the adapter
+	mutexIsAwaitingAdapterDlResponse sync.RWMutex
+	chAdapterDlReady                 chan bool
+	isWaitingForAdapterDlResponse    bool
+	mutexIsAwaitingOnuDlResponse     sync.RWMutex
+	chOnuDlReady                     chan bool
+	isWaitingForOnuDlResponse        bool
+	activateImage                    bool
+	commitImage                      bool
 }
 
 //NewOnuUpgradeFsm is the 'constructor' for the state machine to config the PON ANI ports
@@ -136,6 +156,8 @@
 		waitDelayEndSwDl:            cWaitDelayEndSwDlSeconds,
 	}
 	instFsm.chReceiveExpectedResponse = make(chan bool)
+	instFsm.chAdapterDlReady = make(chan bool)
+	instFsm.chOnuDlReady = make(chan bool)
 
 	instFsm.pAdaptFsm = NewAdapterFsm(aName, instFsm.deviceID, aCommChannel)
 	if instFsm.pAdaptFsm == nil {
@@ -147,17 +169,20 @@
 		upgradeStDisabled,
 		fsm.Events{
 			{Name: upgradeEvStart, Src: []string{upgradeStDisabled}, Dst: upgradeStStarting},
-			{Name: upgradeEvPrepareSwDownload, Src: []string{upgradeStStarting}, Dst: upgradeStPreparingDL},
+			{Name: upgradeEvAdapterDownload, Src: []string{upgradeStStarting}, Dst: upgradeStWaitingAdapterDL},
+			{Name: upgradeEvPrepareSwDownload, Src: []string{upgradeStStarting, upgradeStWaitingAdapterDL}, Dst: upgradeStPreparingDL},
 			{Name: upgradeEvRxStartSwDownload, Src: []string{upgradeStPreparingDL}, Dst: upgradeStDLSection},
 			{Name: upgradeEvWaitWindowAck, Src: []string{upgradeStDLSection}, Dst: upgradeStVerifyWindow},
 			{Name: upgradeEvContinueNextWindow, Src: []string{upgradeStVerifyWindow}, Dst: upgradeStDLSection},
 			{Name: upgradeEvEndSwDownload, Src: []string{upgradeStVerifyWindow}, Dst: upgradeStFinalizeDL},
 			{Name: upgradeEvWaitEndDownload, Src: []string{upgradeStFinalizeDL}, Dst: upgradeStWaitEndDL},
 			{Name: upgradeEvContinueFinalize, Src: []string{upgradeStWaitEndDL}, Dst: upgradeStFinalizeDL},
-			{Name: upgradeEvRequestActivate, Src: []string{upgradeStWaitEndDL}, Dst: upgradeStRequestingActivate},
+			{Name: upgradeEvWaitForActivate, Src: []string{upgradeStWaitEndDL}, Dst: upgradeStWaitForActivate},
+			{Name: upgradeEvRequestActivate, Src: []string{upgradeStStarting, upgradeStWaitEndDL, upgradeStWaitForActivate},
+				Dst: upgradeStRequestingActivate}, //allows also for direct activation (without download) [TODO!!!]
 			{Name: upgradeEvWaitForCommit, Src: []string{upgradeStRequestingActivate}, Dst: upgradeStWaitForCommit},
 			{Name: upgradeEvCommitSw, Src: []string{upgradeStStarting, upgradeStWaitForCommit},
-				Dst: upgradeStCommitSw},
+				Dst: upgradeStCommitSw}, //allows also for direct commitment (without download) [TODO!!!]
 			{Name: upgradeEvCheckCommitted, Src: []string{upgradeStCommitSw}, Dst: upgradeStCheckCommitted},
 
 			/*
@@ -167,19 +192,20 @@
 					upgradeStCreatingGemNCTPs, upgradeStCreatingGemIWs, upgradeStSettingPQs}, Dst: upgradeStStarting},
 			*/
 			// exceptional treatments
-			{Name: upgradeEvReset, Src: []string{upgradeStStarting, upgradeStPreparingDL, upgradeStDLSection,
-				upgradeStVerifyWindow, upgradeStDLSection, upgradeStFinalizeDL, upgradeStWaitEndDL, upgradeStRequestingActivate,
-				upgradeStCommitSw, upgradeStCheckCommitted}, //upgradeStWaitForCommit is not reset (later perhaps also not upgradeStWaitActivate)
+			{Name: upgradeEvReset, Src: []string{upgradeStStarting, upgradeStWaitingAdapterDL, upgradeStPreparingDL, upgradeStDLSection,
+				upgradeStVerifyWindow, upgradeStDLSection, upgradeStFinalizeDL, upgradeStWaitEndDL, upgradeStWaitForActivate,
+				upgradeStRequestingActivate, upgradeStCommitSw, upgradeStCheckCommitted}, //upgradeStWaitForCommit is not reset (later perhaps also not upgradeStWaitActivate)
 				Dst: upgradeStResetting},
-			{Name: upgradeEvAbort, Src: []string{upgradeStStarting, upgradeStPreparingDL, upgradeStDLSection,
-				upgradeStVerifyWindow, upgradeStDLSection, upgradeStFinalizeDL, upgradeStWaitEndDL, upgradeStRequestingActivate,
-				upgradeStWaitForCommit, upgradeStCommitSw, upgradeStCheckCommitted},
+			{Name: upgradeEvAbort, Src: []string{upgradeStStarting, upgradeStWaitingAdapterDL, upgradeStPreparingDL, upgradeStDLSection,
+				upgradeStVerifyWindow, upgradeStDLSection, upgradeStFinalizeDL, upgradeStWaitEndDL, upgradeStWaitForActivate,
+				upgradeStRequestingActivate, upgradeStWaitForCommit, upgradeStCommitSw, upgradeStCheckCommitted},
 				Dst: upgradeStResetting},
 			{Name: upgradeEvRestart, Src: []string{upgradeStResetting}, Dst: upgradeStDisabled},
 		},
 		fsm.Callbacks{
 			"enter_state":                          func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(ctx, e) },
 			"enter_" + upgradeStStarting:           func(e *fsm.Event) { instFsm.enterStarting(ctx, e) },
+			"enter_" + upgradeStWaitingAdapterDL:   func(e *fsm.Event) { instFsm.enterWaitingAdapterDL(ctx, e) },
 			"enter_" + upgradeStPreparingDL:        func(e *fsm.Event) { instFsm.enterPreparingDL(ctx, e) },
 			"enter_" + upgradeStDLSection:          func(e *fsm.Event) { instFsm.enterDownloadSection(ctx, e) },
 			"enter_" + upgradeStVerifyWindow:       func(e *fsm.Event) { instFsm.enterVerifyWindow(ctx, e) },
@@ -203,6 +229,7 @@
 }
 
 //SetDownloadParams configures the needed parameters for a specific download to the ONU
+//  called from 'old' API Activate_image_update()
 func (oFsm *OnuUpgradeFsm) SetDownloadParams(ctx context.Context, aInactiveImageID uint16,
 	apImageDsc *voltha.ImageDownload, apDownloadManager *adapterDownloadManager) error {
 	pBaseFsm := oFsm.pAdaptFsm.pFsm
@@ -214,7 +241,7 @@
 		oFsm.pDownloadManager = apDownloadManager
 
 		go func(aPBaseFsm *fsm.FSM) {
-			// let the upgrade FSm proceed to PreparinDL
+			// let the upgrade FSM proceed to PreparingDL
 			_ = aPBaseFsm.Event(upgradeEvPrepareSwDownload)
 		}(pBaseFsm)
 		return nil
@@ -224,6 +251,193 @@
 	return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer or state for device-id: %s", oFsm.deviceID))
 }
 
+//SetDownloadParamsAfterDownload configures the needed parameters for a specific download to the ONU according to
+//  updated API interface with R2.8: start download to ONU if the image is downloaded to the adapter
+//  called from 'new' API Download_onu_image
+func (oFsm *OnuUpgradeFsm) SetDownloadParamsAfterDownload(ctx context.Context, aInactiveImageID uint16,
+	apImageRequest *voltha.DeviceImageDownloadRequest, apDownloadManager *fileDownloadManager,
+	aImageIdentifier string, aDownloadTimeout time.Duration) error {
+	oFsm.mutexUpgradeParams.Lock()
+	var pBaseFsm *fsm.FSM = nil
+	if oFsm.pAdaptFsm != nil {
+		pBaseFsm = oFsm.pAdaptFsm.pFsm
+	}
+	if pBaseFsm != nil && pBaseFsm.Is(upgradeStStarting) {
+		logger.Debugw(ctx, "OnuUpgradeFsm Parameter setting", log.Fields{
+			"device-id": oFsm.deviceID, "image-description": apImageRequest})
+		oFsm.useAPIVersion43 = true
+		oFsm.inactiveImageMeID = aInactiveImageID //upgrade state machines run on configured inactive ImageId
+		oFsm.pFileManager = apDownloadManager
+		oFsm.imageIdentifier = aImageIdentifier
+		oFsm.imageVersion = apImageRequest.Image.Version
+		oFsm.activateImage = apImageRequest.ActivateOnSuccess
+		oFsm.commitImage = apImageRequest.CommitOnSuccess
+		oFsm.downloadToOnuTimeout4MB = aDownloadTimeout
+		//TODO: currently straightforward options activate and commit are expected to be set and (unconditionally) done
+		//  for separate handling of these options the FSM must accordingly branch from the concerned states - later
+		oFsm.mutexUpgradeParams.Unlock()
+		_ = pBaseFsm.Event(upgradeEvAdapterDownload) //no need to call the FSM event in background here
+		return nil
+	}
+	oFsm.mutexUpgradeParams.Unlock()
+	logger.Errorw(ctx, "OnuUpgradeFsm abort: invalid FSM base pointer or state", log.Fields{
+		"device-id": oFsm.deviceID})
+	return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer or state for device-id: %s", oFsm.deviceID))
+}
+
+//SetActivationParamsRunning sets the activate and commit flags for a running download to the ONU according to adapters rpc call
+//  called from 'new' API Activate_onu_image
+func (oFsm *OnuUpgradeFsm) SetActivationParamsRunning(ctx context.Context,
+	aImageIdentifier string, aCommit bool) error {
+	oFsm.mutexUpgradeParams.Lock()
+	//set activate/commit independent from state, if FSM is already beyond concerned states, then it does not matter anyway
+	//  (as long as the Imageidentifier is correct)
+	logger.Debugw(ctx, "OnuUpgradeFsm activate/commit parameter setting", log.Fields{
+		"device-id": oFsm.deviceID, "image-id": aImageIdentifier, "commit": aCommit})
+	if aImageIdentifier != oFsm.imageIdentifier {
+		logger.Errorw(ctx, "OnuUpgradeFsm abort: mismatching upgrade image", log.Fields{
+			"device-id": oFsm.deviceID, "request-image": aImageIdentifier, "fsm-image": oFsm.imageIdentifier})
+		oFsm.mutexUpgradeParams.Unlock()
+		return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm params ignored: requested image-name not used in current upgrade for device-id: %s",
+			oFsm.deviceID))
+	}
+	oFsm.activateImage = true
+	oFsm.commitImage = aCommit
+	oFsm.mutexUpgradeParams.Unlock()
+	var pBaseFsm *fsm.FSM = nil
+	if oFsm.pAdaptFsm != nil {
+		pBaseFsm = oFsm.pAdaptFsm.pFsm
+	}
+	if pBaseFsm != nil {
+		if pBaseFsm.Is(upgradeStWaitForActivate) {
+			logger.Debugw(ctx, "OnuUpgradeFsm finish waiting for activate", log.Fields{"device-id": oFsm.deviceID})
+			_ = pBaseFsm.Event(upgradeEvRequestActivate) //no need to call the FSM event in background here
+		}
+		return nil
+	}
+	logger.Errorw(ctx, "OnuUpgradeFsm abort: invalid FSM base pointer", log.Fields{
+		"device-id": oFsm.deviceID})
+	return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer for device-id: %s", oFsm.deviceID))
+}
+
+//SetActivationParamsStart starts upgrade processing with immediate activation
+//  called from 'new' API Activate_onu_image
+func (oFsm *OnuUpgradeFsm) SetActivationParamsStart(ctx context.Context, aImageVersion string, aInactiveImageID uint16, aCommit bool) error {
+	oFsm.mutexUpgradeParams.Lock()
+	var pBaseFsm *fsm.FSM = nil
+	if oFsm.pAdaptFsm != nil {
+		pBaseFsm = oFsm.pAdaptFsm.pFsm
+	}
+	if pBaseFsm != nil && pBaseFsm.Is(upgradeStStarting) {
+		logger.Debugw(ctx, "OnuUpgradeFsm Parameter setting to start with activation", log.Fields{
+			"device-id": oFsm.deviceID, "image-version": aImageVersion})
+		oFsm.useAPIVersion43 = true
+		oFsm.inactiveImageMeID = aInactiveImageID //upgrade state machines run on configured inactive ImageId
+		oFsm.imageVersion = aImageVersion
+		oFsm.activateImage = true
+		oFsm.commitImage = aCommit
+		oFsm.mutexUpgradeParams.Unlock()
+		//directly request the FSM to activate the image
+		_ = pBaseFsm.Event(upgradeEvRequestActivate) //no need to call the FSM event in background here
+		return nil
+	}
+	oFsm.mutexUpgradeParams.Unlock()
+	logger.Errorw(ctx, "OnuUpgradeFsm abort: invalid FSM base pointer or state", log.Fields{
+		"device-id": oFsm.deviceID})
+	return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer or state for device-id: %s", oFsm.deviceID))
+}
+
+//SetCommitmentParamsRunning sets the commit flag for a running download to the ONU according to adapters rpc call
+//  called from 'new' API Commit_onu_image
+func (oFsm *OnuUpgradeFsm) SetCommitmentParamsRunning(ctx context.Context, aImageIdentifier string) error {
+	oFsm.mutexUpgradeParams.Lock()
+	//set commit independent from state, if FSM is already beyond commit state (just ready), then it does not matter anyway
+	//  (as long as the Imageidentifier is correct)
+	logger.Debugw(ctx, "OnuUpgradeFsm commit parameter setting", log.Fields{
+		"device-id": oFsm.deviceID, "image-id": aImageIdentifier})
+	if aImageIdentifier != oFsm.imageIdentifier {
+		logger.Errorw(ctx, "OnuUpgradeFsm abort: mismatching upgrade image", log.Fields{
+			"device-id": oFsm.deviceID, "request-image": aImageIdentifier, "fsm-image": oFsm.imageIdentifier})
+		oFsm.mutexUpgradeParams.Unlock()
+		return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm params ignored: requested image-name not used in current upgrade for device-id: %s",
+			oFsm.deviceID))
+	}
+	oFsm.commitImage = true
+	oFsm.mutexUpgradeParams.Unlock()
+	var pBaseFsm *fsm.FSM = nil
+	if oFsm.pAdaptFsm != nil {
+		pBaseFsm = oFsm.pAdaptFsm.pFsm
+	}
+	if pBaseFsm != nil {
+		if pBaseFsm.Is(upgradeStWaitForCommit) {
+			logger.Debugw(ctx, "OnuUpgradeFsm finish waiting for commit", log.Fields{"device-id": oFsm.deviceID})
+			_ = pBaseFsm.Event(upgradeEvCommitSw) //no need to call the FSM event in background here
+		}
+		return nil
+	}
+	logger.Errorw(ctx, "OnuUpgradeFsm abort: invalid FSM base pointer", log.Fields{
+		"device-id": oFsm.deviceID})
+	return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer for device-id: %s", oFsm.deviceID))
+}
+
+//SetCommitmentParamsStart starts upgrade processing with immediate commitment
+//  called from 'new' API Commit_onu_image
+func (oFsm *OnuUpgradeFsm) SetCommitmentParamsStart(ctx context.Context, aImageVersion string, aActiveImageID uint16) error {
+	oFsm.mutexUpgradeParams.Lock()
+	var pBaseFsm *fsm.FSM = nil
+	if oFsm.pAdaptFsm != nil {
+		pBaseFsm = oFsm.pAdaptFsm.pFsm
+	}
+	if pBaseFsm != nil && pBaseFsm.Is(upgradeStStarting) {
+		logger.Debugw(ctx, "OnuUpgradeFsm Parameter setting to start with commitment", log.Fields{
+			"device-id": oFsm.deviceID, "image-version": aImageVersion})
+		oFsm.useAPIVersion43 = true
+		oFsm.inactiveImageMeID = aActiveImageID //upgrade state machines inactive ImageId is the new active ImageId
+		oFsm.imageVersion = aImageVersion
+		oFsm.commitImage = true
+		oFsm.mutexUpgradeParams.Unlock()
+		//directly request the FSM to activate the image
+		_ = pBaseFsm.Event(upgradeEvCommitSw) //no need to call the FSM event in background here
+		return nil
+	}
+	oFsm.mutexUpgradeParams.Unlock()
+	logger.Errorw(ctx, "OnuUpgradeFsm abort: invalid FSM base pointer or state", log.Fields{
+		"device-id": oFsm.deviceID})
+	return fmt.Errorf(fmt.Sprintf("OnuUpgradeFsm abort: invalid FSM base pointer or state for device-id: %s", oFsm.deviceID))
+}
+
+//CancelProcessing ensures that suspended processing at waiting on some response is aborted and reset of FSM
+func (oFsm *OnuUpgradeFsm) CancelProcessing(ctx context.Context) {
+	//mutex protection is required for possible concurrent access to FSM members
+	//attention: for an unbuffered channel the sender is blocked until the value is received (processed)!
+	// accordingly the mutex must be released before sending to channel here (mutex acquired in receiver)
+	oFsm.mutexIsAwaitingAdapterDlResponse.RLock()
+	if oFsm.isWaitingForAdapterDlResponse {
+		oFsm.mutexIsAwaitingAdapterDlResponse.RUnlock()
+		//use channel to indicate that the download response waiting shall be aborted for this device (channel)
+		oFsm.chAdapterDlReady <- false
+	} else {
+		oFsm.mutexIsAwaitingAdapterDlResponse.RUnlock()
+	}
+	//chOnuDlReady is cleared as part of the FSM reset processing (from enterResetting())
+
+	// in any case (even if it might be automatically requested by above cancellation of waiting) ensure resetting the FSM
+	// specific here: If the FSM is in upgradeStWaitForCommit, it is left there for possibly later commit
+	// this possibly also refers later to (not yet existing) upgradeStWaitForActivate (with ctl API changes)
+	pAdaptFsm := oFsm.pAdaptFsm
+	if pAdaptFsm != nil {
+		// calling FSM events in background to avoid blocking of the caller
+		go func(aPAFsm *AdapterFsm) {
+			if aPAFsm.pFsm != nil {
+				if aPAFsm.pFsm.Is(upgradeStWaitEndDL) {
+					oFsm.chReceiveExpectedResponse <- false //which aborts the FSM (activate was not yet sent)
+				}
+				_ = aPAFsm.pFsm.Event(upgradeEvReset) //anyway and for all other states
+			} //else the FSM seems already to be in some released state
+		}(pAdaptFsm)
+	}
+}
+
 func (oFsm *OnuUpgradeFsm) enterStarting(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "OnuUpgradeFsm start", log.Fields{"in state": e.FSM.Current(),
 		"device-id": oFsm.deviceID})
@@ -232,11 +446,26 @@
 	go oFsm.processOmciUpgradeMessages(ctx)
 }
 
+//enterWaitingAdapterDL state can only be reached with useAPIVersion43
+func (oFsm *OnuUpgradeFsm) enterWaitingAdapterDL(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "OnuUpgradeFsm waiting for adapter download", log.Fields{"in state": e.FSM.Current(),
+		"device-id": oFsm.deviceID})
+	go oFsm.waitOnDownloadToAdapterReady(ctx, oFsm.chAdapterDlReady)
+	go oFsm.pFileManager.RequestDownloadReady(ctx, oFsm.imageIdentifier, oFsm.chAdapterDlReady)
+}
+
 func (oFsm *OnuUpgradeFsm) enterPreparingDL(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "OnuUpgradeFsm prepare Download to Onu", log.Fields{"in state": e.FSM.Current(),
 		"device-id": oFsm.deviceID})
 
-	fileLen, err := oFsm.pDownloadManager.getImageBufferLen(ctx, oFsm.pImageDsc.Name, oFsm.pImageDsc.LocalDir)
+	var fileLen int64
+	var err error
+	if oFsm.useAPIVersion43 {
+		//with the new API structure download to adapter is implicit and we have to wait until the image is available
+		fileLen, err = oFsm.pFileManager.GetImageBufferLen(ctx, oFsm.imageIdentifier)
+	} else {
+		fileLen, err = oFsm.pDownloadManager.getImageBufferLen(ctx, oFsm.pImageDsc.Name, oFsm.pImageDsc.LocalDir)
+	}
 	if err != nil || fileLen > int64(cMaxUint32) {
 		logger.Errorw(ctx, "OnuUpgradeFsm abort: problems getting image buffer length", log.Fields{
 			"device-id": oFsm.deviceID, "error": err, "length": fileLen})
@@ -248,8 +477,13 @@
 		return
 	}
 
+	//copy file content to buffer
 	oFsm.imageBuffer = make([]byte, fileLen)
-	oFsm.imageBuffer, err = oFsm.pDownloadManager.getDownloadImageBuffer(ctx, oFsm.pImageDsc.Name, oFsm.pImageDsc.LocalDir)
+	if oFsm.useAPIVersion43 {
+		oFsm.imageBuffer, err = oFsm.pFileManager.GetDownloadImageBuffer(ctx, oFsm.imageIdentifier)
+	} else {
+		oFsm.imageBuffer, err = oFsm.pDownloadManager.getDownloadImageBuffer(ctx, oFsm.pImageDsc.Name, oFsm.pImageDsc.LocalDir)
+	}
 	if err != nil {
 		logger.Errorw(ctx, "OnuUpgradeFsm abort: can't get image buffer", log.Fields{
 			"device-id": oFsm.deviceID, "error": err})
@@ -263,14 +497,16 @@
 
 	oFsm.noOfSections = uint32(fileLen / cOmciDownloadSectionSize)
 	if fileLen%cOmciDownloadSectionSize > 0 {
-		bufferPadding := make([]byte, cOmciDownloadSectionSize-uint32(fileLen%cOmciDownloadSectionSize))
+		bufferPadding := make([]byte, cOmciDownloadSectionSize-uint32((fileLen)%cOmciDownloadSectionSize))
 		//expand the imageBuffer to exactly fit multiples of cOmciDownloadSectionSize with padding
-		oFsm.imageBuffer = append(oFsm.imageBuffer[:fileLen], bufferPadding...)
+		oFsm.imageBuffer = append(oFsm.imageBuffer[:(fileLen)], bufferPadding...)
 		oFsm.noOfSections++
 	}
 	oFsm.origImageLength = uint32(fileLen)
 	oFsm.imageLength = uint32(len(oFsm.imageBuffer))
 
+	go oFsm.waitOnDownloadToOnuReady(ctx, oFsm.chOnuDlReady) // start supervision of the complete download-to-ONU procedure
+
 	logger.Infow(ctx, "OnuUpgradeFsm starts with StartSwDl values", log.Fields{
 		"MeId": oFsm.inactiveImageMeID, "windowSizeLimit": oFsm.omciDownloadWindowSizeLimit,
 		"ImageSize": oFsm.imageLength, "original file size": fileLen})
@@ -457,9 +693,17 @@
 		}
 		if success {
 			//answer received with ready indication
-			go func(a_pAFsm *AdapterFsm) {
-				_ = a_pAFsm.pFsm.Event(upgradeEvRequestActivate)
-			}(pBaseFsm)
+			if oFsm.activateImage {
+				//immediate activation requested
+				go func(a_pAFsm *AdapterFsm) {
+					_ = a_pAFsm.pFsm.Event(upgradeEvRequestActivate)
+				}(pBaseFsm)
+			} else {
+				//have to wait on explicit activation request
+				go func(a_pAFsm *AdapterFsm) {
+					_ = a_pAFsm.pFsm.Event(upgradeEvWaitForActivate)
+				}(pBaseFsm)
+			}
 			return
 		}
 		//timer was aborted
@@ -557,6 +801,16 @@
 func (oFsm *OnuUpgradeFsm) enterResetting(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "OnuUpgradeFsm resetting", log.Fields{"device-id": oFsm.deviceID})
 
+	// in case the download-to-ONU timer is still running - cancel it
+	oFsm.mutexIsAwaitingOnuDlResponse.RLock()
+	if oFsm.isWaitingForOnuDlResponse {
+		oFsm.mutexIsAwaitingOnuDlResponse.RUnlock()
+		//use channel to indicate that the download response waiting shall be aborted for this device (channel)
+		oFsm.chOnuDlReady <- false
+	} else {
+		oFsm.mutexIsAwaitingOnuDlResponse.RUnlock()
+	}
+
 	pConfigupgradeStateAFsm := oFsm.pAdaptFsm
 	if pConfigupgradeStateAFsm != nil {
 		// abort running message processing
@@ -580,12 +834,7 @@
 
 func (oFsm *OnuUpgradeFsm) enterDisabled(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "OnuUpgradeFsm enters disabled state", log.Fields{"device-id": oFsm.deviceID})
-	//flush possible left-over channels
-	select {
-	case <-oFsm.chReceiveExpectedResponse:
-		logger.Debug(ctx, "OnuUpgradeFsm chReceiveExpectedResponse flushed", log.Fields{"for device-id": oFsm.deviceID})
-	default:
-	}
+	// no need to flush possible channels here, Upgrade FSM will be completely removed, garbage collector should find its way
 	if oFsm.pDeviceHandler != nil {
 		//request removal of 'reference' in the Handler (completely clear the FSM and its data)
 		go oFsm.pDeviceHandler.removeOnuUpgradeFsm(ctx)
@@ -732,8 +981,13 @@
 						_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvAbort)
 						return
 					}
-					oFsm.delayEndSwDl = true                                                      //ensure a delay for the EndSwDl message
-					oFsm.imageCRC = crc32a.Checksum(oFsm.imageBuffer[:int(oFsm.origImageLength)]) //store internal for multiple usage
+					oFsm.delayEndSwDl = true //ensure a delay for the EndSwDl message
+					//CRC computation for all data bytes of the file
+					imageCRC := crc32a.Checksum(oFsm.imageBuffer[:int(oFsm.origImageLength)]) //store internal for multiple usage
+					//revert the retrieved CRC Byte Order (seems not to deliver NetworkByteOrder)
+					var byteSlice []byte = make([]byte, 4)
+					binary.LittleEndian.PutUint32(byteSlice, uint32(imageCRC))
+					oFsm.imageCRC = binary.BigEndian.Uint32(byteSlice)
 					_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvEndSwDownload)
 					return
 				}
@@ -792,6 +1046,14 @@
 			}
 			if msgObj.EntityInstance == oFsm.inactiveImageMeID {
 				logger.Debugw(ctx, "Expected EndSwDlResponse received", log.Fields{"device-id": oFsm.deviceID})
+				oFsm.mutexIsAwaitingOnuDlResponse.RLock()
+				if oFsm.isWaitingForOnuDlResponse {
+					oFsm.mutexIsAwaitingOnuDlResponse.RUnlock()
+					//use channel to indicate that the download to ONU was successful
+					oFsm.chOnuDlReady <- true
+				} else {
+					oFsm.mutexIsAwaitingOnuDlResponse.RUnlock()
+				}
 				oFsm.chReceiveExpectedResponse <- true //let the FSM proceed from the waitState
 				return
 			}
@@ -922,12 +1184,27 @@
 			logger.Debugw(ctx, "OnuUpgradeFsm - GetResponse Data for SoftwareImage",
 				log.Fields{"device-id": oFsm.deviceID, "entityID": msgObj.EntityInstance,
 					"version": imageVersion, "isActive": imageIsActive, "isCommitted": imageIsCommitted})
-
-			//a check on the delivered image version is not done, the ONU delivered version might be different from what might have been
-			//  indicated in the download image version string (version must be part of the image content itself)
-			//  so checking that might be quite unreliable
 			if msgObj.EntityInstance == oFsm.inactiveImageMeID && imageIsActive == swIsActive &&
 				imageIsCommitted == swIsCommitted {
+				//a check on the delivered image version is not done, the ONU delivered version might be different from what might have been
+				//  indicated in the download image version string (version must be part of the image content itself)
+				//  so checking that might be quite unreliable
+				//but with new API this was changed, assumption is that omci image version is known at download request and exactly that is used
+				//  in all the API references, so it can and should be checked here now
+				if oFsm.useAPIVersion43 {
+					if imageVersion != oFsm.imageVersion {
+						//new active version indicated on OMCI from ONU is not the expected version
+						logger.Errorw(ctx, "OnuUpgradeFsm SwImage GetResponse indications not matching requested upgrade",
+							log.Fields{"device-id": oFsm.deviceID, "ResponseMeId": msgObj.EntityInstance,
+								"onu-version": imageVersion, "expected-version": oFsm.imageVersion})
+						// TODO!!!: error treatment?
+						//TODO!!!: possibly send event information for aborted upgrade (aborted by omci processing)??
+						_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvAbort)
+						return
+					}
+					logger.Debugw(ctx, "OnuUpgradeFsm - expected ONU image version indicated by the ONU",
+						log.Fields{"device-id": oFsm.deviceID})
+				}
 				logger.Infow(ctx, "requested SW image committed, releasing OnuUpgrade", log.Fields{"device-id": oFsm.deviceID})
 				//releasing the upgrade FSM
 				_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvReset)
@@ -949,23 +1226,106 @@
 	}
 }
 
-/*
-func (oFsm *OnuUpgradeFsm) waitforOmciResponse(ctx context.Context) error {
+//waitOnDownloadToAdapterReady state can only be reached with useAPIVersion43 (usage of pFileManager)
+func (oFsm *OnuUpgradeFsm) waitOnDownloadToAdapterReady(ctx context.Context, aWaitChannel chan bool) {
+	downloadToAdapterTimeout := oFsm.pFileManager.GetDownloadTimeout(ctx)
+	oFsm.mutexIsAwaitingAdapterDlResponse.Lock()
+	oFsm.isWaitingForAdapterDlResponse = true
+	oFsm.mutexIsAwaitingAdapterDlResponse.Unlock()
 	select {
 	// maybe be also some outside cancel (but no context modeled for the moment ...)
 	// case <-ctx.Done():
-	// 		logger.Infow(ctx,"LockState-bridge-init message reception canceled", log.Fields{"for device-id": oFsm.deviceID})
-	case <-time.After(30 * time.Second): //AS FOR THE OTHER OMCI FSM's
-		logger.Warnw(ctx, "OnuUpgradeFsm multi entity timeout", log.Fields{"for device-id": oFsm.deviceID})
-		return fmt.Errorf("OnuUpgradeFsm multi entity timeout %s", oFsm.deviceID)
-	case success := <-oFsm.omciMIdsResponseReceived:
-		if success {
-			logger.Debug(ctx, "OnuUpgradeFsm multi entity response received")
-			return nil
+	// 		logger.Infow("OnuUpgradeFsm-waitOnDownloadToAdapterReady canceled", log.Fields{"for device-id": oFsm.deviceID})
+	case <-time.After(downloadToAdapterTimeout): //10s should be enough for downloading some image to the adapter
+		logger.Warnw(ctx, "OnuUpgradeFsm Waiting-adapter-download timeout", log.Fields{
+			"for device-id": oFsm.deviceID, "image-id": oFsm.imageIdentifier, "timeout": downloadToAdapterTimeout})
+		oFsm.pFileManager.RemoveReadyRequest(ctx, oFsm.imageIdentifier, aWaitChannel)
+		oFsm.mutexIsAwaitingAdapterDlResponse.Lock()
+		oFsm.isWaitingForAdapterDlResponse = false
+		oFsm.mutexIsAwaitingAdapterDlResponse.Unlock()
+		//the upgrade process has to be aborted
+		pUpgradeFsm := oFsm.pAdaptFsm
+		if pUpgradeFsm != nil {
+			_ = pUpgradeFsm.pFsm.Event(upgradeEvReset)
+		} else {
+			logger.Errorw(ctx, "pUpgradeFsm is nil", log.Fields{"device-id": oFsm.deviceID})
 		}
-		// should not happen so far
-		logger.Warnw(ctx, "OnuUpgradeFsm multi entity response error", log.Fields{"for device-id": oFsm.deviceID})
-		return fmt.Errorf("OnuUpgradeFsm multi entity responseError %s", oFsm.deviceID)
+		return
+
+	case success := <-aWaitChannel:
+		if success {
+			logger.Debugw(ctx, "OnuUpgradeFsm image-downloaded received", log.Fields{"device-id": oFsm.deviceID})
+			oFsm.mutexIsAwaitingAdapterDlResponse.Lock()
+			oFsm.isWaitingForAdapterDlResponse = false
+			oFsm.mutexIsAwaitingAdapterDlResponse.Unlock()
+			//let the upgrade process proceed
+			pUpgradeFsm := oFsm.pAdaptFsm
+			if pUpgradeFsm != nil {
+				_ = pUpgradeFsm.pFsm.Event(upgradeEvPrepareSwDownload)
+			} else {
+				logger.Errorw(ctx, "pUpgradeFsm is nil", log.Fields{"device-id": oFsm.deviceID})
+			}
+			return
+		}
+		// waiting was aborted (probably on external request)
+		logger.Debugw(ctx, "OnuUpgradeFsm Waiting-adapter-download aborted", log.Fields{"device-id": oFsm.deviceID})
+		oFsm.pFileManager.RemoveReadyRequest(ctx, oFsm.imageIdentifier, aWaitChannel)
+		oFsm.mutexIsAwaitingAdapterDlResponse.Lock()
+		oFsm.isWaitingForAdapterDlResponse = false
+		oFsm.mutexIsAwaitingAdapterDlResponse.Unlock()
+		//the upgrade process has to be aborted
+		pUpgradeFsm := oFsm.pAdaptFsm
+		if pUpgradeFsm != nil {
+			_ = pUpgradeFsm.pFsm.Event(upgradeEvAbort)
+		} else {
+			logger.Errorw(ctx, "pUpgradeFsm is nil", log.Fields{"device-id": oFsm.deviceID})
+		}
+		return
 	}
 }
-*/
+
+//waitOnDownloadToOnuReady state can only be reached with useAPIVersion43 (usage of pFileManager)
+func (oFsm *OnuUpgradeFsm) waitOnDownloadToOnuReady(ctx context.Context, aWaitChannel chan bool) {
+	downloadToOnuTimeout := time.Duration(1+(oFsm.imageLength/0x400000)) * oFsm.downloadToOnuTimeout4MB
+	logger.Debugw(ctx, "OnuUpgradeFsm start download-to-ONU timer", log.Fields{"device-id": oFsm.deviceID,
+		"duration": downloadToOnuTimeout})
+	oFsm.mutexIsAwaitingOnuDlResponse.Lock()
+	oFsm.isWaitingForOnuDlResponse = true
+	oFsm.mutexIsAwaitingOnuDlResponse.Unlock()
+	select {
+	// maybe be also some outside cancel (but no context modeled for the moment ...)
+	// case <-ctx.Done():
+	// 		logger.Infow("OnuUpgradeFsm-waitOnDownloadToOnuReady canceled", log.Fields{"for device-id": oFsm.deviceID})
+	case <-time.After(downloadToOnuTimeout): //using an image-size depending timout (in minutes)
+		logger.Warnw(ctx, "OnuUpgradeFsm Waiting-ONU-download timeout", log.Fields{
+			"for device-id": oFsm.deviceID, "image-id": oFsm.imageIdentifier, "timeout": downloadToOnuTimeout})
+		oFsm.mutexIsAwaitingOnuDlResponse.Lock()
+		oFsm.isWaitingForOnuDlResponse = false
+		oFsm.mutexIsAwaitingOnuDlResponse.Unlock()
+		//the upgrade process has to be aborted
+		pUpgradeFsm := oFsm.pAdaptFsm
+		if pUpgradeFsm != nil {
+			_ = pUpgradeFsm.pFsm.Event(upgradeEvAbort)
+		} else {
+			logger.Errorw(ctx, "pUpgradeFsm is nil", log.Fields{"device-id": oFsm.deviceID})
+		}
+		return
+
+	case success := <-aWaitChannel:
+		if success {
+			logger.Debugw(ctx, "OnuUpgradeFsm image-downloaded on ONU received", log.Fields{"device-id": oFsm.deviceID})
+			oFsm.mutexIsAwaitingOnuDlResponse.Lock()
+			oFsm.isWaitingForOnuDlResponse = false
+			oFsm.mutexIsAwaitingOnuDlResponse.Unlock()
+			//all fine, let the FSM proceed like defined from the sender of this event
+			return
+		}
+		// waiting was aborted (assumed here to be caused by
+		//   error detection or cancel at download after upgrade FSM reset/abort)
+		logger.Debugw(ctx, "OnuUpgradeFsm Waiting-ONU-download aborted", log.Fields{"device-id": oFsm.deviceID})
+		oFsm.mutexIsAwaitingOnuDlResponse.Lock()
+		oFsm.isWaitingForOnuDlResponse = false
+		oFsm.mutexIsAwaitingOnuDlResponse.Unlock()
+		return
+	}
+}
diff --git a/internal/pkg/onuadaptercore/openonu.go b/internal/pkg/onuadaptercore/openonu.go
index d1d6b09..c61ce5f 100644
--- a/internal/pkg/onuadaptercore/openonu.go
+++ b/internal/pkg/onuadaptercore/openonu.go
@@ -68,10 +68,12 @@
 	maxTimeoutInterAdapterComm time.Duration
 	maxTimeoutReconciling      time.Duration
 	pDownloadManager           *adapterDownloadManager
+	pFileManager               *fileDownloadManager //let coexist 'old and new' DownloadManager as long as 'old' does not get obsolete
 	metricsEnabled             bool
 	mibAuditInterval           time.Duration
 	omciTimeout                int // in seconds
 	alarmAuditInterval         time.Duration
+	dlToOnuTimeout4M           time.Duration
 }
 
 //NewOpenONUAC returns a new instance of OpenONU_AC
@@ -107,6 +109,7 @@
 	// since consumers of OMCI timeout value everywhere in code is in "int seconds", do this useful conversion
 	openOnuAc.omciTimeout = int(cfg.OmciTimeout.Seconds())
 	openOnuAc.alarmAuditInterval = cfg.AlarmAuditInterval
+	openOnuAc.dlToOnuTimeout4M = cfg.DownloadToOnuTimeout4MB
 
 	openOnuAc.pSupportedFsms = &OmciDeviceFsms{
 		"mib-synchronizer": {
@@ -126,6 +129,8 @@
 	}
 
 	openOnuAc.pDownloadManager = newAdapterDownloadManager(ctx)
+	openOnuAc.pFileManager = newFileDownloadManager(ctx)
+	openOnuAc.pFileManager.SetDownloadTimeout(ctx, cfg.DownloadToAdapterTimeout)
 
 	return &openOnuAc
 }
@@ -606,11 +611,87 @@
 }
 
 //if update >= 4.3.0
+// Note: already with the implementation of the 'old' download interface problems were detected when the argument name used here is not the same
+//   as defined in the adapter interface file. That sounds strange and the effects were strange as well.
+//   The reason for that was never finally investigated.
+//   To be on the safe side argument names are left here always as defined in iAdapter.go .
 
 // Download_onu_image downloads (and optionally activates and commits) the indicated ONU image to the requested ONU(s)
 //   if the image is not yet present on the adapter it has to be automatically downloaded
 func (oo *OpenONUAC) Download_onu_image(ctx context.Context, request *voltha.DeviceImageDownloadRequest) (*voltha.DeviceImageResponse, error) {
-	return nil, errors.New("unImplemented")
+	if request != nil && len((*request).DeviceId) > 0 && (*request).Image.Version != "" {
+		loResponse := voltha.DeviceImageResponse{}
+		imageIdentifier := (*request).Image.Version
+		//inform the deviceHandler about (possibly new) requested ONU download requests
+		firstDevice := true
+		var vendorID string
+		for _, pCommonID := range (*request).DeviceId {
+			loDeviceID := (*pCommonID).Id
+			onuVolthaDevice, err := oo.coreProxy.GetDevice(log.WithSpanFromContext(context.TODO(), ctx),
+				loDeviceID, loDeviceID)
+			if err != nil || onuVolthaDevice == nil {
+				logger.Warnw(ctx, "Failed to fetch Onu device for image download",
+					log.Fields{"device-id": loDeviceID, "err": err})
+				continue //try the work with next deviceId
+			}
+			if firstDevice {
+				//start/verify download of the image to the adapter based on first found device only
+				//  use the OnuVendor identification from first given device
+				firstDevice = false
+				vendorID = onuVolthaDevice.VendorId
+				imageIdentifier = vendorID + imageIdentifier //head on vendor ID of the ONU
+				logger.Debugw(ctx, "download request for file", log.Fields{"image-id": imageIdentifier})
+
+				if !oo.pFileManager.ImageExists(ctx, imageIdentifier) {
+					logger.Debugw(ctx, "start image download", log.Fields{"image-description": request})
+					// Download_image is not supposed to be blocking, anyway let's call the DownloadManager still synchronously to detect 'fast' problems
+					// the download itself is later done in background
+					if err := oo.pFileManager.StartDownload(ctx, imageIdentifier, (*request).Image.Url); err != nil {
+						return nil, err
+					}
+				}
+				// image already exists
+				logger.Debugw(ctx, "image already downloaded", log.Fields{"image-description": imageIdentifier})
+			} else {
+				//for all following devices verify the matching vendorID
+				if onuVolthaDevice.VendorId != vendorID {
+					logger.Warnw(ctx, "onu vendor id does not match image vendor id, device ignored",
+						log.Fields{"onu-vendor-id": onuVolthaDevice.VendorId, "image-vendor-id": vendorID})
+					continue //try the work with next deviceId
+				}
+			}
+			// start the ONU download activity for each possible device
+			// assumption here is, that the concerned device was already created (automatic start after device creation not supported)
+			if handler := oo.getDeviceHandler(ctx, loDeviceID, false); handler != nil {
+				logger.Debugw(ctx, "image download on omci requested", log.Fields{
+					"image-id": imageIdentifier, "device-id": loDeviceID})
+				//onu upgrade handling called in background without immediate error evaluation here
+				//  as the processing can be done for multiple ONU's and an error on one ONU should not stop processing for others
+				//  state/progress/success of the request has to be verified using the Get_onu_image_status() API
+				go handler.onuSwUpgradeAfterDownload(ctx, request, oo.pFileManager, imageIdentifier)
+				loDeviceImageState := voltha.DeviceImageState{}
+				loDeviceImageState.DeviceId = loDeviceID
+				loDeviceImageState.ImageState.Version = (*request).Image.Version
+				loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_STARTED
+				loDeviceImageState.ImageState.Reason = voltha.ImageState_NO_ERROR
+				loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_UNKNOWN
+				loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+			} else {
+				//cannot start ONU download for requested device
+				logger.Warnw(ctx, "no handler found for image activation", log.Fields{"device-id": loDeviceID})
+				loDeviceImageState := voltha.DeviceImageState{}
+				loDeviceImageState.DeviceId = loDeviceID
+				loDeviceImageState.ImageState.Version = (*request).Image.Version
+				loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_FAILED
+				loDeviceImageState.ImageState.Reason = voltha.ImageState_UNKNOWN_ERROR
+				loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_UNKNOWN
+				loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+			}
+		}
+		pImageResp := &loResponse
+		return pImageResp, nil
+	}
+	return nil, errors.New("invalid image download parameters")
 }
 
 // Get_onu_image_status delivers the adapter-related information about the download/activation/commitment
@@ -630,15 +711,93 @@
 }
 
 // Activate_onu_image initiates the activation of the image for the requested ONU(s)
-//  precondition: image downloaded and not yet activated
+//  precondition: image downloaded and not yet activated or image refers to current inactive image
 func (oo *OpenONUAC) Activate_onu_image(ctx context.Context, in *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) {
-	return nil, errors.New("unImplemented")
+	if in != nil && len((*in).DeviceId) > 0 && (*in).Version != "" {
+		loResponse := voltha.DeviceImageResponse{}
+		imageIdentifier := (*in).Version
+		//let the deviceHandler find the adequate way of requesting the image activation
+		for _, pCommonID := range (*in).DeviceId {
+			loDeviceID := (*pCommonID).Id
+			//compared to download procedure the vendorID (from device) is secondary here
+			//   and only needed in case the upgrade process is based on some ongoing download process (and can be retrieved in deviceHandler if needed)
+			// start image activation activity for each possible device
+			// assumption here is, that the concerned device was already created (automatic start after device creation not supported)
+			if handler := oo.getDeviceHandler(ctx, loDeviceID, false); handler != nil {
+				logger.Debugw(ctx, "onu image activation requested", log.Fields{
+					"image-id": imageIdentifier, "device-id": loDeviceID})
+				//onu activation handling called in background without immediate error evaluation here
+				//  as the processing can be done for multiple ONU's and an error on one ONU should not stop processing for others
+				//  state/progress/success of the request has to be verified using the Get_onu_image_status() API
+				go handler.onuSwActivateRequest(ctx, imageIdentifier, (*in).CommitOnSuccess)
+				loDeviceImageState := voltha.DeviceImageState{}
+				loDeviceImageState.DeviceId = loDeviceID
+				loDeviceImageState.ImageState.Version = imageIdentifier
+				loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_UNKNOWN
+				loDeviceImageState.ImageState.Reason = voltha.ImageState_NO_ERROR
+				loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_ACTIVATING
+				loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+			} else {
+				//cannot start SW activation for requested device
+				logger.Warnw(ctx, "no handler found for image activation", log.Fields{"device-id": loDeviceID})
+				loDeviceImageState := voltha.DeviceImageState{}
+				loDeviceImageState.DeviceId = loDeviceID
+				loDeviceImageState.ImageState.Version = imageIdentifier
+				loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_UNKNOWN
+				loDeviceImageState.ImageState.Reason = voltha.ImageState_UNKNOWN_ERROR
+				loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_ACTIVATION_ABORTED
+				loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+			}
+		}
+		pImageResp := &loResponse
+		return pImageResp, nil
+	}
+	return nil, errors.New("invalid image activation parameters")
 }
 
 // Commit_onu_image enforces the commitment of the image for the requested ONU(s)
 //  precondition: image activated and not yet committed
 func (oo *OpenONUAC) Commit_onu_image(ctx context.Context, in *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) {
-	return nil, errors.New("unImplemented")
+	if in != nil && len((*in).DeviceId) > 0 && (*in).Version != "" {
+		loResponse := voltha.DeviceImageResponse{}
+		imageIdentifier := (*in).Version
+		//let the deviceHandler find the adequate way of requesting the image activation
+		for _, pCommonID := range (*in).DeviceId {
+			loDeviceID := (*pCommonID).Id
+			//compared to download procedure the vendorID (from device) is secondary here
+			//   and only needed in case the upgrade process is based on some ongoing download process (and can be retrieved in deviceHandler if needed)
+			// start image activation activity for each possible device
+			// assumption here is, that the concerned device was already created (automatic start after device creation not supported)
+			if handler := oo.getDeviceHandler(ctx, loDeviceID, false); handler != nil {
+				logger.Debugw(ctx, "onu image commitment requested", log.Fields{
+					"image-id": imageIdentifier, "device-id": loDeviceID})
+				//onu commitment handling called in background without immediate error evaluation here
+				//  as the processing can be done for multiple ONU's and an error on one ONU should not stop processing for others
+				//  state/progress/success of the request has to be verified using the Get_onu_image_status() API
+				go handler.onuSwCommitRequest(ctx, imageIdentifier)
+				loDeviceImageState := voltha.DeviceImageState{}
+				loDeviceImageState.DeviceId = loDeviceID
+				loDeviceImageState.ImageState.Version = imageIdentifier
+				loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_UNKNOWN
+				loDeviceImageState.ImageState.Reason = voltha.ImageState_NO_ERROR
+				loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_COMMITTING
+				loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+			} else {
+				//cannot start SW commitment for requested device
+				logger.Warnw(ctx, "no handler found for image commitment", log.Fields{"device-id": loDeviceID})
+				loDeviceImageState := voltha.DeviceImageState{}
+				loDeviceImageState.DeviceId = loDeviceID
+				loDeviceImageState.ImageState.Version = imageIdentifier
+				loDeviceImageState.ImageState.DownloadState = voltha.ImageState_DOWNLOAD_UNKNOWN
+				loDeviceImageState.ImageState.Reason = voltha.ImageState_UNKNOWN_ERROR
+				loDeviceImageState.ImageState.ImageState = voltha.ImageState_IMAGE_COMMIT_ABORTED
+				loResponse.DeviceImageStates = append(loResponse.DeviceImageStates, &loDeviceImageState)
+			}
+		}
+		pImageResp := &loResponse
+		return pImageResp, nil
+	}
+	return nil, errors.New("invalid image commitment parameters")
 }
 
 // Adapter interface required methods ################ end #########