[VOL-3024] - MIB download - provide basic omci configuration to ONU

Change-Id: I6d1547280a27656824136bd3989c4cdcd903c9a2
Signed-off-by: Holger Hildebrandt <holger.hildebrandt@adtran.com>
diff --git a/internal/pkg/onuadaptercore/omci_cc.go b/internal/pkg/onuadaptercore/omci_cc.go
index c712b1d..972670f 100644
--- a/internal/pkg/onuadaptercore/omci_cc.go
+++ b/internal/pkg/onuadaptercore/omci_cc.go
@@ -23,6 +23,7 @@
 	"encoding/binary"
 	"encoding/hex"
 	"errors"
+	"strconv"
 	"sync"
 
 	//"time"
@@ -42,8 +43,20 @@
 	//"github.com/opencord/voltha-protos/v3/go/voltha"
 )
 
+// ### OMCI related definitions - retrieved from Python adapter code/trace ####
 const ConstDefaultOmciTimeout = 10 // ( 3 ?) Seconds
 
+const galEthernetEID = uint16(1)
+const maxGemPayloadSize = uint16(48)
+const connectivityModeValue = uint8(5)
+const defaultTPID = uint16(0x8100)
+const broadComDefaultVID = uint16(4091)
+const macBridgeServiceProfileEID = uint16(0x201) // TODO: most all these need better definition or tuning
+const ieeeMapperServiceProfileEID = uint16(0x8001)
+const macBridgePortAniEID = uint16(0x2102)
+
+// ### OMCI related definitions - end
+
 //CallbackPair to be used for ReceiveCallback init
 type CallbackPair struct {
 	cbKey      uint16
@@ -80,10 +93,11 @@
 	uploadSequNo   uint16
 	uploadNoOfCmds uint16
 
-	mutexTxQueue    sync.Mutex
-	txQueue         *list.List
-	mutexRxSchedMap sync.Mutex
-	rxSchedulerMap  map[uint16]func(*omci.OMCI, *gp.Packet) error
+	mutexTxQueue      sync.Mutex
+	txQueue           *list.List
+	mutexRxSchedMap   sync.Mutex
+	rxSchedulerMap    map[uint16]func(*omci.OMCI, *gp.Packet) error
+	pLastTxMeInstance *me.ManagedEntity
 }
 
 //NewOmciCC constructor returns a new instance of a OmciCC
@@ -490,12 +504,17 @@
 		TransactionID: tid,
 		MessageType:   msgType,
 	}
+	return serializeOmciLayer(omciLayer, request)
+}
+
+func serializeOmciLayer(a_omciLayer *omci.OMCI, a_request gopacket.SerializableLayer) ([]byte, error) {
 	var options gopacket.SerializeOptions
 	options.FixLengths = true
 
 	buffer := gopacket.NewSerializeBuffer()
-	err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+	err := gopacket.SerializeLayers(buffer, options, a_omciLayer, a_request)
 	if err != nil {
+		logger.Errorw("Could not create goPacket Omci serial buffer", log.Fields{"Err": err})
 		return nil, err
 	}
 	return buffer.Bytes(), nil
@@ -507,9 +526,6 @@
 	return dst, nil
 }
 
-// ###################################################################################
-// # MIB Action shortcuts  - still dummies - TODO!!!!!
-
 //supply a response handler for the MibSync omci response messages
 func (oo *OmciCC) receiveMibSyncResponse(omciMsg *omci.OMCI, packet *gp.Packet) error {
 
@@ -536,6 +552,31 @@
 	return nil
 }
 
+//supply a response handler for the MibDownload omci response messages
+func (oo *OmciCC) ReceiveMibDownloadResponse(omciMsg *omci.OMCI, packet *gp.Packet) error {
+
+	logger.Debugw("mib-download-omci-message-response received:", log.Fields{"omciMsgType": omciMsg.MessageType,
+		"transCorrId": omciMsg.TransactionID, "deviceId": oo.deviceID})
+
+	if oo.pOnuDeviceEntry == nil {
+		logger.Error("Abort Receive MibDownload OMCI response, DeviceEntryPointer is nil")
+		return errors.New("DeviceEntryPointer is nil")
+	}
+
+	// no further test on SeqNo is done here, assignment from rxScheduler is trusted
+	// MibDownload responses are simply transferred via deviceEntry to MibDownload, no specific analysis here
+	mibDlMsg := Message{
+		Type: OMCI,
+		Data: OmciMessage{
+			OmciMsg:    omciMsg,
+			OmciPacket: packet,
+		},
+	}
+	(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan <- mibDlMsg
+
+	return nil
+}
+
 func (oo *OmciCC) sendMibReset(ctx context.Context, timeout int, highPrio bool) error {
 
 	logger.Debugw("send MibReset-msg to:", log.Fields{"deviceId": oo.deviceID})
@@ -555,7 +596,6 @@
 }
 
 func (oo *OmciCC) sendMibUpload(ctx context.Context, timeout int, highPrio bool) error {
-
 	logger.Debugw("send MibUpload-msg to:", log.Fields{"deviceId": oo.deviceID})
 	request := &omci.MibUploadRequest{
 		MeBasePacket: omci.MeBasePacket{
@@ -576,7 +616,6 @@
 }
 
 func (oo *OmciCC) sendMibUploadNext(ctx context.Context, timeout int, highPrio bool) error {
-
 	logger.Debugw("send MibUploadNext-msg to:", log.Fields{"deviceId": oo.deviceID, "uploadSequNo": oo.uploadSequNo})
 	request := &omci.MibUploadNextRequest{
 		MeBasePacket: omci.MeBasePacket{
@@ -596,14 +635,232 @@
 	return oo.Send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
 }
 
-/* py code example
-...
-def send_mib_upload(self, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
-	frame = OntDataFrame().mib_upload()
-	return self.send(frame, timeout=timeout, high_priority=high_priority)
+func (oo *OmciCC) sendCreateGalEthernetProfile(ctx context.Context, timeout int, highPrio bool) *me.ManagedEntity {
+	tid := oo.GetNextTid(highPrio)
+	logger.Debugw("send GalEnetProfile-Create-msg:", log.Fields{"deviceId": oo.deviceID, "SequNo": strconv.FormatInt(int64(tid), 16)})
 
-def send_mib_upload_next(self, seq_no, timeout=DEFAULT_OMCI_TIMEOUT, high_priority=False):
-	frame = OntDataFrame(sequence_number=seq_no).mib_upload_next()
-	return self.send(frame, timeout=timeout, high_priority=high_priority)
-...
-*/
+	meParams := me.ParamData{
+		EntityID:   galEthernetEID,
+		Attributes: me.AttributeValueMap{"MaximumGemPayloadSize": maxGemPayloadSize},
+	}
+	meInstance, omciErr := me.NewGalEthernetProfile(meParams)
+	if omciErr.GetError() == nil {
+		//all setByCreate parameters already set, no default option required ...
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode GalEnetProfileInstance for create", log.Fields{"Err": err})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize GalEnetProfile create", log.Fields{"Err": err})
+			return nil
+		}
+
+		omciRxCallbackPair := CallbackPair{tid, oo.ReceiveMibDownloadResponse}
+		err = oo.Send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send GalEnetProfile create", log.Fields{"Err": err})
+			return nil
+		} else {
+			logger.Debug("send GalEnetProfile-Create-msg done")
+			return meInstance
+		}
+	} else {
+		logger.Errorw("Cannot generate GalEnetProfileInstance", log.Fields{"Err": omciErr.GetError()})
+		return nil
+	}
+}
+
+// might be needed to extend for parameter arguments, here just for setting the VonnectivityMode!!
+func (oo *OmciCC) sendSetOnu2g(ctx context.Context, timeout int, highPrio bool) *me.ManagedEntity {
+	tid := oo.GetNextTid(highPrio)
+	logger.Debugw("send ONU2-G-Set-msg:", log.Fields{"deviceId": oo.deviceID, "SequNo": strconv.FormatInt(int64(tid), 16)})
+
+	// here we should better use the MibUpload stored ONU2-G data to re-use the given InstanceNumber
+	//   and to verify, if the ONU really supports the desired connectivity mode 5 (in ConnCap)
+	// By now we just use fix values to fire - this is anyway what the python adapter does
+	// read ONU-2G from DB ???? //TODO!!!
+	meParams := me.ParamData{
+		EntityID:   0,
+		Attributes: me.AttributeValueMap{"CurrentConnectivityMode": connectivityModeValue},
+	}
+	meInstance, omciErr := me.NewOnu2G(meParams)
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode ONU2-G instance for set", log.Fields{"Err": err})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize ONU2-G set", log.Fields{"Err": err})
+			return nil
+		}
+
+		omciRxCallbackPair := CallbackPair{tid, oo.ReceiveMibDownloadResponse}
+		err = oo.Send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send ONU2-G set", log.Fields{"Err": err})
+			return nil
+		} else {
+			logger.Debug("send ONU2-G-Set-msg done")
+			return meInstance
+		}
+	} else {
+		logger.Errorw("Cannot generate ONU2-G", log.Fields{"Err": omciErr.GetError()})
+		return nil
+	}
+}
+
+func (oo *OmciCC) sendCreateMBServiceProfile(ctx context.Context,
+	a_pUniPort *OnuUniPort, timeout int, highPrio bool) *me.ManagedEntity {
+	tid := oo.GetNextTid(highPrio)
+	instID := macBridgeServiceProfileEID + uint16(a_pUniPort.macBpNo)
+	logger.Debugw("send MBSP-Create-msg:", log.Fields{
+		"deviceId": oo.deviceID, "SequNo": strconv.FormatInt(int64(tid), 16), "InstId": instID})
+
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"Priority":     0x8000,
+			"MaxAge":       20 * 256, //20s
+			"HelloTime":    2 * 256,  //2s
+			"ForwardDelay": 15 * 256, //15s
+			//note: DynamicFilteringAgeingTime is taken from omci lib default as
+			//  which is obviously different from default value used in python lib,
+			//  where the value seems to be 0 (ONU defined)  - to be considered in case of test artifacts ...
+		},
+	}
+
+	meInstance, omciErr := me.NewMacBridgeServiceProfile(meParams)
+	if omciErr.GetError() == nil {
+		//obviously we have to set all 'untouched' parameters to default by some additional option parameter!!
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+			omci.TransactionID(tid), omci.AddDefaults(true))
+		if err != nil {
+			logger.Errorw("Cannot encode MBSP for create", log.Fields{"Err": err})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize MBSP create", log.Fields{"Err": err})
+			return nil
+		}
+
+		omciRxCallbackPair := CallbackPair{tid, oo.ReceiveMibDownloadResponse}
+		err = oo.Send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send MBSP create", log.Fields{"Err": err})
+			return nil
+		} else {
+			logger.Debug("send MBSP-Create-msg done")
+			return meInstance
+		}
+	} else {
+		logger.Errorw("Cannot generate MBSP Instance", log.Fields{"Err": omciErr.GetError()})
+		return nil
+	}
+}
+
+func (oo *OmciCC) sendCreateMBPConfigData(ctx context.Context,
+	a_pUniPort *OnuUniPort, timeout int, highPrio bool) *me.ManagedEntity {
+	tid := oo.GetNextTid(highPrio)
+	instID := macBridgePortAniEID + a_pUniPort.entityId
+	logger.Debugw("send MBPCD-Create-msg:", log.Fields{
+		"deviceId": oo.deviceID, "SequNo": strconv.FormatInt(int64(tid), 16), "InstId": instID})
+
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"BridgeIdPointer": macBridgeServiceProfileEID + uint16(a_pUniPort.macBpNo),
+			"PortNum":         a_pUniPort.macBpNo,
+			"TpType":          uint8(a_pUniPort.portType),
+			"TpPointer":       a_pUniPort.entityId,
+		},
+	}
+	meInstance, omciErr := me.NewMacBridgePortConfigurationData(meParams)
+	if omciErr.GetError() == nil {
+		//obviously we have to set all 'untouched' parameters to default by some additional option parameter!!
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+			omci.TransactionID(tid), omci.AddDefaults(true))
+		if err != nil {
+			logger.Errorw("Cannot encode MBPCD for create", log.Fields{"Err": err})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize MBPCD create", log.Fields{"Err": err})
+			return nil
+		}
+
+		omciRxCallbackPair := CallbackPair{tid, oo.ReceiveMibDownloadResponse}
+		err = oo.Send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send MBPCD create", log.Fields{"Err": err})
+			return nil
+		} else {
+			logger.Debug("send MBPCD-Create-msg done")
+			return meInstance
+		}
+	} else {
+		logger.Errorw("Cannot generate MBPCD Instance", log.Fields{"Err": omciErr.GetError()})
+		return nil
+	}
+}
+
+func (oo *OmciCC) sendCreateEVTOConfigData(ctx context.Context,
+	a_pUniPort *OnuUniPort, timeout int, highPrio bool) *me.ManagedEntity {
+	tid := oo.GetNextTid(highPrio)
+	//same entityId is used as for MBSP (see there), but just arbitrary ...
+	instID := macBridgeServiceProfileEID + uint16(a_pUniPort.macBpNo)
+	logger.Debugw("send EVTOCD-Create-msg:", log.Fields{
+		"deviceId": oo.deviceID, "SequNo": strconv.FormatInt(int64(tid), 16), "InstId": instID})
+
+	// compare python adapter code WA VOL-1311: this is not done here!
+	//   (setting TPID values for the create would probably anyway be ignored by the omci lib)
+	//    but perhaps we have to be aware of possible problems at get(Next) Request handling for EVTOOCD tables later ...
+	assType := uint8(2) // default AssociationType is PPTPEthUni
+	if a_pUniPort.portType == UniVEIP {
+		assType = uint8(10) // for VEIP
+	}
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"AssociationType":     assType,
+			"AssociatedMePointer": a_pUniPort.entityId,
+		},
+	}
+	meInstance, omciErr := me.NewExtendedVlanTaggingOperationConfigurationData(meParams)
+	if omciErr.GetError() == nil {
+		//all setByCreate parameters already set, no default option required ...
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode EVTOCD for create", log.Fields{"Err": err})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize EVTOCD create", log.Fields{"Err": err})
+			return nil
+		}
+
+		omciRxCallbackPair := CallbackPair{tid, oo.ReceiveMibDownloadResponse}
+		err = oo.Send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send EVTOCD create", log.Fields{"Err": err})
+			return nil
+		} else {
+			logger.Debug("send EVTOCD-Create-msg done")
+			return meInstance
+		}
+	} else {
+		logger.Errorw("Cannot generate EVTOCD Instance", log.Fields{"Err": omciErr.GetError()})
+		return nil
+	}
+}