ncsi-cmd: Add 'discover' subcommand for MCTP
This commit introduces a new 'discover' subcommand to the ncsi-cmd tool,
enabling scanning for available NC-SI packages and channels via MCTP.
The discovery process starts from Package 0 and iterates through
channels to identify functional combinations.
Once a valid package and channel are found, the process stops.
Testing:
./ncsi-cmd -m 91 discover
Change-Id: I2e38b23c5995f3f34e2dc3d22fed4eead8194835
Signed-off-by: eddy lu <puzzy8338@gmail.com>
diff --git a/src/ncsi_cmd.cpp b/src/ncsi_cmd.cpp
index ea0bc12..67c30e8 100644
--- a/src/ncsi_cmd.cpp
+++ b/src/ncsi_cmd.cpp
@@ -45,8 +45,8 @@
struct GlobalOptions
{
std::unique_ptr<Interface> interface;
- std::optional<unsigned int> package;
- std::optional<unsigned int> channel;
+ std::optional<uint8_t> package;
+ std::optional<uint8_t> channel;
};
struct MCTPAddress
@@ -75,6 +75,7 @@
<< "Usage:\n"
" " << progname << " <options> raw TYPE [PAYLOAD...]\n"
" " << progname << " <options> oem [PAYLOAD...]\n"
+ " " << progname << " <options> discover\n"
" " << progname << " <options> core-dump FILE\n"
" " << progname << " <options> crash-dump FILE\n"
"\n"
@@ -90,6 +91,9 @@
"\n"
"Subcommands:\n"
"\n"
+ "discover\n"
+ " Scan for available NC-SI packages and channels.\n"
+ "\n"
"raw TYPE [PAYLOAD...]\n"
" Send a single command using raw type/payload data.\n"
" TYPE NC-SI command type, in hex\n"
@@ -321,13 +325,24 @@
return {};
}
+ // For non-discovery commands, package is required.
+ // If the subcommand is "discover", leave opts.package as nullopt.
if (!package.has_value())
{
- std::cerr << "Missing package, add a --package argument\n";
- return {};
+ if (optind < argc && std::string(argv[optind]) == "discover")
+ {
+ // Do nothing; package remains nullopt for discovery.
+ }
+ else
+ {
+ std::cerr << "Missing package, add a --package argument\n";
+ return {};
+ }
}
-
- opts.package = *package;
+ else
+ {
+ opts.package = static_cast<uint8_t>(*package);
+ }
return std::make_tuple(std::move(opts), optind);
}
@@ -360,10 +375,11 @@
static int ncsiCommand(GlobalOptions& options, uint8_t type,
std::vector<unsigned char> payload)
{
- NCSICommand cmd(type, static_cast<uint8_t>(*options.package),
- options.channel,
- std::span<unsigned char>(payload.data(), payload.size()));
+ uint8_t pkg = options.package.value_or(0);
+ auto ch = options.channel;
+ NCSICommand cmd(type, pkg, ch,
+ std::span<unsigned char>(payload.data(), payload.size()));
lg2::debug("Command: type {TYPE}, payload {PAYLOAD_LEN} bytes: {PAYLOAD}",
"TYPE", lg2::hex, type, "PAYLOAD_LEN", payload.size(), "PAYLOAD",
toHexStr(payload));
@@ -553,9 +569,9 @@
auto payloadArray = generateDumpCmdPayload(chunkNum, handle, false);
std::span<unsigned char> payload(payloadArray.data(),
payloadArray.size());
-
- NCSICommand cmd(ncsiCmdDump, static_cast<uint8_t>(*options.package),
- options.channel, payload);
+ uint8_t pkg = options.package.value_or(0);
+ auto ch = options.channel;
+ NCSICommand cmd(ncsiCmdDump, pkg, ch, payload);
auto resp = options.interface->sendCommand(cmd);
if (!resp)
{
@@ -634,9 +650,9 @@
auto abortPayloadArray = generateDumpCmdPayload(chunkNum, handle, true);
std::span<unsigned char> abortPayload(abortPayloadArray.data(),
abortPayloadArray.size());
- NCSICommand abortCmd(ncsiCmdDump,
- static_cast<uint8_t>(*options.package),
- options.channel, abortPayload);
+ uint8_t pkg = options.package.value_or(0);
+ auto ch = options.channel;
+ NCSICommand abortCmd(ncsiCmdDump, pkg, ch, abortPayload);
auto abortResp = options.interface->sendCommand(abortCmd);
if (!abortResp)
{
@@ -674,6 +690,99 @@
return ncsiDump(options, handle, argv[1]);
}
+static int ncsiDiscover(GlobalOptions& options)
+{
+ constexpr uint8_t ncsiClearInitialState = 0x00;
+ constexpr uint8_t ncsiGetCapabilities = 0x16;
+ constexpr unsigned int maxPackageIndex = 8; // Packages 0–7
+ constexpr unsigned int maxChannelCount = 32; // Channels 0–31
+
+ std::cout << "Starting NC-SI Package and Channel Discovery...\n";
+
+ unsigned int startPackage = 0, endPackage = maxPackageIndex;
+ if (options.package.has_value())
+ {
+ startPackage = *options.package;
+ endPackage = *options.package;
+ std::cout << "Restricting discovery to Package " << *options.package
+ << ".\n";
+ }
+
+ for (unsigned int packageIndex = startPackage; packageIndex <= endPackage;
+ ++packageIndex)
+ {
+ std::cout << "Checking Package " << packageIndex << "...\n";
+
+ // For each channel from 0..7, we:
+ // 1) Send Clear Initial State.
+ // 2) If that succeeds, send Get Capabilities.
+ // 3) If we get a valid response, we parse and stop.
+ bool foundChannel = false;
+ for (unsigned int channelIndex = 0; channelIndex < maxChannelCount;
+ ++channelIndex)
+ {
+ std::cout << " Clearing Initial State on Channel " << channelIndex
+ << "...\n";
+ {
+ std::vector<unsigned char> clearPayload; // No payload
+ NCSICommand clearCmd(ncsiClearInitialState,
+ static_cast<uint8_t>(packageIndex),
+ static_cast<uint8_t>(channelIndex),
+ std::span<unsigned char>(clearPayload));
+
+ auto clearResp = options.interface->sendCommand(clearCmd);
+ if (!clearResp || clearResp->response != 0x0000)
+ {
+ std::cout << " Clear Initial State failed on Channel "
+ << channelIndex << ". Trying next channel.\n";
+ continue; // Try next channel
+ }
+ }
+
+ // Now that Clear Initial State succeeded, try Get Capabilities
+ std::vector<unsigned char> payload; // No payload
+ NCSICommand getCapCmd(ncsiGetCapabilities,
+ static_cast<uint8_t>(packageIndex),
+ static_cast<uint8_t>(channelIndex),
+ std::span<unsigned char>(payload));
+
+ auto resp = options.interface->sendCommand(getCapCmd);
+ if (resp && resp->response == 0x0000 &&
+ resp->full_payload.size() >= 52)
+ {
+ uint8_t channelCount = resp->full_payload[47];
+ std::cout << " Package " << packageIndex << " supports "
+ << static_cast<int>(channelCount) << " channels.\n";
+ std::cout << " Found available Package " << packageIndex
+ << ", Channel " << channelIndex
+ << ". Stopping discovery.\n";
+ foundChannel = true;
+ break;
+ }
+ else
+ {
+ std::cout << " Channel " << channelIndex
+ << " not responding. Trying next channel.\n";
+ }
+ } // end channel loop
+
+ if (foundChannel)
+ {
+ // We found a channel on this package, so we stop discovery
+ return 0;
+ }
+ else
+ {
+ std::cout << " No valid channels found for Package "
+ << packageIndex << ". Moving to next package.\n";
+ }
+ } // end package loop
+
+ std::cout
+ << "No available NC-SI packages or channels found. Discovery complete.\n";
+ return -1;
+}
+
/* A note on log output:
* For output that relates to command-line usage, we just output directly to
* stderr. Once we have a properly parsed command line invocation, we use lg2
@@ -706,6 +815,14 @@
argv += consumed;
std::string subcommand = argv[0];
+
+ // For non-discovery commands, package must be provided.
+ if (subcommand != "discover" && !globalOptions.package.has_value())
+ {
+ std::cerr << "Missing package, add a --package argument\n";
+ return EXIT_FAILURE;
+ }
+
int ret = -1;
if (subcommand == "raw")
@@ -720,6 +837,10 @@
{
ret = ncsiCommandReceiveDump(globalOptions, subcommand, argc, argv);
}
+ else if (subcommand == "discover")
+ {
+ return ncsiDiscover(globalOptions);
+ }
else
{
std::cerr << "Unknown subcommand '" << subcommand << "'\n";