firmware fd: Add PLDM Control version/commands

Supported PLDM firmware (type 5) commands are registered with the PLDM
control responder.

Change-Id: I6a4840a820bf2a1a7967ab9fe927f69681f375d8
Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5d21dea..ab359ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -70,6 +70,9 @@
 11. Add PLDM control responder. PLDM types and support commands/versions can be
     registered.
 
+12. PLDM FD responder accepts a PLDM control handle and will register its
+    version.
+
 ### Fixed
 
 1. dsp: platform: Fix location of closing paren in overflow detection
diff --git a/include/libpldm/firmware_fd.h b/include/libpldm/firmware_fd.h
index 5c45390..d7200b1 100644
--- a/include/libpldm/firmware_fd.h
+++ b/include/libpldm/firmware_fd.h
@@ -11,6 +11,7 @@
 #include <libpldm/pldm.h>
 #include <libpldm/base.h>
 #include <libpldm/utils.h>
+#include <libpldm/control.h>
 #include <libpldm/firmware_update.h>
 
 /** @struct pldm_firmware_component_standalone
@@ -243,13 +244,17 @@
  *                  update behaviour
  * @param[in] ops_ctx - opaque context pointer that will be passed as ctx
  *                      to ops callbacks
+ * @param[in] control - an optional struct pldm_control. If provided
+ *                      the FD responder will set PLDM FW update type
+ *			and commands for the control.
  *
  * @return a malloced struct pldm_fd, owned by the caller. It should be released
  *         with free(). Returns NULL on failure.
  *
  * This will call pldm_fd_setup() on the allocated pldm_fd.
  */
-struct pldm_fd *pldm_fd_new(const struct pldm_fd_ops *ops, void *ops_ctx);
+struct pldm_fd *pldm_fd_new(const struct pldm_fd_ops *ops, void *ops_ctx,
+			    struct pldm_control *control);
 
 /** @brief Initialise a FD responder struct
  *
@@ -261,11 +266,15 @@
  *                  update behaviour
  * @param[in] ops_ctx - opaque context pointer that will be passed as ctx
  *                      to ops callbacks
+ * @param[in] control - an optional struct pldm_control. If provided
+ *                      the FD responder will set PLDM FW update type
+ *			and commands for the control.
  *
  * @return 0 on success, a negative errno value on failure.
  */
 int pldm_fd_setup(struct pldm_fd *fd, size_t pldm_fd_size,
-		  const struct pldm_fd_ops *ops, void *ops_ctx);
+		  const struct pldm_fd_ops *ops, void *ops_ctx,
+		  struct pldm_control *control);
 
 /** @brief Handle a PLDM Firmware Update message
  *
diff --git a/src/firmware_device/fd.c b/src/firmware_device/fd.c
index 116657c..f65cfcd 100644
--- a/src/firmware_device/fd.c
+++ b/src/firmware_device/fd.c
@@ -22,6 +22,33 @@
 static const uint8_t INSTANCE_ID_COUNT = 32;
 static const uint8_t PROGRESS_PERCENT_NOT_SUPPORTED = 101;
 
+#define PLDM_FD_VERSIONS_COUNT 2
+static const uint32_t PLDM_FD_VERSIONS[PLDM_FD_VERSIONS_COUNT] = {
+	/* Only PLDM Firmware 1.1.0 is current implemented. */
+	0xf1f1f000,
+	/* CRC. Calculated with python:
+	hex(crccheck.crc.Crc32.calc(struct.pack('<I', 0xf1f1f000)))
+	*/
+	0x539dbeba,
+};
+const bitfield8_t PLDM_FD_COMMANDS[32] = {
+	// 0x00..0x07
+	{ .byte = (1 << PLDM_QUERY_DEVICE_IDENTIFIERS |
+		   1 << PLDM_GET_FIRMWARE_PARAMETERS) },
+	{ 0 },
+	// 0x10..0x17
+	{ .byte = (1u << PLDM_REQUEST_UPDATE | 1u << PLDM_PASS_COMPONENT_TABLE |
+		   1u << PLDM_UPDATE_COMPONENT) >>
+		  0x10 },
+	// 0x18..0x1f
+	{
+		.byte = (1u << PLDM_ACTIVATE_FIRMWARE | 1u << PLDM_GET_STATUS |
+			 1u << PLDM_CANCEL_UPDATE_COMPONENT |
+			 1u << PLDM_CANCEL_UPDATE) >>
+			0x18,
+	},
+};
+
 /* Ensure that public definition is kept updated */
 static_assert(alignof(struct pldm_fd) == PLDM_ALIGNOF_PLDM_FD,
 	      "PLDM_ALIGNOF_PLDM_FD wrong");
@@ -1174,11 +1201,13 @@
 }
 
 LIBPLDM_ABI_TESTING
-struct pldm_fd *pldm_fd_new(const struct pldm_fd_ops *ops, void *ops_ctx)
+struct pldm_fd *pldm_fd_new(const struct pldm_fd_ops *ops, void *ops_ctx,
+			    struct pldm_control *control)
 {
 	struct pldm_fd *fd = malloc(sizeof(*fd));
 	if (fd) {
-		if (pldm_fd_setup(fd, sizeof(*fd), ops, ops_ctx) == 0) {
+		if (pldm_fd_setup(fd, sizeof(*fd), ops, ops_ctx, control) ==
+		    0) {
 			return fd;
 		}
 		free(fd);
@@ -1189,8 +1218,11 @@
 
 LIBPLDM_ABI_TESTING
 int pldm_fd_setup(struct pldm_fd *fd, size_t pldm_fd_size,
-		  const struct pldm_fd_ops *ops, void *ops_ctx)
+		  const struct pldm_fd_ops *ops, void *ops_ctx,
+		  struct pldm_control *control)
 {
+	int rc;
+
 	if (fd == NULL || ops == NULL) {
 		return -EINVAL;
 	}
@@ -1214,6 +1246,16 @@
 	fd->fd_t1_timeout = DEFAULT_FD_T1_TIMEOUT;
 	fd->fd_t2_retry_time = DEFAULT_FD_T2_RETRY_TIME;
 
+	if (control) {
+		rc = pldm_control_add_type(control, PLDM_FWUP,
+					   &PLDM_FD_VERSIONS,
+					   PLDM_FD_VERSIONS_COUNT,
+					   PLDM_FD_COMMANDS);
+		if (rc) {
+			return rc;
+		}
+	}
+
 	return 0;
 }
 
@@ -1297,7 +1339,8 @@
 		break;
 	}
 
-	/* Dispatch command */
+	/* Dispatch command.
+	 Update PLDM_FD_COMMANDS if adding new handlers */
 	switch (hdr.command) {
 	case PLDM_QUERY_DEVICE_IDENTIFIERS:
 		rc = pldm_fd_qdi(fd, &hdr, req, req_payload_len, resp,