#include #include #include #include #include #include #include #include "main.h" #define MAX_RETRY_COUNT 5 #define USLEEP_READ_STATUS_INTERVAL 8000 #define USLEEP_READ_STATUS_RETRY_INTERVAL 3000 #define USLEEP_MODIFICATION_INTERVAL 500 #define USLEEP_MODIFICATION_RETRY_INTERVAL 3000 #define USLEEP_MODIFICATION_QUERY_RESULT_INTERVAL 2000 static std::map led_name_map = { { "power", UGREEN_LED_POWER }, { "netdev", UGREEN_LED_NETDEV }, { "disk1", UGREEN_LED_DISK1 }, { "disk2", UGREEN_LED_DISK2 }, { "disk3", UGREEN_LED_DISK3 }, { "disk4", UGREEN_LED_DISK4 }, { "disk5", UGREEN_LED_DISK5 }, { "disk6", UGREEN_LED_DISK6 }, { "disk7", UGREEN_LED_DISK7 }, { "disk8", UGREEN_LED_DISK8 }, }; using led_type_pair = std::pair < std::string, ugreen_leds_t::led_type_t > ; ugreen_leds_t::led_data_t get_status_robust(ugreen_leds_t & leds_controller, ugreen_leds_t::led_type_t led) { usleep(USLEEP_READ_STATUS_INTERVAL); auto data = leds_controller.get_status(led); for (int retry_cnt = 1; !data.is_available && retry_cnt < MAX_RETRY_COUNT; ++retry_cnt) { usleep(USLEEP_READ_STATUS_RETRY_INTERVAL); data = leds_controller.get_status(led); } return data; } void show_leds_info(ugreen_leds_t & leds_controller, const std::vector < led_type_pair > & leds) { for (auto led: leds) { auto data = get_status_robust(leds_controller, led.second); if (!data.is_available) { std::printf("%s: unavailable or non-existent\n", led.first.c_str()); continue; } std::string op_mode_txt = "unknown"; switch (data.op_mode) { case ugreen_leds_t::op_mode_t::off: op_mode_txt = "off"; break; case ugreen_leds_t::op_mode_t::on: op_mode_txt = "on"; break; case ugreen_leds_t::op_mode_t::blink: op_mode_txt = "blink"; break; case ugreen_leds_t::op_mode_t::breath: op_mode_txt = "breath"; break; }; std::printf("%s: status = %s, brightness = %d, color = RGB(%d, %d, %d)", led.first.c_str(), op_mode_txt.c_str(), (int) data.brightness, (int) data.color_r, (int) data.color_g, (int) data.color_b); if (data.op_mode == ugreen_leds_t::op_mode_t::blink) { std::printf(", blink_on = %d ms, blink_off = %d ms", (int) data.t_on, (int) data.t_off); } std::puts(""); } } void show_help() { std::cerr << "Usage: ugreen-led-cli [LED-NAME...] [-on] [-off] [-(blink|breath) T_ON T_OFF] [-color R G B] [-brightness BRIGHTNESS] [-status]\n\n" " LED_NAME: Separated by white space, possible values are { power, netdev, disk[1-8], all }.\n\n" " -on / -off: Turn ON / OFF corresponding LEDs.\n\n" " -blink / -breath: set LED to the blink / breath mode. This mode keeps the LED on for T_ON millseconds\n" " and then keeps it off for T_OFF millseconds.\n" " T_ON and T_OFF should belong to [0, 65535].\n\n" " -color: Set the color of corresponding LEDs.\n" " R, G and B should belong to [0, 255].\n\n" " -brightness: Set the brightness of corresponding LEDs.\n" " BRIGHTNESS should belong to [0, 255].\n\n" " -status: Display the status of corresponding LEDs.\n" << std::endl; } void show_help_and_exit() { show_help(); std::exit(-1); } ugreen_leds_t::led_type_t get_led_type(const std::string & name) { if (led_name_map.find(name) == led_name_map.end()) { std::cerr << "Err: unknown LED name " << name << std::endl; show_help_and_exit(); } return led_name_map[name]; } int parse_integer(const std::string & str, int low = 0, int high = 0xffff) { std::size_t size; int x = std::stoi(str, & size); if (size != str.size()) { std::cerr << "Err: " << str << " is not an integer." << std::endl; show_help_and_exit(); } if (x < low || x > high) { std::cerr << "Err: " << str << " is not in [" << low << ", " << high << "]" << std::endl; show_help_and_exit(); } return x; } int main(int argc, char * argv[]) { if (argc < 2) { show_help(); return 0; } ugreen_leds_t leds_controller; if (leds_controller.start() != 0) { std::cerr << "The command-line tool and the kernel module do conflict." << std::endl; std::cerr << "To use the command-line tool, you must unload the led_ugreen module." << std::endl; return -1; } std::deque < std::string > args; for (int i = 1; i < argc; ++i) args.emplace_back(argv[i]); // parse LED names std::vector < led_type_pair > leds; while (!args.empty() && args.front().front() != '-') { if (args.front() == "all") { for (const auto & v: led_name_map) { if (get_status_robust(leds_controller, v.second).is_available) leds.push_back(v); } } else { auto led_type = get_led_type(args.front()); leds.emplace_back(args.front(), led_type); } args.pop_front(); } // if no additional parameters, display current info if (args.empty()) { show_leds_info(leds_controller, leds); return 0; } // (is_modification, callback) using ops_pair = std::pair < bool, std:: function < int(led_type_pair) >> ; std::vector < ops_pair > ops_seq; while (!args.empty()) { if (args.front() == "-on" || args.front() == "-off") { // turn on / off LEDs uint8_t status = args.front() == "-on"; ops_seq.emplace_back(true, [ = , & leds_controller](led_type_pair led) { return leds_controller.set_onoff(led.second, status); }); args.pop_front(); } else if (args.front() == "-blink" || args.front() == "-breath") { // set blink bool is_blink = (args.front() == "-blink"); args.pop_front(); if (args.size() < 2) { std::cerr << "Err: -blink / -breath requires 2 parameters" << std::endl; show_help_and_exit(); } uint16_t t_on = parse_integer(args.front(), 0x0000, 0xffff); args.pop_front(); uint16_t t_off = parse_integer(args.front(), 0x0000, 0xffff); args.pop_front(); ops_seq.emplace_back(true, [ = , & leds_controller](led_type_pair led) { if (is_blink) { return leds_controller.set_blink(led.second, t_on, t_off); } else { return leds_controller.set_breath(led.second, t_on, t_off); } }); } else if (args.front() == "-color") { // set color args.pop_front(); if (args.size() < 3) { std::cerr << "Err: -color requires 3 parameters" << std::endl; show_help_and_exit(); } uint8_t R = parse_integer(args.front(), 0x00, 0xff); args.pop_front(); uint8_t G = parse_integer(args.front(), 0x00, 0xff); args.pop_front(); uint8_t B = parse_integer(args.front(), 0x00, 0xff); args.pop_front(); ops_seq.emplace_back(true, [ = , & leds_controller](led_type_pair led) { return leds_controller.set_rgb(led.second, R, G, B); }); } else if (args.front() == "-brightness") { // set brightness args.pop_front(); if (args.size() < 1) { std::cerr << "Err: -brightness requires 1 parameter" << std::endl; show_help_and_exit(); } uint8_t brightness = parse_integer(args.front(), 0x00, 0xff); args.pop_front(); ops_seq.emplace_back(true, [ = , & leds_controller](led_type_pair led) { return leds_controller.set_brightness(led.second, brightness); }); } else if (args.front() == "-status") { // display the status args.pop_front(); ops_seq.emplace_back(false, [ = , & leds_controller](led_type_pair led) { show_leds_info(leds_controller, { led }); return 0; }); } else { std::cerr << "Err: unknown parameter " << args.front() << std::endl; show_help_and_exit(); } } for (const auto & led: leds) { for (const auto & fn_pair: ops_seq) { bool is_modification = fn_pair.first; const auto & fn = fn_pair.second; int last_status = -1; for (int retry_cnt = 0; retry_cnt < MAX_RETRY_COUNT && last_status != 0; ++retry_cnt) { if (retry_cnt == 0) { if (is_modification) usleep(USLEEP_MODIFICATION_INTERVAL); // usleep_range(200, 0x5dc) } else { usleep(USLEEP_MODIFICATION_RETRY_INTERVAL); } last_status = fn(led); if (last_status == 0 && is_modification) { usleep(USLEEP_MODIFICATION_QUERY_RESULT_INTERVAL); last_status = !leds_controller.is_last_modification_successful(); } } if (last_status != 0) { std::cerr << "failed to change status!" << std::endl; return -1; } } } return 0; }