VOL-3052 Onu Software preliminary upgrade test from Adapter to ONU, version 1.2.5-dev168

Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: I5f48645fa8b392c2c79f13cf8fd0d2369bfd5ca5
diff --git a/internal/pkg/onuadaptercore/adapter_download_manager.go b/internal/pkg/onuadaptercore/adapter_download_manager.go
index cf3718e..aaaacd6 100644
--- a/internal/pkg/onuadaptercore/adapter_download_manager.go
+++ b/internal/pkg/onuadaptercore/adapter_download_manager.go
@@ -18,8 +18,9 @@
 package adaptercoreonu
 
 import (
+	"bufio"
 	"context"
-	//"errors"
+	"os"
 	"sync"
 
 	//"time"
@@ -41,6 +42,8 @@
 type adapterDownloadManager struct {
 	mutexDownloadImageDsc sync.RWMutex
 	downloadImageDscSlice []*voltha.ImageDownload
+	// maybe just for test purpose
+	arrayFileFragment [32]byte
 }
 
 //newAdapterDownloadManager constructor returns a new instance of a adapterDownloadManager
@@ -49,7 +52,6 @@
 	logger.Debug(ctx, "init-adapterDownloadManager")
 	var localDnldMgr adapterDownloadManager
 	localDnldMgr.downloadImageDscSlice = make([]*voltha.ImageDownload, 0)
-
 	return &localDnldMgr
 }
 
@@ -69,11 +71,133 @@
 	return false
 }
 
+//imageLocallyDownloaded returns true if the requested image already exists within the adapter
+func (dm *adapterDownloadManager) imageLocallyDownloaded(ctx context.Context, apImageDsc *voltha.ImageDownload) bool {
+	logger.Debugw(ctx, "checking if image is fully downloaded", log.Fields{"image-name": (*apImageDsc).Name})
+	dm.mutexDownloadImageDsc.RLock()
+	defer dm.mutexDownloadImageDsc.RUnlock()
+
+	for _, pDnldImgDsc := range dm.downloadImageDscSlice {
+		if (*pDnldImgDsc).Name == (*apImageDsc).Name {
+			//image found (by name)
+			if (*pDnldImgDsc).DownloadState == voltha.ImageDownload_DOWNLOAD_SUCCEEDED {
+				logger.Debugw(ctx, "image has been fully downloaded", log.Fields{"image-name": (*apImageDsc).Name})
+				return true
+			}
+			logger.Debugw(ctx, "image not yet fully downloaded", log.Fields{"image-name": (*apImageDsc).Name})
+			return false
+		}
+	}
+	//image not found (by name)
+	logger.Errorw(ctx, "image does not exist", log.Fields{"image-name": (*apImageDsc).Name})
+	return false
+}
+
 //startDownload returns true if the download of the requested image could be started
 func (dm *adapterDownloadManager) startDownload(ctx context.Context, apImageDsc *voltha.ImageDownload) error {
 	logger.Warnw(ctx, "image download requested - but not yet processed", log.Fields{"image-name": apImageDsc.Name})
+	newImageDscPos := len(dm.downloadImageDscSlice)
+	dm.downloadImageDscSlice = append(dm.downloadImageDscSlice, apImageDsc)
+	dm.downloadImageDscSlice[newImageDscPos].DownloadState = voltha.ImageDownload_DOWNLOAD_STARTED
+	//just some basic test file simulation
+	go dm.writeFileToLFS(ctx, apImageDsc.Name, apImageDsc.LocalDir)
 	//return success to comfort the core processing during integration
 	return nil
 	// TODO!!: also verify error response behavior
 	//return fmt.Errorf("onuSwUpgrade not yet implemented")
 }
+
+//writeFileToLFS writes the downloaded file to the local file system
+func (dm *adapterDownloadManager) writeFileToLFS(ctx context.Context, aFileName string, aLocalPath string) {
+	// by now just a simulation to write a file with predefined 'variable' content
+	totalFileLength := 0
+	logger.Debugw(ctx, "Writing fixed size simulation file locally", log.Fields{
+		"image-name": aFileName, "image-path": aLocalPath})
+	file, err := os.Create(aLocalPath + "/" + aFileName)
+	if err == nil {
+		// write 32KB test file
+		for totalFileLength < 32*1024 {
+			if written, wrErr := file.Write(dm.getIncrementalSliceContent(ctx)); wrErr == nil {
+				totalFileLength += written
+			} else {
+				logger.Errorw(ctx, "Could not write to file", log.Fields{"create-error": wrErr})
+				break //stop writing
+			}
+		}
+	} else {
+		logger.Errorw(ctx, "Could not create file", log.Fields{"create-error": err})
+	}
+
+	fileStats, statsErr := file.Stat()
+	if err != nil {
+		logger.Errorw(ctx, "created file can't be accessed", log.Fields{"stat-error": statsErr})
+	}
+	logger.Debugw(ctx, "Written file size is", log.Fields{"length": fileStats.Size()})
+	//nolint:gosec,errcheck
+	file.Close()
+
+	for _, pDnldImgDsc := range dm.downloadImageDscSlice {
+		if (*pDnldImgDsc).Name == aFileName {
+			//image found (by name)
+			(*pDnldImgDsc).DownloadState = voltha.ImageDownload_DOWNLOAD_SUCCEEDED
+			return //can leave directly
+		}
+	}
+}
+
+//getImageBufferLen returns the length of the specified file in bytes (file size)
+func (dm *adapterDownloadManager) getImageBufferLen(ctx context.Context, aFileName string,
+	aLocalPath string) (int64, error) {
+	//maybe we can also use FileSize from dm.downloadImageDscSlice - future option?
+
+	//nolint:gosec
+	file, err := os.Open(aLocalPath + "/" + aFileName)
+	if err != nil {
+		return 0, err
+	}
+	//nolint:errcheck
+	defer file.Close()
+
+	stats, statsErr := file.Stat()
+	if statsErr != nil {
+		return 0, statsErr
+	}
+
+	return stats.Size(), nil
+}
+
+//getDownloadImageBuffer returns the content of the requested file as byte slice
+func (dm *adapterDownloadManager) getDownloadImageBuffer(ctx context.Context, aFileName string,
+	aLocalPath string) ([]byte, error) {
+	//nolint:gosec
+	file, err := os.Open(aLocalPath + "/" + 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
+}
+
+//getIncrementalSliceContent returns a byte slice of incremented bytes of internal array (used for file emulation)
+// (used for file emulation)
+func (dm *adapterDownloadManager) getIncrementalSliceContent(ctx context.Context) []byte {
+	lastValue := dm.arrayFileFragment[len(dm.arrayFileFragment)-1]
+	for index := range dm.arrayFileFragment {
+		lastValue++
+		dm.arrayFileFragment[index] = lastValue
+	}
+	return dm.arrayFileFragment[:]
+}
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index c55afd3..27d04dc 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -19,7 +19,6 @@
 
 import (
 	"context"
-	"encoding/hex"
 	"errors"
 	"fmt"
 	"strconv"
@@ -102,6 +101,7 @@
 	cAniConfigFsm
 	cUniVlanConfigFsm
 	cL2PmFsm
+	cOnuUpgradeFsm
 )
 
 type omciIdleCheckStruct struct {
@@ -117,6 +117,7 @@
 	cAniConfigFsm:     {(*deviceHandler).isAniConfigFsmInOmciIdleState, cAniFsmIdleState},
 	cUniVlanConfigFsm: {(*deviceHandler).isUniVlanConfigFsmInOmciIdleState, cVlanFsmIdleState},
 	cL2PmFsm:          {(*deviceHandler).isFsmInOmciIdleStateDefault, cL2PmFsmIdleState},
+	cOnuUpgradeFsm:    {(*deviceHandler).isFsmInOmciIdleStateDefault, cOnuUpgradeFsmIdleState},
 }
 
 const (
@@ -203,6 +204,8 @@
 	mutexKvStoreContext        sync.Mutex
 	lockVlanConfig             sync.RWMutex
 	UniVlanConfigFsmMap        map[uint8]*UniVlanConfigFsm
+	lockUpgradeFsm             sync.RWMutex
+	pOnuUpradeFsm              *OnuUpgradeFsm
 	reconciling                bool
 	mutexReconcilingFlag       sync.RWMutex
 	chReconcilingFinished      chan bool //channel to indicate that reconciling has been finished
@@ -233,6 +236,7 @@
 	//TODO initialize the support classes.
 	dh.uniEntityMap = make(map[uint32]*onuUniPort)
 	dh.lockVlanConfig = sync.RWMutex{}
+	dh.lockUpgradeFsm = sync.RWMutex{}
 	dh.UniVlanConfigFsmMap = make(map[uint8]*UniVlanConfigFsm)
 	dh.reconciling = false
 	dh.chReconcilingFinished = make(chan bool)
@@ -319,10 +323,12 @@
 		return err
 	}
 
+	/* msg print moved symmetrically to omci_cc, if wanted here as additional debug, than perhaps only based on additional debug setting!
 	//assuming omci message content is hex coded!
 	// with restricted output of 16(?) bytes would be ...omciMsg.Message[:16]
 	logger.Debugw(ctx, "inter-adapter-recv-omci", log.Fields{
 		"device-id": dh.deviceID, "RxOmciMessage": hex.EncodeToString(omciMsg.Message)})
+	*/
 	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
 	if pDevEntry != nil {
 		if pDevEntry.PDevOmciCC != nil {
@@ -965,11 +971,47 @@
 }
 
 //doOnuSwUpgrade initiates the SW download transfer to the ONU and on success activates the (inactive) image
-func (dh *deviceHandler) doOnuSwUpgrade(ctx context.Context, apImageDsc *voltha.ImageDownload) error {
-	logger.Warnw(ctx, "onuSwUpgrade not yet implemented in deviceHandler", log.Fields{
+func (dh *deviceHandler) doOnuSwUpgrade(ctx context.Context, apImageDsc *voltha.ImageDownload,
+	apDownloadManager *adapterDownloadManager) error {
+	logger.Debugw(ctx, "onuSwUpgrade requested", log.Fields{
 		"device-id": dh.deviceID, "image-name": (*apImageDsc).Name})
-	//return success to comfort the core processing during integration
-	return nil
+
+	var err error
+	if dh.ReadyForSpecificOmciConfig {
+		dh.lockUpgradeFsm.Lock()
+		defer dh.lockUpgradeFsm.Unlock()
+		if dh.pOnuUpradeFsm == nil {
+			err = dh.createOnuUpgradeFsm(ctx, OmciOnuSwUpgradeDone)
+			if err == nil {
+				if err = dh.pOnuUpradeFsm.SetDownloadParams(ctx, apImageDsc, apDownloadManager); err != nil {
+					logger.Errorw(ctx, "onu upgrade fsm could not set parameters", log.Fields{
+						"device-id": dh.deviceID, "error": err})
+				}
+			} else {
+				logger.Errorw(ctx, "onu upgrade fsm could not be created", log.Fields{
+					"device-id": dh.deviceID, "error": err})
+			}
+		} else { //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})
+				}
+				err = fmt.Errorf("aborted Onu SW upgrade but not automatically started, try again, device-id: %s", dh.deviceID)
+				//TODO!!!: wait for 'ready' to start and configure - see above SetDownloadParams()
+				// for now a second start of download should work again
+			} else { //should never occur
+				logger.Errorw(ctx, "onu upgrade fsm inconsistent setup", log.Fields{
+					"device-id": dh.deviceID})
+				err = fmt.Errorf("onu upgrade fsm inconsistent setup, baseFsm invalid for device-id: %s", dh.deviceID)
+			}
+		}
+	} else {
+		logger.Errorw(ctx, "Start Onu SW upgrade rejected: no active OMCI connection", log.Fields{"device-id": dh.deviceID})
+	}
+	return err
 }
 
 //  deviceHandler methods that implement the adapters interface requests## end #########
@@ -1601,6 +1643,15 @@
 		dh.stopAlarmManager <- true
 	}
 
+	//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)
+	dh.lockUpgradeFsm.RLock()
+	if dh.pOnuUpradeFsm != nil {
+		_ = dh.pOnuUpradeFsm.pAdaptFsm.pFsm.Event(upgradeEvReset)
+	}
+	dh.lockUpgradeFsm.RUnlock()
+
 	return nil
 }
 
@@ -2108,6 +2159,58 @@
 	}
 }
 
+// createOnuUpgradeFsm initializes and runs the Onu Software upgrade FSM
+func (dh *deviceHandler) createOnuUpgradeFsm(ctx context.Context, devEvent OnuDeviceEvent) error {
+	//in here lockUpgradeFsm is already locked
+	chUpgradeFsm := make(chan Message, 2048)
+	var sFsmName = "OnuSwUpgradeFSM"
+	logger.Debugw(ctx, "create OnuSwUpgradeFSM", log.Fields{"device-id": dh.deviceID})
+	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
+	if pDevEntry == nil {
+		logger.Errorw(ctx, "no valid OnuDevice -abort", log.Fields{"device-id": dh.deviceID})
+		return fmt.Errorf(fmt.Sprintf("no valid OnuDevice - abort for device-id: %s", dh.device.Id))
+	}
+	dh.pOnuUpradeFsm = NewOnuUpgradeFsm(ctx, dh, pDevEntry.PDevOmciCC, pDevEntry.pOnuDB, devEvent,
+		sFsmName, chUpgradeFsm)
+	if dh.pOnuUpradeFsm != nil {
+		pUpgradeStatemachine := dh.pOnuUpradeFsm.pAdaptFsm.pFsm
+		if pUpgradeStatemachine != nil {
+			if pUpgradeStatemachine.Is(upgradeStDisabled) {
+				if err := pUpgradeStatemachine.Event(upgradeEvStart); err != nil {
+					logger.Errorw(ctx, "OnuSwUpgradeFSM: can't start", log.Fields{"err": err})
+					// maybe try a FSM reset and then again ... - TODO!!!
+					return fmt.Errorf(fmt.Sprintf("OnuSwUpgradeFSM could not be started for device-id: %s", dh.device.Id))
+				}
+				/***** LockStateFSM started */
+				logger.Debugw(ctx, "OnuSwUpgradeFSM started", log.Fields{
+					"state": pUpgradeStatemachine.Current(), "device-id": dh.deviceID})
+			} else {
+				logger.Errorw(ctx, "wrong state of OnuSwUpgradeFSM to start - want: disabled", log.Fields{
+					"have": pUpgradeStatemachine.Current(), "device-id": dh.deviceID})
+				// maybe try a FSM reset and then again ... - TODO!!!
+				return fmt.Errorf(fmt.Sprintf("OnuSwUpgradeFSM could not be started for device-id: %s, wrong internal state", dh.device.Id))
+			}
+		} else {
+			logger.Errorw(ctx, "OnuSwUpgradeFSM internal FSM invalid - cannot be executed!!", log.Fields{"device-id": dh.deviceID})
+			// maybe try a FSM reset and then again ... - TODO!!!
+			return fmt.Errorf(fmt.Sprintf("OnuSwUpgradeFSM internal FSM could not be created for device-id: %s", dh.device.Id))
+		}
+	} else {
+		logger.Errorw(ctx, "OnuSwUpgradeFSM could not be created  - abort", log.Fields{"device-id": dh.deviceID})
+		return fmt.Errorf(fmt.Sprintf("OnuSwUpgradeFSM could not be created - abort for device-id: %s", dh.device.Id))
+	}
+	return nil
+}
+
+// removeOnuUpgradeFsm clears the Onu Software upgrade FSM
+func (dh *deviceHandler) removeOnuUpgradeFsm(ctx context.Context) {
+	logger.Debugw(ctx, "remove OnuSwUpgradeFSM StateMachine", log.Fields{
+		"device-id": dh.deviceID})
+	dh.lockUpgradeFsm.Lock()
+	defer dh.lockUpgradeFsm.Unlock()
+	dh.pOnuUpradeFsm = nil //resource clearing is left to garbage collector
+}
+
 //setBackend provides a DB backend for the specified path on the existing KV client
 func (dh *deviceHandler) setBackend(ctx context.Context, aBasePathKvStore string) *db.Backend {
 
@@ -2833,6 +2936,12 @@
 				return true //FSM not active - so there is no activity on omci
 			}
 		}
+	case cOnuUpgradeFsm:
+		{
+			dh.lockUpgradeFsm.RLock()
+			defer dh.lockUpgradeFsm.RUnlock()
+			pFsm = dh.pOnuUpradeFsm.pAdaptFsm.pFsm
+		}
 	default:
 		{
 			logger.Errorw(ctx, "invalid stateMachine selected for idle check", log.Fields{
diff --git a/internal/pkg/onuadaptercore/omci_cc.go b/internal/pkg/onuadaptercore/omci_cc.go
index 1ad4e8b..a21761b 100644
--- a/internal/pkg/onuadaptercore/omci_cc.go
+++ b/internal/pkg/onuadaptercore/omci_cc.go
@@ -25,9 +25,7 @@
 	"fmt"
 	"strconv"
 	"sync"
-	"time"
-
-	//"time"
+	"time" //by now for testing
 
 	"github.com/google/gopacket"
 	// TODO!!! Some references could be resolved auto, but some need specific context ....
@@ -81,6 +79,7 @@
 type callbackPairEntry struct {
 	cbRespChannel chan Message
 	cbFunction    func(context.Context, *omci.OMCI, *gp.Packet, chan Message) error
+	framePrint    bool //true for printing
 }
 
 //callbackPair to be used for ReceiveCallback init
@@ -90,10 +89,11 @@
 }
 
 type omciTransferStructure struct {
-	txFrame  []byte
-	timeout  int
-	retry    int
-	highPrio bool
+	txFrame        []byte
+	timeout        int
+	retry          int
+	highPrio       bool
+	withFramePrint bool
 }
 
 //omciCC structure holds information needed for OMCI communication (to/from OLT Adapter)
@@ -258,6 +258,14 @@
 	*/
 }
 
+func (oo *omciCC) printRxMessage(ctx context.Context, rxMsg []byte) {
+	//assuming omci message content is hex coded!
+	// with restricted output of 16bytes would be ...rxMsg[:16]
+	logger.Debugw(ctx, "omci-message-received:", log.Fields{
+		"RxOmciMessage": hex.EncodeToString(rxMsg),
+		"device-id":     oo.deviceID})
+}
+
 // Rx handler for onu messages
 //    e.g. would call ReceiveOnuMessage() in case of TID=0 or Action=test ...
 func (oo *omciCC) receiveMessage(ctx context.Context, rxMsg []byte) error {
@@ -293,28 +301,33 @@
 	} else {
 		logger.Errorw(ctx, "received omci-message too small for OmciBaseFormat - abort",
 			log.Fields{"Length": len(rxMsg), "device-id": oo.deviceID})
+		oo.printRxMessage(ctx, rxMsg)
 		return fmt.Errorf("rxOmciMessage too small for BaseFormat %s", oo.deviceID)
 	}
 
 	packet := gopacket.NewPacket(rxMsg, omci.LayerTypeOMCI, gopacket.NoCopy)
 	if packet == nil {
 		logger.Errorw(ctx, "omci-message could not be decoded", log.Fields{"device-id": oo.deviceID})
+		oo.printRxMessage(ctx, rxMsg)
 		return fmt.Errorf("could not decode rxMsg as OMCI %s", oo.deviceID)
 	}
 	omciLayer := packet.Layer(omci.LayerTypeOMCI)
 	if omciLayer == nil {
 		logger.Errorw(ctx, "omci-message could not decode omci layer", log.Fields{"device-id": oo.deviceID})
+		oo.printRxMessage(ctx, rxMsg)
 		return fmt.Errorf("could not decode omci layer %s", oo.deviceID)
 	}
 	omciMsg, ok := omciLayer.(*omci.OMCI)
 	if !ok {
 		logger.Errorw(ctx, "omci-message could not assign omci layer", log.Fields{"device-id": oo.deviceID})
+		oo.printRxMessage(ctx, rxMsg)
 		return fmt.Errorf("could not assign omci layer %s", oo.deviceID)
 	}
 	logger.Debugw(ctx, "omci-message-decoded:", log.Fields{"omciMsgType": omciMsg.MessageType,
 		"transCorrId": strconv.FormatInt(int64(omciMsg.TransactionID), 16), "DeviceIdent": omciMsg.DeviceIdentifier})
 	if byte(omciMsg.MessageType)&me.AK == 0 {
 		// Not a response
+		oo.printRxMessage(ctx, rxMsg)
 		logger.Debug(ctx, "RxMsg is no Omci Response Message")
 		if omciMsg.TransactionID == 0 {
 			return oo.receiveOnuMessage(ctx, omciMsg, &packet)
@@ -329,6 +342,9 @@
 	oo.mutexRxSchedMap.Lock()
 	rxCallbackEntry, ok := oo.rxSchedulerMap[omciMsg.TransactionID]
 	if ok && rxCallbackEntry.cbFunction != nil {
+		if rxCallbackEntry.framePrint {
+			oo.printRxMessage(ctx, rxMsg)
+		}
 		//disadvantage of decoupling: error verification made difficult, but anyway the question is
 		// how to react on erroneous frame reception, maybe can simply be ignored
 		go rxCallbackEntry.cbFunction(ctx, omciMsg, &packet, rxCallbackEntry.cbRespChannel)
@@ -339,13 +355,13 @@
 		// having posted the response the request is regarded as 'done'
 		delete(oo.rxSchedulerMap, omciMsg.TransactionID)
 		oo.mutexRxSchedMap.Unlock()
-	} else {
-		oo.mutexRxSchedMap.Unlock()
-		logger.Errorw(ctx, "omci-message-response for not registered transCorrId", log.Fields{"device-id": oo.deviceID, "omciMsg": omciMsg, "transCorrId": omciMsg.TransactionID})
-		return fmt.Errorf("could not find registered response handler tor transCorrId %s", oo.deviceID)
+		return nil
 	}
+	oo.mutexRxSchedMap.Unlock()
+	logger.Errorw(ctx, "omci-message-response for not registered transCorrId", log.Fields{"device-id": oo.deviceID})
+	oo.printRxMessage(ctx, rxMsg)
+	return fmt.Errorf("could not find registered response handler tor transCorrId %s", oo.deviceID)
 
-	return nil
 	/* py code was:
 	           Receive and OMCI message from the proxy channel to the OLT.
 
@@ -464,6 +480,7 @@
 	// 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()
 
 	//just use a simple list for starting - might need some more effort, especially for multi source write access
@@ -472,6 +489,7 @@
 		timeout,
 		retry,
 		highPrio,
+		printFrame,
 	}
 	oo.mutexTxQueue.Lock()
 	oo.txQueue.PushBack(omciTxRequest) // enqueue
@@ -556,12 +574,13 @@
 			return fmt.Errorf("failed to fetch device %s", oo.deviceID)
 		}
 
-		logger.Debugw(ctx, "omci-message-to-send:", log.Fields{
-			"TxOmciMessage": hex.EncodeToString(omciTxRequest.txFrame),
-			"device-id":     oo.deviceID,
-			"toDeviceType":  oo.pBaseDeviceHandler.ProxyAddressType,
-			"proxyDeviceID": oo.pBaseDeviceHandler.ProxyAddressID})
-
+		if omciTxRequest.withFramePrint {
+			logger.Debugw(ctx, "omci-message-to-send:", log.Fields{
+				"TxOmciMessage": hex.EncodeToString(omciTxRequest.txFrame),
+				"device-id":     oo.deviceID,
+				"toDeviceType":  oo.pBaseDeviceHandler.ProxyAddressType,
+				"proxyDeviceID": oo.pBaseDeviceHandler.ProxyAddressID})
+		}
 		omciMsg := &ic.InterAdapterOmciMessage{Message: omciTxRequest.txFrame}
 		if sendErr := oo.adapterProxy.SendInterAdapterMessage(log.WithSpanFromContext(context.Background(), ctx), omciMsg,
 			ic.InterAdapterMessageType_OMCI_REQUEST,
@@ -673,7 +692,7 @@
 	}
 	omciRxCallbackPair := callbackPair{
 		cbKey:   tid,
-		cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+		cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse, true},
 	}
 	return oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 }
@@ -694,7 +713,7 @@
 	}
 	omciRxCallbackPair := callbackPair{
 		cbKey:   tid,
-		cbEntry: callbackPairEntry{oo.pOnuDeviceEntry.omciRebootMessageReceivedChannel, oo.receiveOmciResponse},
+		cbEntry: callbackPairEntry{oo.pOnuDeviceEntry.omciRebootMessageReceivedChannel, oo.receiveOmciResponse, true},
 	}
 
 	err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
@@ -731,7 +750,7 @@
 
 	omciRxCallbackPair := callbackPair{
 		cbKey:   tid,
-		cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+		cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse, true},
 	}
 	return oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 }
@@ -754,8 +773,11 @@
 	oo.uploadSequNo++
 
 	omciRxCallbackPair := callbackPair{
-		cbKey:   tid,
-		cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+		cbKey: tid,
+		//frame printing for MibUpload frames disabled now per default to avoid log file abort situations (size/speed?)
+		// if wanted, rx frame printing should be specifically done within the MibUpload FSM or controlled via extra parameter
+		// compare also software upgrade download section handling
+		cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse, false},
 	}
 	return oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 }
@@ -788,7 +810,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -836,7 +858,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -890,7 +912,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -942,7 +964,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -999,7 +1021,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1044,7 +1066,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1089,7 +1111,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1136,7 +1158,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1182,7 +1204,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1224,7 +1246,7 @@
 		}
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1279,7 +1301,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1322,7 +1344,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1365,7 +1387,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1408,7 +1430,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1449,7 +1471,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1490,7 +1512,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1531,7 +1553,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1577,7 +1599,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1623,7 +1645,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1664,7 +1686,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1705,7 +1727,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1746,7 +1768,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1792,7 +1814,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1838,7 +1860,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1884,7 +1906,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1930,7 +1952,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1976,7 +1998,7 @@
 
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -1992,7 +2014,6 @@
 	return nil
 }
 
-// nolint: unused
 func (oo *omciCC) sendCreateMulticastGemIWTPVar(ctx context.Context, timeout int, highPrio bool,
 	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
 	tid := oo.getNextTid(highPrio)
@@ -2016,7 +2037,7 @@
 		}
 
 		omciRxCallbackPair := callbackPair{cbKey: tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -2031,7 +2052,6 @@
 	return nil
 }
 
-// nolint: unused
 func (oo *omciCC) sendSetMulticastGemIWTPVar(ctx context.Context, timeout int, highPrio bool,
 	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
 	tid := oo.getNextTid(highPrio)
@@ -2055,7 +2075,7 @@
 		}
 
 		omciRxCallbackPair := callbackPair{cbKey: tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -2070,7 +2090,6 @@
 	return nil
 }
 
-// nolint: unused
 func (oo *omciCC) sendCreateMulticastOperationProfileVar(ctx context.Context, timeout int, highPrio bool,
 	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
 	tid := oo.getNextTid(highPrio)
@@ -2096,7 +2115,7 @@
 		}
 
 		omciRxCallbackPair := callbackPair{cbKey: tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -2112,7 +2131,6 @@
 	return nil
 }
 
-// nolint: unused
 func (oo *omciCC) sendSetMulticastOperationProfileVar(ctx context.Context, timeout int, highPrio bool,
 	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
 	tid := oo.getNextTid(highPrio)
@@ -2138,7 +2156,7 @@
 		}
 
 		omciRxCallbackPair := callbackPair{cbKey: tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -2154,7 +2172,6 @@
 	return nil
 }
 
-// nolint: unused
 func (oo *omciCC) sendCreateMulticastSubConfigInfoVar(ctx context.Context, timeout int, highPrio bool,
 	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
 	tid := oo.getNextTid(highPrio)
@@ -2180,7 +2197,7 @@
 		}
 
 		omciRxCallbackPair := callbackPair{cbKey: tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -2229,7 +2246,7 @@
 	}
 
 	omciRxCallbackPair := callbackPair{cbKey: tid,
-		cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 	}
 	err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 	if err != nil {
@@ -2279,7 +2296,7 @@
 		}
 
 		omciRxCallbackPair := callbackPair{cbKey: tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -2331,7 +2348,7 @@
 		}
 
 		omciRxCallbackPair := callbackPair{cbKey: tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -2383,7 +2400,7 @@
 		}
 
 		omciRxCallbackPair := callbackPair{cbKey: tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -2435,7 +2452,7 @@
 		}
 
 		omciRxCallbackPair := callbackPair{cbKey: tid,
-			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
 		}
 		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 		if err != nil {
@@ -2452,6 +2469,364 @@
 	return nil
 }
 
+func (oo *omciCC) sendStartSoftwareDownload(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, aImageMeID uint16, aDownloadWindowSize uint8, aFileLen uint32) error {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw(ctx, "send StartSwDlRequest:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(aImageMeID), 16)})
+
+	omciLayer := &omci.OMCI{
+		TransactionID: tid,
+		MessageType:   omci.StartSoftwareDownloadRequestType,
+		// DeviceIdentifier: omci.BaselineIdent,		// Optional, defaults to Baseline
+		// Length:           0x28,						// Optional, defaults to 40 octets
+	}
+	request := &omci.StartSoftwareDownloadRequest{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.SoftwareImageClassID,
+			EntityInstance: aImageMeID, //inactive image
+		},
+		WindowSize:           aDownloadWindowSize,
+		ImageSize:            aFileLen,
+		NumberOfCircuitPacks: 1,           //parallel download to multiple circuit packs not supported
+		CircuitPacks:         []uint16{0}, //circuit pack indication don't care for NumberOfCircuitPacks=1, but needed by omci-lib
+	}
+
+	var options gopacket.SerializeOptions
+	options.FixLengths = true
+	buffer := gopacket.NewSerializeBuffer()
+	err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+	if err != nil {
+		logger.Errorw(ctx, "Cannot serialize StartSwDlRequest", log.Fields{"Err": err,
+			"device-id": oo.deviceID})
+		return err
+	}
+	outgoingPacket := buffer.Bytes()
+
+	omciRxCallbackPair := callbackPair{cbKey: tid,
+		cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
+	}
+	err = oo.send(ctx, outgoingPacket, timeout, 0, highPrio, omciRxCallbackPair)
+	if err != nil {
+		logger.Errorw(ctx, "Cannot send StartSwDlRequest", log.Fields{"Err": err,
+			"device-id": oo.deviceID})
+		return err
+	}
+	logger.Debug(ctx, "send StartSwDlRequest done")
+
+	//**** test simulation - as long as BBSIM does not support ONU SW upgrade *** start *****
+	time.Sleep(time.Millisecond * 200) //give some response time
+	respOmciLayer := &omci.OMCI{
+		TransactionID: tid,
+		MessageType:   omci.StartSoftwareDownloadResponseType,
+		// DeviceIdentifier: omci.BaselineIdent,		// Optional, defaults to Baseline
+		// Length:           0x28,						// Optional, defaults to 40 octets
+	}
+	response := &omci.StartSoftwareDownloadResponse{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.SoftwareImageClassID,
+			EntityInstance: aImageMeID, //inactive image
+		},
+		Result:            0,
+		WindowSize:        aDownloadWindowSize,
+		NumberOfInstances: 0, //seems at the moment I can only generate 0 instances, using 1 here panics as MeResult can not be set below
+		//MeResults: cannot set here: downloadResults type not exported from omci-lib!
+	}
+	var respOptions gopacket.SerializeOptions
+	respOptions.FixLengths = true
+	respBuffer := gopacket.NewSerializeBuffer()
+	respErr := gopacket.SerializeLayers(respBuffer, respOptions, respOmciLayer, response)
+	if respErr != nil {
+		logger.Errorw(ctx, "Cannot serialize StartSwDlResponse", log.Fields{"Err": respErr,
+			"device-id": oo.deviceID})
+		return respErr
+	}
+	respPacket := respBuffer.Bytes()
+	logger.Debugw(ctx, "simulate StartSwDlResponse", log.Fields{"device-id": oo.deviceID,
+		"SequNo":     strconv.FormatInt(int64(tid), 16),
+		"InstId":     strconv.FormatInt(int64(aImageMeID), 16),
+		"windowSize": aDownloadWindowSize})
+	go func(oo *omciCC) {
+		_ = oo.receiveMessage(ctx, respPacket)
+	}(oo)
+	//**** test simulation - as long as BBSIM does not support ONU SW upgrade *** stop *****
+	return nil
+}
+
+func (oo *omciCC) sendDownloadSection(ctx context.Context, timeout 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,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(aImageMeID), 16)})
+
+	//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
+	omciLayer := &omci.OMCI{
+		TransactionID: tid,
+		MessageType:   omci.DownloadSectionRequestType,
+		// DeviceIdentifier: omci.BaselineIdent,		// Optional, defaults to Baseline
+		// Length:           0x28,						// Optional, defaults to 40 octets
+	}
+	//TODO!!!: omci-lib wrongly defines just 29 byte data section (which should be 31 bytes)
+	//  as long as this is valid and testing is done with some dummy image we omit the last two bytes in each section!!!
+	var localSectionData [29]byte
+	copy(localSectionData[:], aSection)
+	request := &omci.DownloadSectionRequest{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.SoftwareImageClassID,
+			EntityInstance: aImageMeID, //inactive image
+		},
+		SectionNumber: aDownloadSectionNo,
+		SectionData:   localSectionData,
+	}
+
+	var options gopacket.SerializeOptions
+	options.FixLengths = true
+	buffer := gopacket.NewSerializeBuffer()
+	err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+	if err != nil {
+		logger.Errorw(ctx, "Cannot serialize DlSectionRequest", log.Fields{"Err": err,
+			"device-id": oo.deviceID})
+		return err
+	}
+	outgoingPacket := buffer.Bytes()
+
+	omciRxCallbackPair := callbackPair{cbKey: tid,
+		cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, aPrint},
+	}
+	err = oo.send(ctx, outgoingPacket, timeout, 0, highPrio, omciRxCallbackPair)
+	if err != nil {
+		logger.Errorw(ctx, "Cannot send DlSectionRequest", log.Fields{"Err": err,
+			"device-id": oo.deviceID})
+		return err
+	}
+	logger.Debug(ctx, "send DlSectionRequest done")
+
+	//**** test simulation - as long as BBSIM does not support ONU SW upgrade *** start *****
+	if aAckRequest > 0 {
+		time.Sleep(time.Millisecond * 200) //give some response time
+		respOmciLayer := &omci.OMCI{
+			TransactionID: tid,
+			MessageType:   omci.DownloadSectionResponseType,
+			// DeviceIdentifier: omci.BaselineIdent,		// Optional, defaults to Baseline
+			// Length:           0x28,						// Optional, defaults to 40 octets
+		}
+		response := &omci.DownloadSectionResponse{
+			MeBasePacket: omci.MeBasePacket{
+				EntityClass:    me.SoftwareImageClassID,
+				EntityInstance: aImageMeID, //inactive image
+			},
+			Result:        0,
+			SectionNumber: aDownloadSectionNo,
+		}
+		var respOptions gopacket.SerializeOptions
+		respOptions.FixLengths = true
+		respBuffer := gopacket.NewSerializeBuffer()
+		respErr := gopacket.SerializeLayers(respBuffer, respOptions, respOmciLayer, response)
+		if respErr != nil {
+			logger.Errorw(ctx, "Cannot serialize DlSectionResponse", log.Fields{"Err": respErr,
+				"device-id": oo.deviceID})
+			return err
+		}
+		respPacket := respBuffer.Bytes()
+		if aPrint {
+			logger.Debugw(ctx, "simulate DlSectionResponse", log.Fields{"device-id": oo.deviceID,
+				"SequNo": strconv.FormatInt(int64(tid), 16),
+				"InstId": strconv.FormatInt(int64(aImageMeID), 16),
+				"packet": hex.EncodeToString(respPacket)})
+		} else {
+			logger.Debugw(ctx, "simulate DlSectionResponse", log.Fields{"device-id": oo.deviceID,
+				"SequNo": strconv.FormatInt(int64(tid), 16),
+				"InstId": strconv.FormatInt(int64(aImageMeID), 16)})
+		}
+		go func(oo *omciCC) {
+			_ = oo.receiveMessage(ctx, respPacket)
+		}(oo)
+	}
+	//**** test simulation - as long as BBSIM does not support ONU SW upgrade *** stop *****
+	return nil
+}
+
+func (oo *omciCC) sendEndSoftwareDownload(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, aImageMeID uint16, aFileLen uint32, aImageCrc uint32) error {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw(ctx, "send EndSwDlRequest:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(aImageMeID), 16)})
+
+	//**** test simulation - as long as omci-lib serialize for this type is not corrected - just bypass sending *** start *****
+	/*
+		omciLayer := &omci.OMCI{
+			TransactionID: tid,
+			MessageType:   omci.EndSoftwareDownloadRequestType,
+			// DeviceIdentifier: omci.BaselineIdent,		// Optional, defaults to Baseline
+			// Length:           0x28,						// Optional, defaults to 40 octets
+		}
+		request := &omci.EndSoftwareDownloadRequest{
+			MeBasePacket: omci.MeBasePacket{
+				EntityClass:    me.SoftwareImageClassID,
+				EntityInstance: aImageMeID, //inactive image
+			},
+			CRC32:             aImageCrc,
+			ImageSize:         aFileLen,
+			NumberOfInstances: 1,           //parallel download to multiple circuit packs not supported
+			ImageInstances:    []uint16{0}, //don't care for NumberOfInstances=1, but probably needed by omci-lib as in startSwDlRequest
+		}
+
+		var options gopacket.SerializeOptions
+		options.FixLengths = true
+		buffer := gopacket.NewSerializeBuffer()
+		err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+		if err != nil {
+			logger.Errorw(ctx, "Cannot serialize EndSwDlRequest", log.Fields{"Err": err,
+				"device-id": oo.deviceID})
+			return err
+		}
+		outgoingPacket := buffer.Bytes()
+
+		omciRxCallbackPair := callbackPair{cbKey: tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
+		}
+		err = oo.send(ctx, outgoingPacket, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw(ctx, "Cannot send EndSwDlRequest", log.Fields{"Err": err,
+				"device-id": oo.deviceID})
+			return err
+		}
+	*/
+	//**** test simulation - as long as omci-lib serialize for this type is not corrected - just bypass sending *** end *****
+	logger.Debug(ctx, "send EndSwDlRequest done")
+
+	//**** test simulation - as long as BBSIM does not support ONU SW upgrade *** start *****
+	//callback code necessary here only as long as sending the request is not possible
+	omciRxCallbackPair := callbackPair{cbKey: tid,
+		cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
+	}
+	oo.mutexRxSchedMap.Lock()
+	oo.rxSchedulerMap[omciRxCallbackPair.cbKey] = omciRxCallbackPair.cbEntry
+	oo.mutexRxSchedMap.Unlock()
+	//callback code necessary here only as long as sending the request is not possible
+
+	time.Sleep(time.Millisecond * 200) //give some response time
+	respOmciLayer := &omci.OMCI{
+		TransactionID: tid,
+		MessageType:   omci.EndSoftwareDownloadResponseType,
+		// DeviceIdentifier: omci.BaselineIdent,		// Optional, defaults to Baseline
+		// Length:           0x28,						// Optional, defaults to 40 octets
+	}
+	response := &omci.EndSoftwareDownloadResponse{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.SoftwareImageClassID,
+			EntityInstance: aImageMeID, //inactive image
+		},
+		Result:            0, //simulate done, option would be busy
+		NumberOfInstances: 0, //seems at the moment I can only generate 0 instances, using 1 here panics as MeResult can not be set below
+		//MeResults: cannot set here: downloadResults type not exported from omci-lib!
+	}
+	var respOptions gopacket.SerializeOptions
+	respOptions.FixLengths = true
+	respBuffer := gopacket.NewSerializeBuffer()
+	respErr := gopacket.SerializeLayers(respBuffer, respOptions, respOmciLayer, response)
+	if respErr != nil {
+		logger.Errorw(ctx, "Cannot serialize EndSwDlResponse", log.Fields{"Err": respErr,
+			"device-id": oo.deviceID})
+		return respErr
+	}
+	respPacket := respBuffer.Bytes()
+	logger.Debugw(ctx, "simulate EndSwDlResponse", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(aImageMeID), 16),
+		"result": 0})
+	go func(oo *omciCC) {
+		_ = oo.receiveMessage(ctx, respPacket)
+	}(oo)
+	//**** test simulation - as long as BBSIM does not support ONU SW upgrade *** stop *****
+	return nil
+}
+
+func (oo *omciCC) sendActivateSoftware(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, aImageMeID uint16) error {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw(ctx, "send ActivateSwRequest:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(aImageMeID), 16)})
+
+	omciLayer := &omci.OMCI{
+		TransactionID: tid,
+		MessageType:   omci.ActivateSoftwareRequestType,
+		// DeviceIdentifier: omci.BaselineIdent,		// Optional, defaults to Baseline
+		// Length:           0x28,						// Optional, defaults to 40 octets
+	}
+	request := &omci.ActivateSoftwareRequest{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.SoftwareImageClassID,
+			EntityInstance: aImageMeID, //inactive image
+		},
+		ActivateFlags: 0, //unconditionally reset as the only relevant option here (regardless of VOIP)
+	}
+
+	var options gopacket.SerializeOptions
+	options.FixLengths = true
+	buffer := gopacket.NewSerializeBuffer()
+	err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+	if err != nil {
+		logger.Errorw(ctx, "Cannot serialize ActivateSwRequest", log.Fields{"Err": err,
+			"device-id": oo.deviceID})
+		return err
+	}
+	outgoingPacket := buffer.Bytes()
+
+	omciRxCallbackPair := callbackPair{cbKey: tid,
+		cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse, true},
+	}
+	err = oo.send(ctx, outgoingPacket, timeout, 0, highPrio, omciRxCallbackPair)
+	if err != nil {
+		logger.Errorw(ctx, "Cannot send ActivateSwRequest", log.Fields{"Err": err,
+			"device-id": oo.deviceID})
+		return err
+	}
+	logger.Debug(ctx, "send ActivateSwRequest done")
+
+	//**** test simulation - as long as BBSIM does not support ONU SW upgrade *** start *****
+
+	time.Sleep(time.Millisecond * 50) //give some response time
+
+	respOmciLayer := &omci.OMCI{
+		TransactionID: tid,
+		MessageType:   omci.ActivateSoftwareResponseType,
+		// DeviceIdentifier: omci.BaselineIdent,		// Optional, defaults to Baseline
+		// Length:           0x28,						// Optional, defaults to 40 octets
+	}
+	response := &omci.ActivateSoftwareResponse{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.SoftwareImageClassID,
+			EntityInstance: aImageMeID, //inactive image
+		},
+		Result: 0, //simulate done, option would be busy
+	}
+	var respOptions gopacket.SerializeOptions
+	respOptions.FixLengths = true
+	respBuffer := gopacket.NewSerializeBuffer()
+	respErr := gopacket.SerializeLayers(respBuffer, respOptions, respOmciLayer, response)
+	if respErr != nil {
+		logger.Errorw(ctx, "Cannot serialize ActivateSwResponse", log.Fields{"Err": respErr,
+			"device-id": oo.deviceID})
+		return respErr
+	}
+	respPacket := respBuffer.Bytes()
+	logger.Debugw(ctx, "simulate ActivateSwResponse", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(aImageMeID), 16),
+		"result": 0})
+	go func(oo *omciCC) {
+		_ = oo.receiveMessage(ctx, respPacket)
+	}(oo)
+	//**** test simulation - as long as BBSIM does not support ONU SW upgrade *** stop *****
+	return nil
+}
+
 func isResponseWithMibDataSync(msgType omci.MessageType) bool {
 	for _, v := range responsesWithMibDataSync {
 		if v == msgType {
diff --git a/internal/pkg/onuadaptercore/omci_onu_upgrade.go b/internal/pkg/onuadaptercore/omci_onu_upgrade.go
new file mode 100644
index 0000000..7842717
--- /dev/null
+++ b/internal/pkg/onuadaptercore/omci_onu_upgrade.go
@@ -0,0 +1,668 @@
+/*
+ * 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 (
+	"context"
+	"fmt"
+	"strconv"
+	"time"
+
+	"github.com/boguslaw-wojcik/crc32a"
+	"github.com/looplab/fsm"
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v4/pkg/log"
+	"github.com/opencord/voltha-protos/v4/go/voltha"
+)
+
+const cMaxUint32 = ^uint32(0)
+
+const (
+	// internal predefined values - some off them should later be configurable (perhaps with theses as defaults)
+	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?)
+	//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"
+	upgradeEvPrepareSwDownload  = "upgradeEvPrepareSwDownload"
+	upgradeEvRxStartSwDownload  = "upgradeEvRxStartSwDownload"
+	upgradeEvWaitWindowAck      = "upgradeEvWaitWindowAck"
+	upgradeEvContinueNextWindow = "upgradeEvContinueNextWindow"
+	upgradeEvEndSwDownload      = "upgradeEvEndSwDownload"
+	upgradeEvRequestActivate    = "upgradeEvRequestActivate"
+	upgradeEvWaitForCommit      = "upgradeEvWaitForCommit"
+	upgradeEvCommitSw           = "upgradeEvCommitSw"
+
+	//upgradeEvTimeoutSimple  = "upgradeEvTimeoutSimple"
+	//upgradeEvTimeoutMids    = "upgradeEvTimeoutMids"
+	upgradeEvReset   = "upgradeEvReset"
+	upgradeEvAbort   = "upgradeEvAbort"
+	upgradeEvRestart = "upgradeEvRestart"
+)
+
+const (
+	// states of config PON ANI port FSM
+	upgradeStDisabled           = "upgradeStDisabled"
+	upgradeStStarting           = "upgradeStStarting"
+	upgradeStPreparingDL        = "upgradeStPreparingDL"
+	upgradeStDLSection          = "upgradeStDLSection"
+	upgradeStVerifyWindow       = "upgradeStVerifyWindow"
+	upgradeStFinalizeDL         = "upgradeStFinalizeDL"
+	upgradeStRequestingActivate = "upgradeStRequestingActivate"
+	upgradeStWaitForCommit      = "upgradeStWaitForCommit"
+	upgradeStCommitSw           = "upgradeStCommitSw"
+	upgradeStResetting          = "upgradeStResetting"
+)
+
+//required definition for IdleState detection for activities on OMCI
+const cOnuUpgradeFsmIdleState = upgradeStWaitForCommit
+
+//OnuUpgradeFsm defines the structure for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+type OnuUpgradeFsm struct {
+	pDeviceHandler   *deviceHandler
+	pDownloadManager *adapterDownloadManager
+	deviceID         string
+	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
+	imageLength                       uint32        //including last bytes padding
+	omciDownloadWindowSizeLimit       uint8         //windowSize-1
+	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
+	omciSectionInterleaveMilliseconds time.Duration //DownloadSectionInterleave delay in milliseconds
+	delayEndSwDl                      bool          //flag to provide a delay between last section and EndSwDl
+}
+
+//NewOnuUpgradeFsm is the 'constructor' for the state machine to config the PON ANI ports
+//  of ONU UNI ports via OMCI
+func NewOnuUpgradeFsm(ctx context.Context, apDeviceHandler *deviceHandler,
+	apDevOmciCC *omciCC, apOnuDB *onuDeviceDB,
+	aRequestEvent OnuDeviceEvent, aName string, aCommChannel chan Message) *OnuUpgradeFsm {
+	instFsm := &OnuUpgradeFsm{
+		pDeviceHandler:                    apDeviceHandler,
+		deviceID:                          apDeviceHandler.deviceID,
+		pOmciCC:                           apDevOmciCC,
+		pOnuDB:                            apOnuDB,
+		requestEvent:                      aRequestEvent,
+		omciDownloadWindowSizeLimit:       cOmciDownloadWindowSizeLimit,
+		omciSectionInterleaveMilliseconds: cOmciSectionInterleaveMilliseconds,
+	}
+
+	instFsm.pAdaptFsm = NewAdapterFsm(aName, instFsm.deviceID, aCommChannel)
+	if instFsm.pAdaptFsm == nil {
+		logger.Errorw(ctx, "OnuUpgradeFsm's AdapterFsm could not be instantiated!!", log.Fields{
+			"device-id": instFsm.deviceID})
+		return nil
+	}
+	instFsm.pAdaptFsm.pFsm = fsm.NewFSM(
+		upgradeStDisabled,
+		fsm.Events{
+			{Name: upgradeEvStart, Src: []string{upgradeStDisabled}, Dst: upgradeStStarting},
+			{Name: upgradeEvPrepareSwDownload, Src: []string{upgradeStStarting}, 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: upgradeEvRequestActivate, Src: []string{upgradeStFinalizeDL}, Dst: upgradeStRequestingActivate},
+			{Name: upgradeEvWaitForCommit, Src: []string{upgradeStRequestingActivate}, Dst: upgradeStWaitForCommit},
+			{Name: upgradeEvCommitSw, Src: []string{upgradeStStarting, upgradeStWaitForCommit},
+				Dst: upgradeStCommitSw},
+
+			/*
+				{Name: upgradeEvTimeoutSimple, Src: []string{
+					upgradeStCreatingDot1PMapper, upgradeStCreatingMBPCD, upgradeStSettingTconts, upgradeStSettingDot1PMapper}, Dst: upgradeStStarting},
+				{Name: upgradeEvTimeoutMids, Src: []string{
+					upgradeStCreatingGemNCTPs, upgradeStCreatingGemIWs, upgradeStSettingPQs}, Dst: upgradeStStarting},
+			*/
+			// exceptional treatments
+			{Name: upgradeEvReset, Src: []string{upgradeStStarting, upgradeStPreparingDL, upgradeStDLSection,
+				upgradeStVerifyWindow, upgradeStDLSection, upgradeStFinalizeDL, upgradeStRequestingActivate,
+				upgradeStCommitSw}, //upgradeStWaitForCommit is not reset (later perhaps also not upgradeStWaitActivate)
+				Dst: upgradeStResetting},
+			{Name: upgradeEvAbort, Src: []string{upgradeStStarting, upgradeStPreparingDL, upgradeStDLSection,
+				upgradeStVerifyWindow, upgradeStDLSection, upgradeStFinalizeDL, upgradeStRequestingActivate,
+				upgradeStWaitForCommit, upgradeStCommitSw},
+				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_" + 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) },
+			"enter_" + upgradeStFinalizeDL:         func(e *fsm.Event) { instFsm.enterFinalizeDL(ctx, e) },
+			"enter_" + upgradeStRequestingActivate: func(e *fsm.Event) { instFsm.enterActivateSw(ctx, e) },
+			"enter_" + upgradeStCommitSw:           func(e *fsm.Event) { instFsm.enterCommitSw(ctx, e) },
+			"enter_" + upgradeStResetting:          func(e *fsm.Event) { instFsm.enterResetting(ctx, e) },
+			"enter_" + upgradeStDisabled:           func(e *fsm.Event) { instFsm.enterDisabled(ctx, e) },
+		},
+	)
+	if instFsm.pAdaptFsm.pFsm == nil {
+		logger.Errorw(ctx, "OnuUpgradeFsm's Base FSM could not be instantiated!!", log.Fields{
+			"device-id": instFsm.deviceID})
+		return nil
+	}
+
+	logger.Debugw(ctx, "OnuUpgradeFsm created", log.Fields{"device-id": instFsm.deviceID})
+	return instFsm
+}
+
+//SetDownloadParams configures the needed parameters for a specific download to the ONU
+func (oFsm *OnuUpgradeFsm) SetDownloadParams(ctx context.Context, apImageDsc *voltha.ImageDownload,
+	apDownloadManager *adapterDownloadManager) error {
+	pBaseFsm := oFsm.pAdaptFsm.pFsm
+	if pBaseFsm != nil && pBaseFsm.Is(upgradeStStarting) {
+		logger.Debugw(ctx, "OnuUpgradeFsm Parameter setting", log.Fields{
+			"device-id": oFsm.deviceID, "image-description": apImageDsc})
+		oFsm.pImageDsc = apImageDsc
+		oFsm.pDownloadManager = apDownloadManager
+
+		go func(aPBaseFsm *fsm.FSM) {
+			// let the upgrade FSm proceed to PreparinDL
+			_ = aPBaseFsm.Event(upgradeEvPrepareSwDownload)
+		}(pBaseFsm)
+		return nil
+	}
+	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))
+}
+
+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})
+
+	// start go routine for processing of LockState messages
+	go oFsm.processOmciUpgradeMessages(ctx)
+}
+
+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)
+	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})
+		pBaseFsm := oFsm.pAdaptFsm
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pAFsm *AdapterFsm) {
+			_ = a_pAFsm.pFsm.Event(vlanEvReset)
+		}(pBaseFsm)
+		return
+	}
+
+	oFsm.imageBuffer = make([]byte, fileLen)
+	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})
+		pBaseFsm := oFsm.pAdaptFsm
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pAFsm *AdapterFsm) {
+			_ = a_pAFsm.pFsm.Event(vlanEvReset)
+		}(pBaseFsm)
+		return
+	}
+
+	oFsm.noOfSections = uint32(fileLen / cOmciDownloadSectionSize)
+	if fileLen%cOmciDownloadSectionSize > 0 {
+		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.noOfSections++
+	}
+	oFsm.origImageLength = uint32(fileLen)
+	oFsm.imageLength = uint32(len(oFsm.imageBuffer))
+	oFsm.inactiveImageMeID = uint16(1) //just to start with, must be detected from upload data or even determined per new request?
+
+	logger.Infow(ctx, "OnuUpgradeFsm starts with StartSwDl values", log.Fields{
+		"MeId": oFsm.inactiveImageMeID, "windowSizeLimit": oFsm.omciDownloadWindowSizeLimit,
+		"ImageSize": oFsm.imageLength, "original file size": fileLen})
+	//"NumberOfCircuitPacks": oFsm.numberCircuitPacks, "CircuitPacks MeId": 0}) //parallel circuit packs download not supported
+	err = oFsm.pOmciCC.sendStartSoftwareDownload(log.WithSpanFromContext(context.TODO(), ctx), ConstDefaultOmciTimeout, false,
+		oFsm.pAdaptFsm.commChan, oFsm.inactiveImageMeID, oFsm.omciDownloadWindowSizeLimit, oFsm.origImageLength)
+	if err != nil {
+		logger.Errorw(ctx, "StartSwDl abort: can't send section", log.Fields{
+			"device-id": oFsm.deviceID, "error": err})
+		//TODO!!!: define some more sophisticated error treatment with some repetition, for now just reset the FSM
+		pBaseFsm := oFsm.pAdaptFsm
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pAFsm *AdapterFsm) {
+			_ = a_pAFsm.pFsm.Event(vlanEvReset)
+		}(pBaseFsm)
+		return
+	}
+}
+
+func (oFsm *OnuUpgradeFsm) enterDownloadSection(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "OnuUpgradeFsm start downloading sections", log.Fields{
+		"device-id": oFsm.deviceID, "absolute window": oFsm.nextDownloadWindow})
+
+	var windowAckRequest uint8 = 0
+	var bufferStartOffset uint32
+	var bufferEndOffset uint32
+	var downloadSection []byte
+	framePrint := false //default no printing of downloadSection frames
+	if oFsm.nextDownloadSectionsAbsolute == 0 {
+		//debug print of first section frame
+		framePrint = true
+	}
+
+	for {
+		bufferStartOffset = oFsm.nextDownloadSectionsAbsolute * cOmciDownloadSectionSize
+		bufferEndOffset = bufferStartOffset + cOmciDownloadSectionSize - 1 //for representing cOmciDownloadSectionSizeLimit values
+		logger.Debugw(ctx, "DlSection values are", log.Fields{
+			"DlSectionNoAbsolute": oFsm.nextDownloadSectionsAbsolute,
+			"DlSectionWindow":     oFsm.nextDownloadSectionsWindow,
+			"startOffset":         bufferStartOffset, "endOffset": bufferEndOffset})
+		if bufferStartOffset+1 > oFsm.imageLength || bufferEndOffset+1 > oFsm.imageLength { //should never occur in this state
+			logger.Errorw(ctx, "OnuUpgradeFsm buffer error: exceeded length", log.Fields{
+				"device-id": oFsm.deviceID, "bufferStartOffset": bufferStartOffset,
+				"bufferEndOffset": bufferEndOffset, "imageLength": oFsm.imageLength})
+			//logical error -- reset the FSM
+			pBaseFsm := oFsm.pAdaptFsm
+			// Can't call FSM Event directly, decoupling it
+			go func(a_pAFsm *AdapterFsm) {
+				_ = a_pAFsm.pFsm.Event(vlanEvReset)
+			}(pBaseFsm)
+			return
+		}
+		downloadSection = oFsm.imageBuffer[bufferStartOffset : bufferEndOffset+1]
+		if oFsm.nextDownloadSectionsWindow == oFsm.omciDownloadWindowSizeLimit {
+			windowAckRequest = 1
+			logger.Debugw(ctx, "DlSection expect Response for complete window", log.Fields{
+				"device-id": oFsm.deviceID, "in window": oFsm.nextDownloadWindow})
+		}
+		if oFsm.nextDownloadSectionsAbsolute+1 >= oFsm.noOfSections {
+			windowAckRequest = 1
+			framePrint = true //debug print of last frame
+			logger.Debugw(ctx, "DlSection expect Response for last window (section)", log.Fields{
+				"device-id": oFsm.deviceID, "DlSectionNoAbsolute": oFsm.nextDownloadSectionsAbsolute})
+		}
+		err := oFsm.pOmciCC.sendDownloadSection(log.WithSpanFromContext(context.TODO(), ctx), ConstDefaultOmciTimeout, false,
+			oFsm.pAdaptFsm.commChan, oFsm.inactiveImageMeID, windowAckRequest, oFsm.nextDownloadSectionsWindow, downloadSection, framePrint)
+		if err != nil {
+			logger.Errorw(ctx, "DlSection abort: can't send section", log.Fields{
+				"device-id": oFsm.deviceID, "error": err})
+			//TODO!!!: define some more sophisticated error treatment with some repetition, for now just reset the FSM
+			pBaseFsm := oFsm.pAdaptFsm
+			// Can't call FSM Event directly, decoupling it
+			go func(a_pAFsm *AdapterFsm) {
+				_ = a_pAFsm.pFsm.Event(vlanEvReset)
+			}(pBaseFsm)
+			return
+		}
+
+		oFsm.nextDownloadSectionsAbsolute++ //always increase the absolute section counter after having sent one
+		if windowAckRequest == 1 {
+			pBaseFsm := oFsm.pAdaptFsm
+			// Can't call FSM Event directly, decoupling it
+			go func(a_pAFsm *AdapterFsm) {
+				_ = a_pAFsm.pFsm.Event(upgradeEvWaitWindowAck) //state transition to upgradeStVerifyWindow
+			}(pBaseFsm)
+			return
+		}
+		framePrint = false                //for the next Section frame (if wanted, can be enabled in logic before sendXXX())
+		oFsm.nextDownloadSectionsWindow++ //increase the window related section counter only if not in the last section
+		if oFsm.omciSectionInterleaveMilliseconds > 0 {
+			//ensure a defined intersection-time-gap to leave space for further processing, other ONU's ...
+			time.Sleep(oFsm.omciSectionInterleaveMilliseconds * time.Millisecond)
+		}
+	}
+}
+
+func (oFsm *OnuUpgradeFsm) enterVerifyWindow(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "OnuUpgradeFsm verify DL window ack", log.Fields{
+		"for window": oFsm.nextDownloadWindow, "device-id": oFsm.deviceID})
+}
+
+func (oFsm *OnuUpgradeFsm) enterFinalizeDL(ctx context.Context, e *fsm.Event) {
+	imageCRC := crc32a.Checksum(oFsm.imageBuffer[:int(oFsm.origImageLength)]) //ITU I.363.5 crc
+	logger.Infow(ctx, "OnuUpgradeFsm finalize DL", log.Fields{
+		"device-id": oFsm.deviceID, "crc": strconv.FormatInt(int64(imageCRC), 16), "delay": oFsm.delayEndSwDl})
+
+	if oFsm.delayEndSwDl {
+		//give the ONU some time for image evaluation (hoping it does not base that on first EndSwDl itself)
+		// should not be set in case this state is used for real download abort (not yet implemented)
+		time.Sleep(cOmciEndSwDlDelaySeconds * time.Second)
+	}
+
+	err := oFsm.pOmciCC.sendEndSoftwareDownload(log.WithSpanFromContext(context.TODO(), ctx), ConstDefaultOmciTimeout, false,
+		oFsm.pAdaptFsm.commChan, oFsm.inactiveImageMeID, oFsm.origImageLength, imageCRC)
+	if err != nil {
+		logger.Errorw(ctx, "EndSwDl abort: can't send section", log.Fields{
+			"device-id": oFsm.deviceID, "error": err})
+		//TODO!!!: define some more sophisticated error treatment with some repetition, for now just reset the FSM
+		pBaseFsm := oFsm.pAdaptFsm
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pAFsm *AdapterFsm) {
+			_ = a_pAFsm.pFsm.Event(vlanEvReset)
+		}(pBaseFsm)
+		return
+	}
+}
+
+func (oFsm *OnuUpgradeFsm) enterActivateSw(ctx context.Context, e *fsm.Event) {
+	logger.Infow(ctx, "OnuUpgradeFsm activate SW", log.Fields{
+		"device-id": oFsm.deviceID, "me-id": oFsm.inactiveImageMeID})
+
+	err := oFsm.pOmciCC.sendActivateSoftware(log.WithSpanFromContext(context.TODO(), ctx), ConstDefaultOmciTimeout, false,
+		oFsm.pAdaptFsm.commChan, oFsm.inactiveImageMeID)
+	if err != nil {
+		logger.Errorw(ctx, "ActivateSw abort: can't send activate frame", log.Fields{
+			"device-id": oFsm.deviceID, "error": err})
+		//TODO!!!: define some more sophisticated error treatment with some repetition, for now just reset the FSM
+		pBaseFsm := oFsm.pAdaptFsm
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pAFsm *AdapterFsm) {
+			_ = a_pAFsm.pFsm.Event(vlanEvReset)
+		}(pBaseFsm)
+		return
+	}
+}
+
+func (oFsm *OnuUpgradeFsm) enterCommitSw(ctx context.Context, e *fsm.Event) {
+	logger.Infow(ctx, "OnuUpgradeFsm commit SW - not yet implemented", log.Fields{
+		"device-id": oFsm.deviceID, "me-id": oFsm.inactiveImageMeID})
+	//here should be the sending of the software commit message and staying here while waiting for the response
+}
+
+func (oFsm *OnuUpgradeFsm) enterResetting(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "OnuUpgradeFsm resetting", log.Fields{"device-id": oFsm.deviceID})
+
+	pConfigupgradeStateAFsm := oFsm.pAdaptFsm
+	if pConfigupgradeStateAFsm != nil {
+		// abort running message processing
+		fsmAbortMsg := Message{
+			Type: TestMsg,
+			Data: TestMessage{
+				TestMessageVal: AbortMessageProcessing,
+			},
+		}
+		pConfigupgradeStateAFsm.commChan <- fsmAbortMsg
+
+		//try to restart the FSM to 'disabled'
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pAFsm *AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+				_ = a_pAFsm.pFsm.Event(upgradeEvRestart)
+			}
+		}(pConfigupgradeStateAFsm)
+	}
+}
+
+func (oFsm *OnuUpgradeFsm) enterDisabled(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "OnuUpgradeFsm enters disabled state", log.Fields{"device-id": oFsm.deviceID})
+	if oFsm.pDeviceHandler != nil {
+		//request removal of 'reference' in the Handler (completely clear the FSM and its data)
+		go oFsm.pDeviceHandler.removeOnuUpgradeFsm(ctx)
+	}
+}
+
+func (oFsm *OnuUpgradeFsm) processOmciUpgradeMessages(ctx context.Context) { //ctx context.Context?
+	logger.Debugw(ctx, "Start OnuUpgradeFsm Msg processing", log.Fields{"for device-id": oFsm.deviceID})
+loop:
+	for {
+		// case <-ctx.Done():
+		// 	logger.Info(ctx,"MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.deviceID})
+		// 	break loop
+		message, ok := <-oFsm.pAdaptFsm.commChan
+		if !ok {
+			logger.Info(ctx, "OnuUpgradeFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.deviceID})
+			// but then we have to ensure a restart of the FSM as well - as exceptional procedure
+			_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvReset)
+			break loop
+		}
+		logger.Debugw(ctx, "OnuUpgradeFsm Rx Msg", log.Fields{"device-id": oFsm.deviceID})
+
+		switch message.Type {
+		case TestMsg:
+			msg, _ := message.Data.(TestMessage)
+			if msg.TestMessageVal == AbortMessageProcessing {
+				logger.Infow(ctx, "OnuUpgradeFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.deviceID})
+				break loop
+			}
+			logger.Warnw(ctx, "OnuUpgradeFsm unknown TestMessage", log.Fields{"device-id": oFsm.deviceID, "MessageVal": msg.TestMessageVal})
+		case OMCI:
+			msg, _ := message.Data.(OmciMessage)
+			oFsm.handleOmciOnuUpgradeMessage(ctx, msg)
+		default:
+			logger.Warn(ctx, "OnuUpgradeFsm Rx unknown message", log.Fields{"device-id": oFsm.deviceID,
+				"message.Type": message.Type})
+		}
+	}
+	logger.Infow(ctx, "End OnuUpgradeFsm Msg processing", log.Fields{"device-id": oFsm.deviceID})
+}
+
+//nolint: gocyclo
+func (oFsm *OnuUpgradeFsm) handleOmciOnuUpgradeMessage(ctx context.Context, msg OmciMessage) {
+	logger.Debugw(ctx, "Rx OMCI OnuUpgradeFsm Msg", log.Fields{"device-id": oFsm.deviceID,
+		"msgType": msg.OmciMsg.MessageType})
+
+	switch msg.OmciMsg.MessageType {
+	case omci.StartSoftwareDownloadResponseType:
+		{
+			msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeStartSoftwareDownloadResponse)
+			if msgLayer == nil {
+				logger.Errorw(ctx, "Omci Msg layer could not be detected for StartSwDlResponse",
+					log.Fields{"device-id": oFsm.deviceID})
+				return
+			}
+			msgObj, msgOk := msgLayer.(*omci.StartSoftwareDownloadResponse)
+			if !msgOk {
+				logger.Errorw(ctx, "Omci Msg layer could not be assigned for StartSwDlResponse",
+					log.Fields{"device-id": oFsm.deviceID})
+				return
+			}
+			logger.Debugw(ctx, "OnuUpgradeFsm StartSwDlResponse data", log.Fields{
+				"device-id": oFsm.deviceID, "data-fields": msgObj})
+			if msgObj.Result != me.Success {
+				logger.Errorw(ctx, "OnuUpgradeFsm StartSwDlResponse result error - later: drive FSM to abort state ?",
+					log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+				// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+				return
+			}
+			if msgObj.EntityInstance == oFsm.inactiveImageMeID {
+				logger.Debugw(ctx, "Expected StartSwDlResponse received", log.Fields{"device-id": oFsm.deviceID})
+				if msgObj.WindowSize != oFsm.omciDownloadWindowSizeLimit {
+					// also response WindowSize = 0 is a valid number for used Window size 1
+					logger.Debugw(ctx, "different StartSwDlResponse window size requested by ONU", log.Fields{
+						"acceptedOnuWindowSizeLimit": msgObj.WindowSize, "device-id": oFsm.deviceID})
+					oFsm.omciDownloadWindowSizeLimit = msgObj.WindowSize
+				}
+				oFsm.noOfWindows = oFsm.noOfSections / uint32(oFsm.omciDownloadWindowSizeLimit+1)
+				if oFsm.noOfSections%uint32(oFsm.omciDownloadWindowSizeLimit+1) > 0 {
+					oFsm.noOfWindows++
+				}
+				logger.Debugw(ctx, "OnuUpgradeFsm will use", log.Fields{
+					"windows": oFsm.noOfWindows, "sections": oFsm.noOfSections,
+					"at WindowSizeLimit": oFsm.omciDownloadWindowSizeLimit})
+				oFsm.nextDownloadSectionsAbsolute = 0
+				oFsm.nextDownloadSectionsWindow = 0
+				oFsm.nextDownloadWindow = 0
+
+				_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvRxStartSwDownload)
+				return
+			}
+			logger.Errorw(ctx, "OnuUpgradeFsm StartSwDlResponse wrong ME instance: try again (later)?",
+				log.Fields{"device-id": oFsm.deviceID, "ResponseMeId": msgObj.EntityInstance})
+			// TODO!!!: possibly repeat the start request (once)?
+			return
+		} //StartSoftwareDownloadResponseType
+	case omci.DownloadSectionResponseType:
+		{
+			/* TODO!!!: Have to remove the check here as current used omci-lib does not allow msg layering here
+			msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeDownloadSectionResponse)
+			if msgLayer == nil {
+				logger.Errorw(ctx, "Omci Msg layer could not be detected for DlSectionResponse",
+					log.Fields{"device-id": oFsm.deviceID, "omci-message": msg.OmciMsg})
+				return
+			}
+			msgObj, msgOk := msgLayer.(*omci.DownloadSectionResponse)
+			if !msgOk {
+				logger.Errorw(ctx, "Omci Msg layer could not be assigned for DlSectionResponse",
+					log.Fields{"device-id": oFsm.deviceID})
+				return
+			}
+			logger.Debugw(ctx, "OnuUpgradeFsm DlSectionResponse Data", log.Fields{
+				"device-id": oFsm.deviceID, "data-fields": msgObj})
+			if msgObj.Result != me.Success {
+				logger.Errorw(ctx, "OnuUpgradeFsm DlSectionResponse result error - later: repeat window once?", //TODO!!!
+					log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+				return
+			}
+			if msgObj.EntityInstance == oFsm.inactiveImageMeID {
+				sectionNumber := msgObj.SectionNumber
+			*/
+			sectionNumber := oFsm.omciDownloadWindowSizeLimit //as long as access from omci-lib is not given above!!!
+			logger.Debugw(ctx, "DlSectionResponse received", log.Fields{
+				"window section-number": sectionNumber, "device-id": oFsm.deviceID})
+			if sectionNumber != oFsm.omciDownloadWindowSizeLimit {
+				logger.Errorw(ctx, "OnuUpgradeFsm DlSectionResponse section error - later: repeat window once?", //TODO!!!
+					log.Fields{"device-id": oFsm.deviceID, "window-section-limit": oFsm.omciDownloadWindowSizeLimit})
+				return
+			}
+
+			oFsm.nextDownloadWindow++
+			if oFsm.nextDownloadWindow >= oFsm.noOfWindows {
+				oFsm.delayEndSwDl = true //ensure a delay for the EndSwDl message
+				_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvEndSwDownload)
+				return
+			}
+			oFsm.nextDownloadSectionsWindow = 0
+			_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvContinueNextWindow)
+			return
+			/* }
+			logger.Errorw(ctx, "OnuUpgradeFsm Omci StartSwDlResponse wrong ME instance: try again (later)?",
+				log.Fields{"device-id": oFsm.deviceID, "ResponseMeId": msgObj.EntityInstance})
+			// TODO!!!: possibly repeat the start request (once)?
+			return
+			*/
+		} //DownloadSectionResponseType
+	case omci.EndSoftwareDownloadResponseType:
+		{
+			msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeEndSoftwareDownloadResponse)
+			if msgLayer == nil {
+				logger.Errorw(ctx, "Omci Msg layer could not be detected for EndSwDlResponse",
+					log.Fields{"device-id": oFsm.deviceID})
+				return
+			}
+			msgObj, msgOk := msgLayer.(*omci.EndSoftwareDownloadResponse)
+			if !msgOk {
+				logger.Errorw(ctx, "Omci Msg layer could not be assigned for EndSwDlResponse",
+					log.Fields{"device-id": oFsm.deviceID})
+				return
+			}
+			logger.Debugw(ctx, "OnuUpgradeFsm EndSwDlResponse data", log.Fields{
+				"device-id": oFsm.deviceID, "data-fields": msgObj})
+			if msgObj.Result != me.Success {
+				//TODO!!: Busy must be handled to give the ONU time for internal image storage, perhaps also processing error (CRC incorrect)
+				logger.Errorw(ctx, "OnuUpgradeFsm EndSwDlResponse result error - later: drive FSM to abort state ?",
+					log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+				// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+				return
+			}
+			if msgObj.EntityInstance == oFsm.inactiveImageMeID {
+				logger.Debugw(ctx, "Expected EndSwDlResponse received", log.Fields{"device-id": oFsm.deviceID})
+				_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvRequestActivate)
+				return
+			}
+			logger.Errorw(ctx, "OnuUpgradeFsm StartSwDlResponse wrong ME instance: try again (later)?",
+				log.Fields{"device-id": oFsm.deviceID, "ResponseMeId": msgObj.EntityInstance})
+			// TODO!!!: possibly repeat the end request (once)? or verify ONU upgrade state?
+			return
+		} //EndSoftwareDownloadResponseType
+	case omci.ActivateSoftwareResponseType:
+		{
+			msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeActivateSoftwareResponse)
+			if msgLayer == nil {
+				logger.Errorw(ctx, "Omci Msg layer could not be detected for ActivateSw",
+					log.Fields{"device-id": oFsm.deviceID})
+				return
+			}
+			msgObj, msgOk := msgLayer.(*omci.ActivateSoftwareResponse)
+			if !msgOk {
+				logger.Errorw(ctx, "Omci Msg layer could not be assigned for ActivateSw",
+					log.Fields{"device-id": oFsm.deviceID})
+				return
+			}
+			logger.Debugw(ctx, "OnuUpgradeFsm ActivateSwResponse data", log.Fields{
+				"device-id": oFsm.deviceID, "data-fields": msgObj})
+			if msgObj.Result != me.Success {
+				logger.Errorw(ctx, "OnuUpgradeFsm ActivateSwResponse result error - later: drive FSM to abort state ?",
+					log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+				// TODO!!!: error treatment?, perhaps in the end reset the FSM
+				return
+			}
+			if msgObj.EntityInstance == oFsm.inactiveImageMeID {
+				logger.Debugw(ctx, "Expected ActivateSwResponse received", log.Fields{"device-id": oFsm.deviceID})
+				_ = oFsm.pAdaptFsm.pFsm.Event(upgradeEvWaitForCommit)
+				return
+			}
+			logger.Errorw(ctx, "OnuUpgradeFsm ActivateSwResponse wrong ME instance: abort",
+				log.Fields{"device-id": oFsm.deviceID, "ResponseMeId": msgObj.EntityInstance})
+			// TODO!!!: error treatment?, perhaps in the end reset the FSM
+			return
+		} //ActivateSoftwareResponseType
+	default:
+		{
+			logger.Errorw(ctx, "Rx OMCI unhandled MsgType",
+				log.Fields{"omciMsgType": msg.OmciMsg.MessageType, "device-id": oFsm.deviceID})
+			return
+		}
+	}
+}
+
+/*
+func (oFsm *OnuUpgradeFsm) waitforOmciResponse(ctx context.Context) error {
+	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
+		}
+		// 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)
+	}
+}
+*/
diff --git a/internal/pkg/onuadaptercore/omci_test_request.go b/internal/pkg/onuadaptercore/omci_test_request.go
index fb8dceb..410dda0 100644
--- a/internal/pkg/onuadaptercore/omci_test_request.go
+++ b/internal/pkg/onuadaptercore/omci_test_request.go
@@ -75,7 +75,7 @@
 		onu2gBaseGet, _ := oo.createOnu2gBaseGet(ctx, tid)
 		omciRxCallbackPair := callbackPair{
 			cbKey:   tid,
-			cbEntry: callbackPairEntry{nil, oo.receiveOmciVerifyResponse},
+			cbEntry: callbackPairEntry{nil, oo.receiveOmciVerifyResponse, true},
 		}
 
 		logger.Debugw(ctx, "performOmciTest-start sending frame", log.Fields{"for device-id": oo.deviceID})
diff --git a/internal/pkg/onuadaptercore/onu_device_entry.go b/internal/pkg/onuadaptercore/onu_device_entry.go
index 816ebcd..301a973 100644
--- a/internal/pkg/onuadaptercore/onu_device_entry.go
+++ b/internal/pkg/onuadaptercore/onu_device_entry.go
@@ -154,6 +154,8 @@
 	OmciVlanFilterRemDone // needs to be the successor of OmciVlanFilterAddDoneNoKvStore!
 	// OmciVlanFilterRemDoneNoKvStore - Omci Vlan config done according to flow-remove without writing kvStore
 	OmciVlanFilterRemDoneNoKvStore // needs to be the successor of OmciVlanFilterRemDone!
+	// OmciOnuSwUpgradeDone - SoftwareUpgrade to ONU finished
+	OmciOnuSwUpgradeDone
 	// Add other events here as needed (alarms separate???)
 )
 
diff --git a/internal/pkg/onuadaptercore/openonu.go b/internal/pkg/onuadaptercore/openonu.go
index b77276e..460c7c8 100644
--- a/internal/pkg/onuadaptercore/openonu.go
+++ b/internal/pkg/onuadaptercore/openonu.go
@@ -479,11 +479,11 @@
 //Activate_image_update requests downloading some Onu Software image to the INU via OMCI
 //  according to indications as given in apRequest and on success activate the image on the ONU
 func (oo *OpenONUAC) Activate_image_update(ctx context.Context, device *voltha.Device, apRequest *voltha.ImageDownload) (*voltha.ImageDownload, error) {
-	if oo.pDownloadManager.imageExists(ctx, apRequest) {
+	if oo.pDownloadManager.imageLocallyDownloaded(ctx, apRequest) {
 		if handler := oo.getDeviceHandler(ctx, device.Id, false); handler != nil {
 			logger.Debugw(ctx, "image download on omci requested", log.Fields{
 				"image-description": apRequest, "device-id": device.Id})
-			err := handler.doOnuSwUpgrade(ctx, apRequest)
+			err := handler.doOnuSwUpgrade(ctx, apRequest, oo.pDownloadManager)
 			return apRequest, err
 		}
 		logger.Warnw(ctx, "no handler found for image activation", log.Fields{"device-id": device.Id})