ncsi: encapsulate NC-SI commands with NCSICommand / NCSIResponse structs
... and rename Interface::sendOemCommand to Interface::sendCommand.
This provides a more clear facility to pass command and response objects
around, for future command and transport implementations.
Change-Id: I46e594ab6467ed87cfc27189c3ec4bd321726ee5
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
diff --git a/src/ncsi_netlink_main.cpp b/src/ncsi_netlink_main.cpp
index 3089b7c..ce9b3f2 100644
--- a/src/ncsi_netlink_main.cpp
+++ b/src/ncsi_netlink_main.cpp
@@ -209,16 +209,19 @@
lg2::debug("Payload: {PAYLOAD}", "PAYLOAD", toHexStr(payload));
}
- auto cmd =
- std::span<const unsigned char>(payload.begin(), payload.end());
- auto resp =
- interface.sendOemCommand(packageInt, channelInt, operationInt, cmd);
+ std::optional<uint8_t> chan = channelInt != DEFAULT_VALUE
+ ? std::make_optional(channelInt)
+ : std::nullopt;
+ NCSICommand cmd(operationInt, packageInt, chan, payload);
+
+ auto resp = interface.sendCommand(cmd);
if (!resp)
{
return EXIT_FAILURE;
}
lg2::debug("Response {DATA_LEN} bytes: {DATA}", "DATA_LEN",
- resp->size(), "DATA", toHexStr(*resp));
+ resp->full_payload.size(), "DATA",
+ toHexStr(resp->full_payload));
}
else if ((options)["set"] == "true")
{
diff --git a/src/ncsi_util.cpp b/src/ncsi_util.cpp
index 13c41f6..d8ad3b2 100644
--- a/src/ncsi_util.cpp
+++ b/src/ncsi_util.cpp
@@ -7,6 +7,8 @@
#include <phosphor-logging/lg2.hpp>
+#include <optional>
+#include <span>
#include <vector>
namespace phosphor
@@ -16,6 +18,19 @@
namespace ncsi
{
+NCSICommand::NCSICommand(uint8_t opcode, uint8_t package,
+ std::optional<uint8_t> channel,
+ std::span<unsigned char> payload) :
+ opcode(opcode), package(package), channel(channel)
+{
+ this->payload.assign(payload.begin(), payload.end());
+}
+
+uint8_t NCSICommand::getChannel()
+{
+ return channel.value_or(CHANNEL_ID_NONE);
+}
+
using CallBack = int (*)(struct nl_msg* msg, void* arg);
namespace internal
@@ -33,6 +48,12 @@
uint32_t rsvd[2];
};
+struct NCSIResponsePayload
+{
+ uint16_t response;
+ uint16_t reason;
+};
+
class NetlinkCommand
{
public:
@@ -203,7 +224,7 @@
struct sendCallBackContext
{
- std::vector<unsigned char> msg;
+ NCSIResponse resp;
};
CallBack sendCallBack = [](struct nl_msg* msg, void* arg) {
@@ -237,11 +258,16 @@
return -1;
}
- auto data_len = nla_len(tb[NCSI_ATTR_DATA]) - sizeof(NCSIPacketHeader);
- unsigned char* data =
- (unsigned char*)nla_data(tb[NCSI_ATTR_DATA]) + sizeof(NCSIPacketHeader);
+ size_t data_len = nla_len(tb[NCSI_ATTR_DATA]);
+ unsigned char* data = (unsigned char*)nla_data(tb[NCSI_ATTR_DATA]);
- ctx->msg.assign(data, data + data_len);
+ ctx->resp.full_payload.assign(data, data + data_len);
+
+ int rc = ctx->resp.parseFullPayload();
+ if (rc)
+ {
+ return -1;
+ }
return static_cast<int>(NL_STOP);
};
@@ -399,29 +425,27 @@
return std::to_string(interface.ifindex);
}
-std::optional<std::vector<unsigned char>>
- Interface::sendOemCommand(int package, int channel, int operation,
- std::span<const unsigned char> payload)
+std::optional<NCSIResponse> Interface::sendCommand(NCSICommand& cmd)
{
- lg2::debug("Send OEM Command, CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, "
+ lg2::debug("Send Command, CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, "
"INTERFACE: {INTERFACE}",
- "CHANNEL", lg2::hex, channel, "PACKAGE", lg2::hex, package,
- "INTERFACE", this);
+ "CHANNEL", lg2::hex, cmd.getChannel(), "PACKAGE", lg2::hex,
+ cmd.package, "INTERFACE", this);
internal::sendCallBackContext ctx{};
- internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SEND_CMD, operation,
- payload);
+ internal::NetlinkCommand nl_cmd(ncsi_nl_commands::NCSI_CMD_SEND_CMD,
+ cmd.opcode, cmd.payload);
- int rc = internal::applyCmd(*this, cmd, package, channel, NONE,
- internal::sendCallBack, &ctx);
+ int rc = internal::applyCmd(*this, nl_cmd, cmd.package, cmd.getChannel(),
+ NONE, internal::sendCallBack, &ctx);
if (rc < 0)
{
return {};
}
- return ctx.msg;
+ return ctx.resp;
}
int Interface::setChannel(int package, int channel)
@@ -497,6 +521,41 @@
return internal::applyCmd(*this, cmd);
}
+int NCSIResponse::parseFullPayload()
+{
+ if (this->full_payload.size() < sizeof(internal::NCSIPacketHeader) +
+ sizeof(internal::NCSIResponsePayload))
+ {
+ lg2::error("Response: Not enough data for a response message");
+ return -1;
+ }
+
+ internal::NCSIPacketHeader* respHeader =
+ reinterpret_cast<decltype(respHeader)>(this->full_payload.data());
+
+ unsigned int payloadLen = ntohs(respHeader->length & htons(0x0fff));
+ /* we have determined that the payload size is larger than *respHeader,
+ * so cannot underflow here */
+ if (payloadLen > this->full_payload.size() - sizeof(*respHeader))
+ {
+ lg2::error("Invalid header length {HDRLEN} (vs {LEN}) in response",
+ "HDRLEN", payloadLen, "LEN",
+ this->full_payload.size() - sizeof(*respHeader));
+ return -1;
+ }
+
+ this->opcode = respHeader->type;
+ this->payload =
+ std::span(this->full_payload.begin() + sizeof(*respHeader), payloadLen);
+
+ internal::NCSIResponsePayload* respPayload =
+ reinterpret_cast<decltype(respPayload)>(this->payload.data());
+ this->response = ntohs(respPayload->response);
+ this->reason = ntohs(respPayload->reason);
+
+ return 0;
+}
+
} // namespace ncsi
} // namespace network
} // namespace phosphor
diff --git a/src/ncsi_util.hpp b/src/ncsi_util.hpp
index 048bb71..0ca0df1 100644
--- a/src/ncsi_util.hpp
+++ b/src/ncsi_util.hpp
@@ -16,6 +16,7 @@
constexpr auto DEFAULT_VALUE = -1;
constexpr auto NONE = 0;
+constexpr uint8_t CHANNEL_ID_NONE = 0x1f;
struct ChannelInfo
{
@@ -40,10 +41,37 @@
std::vector<PackageInfo> packages;
};
+struct NCSICommand
+{
+ /* constructs a message; the payload span is copied into the internal
+ * command vector */
+ NCSICommand(uint8_t opcode, uint8_t package, std::optional<uint8_t> channel,
+ std::span<unsigned char> payload);
+
+ uint8_t getChannel();
+
+ uint8_t opcode;
+ uint8_t package;
+ std::optional<uint8_t> channel;
+ std::vector<unsigned char> payload;
+};
+
+struct NCSIResponse
+{
+ uint8_t opcode;
+ uint8_t response, reason;
+ std::span<unsigned char> payload;
+ std::vector<unsigned char> full_payload;
+
+ /* Given an incoming response with full_payload set, check that we have
+ * enough data for a correct response, and populate the rest of the struct
+ * to suit
+ */
+ int parseFullPayload();
+};
+
struct Interface
{
- using ncsiMessage = std::span<const unsigned char>;
-
/* @brief This function will ask underlying NCSI driver
* to send an OEM command (command type 0x50) with
* the specified payload as the OEM data.
@@ -55,8 +83,7 @@
* @param[in] payload - OEM data to send.
* @returns the NCSI response message to this command, or no value on error.
*/
- std::optional<std::vector<unsigned char>> sendOemCommand(
- int package, int channel, int opcode, ncsiMessage payload);
+ std::optional<NCSIResponse> sendCommand(NCSICommand& cmd);
/* @brief This function will ask underlying NCSI driver
* to set a specific package or package/channel