969 lines
37 KiB
C++
Executable File
969 lines
37 KiB
C++
Executable File
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
/* */
|
|
/* file: main.c */
|
|
/* */
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
/* */
|
|
/* description: Example CLI application for basic hub functionality. */
|
|
/* */
|
|
/* */
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
/* */
|
|
/* Copyright (c) 2023 Acroname Inc. - All Rights Reserved */
|
|
/* */
|
|
/* This file is part of the BrainStem release. See the license.txt */
|
|
/* file included with this package or go to */
|
|
/* https://acroname.com/software/brainstem-development-kit */
|
|
/* for full license details. */
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Includes
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include "BrainStem2/BrainStem-all.h"
|
|
|
|
//The command line arguments are parsed using the open source cxxopts.hpp header
|
|
//implementation. The original source of this file can be found at:
|
|
//https://github.com/jarro2783/cxxopts
|
|
//FYI: Windows Users std::min() and std::max() are used by cxxopts.hpp. This
|
|
// can cause an issues when including windows.h which defines these as macros.
|
|
// To combat this I added NOMINMAX to the preprocessor defines for this project.
|
|
// Debug -> BrainStem2Example Properties -> Configuration Properties -> C/C++ -> preprocessor
|
|
#include "cxxopts.hpp"
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Private Macros
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Platform specific line feed.
|
|
#if defined(_MSC_VER)
|
|
#define _LF "\r\n"
|
|
#define LF_LEN 2
|
|
#else
|
|
#define _LF "\n"
|
|
#define LF_LEN 1
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Private Constants
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
static const uint8_t MIN_MAJOR = 2;
|
|
static const uint8_t MIN_MINOR = 10;
|
|
static const uint8_t MIN_PATCH = 0;
|
|
|
|
|
|
static const char* EXAMPLES_COMMANDS = _LF
|
|
"Examples:" _LF
|
|
"Note: When --serial is NOT used, the application will default to the first Acroname Hub found" _LF
|
|
"\"AcronameHubCLI --ports 0 --enable 0\" - Disables Port 0" _LF
|
|
"\"AcronameHubCLI --ports 0 --enable 1\" - Enables Port 0" _LF
|
|
"\"AcronameHubCLI --ports=0,1,2 --enable 0\" - Disables Ports 0, 1 and 2" _LF
|
|
"\"AcronameHubCLI --ports 1 --enable 1 --serial FEEDBEEF\" - Enables port 1 for device 0xFEEDBEEF" _LF
|
|
"\"AcronameHubCLI --ports 1 --enable 1 --data\" - Enables port 1 data lines only" _LF
|
|
"\"AcronameHubCLI --ports 1 --enable 0 --data\" - Disables port 1 data lines only" _LF
|
|
"\"AcronameHubCLI --ports 2 --enable 1 --power\" - Enables port 2 power lines only" _LF
|
|
"\"AcronameHubCLI --ports 2 --enable 0 --power\" - Disables port 2 power lines only" _LF
|
|
"\"AcronameHubCLI --ports 3 --voltage --current\" - Gets the voltage and current of port 3" _LF
|
|
;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Private Types
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef enum PORT_ACTIONS : uint8_t {
|
|
kPORT_ACTIONS_ENABLE = 0,
|
|
kPORT_ACTIONS_TOGGLE,
|
|
kPORT_ACTIONS_DATA,
|
|
kPORT_ACTIONS_UNKNOWN,
|
|
kPORT_ACTIONS_LAST
|
|
} PORT_ACTIONS_t;
|
|
|
|
|
|
typedef struct CLIObject {
|
|
std::shared_ptr<Acroname::BrainStem::Module> stem = nullptr;
|
|
std::shared_ptr<cxxopts::Options> options = nullptr;
|
|
std::shared_ptr<linkSpec> spec = nullptr;
|
|
|
|
unsigned long serialNumber = 0;
|
|
std::vector<int> ports;
|
|
int enable = 0;
|
|
int upstream = 0;
|
|
bool power = true;
|
|
bool data = true;
|
|
PORT_ACTIONS_t portsAction = kPORT_ACTIONS_UNKNOWN;
|
|
cxxopts::ParseResult result;
|
|
} CLIObject_t;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Private Function Prototypes
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
static void _config_cxxopts(CLIObject_t& cliObj);
|
|
|
|
static bool _parse(CLIObject_t& cliObj, int argc, char* argv[]);
|
|
static bool _parse_serial(CLIObject_t& cliObj);
|
|
static bool _parse_port(CLIObject_t& cliObj);
|
|
static bool _parse_upstream(CLIObject_t& cliObj);
|
|
|
|
//If (int < 0) A program error has occurred.
|
|
//If (int > 0) A BrainStem error has occurred.
|
|
//If (int == 0) Success.
|
|
static int _process(CLIObject_t& cliObj);
|
|
static int _process_createStem(CLIObject_t& cliObj);
|
|
static aErr _process_reset(CLIObject_t& cliObj);
|
|
static aErr _process_ports(CLIObject_t& cliObj);
|
|
static aErr _process_upstream(CLIObject_t& cliObj);
|
|
static aErr _process_verbose(CLIObject_t& cliObj);
|
|
|
|
static aErr _portAction_toggle(CLIObject_t& cliObj, const int& port);
|
|
static aErr _portAction_enable(CLIObject_t& cliObj, const int& port);
|
|
static aErr _portAction_voltage(CLIObject_t& cliObj, const int& port);
|
|
static aErr _portAction_current(CLIObject_t& cliObj, const int& port);
|
|
|
|
static int _translate_UpstreamPort(uint8_t model, int providedPort);
|
|
|
|
static aErr _kickConnection(CLIObject_t& cliObj);
|
|
|
|
static void _verbose_printMode_USBHub3c(const uint32_t& mode, const int& port, const bool& showHeader = false);
|
|
static void _verbose_printMode_USBHub3p(const uint32_t& mode, const int& port, const bool& showHeader = false);
|
|
static void _verbose_printMode_USBHub2x4(const uint32_t& mode, const int& port, const bool& showHeader = false);
|
|
static std::string _verbose_printMode_decodeModeString(const uint32_t& mode);
|
|
static std::string _verbose_upstreamPortNumber(const uint32_t& model, const uint8_t& upstreamPort);
|
|
|
|
static bool _checkVersion(std::shared_ptr<Acroname::BrainStem::Module> stem);
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Public Function Implementations
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
CLIObject_t cliObj;
|
|
aErr err = aErrNone;
|
|
|
|
try
|
|
{
|
|
_config_cxxopts(cliObj);
|
|
|
|
//Parse options/arguments.
|
|
if(! _parse(cliObj, argc, argv)) { return -1; }
|
|
|
|
//Process options/arguments.
|
|
int success = _process(cliObj);
|
|
if((success >= 0) &&
|
|
(success <= aErrUnknown))
|
|
{
|
|
err = (aErr)success;
|
|
}
|
|
else { return success; }
|
|
|
|
}
|
|
catch (const cxxopts::OptionException& e) {
|
|
std::cerr << "Error parsing options: " << e.what() << std::endl;
|
|
std::cerr << cliObj.options->help() << std::endl;
|
|
return -2;
|
|
}
|
|
catch (...) {
|
|
std::cerr << "Unknown Exception Caught: " << std::endl;
|
|
std::cerr << cliObj.options->help() << std::endl;
|
|
return -3;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static void
|
|
_config_cxxopts(CLIObject_t& cliObj) {
|
|
//Create cxxopts options object
|
|
cliObj.options.reset(new cxxopts::Options("AcronameHubCLI.exe", "Acroname Programmable Hub Command Line Interface"));
|
|
|
|
cliObj.options->add_options()
|
|
("h, help", "Prints this help message")
|
|
("p, ports", "Port(s) in which a \"Port Action\" will be executed on. The default action is enable. This can be a comma-separated list.", cxxopts::value<std::vector<int>>())
|
|
("e, enable", "0 = Disable; 1 = Enable (Port Action) ", cxxopts::value<int>()->default_value("1"))
|
|
("t, toggle", "Toggle port(s) between enabled and disabled. (Port Action) ")
|
|
("b, voltage", "Reports the voltage of a given port(s) (Port Action)")
|
|
("c, current", "Reports the current of a given port(s) (Port Action)")
|
|
("w, power", "Only apply enable or toggle actions to port power lines")
|
|
("d, data", "Only apply enable or toggle actions to port data lines")
|
|
("u, upstream", "Manually select an upstream port", cxxopts::value<int>())
|
|
("a, auto", "Enable automatic upstream port selection. If this option is present, the -u/--upstream option is ignored.")
|
|
("s, serial", "Hub serial number in hexadecimal representation. Use 0 for automatic discovery.", cxxopts::value<std::string>()->default_value("0"))
|
|
("v, verbose", "Report port states after processing other options")
|
|
("r, reset", "Reset the hub. All port options are ignored.")
|
|
("x, examples", "Displays a list of common examples");
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Private Function Implementations
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool
|
|
_parse(CLIObject_t& cliObj, int argc, char* argv[]) {
|
|
cliObj.result = cliObj.options->parse(argc, argv);
|
|
|
|
//Print arguments, if non were provided print the usage.
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
std::cout << "Arguments: ";
|
|
for (int x = 0; x < argc; x++) { std::cout << argv[x] << " "; }
|
|
std::cout << std::endl;
|
|
|
|
if(argc == 1) { //First argument is always the app path.
|
|
std::cout << "No arguments were provided. Printing usage" << std::endl;
|
|
std::cout << cliObj.options->help() << std::endl;
|
|
std::cout << EXAMPLES_COMMANDS << std::endl;
|
|
return false;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Parser - Help
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if (cliObj.result.count("help")) {
|
|
std::cout << cliObj.options->help() << std::endl;
|
|
std::cout << EXAMPLES_COMMANDS << std::endl;
|
|
return false;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Parser - Help
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if (cliObj.result.count("examples")) {
|
|
std::cout << EXAMPLES_COMMANDS << std::endl;
|
|
return false;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Parser - Serial
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(! _parse_serial(cliObj)) {
|
|
return false;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Parser - Port
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(! _parse_port(cliObj)) {
|
|
return false;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Parser - Enable - We need to check the value is within range.
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
cliObj.enable = cliObj.result["enable"].as<int>();
|
|
if ((1 != cliObj.enable) &&
|
|
(0 != cliObj.enable))
|
|
{
|
|
std::cerr << "Incorrect value for enable" << std::endl;
|
|
std::cerr << "Acceptable values are 0 for false and 1 for true" << std::endl;
|
|
std::cerr << cliObj.options->help() << std::endl;
|
|
return false;
|
|
}
|
|
/// End Parse Enable/////////////////////////////////////////////////////////////////
|
|
|
|
//Parser - Toggle
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//Nothing specific needs to be handled or tested. Option will be checked in "work".
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Parser - Upstream - We need to check the value is within range.
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(! _parse_upstream(cliObj)) {
|
|
return false;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Parser - Power and Data
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// act on both power and data by default (both flags present or both flags absent)
|
|
cliObj.power = true;
|
|
cliObj.data = true;
|
|
if (cliObj.result.count("power") && !cliObj.result.count("data")) {
|
|
// only power flag present: only act on power lines
|
|
cliObj.power = true;
|
|
cliObj.data = false;
|
|
}
|
|
if (cliObj.result.count("data") && !cliObj.result.count("power")) {
|
|
// only data flag present: only act on power lines
|
|
cliObj.power = false;
|
|
cliObj.data = true;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Parser - Auto
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//Nothing specific needs to be handled or tested. Option will be checked in "work".
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool
|
|
_parse_serial(CLIObject_t& cliObj) {
|
|
if (cliObj.result.count("serial")) {
|
|
cliObj.serialNumber = std::stoul(cliObj.result["serial"].as<std::string>(), nullptr, 16);
|
|
if (cliObj.serialNumber == 0) {
|
|
cliObj.spec.reset(aDiscovery_FindFirstModule(USB, LOCALHOST_IP_ADDRESS));
|
|
}
|
|
else {
|
|
cliObj.spec.reset(aDiscovery_FindModule(USB,
|
|
(uint32_t)cliObj.serialNumber,
|
|
LOCALHOST_IP_ADDRESS));
|
|
}
|
|
|
|
//Ensure that we found something.
|
|
if (cliObj.spec == NULL) {
|
|
std::cerr << "Could not find any BrainStem Devices" << std::endl;
|
|
std::cerr << cliObj.options->help() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
//No serial number was provided. Use the first thing we find.
|
|
cliObj.spec.reset(aDiscovery_FindFirstModule(USB, LOCALHOST_IP_ADDRESS));
|
|
if (cliObj.spec == NULL) {
|
|
std::cerr << "Could not find any BrainStem Devices" << std::endl;
|
|
std::cerr << cliObj.options->help() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ((cliObj.spec->model != aMODULE_TYPE_USBHub3p) &&
|
|
(cliObj.spec->model != aMODULE_TYPE_USBHub2x4) &&
|
|
(cliObj.spec->model != aMODULE_TYPE_USBHub3c))
|
|
{
|
|
std::cerr << "The device that was found is not a hub. Model: " << aDefs_GetModelName(cliObj.spec->model) << std::endl;
|
|
std::cerr << cliObj.options->help() << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool
|
|
_parse_port(CLIObject_t& cliObj) {
|
|
if (cliObj.result.count("ports")) {
|
|
|
|
if (cliObj.result.count("enable")) {
|
|
cliObj.portsAction = kPORT_ACTIONS_ENABLE;
|
|
}
|
|
else if(cliObj.result.count("toggle")) {
|
|
cliObj.portsAction = kPORT_ACTIONS_TOGGLE;
|
|
}
|
|
else if((cliObj.result.count("voltage")) ||
|
|
(cliObj.result.count("current")))
|
|
{
|
|
cliObj.portsAction = kPORT_ACTIONS_DATA;
|
|
}
|
|
else {
|
|
//This should preserve backwards compatibility.
|
|
//Specifically AcronameHubCLIE.exe --ports 1 -> This enables the port.
|
|
//ie. (--enable not required).
|
|
cliObj.portsAction = kPORT_ACTIONS_ENABLE;
|
|
}
|
|
|
|
//We need to check the range based on the device type
|
|
cliObj.ports = cliObj.result["ports"].as<std::vector<int>>();
|
|
for (const auto& port : cliObj.ports) {
|
|
if ((cliObj.spec->model == aMODULE_TYPE_USBHub3p) ||
|
|
(cliObj.spec->model == aMODULE_TYPE_USBHub3c))
|
|
{
|
|
if ((port > 7) ||
|
|
(port < 0))
|
|
{
|
|
std::cerr << "Incorrect port value: " << int(port) << std::endl;
|
|
std::cerr << "The " << aDefs_GetModelName(cliObj.spec->model) << " ports range from 0-7" << std::endl;
|
|
std::cerr << cliObj.options->help() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
else if (cliObj.spec->model == aMODULE_TYPE_USBHub2x4) {
|
|
if ((port > 3) ||
|
|
(port < 0))
|
|
{
|
|
std::cerr << "Incorrect port value: " << int(port) << std::endl;
|
|
std::cerr << "The " << aDefs_GetModelName(cliObj.spec->model) << " ports range from 0-3" << std::endl;
|
|
std::cerr << cliObj.options->help() << std::endl;
|
|
return false;
|
|
}
|
|
}//end model check.
|
|
}//end ports for loop
|
|
}// end "ports" argument
|
|
else {
|
|
//These parameters require --ports.
|
|
//Notify the user and exit.
|
|
std::string s = "";
|
|
if (cliObj.result.count("enable")) { s = "enable"; }
|
|
else if(cliObj.result.count("toggle")) { s = "toggle"; }
|
|
else if(cliObj.result.count("voltage")) { s = "voltage"; }
|
|
else if(cliObj.result.count("current")) { s = "current"; }
|
|
|
|
if(s.size()) {
|
|
std::cout << "Parameter: \"" << s << "\" must accompany the \"ports\" parameter" << std::endl;
|
|
std::cerr << cliObj.options->help() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool
|
|
_parse_upstream(CLIObject_t& cliObj) {
|
|
if (cliObj.result.count("upstream")) {
|
|
bool handleError = false;
|
|
|
|
cliObj.upstream = cliObj.result["upstream"].as<int>();
|
|
if (cliObj.spec->model == aMODULE_TYPE_USBHub3c) {
|
|
if(cliObj.upstream >= 6) {
|
|
handleError = true;
|
|
}
|
|
}
|
|
else {
|
|
if ((cliObj.upstream != 1) &&
|
|
(cliObj.upstream != 0))
|
|
{
|
|
handleError = true;
|
|
}
|
|
}
|
|
|
|
if(handleError) {
|
|
std::cerr << "Incorrect value for upstream" << std::endl;
|
|
if (cliObj.spec->model == aMODULE_TYPE_USBHub3c) {
|
|
std::cerr << "Acceptable values 0-5" << std::endl;
|
|
}
|
|
else {
|
|
std::cerr << "Acceptable values are 0 for UP0 and 1 for UP1" << std::endl;
|
|
}
|
|
std::cerr << cliObj.options->help() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static int
|
|
_process(CLIObject_t& cliObj) {
|
|
aErr err = aErrNone;
|
|
|
|
int success = _process_createStem(cliObj);
|
|
if(0 != success) { return success; }
|
|
|
|
err = cliObj.stem->connectFromSpec(*cliObj.spec);
|
|
if (aErrNone != err) {
|
|
std::cerr << "Error connecting to the device. Error: " << err << std::endl;
|
|
return err;
|
|
}
|
|
else {
|
|
std::cout << "Sucessfully connected to " << aDefs_GetModelName(cliObj.spec->model);
|
|
auto prevState = std::cout.flags();
|
|
std::cout << " SN: 0x" << std::setfill('0') << std::setw(8) << std::hex << std::uppercase << cliObj.spec->serial_num << std::endl;
|
|
std::cout.flags(prevState);
|
|
}
|
|
|
|
if (cliObj.result.count("reset")) {
|
|
return _process_reset(cliObj);
|
|
}
|
|
|
|
if (aErrNone == err) { err = _process_ports(cliObj); }
|
|
if (aErrNone == err) { err = _process_upstream(cliObj); }
|
|
if (aErrNone == err) { err = _process_verbose(cliObj); }
|
|
|
|
if(cliObj.stem->isConnected()) { cliObj.stem->disconnect(); }
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
return (int)err;
|
|
}
|
|
|
|
|
|
static int
|
|
_process_createStem(CLIObject_t& cliObj) {
|
|
if (cliObj.spec->model == aMODULE_TYPE_USBHub3p) {
|
|
cliObj.stem.reset(new Acroname::BrainStem::Module(aUSBHUB3P_MODULE, true, aMODULE_TYPE_USBHub3p));
|
|
}
|
|
else if (cliObj.spec->model == aMODULE_TYPE_USBHub2x4) {
|
|
cliObj.stem.reset(new Acroname::BrainStem::Module(aUSBHUB2X4_MODULE, true, aMODULE_TYPE_USBHub2x4));
|
|
}
|
|
else if (cliObj.spec->model == aMODULE_TYPE_USBHub3c) {
|
|
cliObj.stem.reset(new Acroname::BrainStem::Module(aUSBHUB3C_MODULE, true, aMODULE_TYPE_USBHub3c));
|
|
}
|
|
else {
|
|
std::cerr << "Error: device object not created" << std::endl;
|
|
return -5;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static aErr
|
|
_process_reset(CLIObject_t& cliObj) {
|
|
std::cout << "Resetting device" << std::endl;
|
|
if(cliObj.stem->isConnected()) {
|
|
Acroname::BrainStem::SystemClass system;
|
|
system.init(cliObj.stem.get(), 0);
|
|
system.reset();
|
|
return aErrNone;
|
|
}
|
|
else { return aErrConnection; }
|
|
}
|
|
|
|
|
|
static aErr
|
|
_process_ports(CLIObject_t& cliObj) {
|
|
aErr err = aErrNone;
|
|
|
|
//Loop through provided ports.
|
|
for (const auto& port : cliObj.ports) {
|
|
switch(cliObj.portsAction) {
|
|
case kPORT_ACTIONS_ENABLE:
|
|
err = _portAction_enable(cliObj, port);
|
|
break;
|
|
|
|
case kPORT_ACTIONS_TOGGLE:
|
|
err = _portAction_toggle(cliObj, port);
|
|
break;
|
|
|
|
case kPORT_ACTIONS_DATA:
|
|
if(aErrNone != err) { break; }
|
|
if(cliObj.result.count("voltage")) {
|
|
err = _portAction_voltage(cliObj, port);
|
|
}
|
|
|
|
if(aErrNone != err) { break; }
|
|
if(cliObj.result.count("current")) {
|
|
err = _portAction_current(cliObj, port);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
//Given the current logic we shouldn't get here.
|
|
cliObj.portsAction = kPORT_ACTIONS_UNKNOWN;
|
|
std::cout << "Unknown Action" << std::endl;
|
|
return aErrUnknown;
|
|
}
|
|
|
|
if(aErrNone != err) { break; }
|
|
} // for (const auto& port : ports)
|
|
|
|
return err;
|
|
}
|
|
|
|
static int _translate_UpstreamPort(uint8_t model, int providedPort) {
|
|
|
|
switch(model) {
|
|
case aMODULE_TYPE_USBHub3p:
|
|
if(providedPort == 0) {
|
|
return aUSBHub3p::PORT_ID_t::kPORT_ID_UP0;
|
|
}
|
|
else if(providedPort == 1) {
|
|
return aUSBHub3p::PORT_ID_t::kPORT_ID_UP1;
|
|
}
|
|
case aMODULE_TYPE_USBHub2x4:
|
|
if(providedPort == 0) {
|
|
return aUSBHub2x4::PORT_ID_t::kPORT_ID_UP0;
|
|
}
|
|
else if(providedPort == 1) {
|
|
return aUSBHub2x4::PORT_ID_t::kPORT_ID_UP1;
|
|
}
|
|
// case aMODULE_TYPE_USBHub3c: //Nothing to translate
|
|
}
|
|
|
|
return providedPort;
|
|
}
|
|
|
|
static aErr
|
|
_process_upstream(CLIObject_t& cliObj) {
|
|
if(! _checkVersion(cliObj.stem)) { return aErrVersion; }
|
|
|
|
Acroname::BrainStem::USBSystemClass usbSystem;
|
|
usbSystem.init(cliObj.stem.get(), 0);
|
|
|
|
aErr err = aErrNone;
|
|
if (cliObj.result.count("auto")) {
|
|
err = usbSystem.setDataRoleBehavior(usbsystemDataBehavior_PortPriority);
|
|
if (aErrTimeout == err) { err = _kickConnection(cliObj); }
|
|
|
|
if(aErrNone != err) {
|
|
std::cerr << "Error (" << err << ") " << "changing the data role to auto (Port Priority)." << std::endl;
|
|
return err;
|
|
}
|
|
|
|
std::cout << "Automatic upstream selection configured successfully." << std::endl;
|
|
}
|
|
else if (cliObj.result.count("upstream")) {
|
|
err = usbSystem.setDataRoleBehavior(usbsystemDataBehavior_HardCoded);
|
|
if (aErrTimeout == err) { err = _kickConnection(cliObj); }
|
|
|
|
if(aErrNone != err) {
|
|
std::cerr << "Error (" << err << ") " << "changing the data role to fixed (Hard Coded)." << std::endl;
|
|
return err;
|
|
}
|
|
|
|
err = usbSystem.setUpstream(_translate_UpstreamPort(cliObj.spec->model, cliObj.upstream));
|
|
if (aErrTimeout == err) { err = _kickConnection(cliObj); }
|
|
|
|
if(aErrNone != err) {
|
|
std::cerr << "There was an error (" << err << ") " << "changing the upstream port." << std::endl;
|
|
std::cerr << "If you changed the upstream connection away from this machine and you have no control port connection then this is expected." << std::endl;
|
|
return err;
|
|
}
|
|
|
|
std::cout << "Upstream successfully set to port: " << cliObj.upstream << std::endl;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static aErr
|
|
_process_verbose(CLIObject_t& cliObj) {
|
|
aErr err = aErrNone;
|
|
|
|
if(cliObj.result.count("verbose")) {
|
|
if(! _checkVersion(cliObj.stem)) { return aErrVersion; }
|
|
|
|
Acroname::BrainStem::USBSystemClass usbSystem;
|
|
usbSystem.init(cliObj.stem.get(), 0);
|
|
|
|
uint8_t upstreamPort = 0;
|
|
err = usbSystem.getUpstream(&upstreamPort);
|
|
if(aErrNone != err) {
|
|
std::cerr << "Error (" << err << ") " << "getting the upstream port." << std::endl;
|
|
return err;
|
|
}
|
|
|
|
std::cout << "Active Upstream: " << _verbose_upstreamPortNumber(cliObj.spec->model, upstreamPort) << std::endl;
|
|
|
|
|
|
//Iterate up to a large value of ports to support all products.
|
|
//We will break out when we get aErrIndexRange as an error code
|
|
for(int port = 0; port < 20; port++) {
|
|
Acroname::BrainStem::PortClass portClass;
|
|
portClass.init(cliObj.stem.get(), port);
|
|
|
|
uint32_t mode = 0;
|
|
err = portClass.getMode(&mode);
|
|
|
|
//Once we hit an invalid index bail.
|
|
if(aErrIndexRange == err) {
|
|
return aErrNone; //Overwrite the error since we expect this case.
|
|
}
|
|
else if(aErrUnimplemented == err) {
|
|
//Some ports do not support this API.
|
|
//For instance: Ports: Up0, Up1, DownA and Control on the USBHub3p.
|
|
continue;
|
|
}
|
|
else if(aErrNone != err) {
|
|
std::cerr << "Error (" << err << ") " << "getting the mode." << std::endl;
|
|
return err;
|
|
}
|
|
|
|
bool printHeader = (0 == port);
|
|
|
|
switch (cliObj.spec->model) {
|
|
case aMODULE_TYPE_USBHub3c:
|
|
_verbose_printMode_USBHub3c(mode, port, printHeader);
|
|
break;
|
|
|
|
case aMODULE_TYPE_USBHub3p:
|
|
_verbose_printMode_USBHub3p(mode, port, printHeader);
|
|
break;
|
|
|
|
case aMODULE_TYPE_USBHub2x4:
|
|
_verbose_printMode_USBHub2x4(mode, port, printHeader);
|
|
break;
|
|
|
|
default: break;
|
|
}//end switch
|
|
}//end for loop (ports)
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static aErr
|
|
_portAction_toggle(CLIObject_t& cliObj, const int& port) {
|
|
|
|
if(! _checkVersion(cliObj.stem)) { return aErrVersion; }
|
|
|
|
Acroname::BrainStem::PortClass portClass;
|
|
portClass.init(cliObj.stem.get(), port);
|
|
|
|
aErr err = aErrNone;
|
|
uint8_t vbusState = 0;
|
|
uint8_t dataState = 0;
|
|
|
|
err = portClass.getDataEnabled(&dataState);
|
|
if(aErrNone != err) {
|
|
std::cerr << "Error (" << err << ") getting port: " << int(port) << " data state" << std::endl;
|
|
return err;
|
|
}
|
|
|
|
err = portClass.getPowerEnabled(&vbusState);
|
|
if(aErrNone != err) {
|
|
std::cerr << "Error (" << err << ") getting port: " << int(port) << " power state" << std::endl;
|
|
return err;
|
|
}
|
|
|
|
//Apply Data Settings
|
|
if (cliObj.data) {
|
|
err = portClass.setDataEnabled(! dataState);
|
|
if (aErrNone != err) {
|
|
std::cerr << "Error (" << err << ") toggling port: " << int(port) << " data" << std::endl;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
//Apply Power Settings
|
|
if (cliObj.power) {
|
|
err = portClass.setPowerEnabled(! vbusState);
|
|
if (aErrNone != err) {
|
|
std::cerr << "Error (" << err << ") toggling port: " << int(port) << " power" << std::endl;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
std::cout << "Port: " << int(port) << " was sucessfully toggled" << std::endl;
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static aErr
|
|
_portAction_enable(CLIObject_t& cliObj, const int& port)
|
|
{
|
|
if(! _checkVersion(cliObj.stem)) { return aErrVersion; }
|
|
|
|
Acroname::BrainStem::PortClass portClass;
|
|
portClass.init(cliObj.stem.get(), port);
|
|
|
|
aErr err = aErrNone;
|
|
|
|
if ((cliObj.power) &&
|
|
(cliObj.data))
|
|
{
|
|
err = portClass.setEnabled(cliObj.enable);
|
|
}
|
|
else {
|
|
if (cliObj.power) { err = portClass.setPowerEnabled(cliObj.enable); }
|
|
if (cliObj.data) { err = portClass.setDataEnabled(cliObj.enable); }
|
|
}
|
|
|
|
if (err != aErrNone) {
|
|
std::cerr << "There was an error (" << err << ") " << (cliObj.enable ? "enabling" : "disabling") << " port " << int(port) << std::endl;
|
|
}
|
|
else {
|
|
std::cout << "Port: " << int(port) << " was sucessfully " << (cliObj.enable ? "enabled" : "disabled") << std::endl;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static aErr
|
|
_portAction_voltage(CLIObject_t& cliObj, const int& port) {
|
|
aErr err = aErrNone;
|
|
|
|
if(! _checkVersion(cliObj.stem)) { return aErrVersion; }
|
|
|
|
Acroname::BrainStem::PortClass p;
|
|
p.init(cliObj.stem.get(), port);
|
|
|
|
int32_t voltage = 0;
|
|
err = p.getVbusVoltage(&voltage);
|
|
|
|
std::cout << "Port: " << int(port) << " Voltage: " << double(voltage)/1000000.0 << " VDC" << std::endl;
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static aErr
|
|
_portAction_current(CLIObject_t& cliObj, const int& port) {
|
|
aErr err = aErrNone;
|
|
|
|
if(! _checkVersion(cliObj.stem)) { return aErrVersion; }
|
|
|
|
Acroname::BrainStem::PortClass p;
|
|
p.init(cliObj.stem.get(), port);
|
|
|
|
int32_t current = 0;
|
|
err = p.getVbusCurrent(¤t);
|
|
|
|
std::cout << "Port: " << int(port) << " Current: " << double(current)/1000000.0 << " A" << std::endl;
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static bool
|
|
_checkVersion(std::shared_ptr<Acroname::BrainStem::Module> stem) {
|
|
|
|
//Cached variables in order to reduce calls to getVersion
|
|
static std::weak_ptr<Acroname::BrainStem::Module> _stem;
|
|
static uint8_t major = 0;
|
|
static uint8_t minor = 0;
|
|
static uint8_t patch = 0;
|
|
|
|
auto s = _stem.lock();
|
|
if((nullptr == s) ||
|
|
(s.get() != stem.get()))
|
|
{
|
|
Acroname::BrainStem::SystemClass sys;
|
|
sys.init(stem.get(), 0);
|
|
uint32_t build = 0;
|
|
aErr err = sys.getVersion(&build);
|
|
if(aErrNone == err) {
|
|
major = aVersion_ParseMajor(build);
|
|
minor = aVersion_ParseMinor(build);
|
|
patch = aVersion_ParsePatch(build);
|
|
_stem = stem;
|
|
}
|
|
}
|
|
|
|
if(aVersion_IsAtLeastCompare(major, minor, patch, 2, 10, 0)) {
|
|
return true;
|
|
}
|
|
else {
|
|
std::cout << "Current firmware version: "
|
|
<< int(major) << "." << int(minor) << "." << int(patch) << std::endl;
|
|
std::cout << "Minimum firmware version required: "
|
|
<< int(MIN_MAJOR) << "." << int(MIN_MINOR) << "." << int(MIN_PATCH) << std::endl;
|
|
std::cout << "Please upgrade you firmware via HubTool or Updater" << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
static aErr
|
|
_kickConnection(CLIObject_t& cliObj) {
|
|
cliObj.stem->disconnect();
|
|
cliObj.stem->reconnect();
|
|
return (cliObj.stem->isConnected()) ? (aErrNone) : (aErrConnection);
|
|
}
|
|
|
|
|
|
static void
|
|
_verbose_printMode_USBHub3c(const uint32_t& mode, const int& port, const bool& showHeader) {
|
|
int power = (mode & (1 << portPortMode_powerEnabled_Bit)) >> portPortMode_powerEnabled_Bit;
|
|
int hs1 = (mode & (1 << portPortMode_HS1Enabled_Bit)) >> portPortMode_HS1Enabled_Bit;
|
|
int hs2 = (mode & (1 << portPortMode_HS2Enabled_Bit)) >> portPortMode_HS2Enabled_Bit;
|
|
int ss1 = (mode & (1 << portPortMode_SS1Enabled_Bit)) >> portPortMode_SS1Enabled_Bit;
|
|
int ss2 = (mode & (1 << portPortMode_SS2Enabled_Bit)) >> portPortMode_SS2Enabled_Bit;
|
|
int cc1 = (mode & (1 << portPortMode_CC1Enabled_Bit)) >> portPortMode_CC1Enabled_Bit;
|
|
int cc2 = (mode & (1 << portPortMode_CC2Enabled_Bit)) >> portPortMode_CC2Enabled_Bit;
|
|
int vconn1 = (mode & (1 << portPortMode_Vconn1Enabled_Bit)) >> portPortMode_Vconn1Enabled_Bit;
|
|
int vconn2 = (mode & (1 << portPortMode_Vconn2Enabled_Bit)) >> portPortMode_Vconn2Enabled_Bit;
|
|
int pwrMode = (mode & (portPortMode_portPowerMode_Mask << portPortMode_portPowerMode_Offset)) >> portPortMode_portPowerMode_Offset;
|
|
|
|
if(showHeader) {
|
|
std::cout << "Port Power HS1 HS2 SS1 SS2 CC1 CC2 VConn1 VConn2 Power-Mode" << std::endl;
|
|
}
|
|
|
|
std::cout << std::setfill(' ') << std::setw(4) << port;
|
|
std::cout << std::setfill(' ') << std::setw(6) << power;
|
|
std::cout << std::setfill(' ') << std::setw(4) << hs1;
|
|
std::cout << std::setfill(' ') << std::setw(4) << hs2;
|
|
std::cout << std::setfill(' ') << std::setw(4) << ss1;
|
|
std::cout << std::setfill(' ') << std::setw(4) << ss2;
|
|
std::cout << std::setfill(' ') << std::setw(4) << cc1;
|
|
std::cout << std::setfill(' ') << std::setw(4) << cc2;
|
|
std::cout << std::setfill(' ') << std::setw(7) << vconn1;
|
|
std::cout << std::setfill(' ') << std::setw(7) << vconn2;
|
|
|
|
std::cout << std::setfill(' ') << std::setw(11) << _verbose_printMode_decodeModeString(pwrMode);
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
|
|
static void
|
|
_verbose_printMode_USBHub3p(const uint32_t& mode, const int& port, const bool& showHeader) {
|
|
int power = (mode & (1 << portPortMode_powerEnabled_Bit)) >> portPortMode_powerEnabled_Bit;
|
|
int hs = (mode & (1 << portPortMode_HS1Enabled_Bit)) >> portPortMode_HS1Enabled_Bit;
|
|
int ss = (mode & (1 << portPortMode_SS1Enabled_Bit)) >> portPortMode_SS1Enabled_Bit;
|
|
int pwrMode = (mode & (portPortMode_portPowerMode_Mask << portPortMode_portPowerMode_Offset)) >> portPortMode_portPowerMode_Offset;
|
|
|
|
if(showHeader) {
|
|
std::cout << "Port Power HS SS Power-Mode" << std::endl;
|
|
}
|
|
|
|
std::cout << std::setfill(' ') << std::setw(4) << port;
|
|
std::cout << std::setfill(' ') << std::setw(6) << power;
|
|
std::cout << std::setfill(' ') << std::setw(3) << hs;
|
|
std::cout << std::setfill(' ') << std::setw(3) << ss;
|
|
|
|
std::cout << std::setfill(' ') << std::setw(11) << _verbose_printMode_decodeModeString(pwrMode);
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
|
|
static void
|
|
_verbose_printMode_USBHub2x4(const uint32_t& mode, const int& port, const bool& showHeader) {
|
|
int power = (mode & (1 << portPortMode_powerEnabled_Bit)) >> portPortMode_powerEnabled_Bit;
|
|
int hs = (mode & (1 << portPortMode_HS1Enabled_Bit)) >> portPortMode_HS1Enabled_Bit;
|
|
int pwrMode = (mode & (portPortMode_portPowerMode_Mask << portPortMode_portPowerMode_Offset)) >> portPortMode_portPowerMode_Offset;
|
|
|
|
if(showHeader) {
|
|
std::cout << "Port Power HS Power-Mode" << std::endl;
|
|
}
|
|
|
|
std::cout << std::setfill(' ') << std::setw(4) << port;
|
|
std::cout << std::setfill(' ') << std::setw(6) << power;
|
|
std::cout << std::setfill(' ') << std::setw(3) << hs;
|
|
|
|
std::cout << std::setfill(' ') << std::setw(11) << _verbose_printMode_decodeModeString(pwrMode);
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
|
|
static std::string
|
|
_verbose_printMode_decodeModeString(const uint32_t& mode) {
|
|
switch (mode) {
|
|
case portPowerMode_none_Value: return "NONE";
|
|
case portPowerMode_sdp_Value: return "SDP";
|
|
case portPowerMode_cdp_dcp_Value: return "CDP/DCP";
|
|
case portPowerMode_qc_Value: return "QC";
|
|
case portPowerMode_pd_Value: return "PD";
|
|
case portPowerMode_ps_Value: return "PS";
|
|
case portPowerMode_usbc_Value: return "USBC";
|
|
default: return "Unknown";
|
|
}
|
|
}
|
|
|
|
|
|
static std::string
|
|
_verbose_upstreamPortNumber(const uint32_t& model, const uint8_t& upstreamPort) {
|
|
std::string s = "(Port: " + std::to_string(int(upstreamPort)) + ")";
|
|
switch (model) {
|
|
case aMODULE_TYPE_USBHub3c:
|
|
return std::to_string(int(upstreamPort));
|
|
|
|
case aMODULE_TYPE_USBHub3p:
|
|
if (9 == upstreamPort) { return "Up0 " + s; }
|
|
else if(10 == upstreamPort) { return "Up1 " + s; }
|
|
else { return "USBHub3p: Invalid upstream port number"; }
|
|
|
|
case aMODULE_TYPE_USBHub2x4:
|
|
if (4 == upstreamPort) { return "0 " + s; }
|
|
else if(5 == upstreamPort) { return "1 " + s; }
|
|
else { return "USBHub2x4: Invalid upstream port number"; }
|
|
|
|
default:
|
|
return "Unsupported model: " + std::to_string(int(model));
|
|
}//end switch
|
|
}
|