VOL-3984: OMCI-LIB-GO: Support for Extended message set for DownloadSection Request/Response type

Change-Id: Ieb421aabe145a059baa7b015f8ab0fd2b52d2a14
diff --git a/messagetypes.go b/messagetypes.go
index 456bef8..94fc257 100644
--- a/messagetypes.go
+++ b/messagetypes.go
@@ -2353,17 +2353,18 @@
 	return nil
 }
 
-/////////////////////////////////////////////////////////////////////////////
-//
+// DownloadSectionRequest data is bound by the message set in use. For the
+// Baseline message set use MaxDownloadSectionLength and for the Extended message
+// set, MaxDownloadSectionExtendedLength is provided
 type DownloadSectionRequest struct {
 	MeBasePacket  // Note: EntityInstance for software download is two specific values
 	SectionNumber byte
-	SectionData   [31]byte // 0 padding if final transfer requires only a partial block
+	SectionData   []byte // 0 padding if final transfer requires only a partial block for baseline set
 }
 
 func (omci *DownloadSectionRequest) String() string {
-	return fmt.Sprintf("%v, Section #: %v",
-		omci.MeBasePacket.String(), omci.SectionNumber)
+	return fmt.Sprintf("%v, Section #: %v, Data Length: %v",
+		omci.MeBasePacket.String(), omci.SectionNumber, len(omci.SectionData))
 }
 
 // DecodeFromBytes decodes the given bytes of a Download Section Request into this layer
@@ -2386,8 +2387,28 @@
 	if omci.EntityClass != me.SoftwareImageClassID {
 		return me.NewProcessingError("invalid Entity Class for Download Section request")
 	}
-	omci.SectionNumber = data[4]
-	copy(omci.SectionData[0:], data[5:])
+	if omci.Extended {
+		if len(data) < 7 {
+			p.SetTruncated()
+			return errors.New("frame too small")
+		}
+		if len(data[7:]) > MaxDownloadSectionExtendedLength {
+			return errors.New(fmt.Sprintf("software image data too large. Received %v, Max: %v",
+				len(data[7:]), MaxDownloadSectionExtendedLength))
+		}
+		omci.SectionData = make([]byte, len(data[7:]))
+		omci.SectionNumber = data[6]
+		copy(omci.SectionData, data[7:])
+	} else {
+		if len(data[5:]) != MaxDownloadSectionLength {
+			p.SetTruncated()
+			return errors.New(fmt.Sprintf("software image size invalid. Received %v, Expected: %v",
+				len(data[5:]), MaxDownloadSectionLength))
+		}
+		omci.SectionData = make([]byte, MaxDownloadSectionLength)
+		omci.SectionNumber = data[4]
+		copy(omci.SectionData, data[5:])
+	}
 	return nil
 }
 
@@ -2397,6 +2418,13 @@
 	return decodingLayerDecoder(omci, data, p)
 }
 
+func decodeDownloadSectionRequestExtended(data []byte, p gopacket.PacketBuilder) error {
+	omci := &DownloadSectionRequest{}
+	omci.MsgLayerType = LayerTypeDownloadSectionRequest
+	omci.Extended = true
+	return decodingLayerDecoder(omci, data, p)
+}
+
 // SerializeTo provides serialization of an Download Section Request message
 func (omci *DownloadSectionRequest) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
 	// Basic (common) OMCI Header is 8 octets, 10
@@ -2417,12 +2445,40 @@
 	if omci.EntityClass != me.SoftwareImageClassID {
 		return me.NewProcessingError("invalid Entity Class for Download Section response")
 	}
-	bytes, err := b.AppendBytes(1 + len(omci.SectionData))
-	if err != nil {
-		return err
+	sectionLength := len(omci.SectionData)
+	if omci.Extended {
+		if sectionLength > MaxDownloadSectionExtendedLength {
+			msg := fmt.Sprintf("invalid Download Section data length, must be <= %v, received: %v",
+				MaxDownloadSectionExtendedLength, sectionLength)
+			return me.NewProcessingError(msg)
+		}
+		// Append section data
+		bytes, err := b.AppendBytes(3 + sectionLength)
+		if err != nil {
+			return err
+		}
+		binary.BigEndian.PutUint16(bytes, uint16(1+sectionLength))
+		bytes[2] = omci.SectionNumber
+		copy(bytes[3:], omci.SectionData)
+	} else {
+		if sectionLength > MaxDownloadSectionLength {
+			msg := fmt.Sprintf("invalid Download Section data length, must be <= %v, received: %v",
+				MaxDownloadSectionLength, sectionLength)
+			return me.NewProcessingError(msg)
+		}
+		// Append section data
+		bytes, err := b.AppendBytes(1 + MaxDownloadSectionLength)
+		if err != nil {
+			return err
+		}
+		bytes[0] = omci.SectionNumber
+		copy(bytes[1:], omci.SectionData)
+
+		// Zero extended if needed
+		if sectionLength < MaxDownloadSectionLength {
+			copy(omci.SectionData[sectionLength:], lotsOfZeros[:MaxDownloadSectionLength-sectionLength])
+		}
 	}
-	bytes[0] = omci.SectionNumber
-	copy(bytes[1:], omci.SectionData[0:])
 	return nil
 }
 
@@ -2459,13 +2515,22 @@
 	if omci.EntityClass != me.SoftwareImageClassID {
 		return me.NewProcessingError("invalid Entity Class for Download Section response")
 	}
-	omci.Result = me.Results(data[4])
+	if omci.Extended {
+		if len(data) < 8 {
+			p.SetTruncated()
+			return errors.New("frame too small")
+		}
+		omci.Result = me.Results(data[6])
+		omci.SectionNumber = data[7]
+	} else {
+		omci.Result = me.Results(data[4])
+		omci.SectionNumber = data[5]
+	}
 	if omci.Result > me.DeviceBusy {
 		msg := fmt.Sprintf("invalid results for Download Section response: %v, must be 0..6",
 			omci.Result)
 		return errors.New(msg)
 	}
-	omci.SectionNumber = data[5]
 	return nil
 }
 
@@ -2475,6 +2540,13 @@
 	return decodingLayerDecoder(omci, data, p)
 }
 
+func decodeDownloadSectionResponseExtended(data []byte, p gopacket.PacketBuilder) error {
+	omci := &DownloadSectionResponse{}
+	omci.MsgLayerType = LayerTypeDownloadSectionResponse
+	omci.Extended = true
+	return decodingLayerDecoder(omci, data, p)
+}
+
 // SerializeTo provides serialization of an Download Section Response message
 func (omci *DownloadSectionResponse) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
 	// Basic (common) OMCI Header is 8 octets, 10
@@ -2495,17 +2567,27 @@
 	if omci.EntityClass != me.SoftwareImageClassID {
 		return me.NewProcessingError("invalid Entity Class for Download Section response")
 	}
-	bytes, err := b.AppendBytes(2)
-	if err != nil {
-		return err
-	}
 	if omci.Result > me.DeviceBusy {
 		msg := fmt.Sprintf("invalid results for Download Section response: %v, must be 0..6",
 			omci.Result)
 		return errors.New(msg)
 	}
-	bytes[0] = byte(omci.Result)
-	bytes[1] = omci.SectionNumber
+	if omci.Extended {
+		bytes, err := b.AppendBytes(4)
+		if err != nil {
+			return err
+		}
+		binary.BigEndian.PutUint16(bytes, uint16(2))
+		bytes[2] = byte(omci.Result)
+		bytes[3] = omci.SectionNumber
+	} else {
+		bytes, err := b.AppendBytes(2)
+		if err != nil {
+			return err
+		}
+		bytes[0] = byte(omci.Result)
+		bytes[1] = omci.SectionNumber
+	}
 	return nil
 }