pdr: Add decode_numeric_effecter_pdr_data()

Add `decode_numeric_effecter_pdr_data` API to decode the numeric
effecter PDR data in DSP0248_1.2.2 table 87. The API will be used to
retrieve the data fields of numeric effecter from the PDRs in `pldmd`.

Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I323a1288cb0262bd39f4f28701ddc7dbb70c33c8
diff --git a/src/msgbuf/platform.h b/src/msgbuf/platform.h
index 7ca36c9..c52a66d 100644
--- a/src/msgbuf/platform.h
+++ b/src/msgbuf/platform.h
@@ -143,6 +143,44 @@
 	return -PLDM_ERROR_INVALID_DATA;
 }
 
+#define pldm_msgbuf_extract_effecter_data(ctx, tag, dst)                       \
+	pldm_msgbuf_extract_typecheck(union_effecter_data_size,                \
+				      pldm__msgbuf_extract_range_field_format, \
+				      dst, ctx, tag, (void *)&(dst))
+__attribute__((always_inline)) static inline int
+pldm__msgbuf_extract_effecter_data(struct pldm_msgbuf *ctx,
+				   enum pldm_effecter_data_size tag, void *ed)
+{
+	switch (tag) {
+	case PLDM_EFFECTER_DATA_SIZE_UINT8:
+		return pldm__msgbuf_extract_uint8(
+			ctx, ((char *)ed) + offsetof(union_effecter_data_size,
+						     value_u8));
+	case PLDM_EFFECTER_DATA_SIZE_SINT8:
+		return pldm__msgbuf_extract_int8(
+			ctx, ((char *)ed) + offsetof(union_effecter_data_size,
+						     value_s8));
+	case PLDM_EFFECTER_DATA_SIZE_UINT16:
+		return pldm__msgbuf_extract_uint16(
+			ctx, ((char *)ed) + offsetof(union_effecter_data_size,
+						     value_u16));
+	case PLDM_EFFECTER_DATA_SIZE_SINT16:
+		return pldm__msgbuf_extract_int16(
+			ctx, ((char *)ed) + offsetof(union_effecter_data_size,
+						     value_s16));
+	case PLDM_EFFECTER_DATA_SIZE_UINT32:
+		return pldm__msgbuf_extract_uint32(
+			ctx, ((char *)ed) + offsetof(union_effecter_data_size,
+						     value_u32));
+	case PLDM_EFFECTER_DATA_SIZE_SINT32:
+		return pldm__msgbuf_extract_int32(
+			ctx, ((char *)ed) + offsetof(union_effecter_data_size,
+						     value_s32));
+	}
+
+	return -PLDM_ERROR_INVALID_DATA;
+}
+
 #ifdef __cplusplus
 #include <type_traits>
 
diff --git a/src/platform.c b/src/platform.c
index 6f7b940..75f94c5 100644
--- a/src/platform.c
+++ b/src/platform.c
@@ -2429,3 +2429,94 @@
 
 	return pldm_msgbuf_destroy_consumed(buf);
 }
+
+LIBPLDM_ABI_TESTING
+int decode_numeric_effecter_pdr_data(
+	const void *pdr_data, size_t pdr_data_length,
+	struct pldm_numeric_effecter_value_pdr *pdr_value)
+{
+	struct pldm_msgbuf _buf;
+	struct pldm_msgbuf *buf = &_buf;
+	struct pldm_value_pdr_hdr hdr;
+	int rc;
+
+	rc = pldm_msgbuf_init(buf, PLDM_PDR_NUMERIC_EFFECTER_PDR_MIN_LENGTH,
+			      pdr_data, pdr_data_length);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_extract_value_pdr_hdr(buf, &hdr);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_platform_pdr_hdr_validate(
+		&hdr, PLDM_PDR_NUMERIC_EFFECTER_PDR_MIN_LENGTH,
+		pdr_data_length);
+	if (rc) {
+		return rc;
+	}
+
+	memcpy(&pdr_value->hdr, &hdr, sizeof(hdr));
+
+	pldm_msgbuf_extract(buf, pdr_value->terminus_handle);
+	pldm_msgbuf_extract(buf, pdr_value->effecter_id);
+	pldm_msgbuf_extract(buf, pdr_value->entity_type);
+	pldm_msgbuf_extract(buf, pdr_value->entity_instance);
+	pldm_msgbuf_extract(buf, pdr_value->container_id);
+	pldm_msgbuf_extract(buf, pdr_value->effecter_semantic_id);
+	pldm_msgbuf_extract(buf, pdr_value->effecter_init);
+	pldm_msgbuf_extract(buf, pdr_value->effecter_auxiliary_names);
+	pldm_msgbuf_extract(buf, pdr_value->base_unit);
+	pldm_msgbuf_extract(buf, pdr_value->unit_modifier);
+	pldm_msgbuf_extract(buf, pdr_value->rate_unit);
+	pldm_msgbuf_extract(buf, pdr_value->base_oem_unit_handle);
+	pldm_msgbuf_extract(buf, pdr_value->aux_unit);
+	pldm_msgbuf_extract(buf, pdr_value->aux_unit_modifier);
+	pldm_msgbuf_extract(buf, pdr_value->aux_rate_unit);
+	pldm_msgbuf_extract(buf, pdr_value->aux_oem_unit_handle);
+	pldm_msgbuf_extract(buf, pdr_value->is_linear);
+
+	rc = pldm_msgbuf_extract(buf, pdr_value->effecter_data_size);
+	if (rc) {
+		return rc;
+	}
+	if (pdr_value->effecter_data_size > PLDM_SENSOR_DATA_SIZE_MAX) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	pldm_msgbuf_extract(buf, pdr_value->resolution);
+	pldm_msgbuf_extract(buf, pdr_value->offset);
+	pldm_msgbuf_extract(buf, pdr_value->accuracy);
+	pldm_msgbuf_extract(buf, pdr_value->plus_tolerance);
+	pldm_msgbuf_extract(buf, pdr_value->minus_tolerance);
+	pldm_msgbuf_extract(buf, pdr_value->state_transition_interval);
+	pldm_msgbuf_extract(buf, pdr_value->transition_interval);
+	pldm_msgbuf_extract_effecter_data(buf, pdr_value->effecter_data_size,
+					  pdr_value->max_settable);
+	pldm_msgbuf_extract_effecter_data(buf, pdr_value->effecter_data_size,
+					  pdr_value->min_settable);
+
+	rc = pldm_msgbuf_extract(buf, pdr_value->range_field_format);
+	if (rc) {
+		return rc;
+	}
+	if (pdr_value->range_field_format > PLDM_RANGE_FIELD_FORMAT_MAX) {
+		return PLDM_ERROR_INVALID_DATA;
+	}
+
+	pldm_msgbuf_extract(buf, pdr_value->range_field_support.byte);
+	pldm_msgbuf_extract_range_field_format(
+		buf, pdr_value->range_field_format, pdr_value->nominal_value);
+	pldm_msgbuf_extract_range_field_format(
+		buf, pdr_value->range_field_format, pdr_value->normal_max);
+	pldm_msgbuf_extract_range_field_format(
+		buf, pdr_value->range_field_format, pdr_value->normal_min);
+	pldm_msgbuf_extract_range_field_format(
+		buf, pdr_value->range_field_format, pdr_value->rated_max);
+	pldm_msgbuf_extract_range_field_format(
+		buf, pdr_value->range_field_format, pdr_value->rated_min);
+
+	return pldm_msgbuf_destroy_consumed(buf);
+}