| /* Copyright 2013 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "battery.h" |
| #include "chipset.h" |
| #include "comm-host.h" |
| #include "comm-usb.h" |
| #include "compile_time_macros.h" |
| #include "crc.h" |
| #include "cros_ec_dev.h" |
| #include "ec_flash.h" |
| #include "ec_version.h" |
| #include "ectool.h" |
| #include "i2c.h" |
| #include "lightbar.h" |
| #include "lock/gec_lock.h" |
| #include "misc_util.h" |
| #include "panic.h" |
| #include "tablet_mode.h" |
| #include "usb_pd.h" |
| #include "util.h" |
| |
| /* TODO(b/395723202): These macros from timer.h, included transitively in |
| * usb_pd.h, conflict with constants declared in json_reader.h below. Ideally, |
| * timer.h should use namespaced variables instead of macros to avoid |
| * collisions. Until then, avoid build failures by undefining the conflicting |
| * macros. ectool.cc does not depend on them specifically. |
| */ |
| #undef SECOND |
| #undef MINUTE |
| #undef HOUR |
| |
| #include <assert.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <inttypes.h> |
| #include <signal.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| |
| #include <getopt.h> |
| #include <iomanip> |
| #include <iostream> |
| #include <libchrome/base/json/json_reader.h> |
| #include <libec/add_entropy_command.h> |
| #include <libec/ec_command_factory.h> |
| #include <libec/ec_command_version_supported.h> |
| #include <libec/ec_panicinfo.h> |
| #include <libec/fingerprint/fp_encryption_status_command.h> |
| #include <libec/fingerprint/fp_frame_command.h> |
| #include <libec/fingerprint/fp_mode_command.h> |
| #include <libec/mkbp_event.h> |
| #include <libec/rand_num_command.h> |
| #include <libec/versions_command.h> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <unistd.h> |
| #include <vector> |
| |
| /* Maximum flash size (16 MB, conservative) */ |
| #define MAX_FLASH_SIZE 0x1000000 |
| |
| /* |
| * Calculate the expected response for a hello ec command. |
| */ |
| #define HELLO_RESP(in_data) ((in_data) + 0x01020304) |
| |
| #define USB_VID_GOOGLE 0x18d1 |
| #define USB_PID_HAMMER 0x5022 |
| |
| /* Command line options */ |
| enum { |
| OPT_DEV = 1000, |
| OPT_INTERFACE, |
| OPT_NAME, |
| OPT_ASCII, |
| OPT_I2C_BUS, |
| OPT_DEVICE, |
| OPT_VERBOSE, |
| }; |
| |
| static struct option long_opts[] = { { "dev", 1, 0, OPT_DEV }, |
| { "interface", 1, 0, OPT_INTERFACE }, |
| { "name", 1, 0, OPT_NAME }, |
| { "ascii", 0, 0, OPT_ASCII }, |
| { "i2c_bus", 1, 0, OPT_I2C_BUS }, |
| { "device", 1, 0, OPT_DEVICE }, |
| { "verbose", no_argument, NULL, |
| OPT_VERBOSE }, |
| { NULL, 0, 0, 0 } }; |
| |
| #define GEC_LOCK_TIMEOUT_SECS 30 /* 30 secs */ |
| |
| const char help_str[] = "Commands:"; |
| |
| /* Note: depends on enum ec_image */ |
| static const char *const image_names[] = { "unknown", "RO", "RW" }; |
| |
| /* Note: depends on enum ec_led_colors */ |
| static const char *const led_color_names[] = { "red", "green", "blue", |
| "yellow", "white", "amber", |
| "magenta" }; |
| BUILD_ASSERT(ARRAY_SIZE(led_color_names) == EC_LED_COLOR_COUNT); |
| |
| /* Note: depends on enum ec_led_id */ |
| static const char *const led_names[] = { "battery", "power", |
| "adapter", "left", |
| "right", "recovery_hwreinit", |
| "sysrq debug", "lightbar" }; |
| BUILD_ASSERT(ARRAY_SIZE(led_names) == EC_LED_ID_COUNT); |
| |
| /* ASCII mode for printing, default off */ |
| int ascii_mode; |
| |
| /* Message verbosity */ |
| static int verbose = 0; |
| |
| const command *commands_find(const char *name); |
| |
| namespace ec |
| { |
| |
| class EcCommandVersionSupported : public EcCommandVersionSupportedInterface { |
| public: |
| EcCommandVersionSupported() = default; |
| EcCommandVersionSupported(const EcCommandVersionSupported &) = delete; |
| EcCommandVersionSupported & |
| operator=(const EcCommandVersionSupported &) = delete; |
| ~EcCommandVersionSupported() override = default; |
| |
| EcCmdVersionSupportStatus EcCmdVersionSupported(uint16_t cmd, |
| uint32_t ver) override; |
| }; |
| |
| EcCmdVersionSupportStatus |
| EcCommandVersionSupported::EcCmdVersionSupported(uint16_t cmd, uint32_t ver) |
| { |
| constexpr int kMaxIoAttempts = 20; |
| VersionsCommand versions_cmd(cmd); |
| if (!versions_cmd.RunWithMultipleAttempts(comm_get_fd(), |
| kMaxIoAttempts)) { |
| fprintf(stderr, "Failed to get version info for command %d\n", |
| cmd); |
| return EcCmdVersionSupportStatus::UNKNOWN; |
| } |
| return versions_cmd.IsVersionSupported(ver); |
| } |
| |
| } // namespace ec |
| |
| /* Check SBS numerical value range */ |
| int is_battery_range(int val) |
| { |
| return (val >= 0 && val <= 65535) ? 1 : 0; |
| } |
| |
| int parse_bool(const char *s, int *dest) |
| { |
| if (!strcasecmp(s, "off") || !strncasecmp(s, "dis", 3) || |
| tolower(*s) == 'f' || tolower(*s) == 'n') { |
| *dest = 0; |
| return 1; |
| } else if (!strcasecmp(s, "on") || !strncasecmp(s, "ena", 3) || |
| tolower(*s) == 't' || tolower(*s) == 'y') { |
| *dest = 1; |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| /** |
| * @brief Find the enum value associated the string of enum text or value. |
| * |
| * @param str The input string to parse an enum from. |
| * @param enum_text_map The array that maps enum value (index) to text. |
| * @param enum_text_map_length The length of the enum_text_map array. |
| * @param enum_value Output parsed enum value. |
| * @return int 0 on success, -1 if result cannot be found |
| */ |
| static int find_enum_from_text(const char *str, |
| const char *const enum_text_map[], |
| long enum_text_map_length, long *enum_value) |
| { |
| char *e; |
| long value; |
| |
| assert(str); |
| assert(enum_value); |
| assert(enum_text_map); |
| assert(enum_text_map_length >= 0); |
| |
| if (*str == '\0') |
| return -1; |
| |
| value = strtol(str, &e, 0); |
| if (!e || !*e) { |
| *enum_value = value; |
| return 0; |
| } |
| |
| for (value = 0; value < enum_text_map_length; value++) { |
| if (!enum_text_map[value]) |
| continue; |
| if (strcasecmp(str, enum_text_map[value]) == 0) { |
| *enum_value = value; |
| return 0; |
| } |
| } |
| |
| return -1; |
| } |
| |
| static uint8_t read_mapped_mem8(uint8_t offset) |
| { |
| int ret; |
| uint8_t val; |
| |
| ret = ec_readmem(offset, sizeof(val), &val); |
| if (ret <= 0) { |
| fprintf(stderr, "failure in %s(): %d\n", __func__, ret); |
| exit(1); |
| } |
| return val; |
| } |
| |
| static uint16_t read_mapped_mem16(uint8_t offset) |
| { |
| int ret; |
| uint16_t val; |
| |
| ret = ec_readmem(offset, sizeof(val), &val); |
| if (ret <= 0) { |
| fprintf(stderr, "failure in %s(): %d\n", __func__, ret); |
| exit(1); |
| } |
| return val; |
| } |
| |
| static uint32_t read_mapped_mem32(uint8_t offset) |
| { |
| int ret; |
| uint32_t val; |
| |
| ret = ec_readmem(offset, sizeof(val), &val); |
| if (ret <= 0) { |
| fprintf(stderr, "failure in %s(): %d\n", __func__, ret); |
| exit(1); |
| } |
| return val; |
| } |
| |
| static int read_mapped_string(uint8_t offset, char *buffer, int max_size) |
| { |
| int ret; |
| |
| ret = ec_readmem(offset, max_size, buffer); |
| if (ret <= 0) { |
| fprintf(stderr, "failure in %s(): %d\n", __func__, ret); |
| exit(1); |
| } |
| return ret; |
| } |
| |
| static int wait_event_mask(unsigned long event_mask, |
| struct ec_response_get_next_event_v1 *buffer, |
| size_t buffer_size, long timeout) |
| { |
| int rv; |
| |
| rv = ec_pollevent(event_mask, buffer, buffer_size, timeout); |
| if (rv == 0) { |
| fprintf(stderr, "Timeout waiting for MKBP event\n"); |
| return -ETIMEDOUT; |
| } else if (rv < 0) { |
| perror("Error polling for MKBP event\n"); |
| return -EIO; |
| } |
| |
| return rv; |
| } |
| |
| static int wait_event(long event_type, |
| struct ec_response_get_next_event_v1 *buffer, |
| size_t buffer_size, long timeout) |
| { |
| return wait_event_mask(1 << event_type, buffer, buffer_size, timeout); |
| } |
| |
| int cmd_adc_read(int argc, char *argv[]) |
| { |
| char *e; |
| struct ec_params_adc_read p; |
| struct ec_response_adc_read r; |
| int rv; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s <adc channel>\n", argv[0]); |
| return -1; |
| } |
| |
| p.adc_channel = (uint8_t)strtoull(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "\"%s\": invalid channel!\n", argv[1]); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_ADC_READ, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv > 0) { |
| printf("%s: %d\n", argv[1], r.adc_value); |
| return 0; |
| } |
| |
| return rv; |
| } |
| |
| int cmd_add_entropy(int argc, char *argv[]) |
| { |
| bool reset = false; |
| if (argc >= 2 && !strcmp(argv[1], "reset")) |
| reset = true; |
| |
| ec::AddEntropyCommand add_entropy_command(reset); |
| if (!add_entropy_command.Run(comm_get_fd())) { |
| fprintf(stderr, "Failed to run addentropy command\n"); |
| return -1; |
| } |
| |
| int rv = add_entropy_command.Result(); |
| if (rv != EC_RES_SUCCESS) { |
| rv = -EECRESULT - add_entropy_command.Result(); |
| fprintf(stderr, "Failed to add entropy: %d\n", rv); |
| return rv; |
| } |
| |
| printf("Entropy added successfully\n"); |
| return rv; |
| } |
| |
| int cmd_hello(int argc, char *argv[]) |
| { |
| struct ec_params_hello p; |
| struct ec_response_hello r; |
| int rv; |
| |
| p.in_data = 0xa0b0c0d0; |
| |
| rv = ec_command(EC_CMD_HELLO, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| if (r.out_data != 0xa1b2c3d4) { |
| fprintf(stderr, "Expected response 0x%08x, got 0x%08x\n", |
| 0xa1b2c3d4, r.out_data); |
| return -1; |
| } |
| |
| printf("EC says hello!\n"); |
| return 0; |
| } |
| |
| int cmd_hibdelay(int argc, char *argv[]) |
| { |
| struct ec_params_hibernation_delay p; |
| struct ec_response_hibernation_delay r; |
| char *e; |
| int rv; |
| |
| if (argc < 2) { |
| p.seconds = 0; /* Just read the current settings. */ |
| } else { |
| p.seconds = strtoull(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "invalid number\n"); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_HIBERNATION_DELAY, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) { |
| fprintf(stderr, "err: rv=%d\n", rv); |
| return -1; |
| } |
| |
| printf("Hibernation delay: %u s\n", r.hibernate_delay); |
| return 0; |
| } |
| |
| static void cmd_hostevent_help(char *cmd) |
| { |
| fprintf(stderr, |
| " Usage: %s get <type>\n" |
| " Usage: %s set <type> <value>\n" |
| " <type> is one of:\n" |
| " 1: EC_HOST_EVENT_B\n" |
| " 2: EC_HOST_EVENT_SCI_MASK\n" |
| " 3: EC_HOST_EVENT_SMI_MASK\n" |
| " 4: EC_HOST_EVENT_ALWAYS_REPORT_MASK\n" |
| " 5: EC_HOST_EVENT_ACTIVE_WAKE_MASK\n" |
| " 6: EC_HOST_EVENT_LAZY_WAKE_MASK_S0IX\n" |
| " 7: EC_HOST_EVENT_LAZY_WAKE_MASK_S3\n" |
| " 8: EC_HOST_EVENT_LAZY_WAKE_MASK_S5\n", |
| cmd, cmd); |
| } |
| |
| static int cmd_hostevent(int argc, char *argv[]) |
| { |
| struct ec_params_host_event p; |
| struct ec_response_host_event r; |
| char *e; |
| int rv; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_hostevent_help(argv[0]); |
| return -1; |
| } |
| |
| if (!strcasecmp(argv[1], "get")) { |
| if (argc != 3) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_hostevent_help(argv[0]); |
| return -1; |
| } |
| p.action = EC_HOST_EVENT_GET; |
| } else if (!strcasecmp(argv[1], "set")) { |
| if (argc != 4) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_hostevent_help(argv[0]); |
| return -1; |
| } |
| p.action = EC_HOST_EVENT_SET; |
| p.value = strtoull(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad value\n"); |
| return -1; |
| } |
| } else { |
| fprintf(stderr, "Bad subcommand: %s\n", argv[1]); |
| return -1; |
| } |
| |
| p.mask_type = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad type\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_HOST_EVENT, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv == -EC_RES_ACCESS_DENIED - EECRESULT) { |
| fprintf(stderr, "%s isn't permitted for mask %d.\n", |
| p.action == EC_HOST_EVENT_SET ? "Set" : "Get", |
| p.mask_type); |
| return rv; |
| } else if (rv < 0) { |
| return rv; |
| } |
| |
| if (p.action == EC_HOST_EVENT_GET) |
| printf("0x%" PRIx64 "\n", r.value); |
| |
| return 0; |
| } |
| |
| static int get_latest_cmd_version(uint8_t cmd, int *version) |
| { |
| struct ec_params_get_cmd_versions p; |
| struct ec_response_get_cmd_versions r; |
| int rv; |
| |
| *version = 0; |
| /* Figure out the latest version of the given command the EC supports */ |
| p.cmd = cmd; |
| rv = ec_command(EC_CMD_GET_CMD_VERSIONS, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) { |
| if (rv == -EC_RES_INVALID_PARAM) |
| printf("Command 0x%02x not supported by EC.\n", |
| EC_CMD_GET_CMD_VERSIONS); |
| return rv; |
| } |
| |
| if (r.version_mask) |
| *version = __fls(r.version_mask); |
| |
| return rv; |
| } |
| |
| int cmd_hostsleepstate(int argc, char *argv[]) |
| { |
| struct ec_params_host_sleep_event p; |
| struct ec_params_host_sleep_event_v1 p1; |
| struct ec_response_host_sleep_event_v1 r; |
| void *pp = &p; |
| size_t psize = sizeof(p), rsize = 0; |
| char *afterscan; |
| int rv; |
| int version = 0, max_version = 0; |
| uint32_t timeout, transitions; |
| |
| if (argc < 2) { |
| fprintf(stderr, |
| "Usage: %s " |
| "[suspend|wsuspend|resume|freeze|thaw] [timeout]\n", |
| argv[0]); |
| return -1; |
| } |
| |
| rv = get_latest_cmd_version(EC_CMD_HOST_SLEEP_EVENT, &max_version); |
| if (rv < 0) |
| return rv; |
| |
| if (!strcmp(argv[1], "suspend")) |
| p.sleep_event = HOST_SLEEP_EVENT_S3_SUSPEND; |
| else if (!strcmp(argv[1], "wsuspend")) |
| p.sleep_event = HOST_SLEEP_EVENT_S3_WAKEABLE_SUSPEND; |
| else if (!strcmp(argv[1], "resume")) |
| p.sleep_event = HOST_SLEEP_EVENT_S3_RESUME; |
| else if (!strcmp(argv[1], "freeze")) { |
| p.sleep_event = HOST_SLEEP_EVENT_S0IX_SUSPEND; |
| if (max_version >= 1) { |
| p1.sleep_event = p.sleep_event; |
| p1.reserved = 0; |
| p1.suspend_params.sleep_timeout_ms = |
| EC_HOST_SLEEP_TIMEOUT_DEFAULT; |
| |
| if (argc > 2) { |
| p1.suspend_params.sleep_timeout_ms = |
| strtoull(argv[2], &afterscan, 0); |
| |
| if ((*afterscan != '\0') || |
| (afterscan == argv[2])) { |
| fprintf(stderr, "Invalid value: %s\n", |
| argv[2]); |
| |
| return -1; |
| } |
| } |
| |
| pp = &p1; |
| psize = sizeof(p1); |
| version = 1; |
| } |
| |
| } else if (!strcmp(argv[1], "thaw")) { |
| p.sleep_event = HOST_SLEEP_EVENT_S0IX_RESUME; |
| if (max_version >= 1) { |
| version = 1; |
| rsize = sizeof(r); |
| } |
| } else { |
| fprintf(stderr, "Unknown command: %s\n", argv[1]); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_HOST_SLEEP_EVENT, version, pp, psize, &r, rsize); |
| if (rv < 0) { |
| fprintf(stderr, "EC host sleep command failed: %d\n", rv); |
| return rv; |
| } |
| |
| if (rsize) { |
| timeout = r.resume_response.sleep_transitions & |
| EC_HOST_RESUME_SLEEP_TIMEOUT; |
| |
| transitions = r.resume_response.sleep_transitions & |
| EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK; |
| |
| printf("%s%d sleep line transitions.\n", |
| timeout ? "Timeout: " : "", transitions); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_test(int argc, char *argv[]) |
| { |
| struct ec_params_test_protocol p = { |
| .buf = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, |
| 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 } |
| }; |
| struct ec_response_test_protocol r; |
| int rv, version = 0; |
| char *e; |
| |
| if (argc < 3) { |
| fprintf(stderr, "Usage: %s result length [version]\n", argv[0]); |
| return -1; |
| } |
| |
| p.ec_result = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "invalid param (result)\n"); |
| return -1; |
| } |
| p.ret_len = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "invalid param (length)\n"); |
| return -1; |
| } |
| |
| if (argc > 3) { |
| version = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "invalid param (version)\n"); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_TEST_PROTOCOL, version, &p, sizeof(p), &r, |
| sizeof(r)); |
| printf("rv = %d\n", rv); |
| |
| return rv; |
| } |
| |
| int cmd_s5(int argc, char *argv[]) |
| { |
| struct ec_params_get_set_value p; |
| struct ec_response_get_set_value r; |
| int rv, param; |
| |
| p.flags = 0; |
| |
| if (argc > 1) { |
| p.flags |= EC_GSV_SET; |
| if (!parse_bool(argv[1], ¶m)) { |
| fprintf(stderr, "invalid arg \"%s\"\n", argv[1]); |
| return -1; |
| } |
| p.value = param; |
| } |
| |
| rv = ec_command(EC_CMD_GSV_PAUSE_IN_S5, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv > 0) |
| printf("%s\n", r.value ? "on" : "off"); |
| |
| return rv < 0; |
| } |
| |
| static const char *const ec_feature_names[] = { |
| [EC_FEATURE_LIMITED] = "Limited image, load RW for more", |
| [EC_FEATURE_FLASH] = "Flash", |
| [EC_FEATURE_PWM_FAN] = "Direct Fan power management", |
| [EC_FEATURE_PWM_KEYB] = "Keyboard backlight", |
| [EC_FEATURE_LIGHTBAR] = "Lightbar", |
| [EC_FEATURE_LED] = "LED", |
| [EC_FEATURE_MOTION_SENSE] = "Motion Sensors", |
| [EC_FEATURE_KEYB] = "Keyboard", |
| [EC_FEATURE_PSTORE] = "Host Permanent Storage", |
| [EC_FEATURE_PORT80] = "BIOS Port 80h access", |
| [EC_FEATURE_THERMAL] = "Thermal management", |
| [EC_FEATURE_BKLIGHT_SWITCH] = "Switch backlight on/off", |
| [EC_FEATURE_WIFI_SWITCH] = "Switch wifi on/off", |
| [EC_FEATURE_HOST_EVENTS] = "Host event", |
| [EC_FEATURE_GPIO] = "GPIO", |
| [EC_FEATURE_I2C] = "I2C controller", |
| [EC_FEATURE_CHARGER] = "Charger", |
| [EC_FEATURE_BATTERY] = "Simple Battery", |
| [EC_FEATURE_SMART_BATTERY] = "Smart Battery", |
| [EC_FEATURE_HANG_DETECT] = "Host hang detection", |
| [EC_FEATURE_PMU] = "Power Management", |
| [EC_FEATURE_SUB_MCU] = "Control downstream MCU", |
| [EC_FEATURE_USB_PD] = "USB Cros Power Delivery", |
| [EC_FEATURE_USB_MUX] = "USB Multiplexer", |
| [EC_FEATURE_MOTION_SENSE_FIFO] = "FIFO for Motion Sensors events", |
| [EC_FEATURE_VSTORE] = "Temporary secure vstore", |
| [EC_FEATURE_USBC_SS_MUX_VIRTUAL] = "Host-controlled USB-C SS mux", |
| [EC_FEATURE_RTC] = "Real-time clock", |
| [EC_FEATURE_FINGERPRINT] = "Fingerprint", |
| [EC_FEATURE_TOUCHPAD] = "Touchpad", |
| [EC_FEATURE_RWSIG] = "RWSIG task", |
| [EC_FEATURE_DEVICE_EVENT] = "Device events reporting", |
| [EC_FEATURE_UNIFIED_WAKE_MASKS] = "Unified wake masks for LPC/eSPI", |
| [EC_FEATURE_HOST_EVENT64] = "64-bit host events", |
| [EC_FEATURE_EXEC_IN_RAM] = "Execute code in RAM", |
| [EC_FEATURE_CEC] = "Consumer Electronics Control", |
| [EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS] = |
| "Tight timestamp for sensors events", |
| [EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS] = |
| "Refined tablet mode hysteresis", |
| [EC_FEATURE_EFS2] = "Early Firmware Selection v2", |
| [EC_FEATURE_ISH] = "Intel Integrated Sensor Hub", |
| [EC_FEATURE_TYPEC_CMD] = "TCPMv2 Type-C commands", |
| [EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY] = |
| "Host-controlled Type-C mode entry", |
| [EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK] = |
| "AP ack for Type-C mux configuration", |
| [EC_FEATURE_S4_RESIDENCY] = "S4 residency", |
| [EC_FEATURE_TYPEC_AP_MUX_SET] = "AP directed mux sets", |
| [EC_FEATURE_TYPEC_AP_VDM_SEND] = "AP directed VDM Request messages", |
| [EC_FEATURE_SYSTEM_SAFE_MODE] = "System Safe Mode support", |
| [EC_FEATURE_ASSERT_REBOOTS] = "Assert reboots", |
| [EC_FEATURE_TOKENIZED_LOGGING] = "Tokenized Logging", |
| [EC_FEATURE_AMD_STB_DUMP] = "AMD STB dump", |
| [EC_FEATURE_MEMORY_DUMP] = "Memory Dump", |
| [EC_FEATURE_UCSI_PPM] = "UCSI PPM", |
| [EC_FEATURE_STRAUSS] = "Strauss", |
| [EC_FEATURE_POE] = "POE", |
| [EC_FEATURE_CHARGER_HYBRID_POWER_BOOST] = "Hybrid Power Boost charger", |
| }; |
| |
| int cmd_inventory(int argc, char *argv[]) |
| { |
| struct ec_response_get_features r; |
| int rv, i, j, idx; |
| |
| rv = ec_command(EC_CMD_GET_FEATURES, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("EC supported features:\n"); |
| for (i = 0, idx = 0; i < 2; i++) { |
| for (j = 0; j < 32; j++, idx++) { |
| if (r.flags[i] & BIT(j)) { |
| if (idx >= ARRAY_SIZE(ec_feature_names) || |
| !ec_feature_names[idx] || |
| strlen(ec_feature_names[idx]) == 0) |
| printf("%-4d: Unknown feature\n", idx); |
| else |
| printf("%-4d: %s support\n", idx, |
| ec_feature_names[idx]); |
| } |
| } |
| } |
| return 0; |
| } |
| |
| static int get_cmdversions_v0(uint8_t cmd, uint32_t *version_mask) |
| { |
| struct ec_params_get_cmd_versions p; |
| struct ec_response_get_cmd_versions r; |
| int rv; |
| |
| p.cmd = cmd; |
| rv = ec_command(EC_CMD_GET_CMD_VERSIONS, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) { |
| if (rv == -EC_RES_INVALID_PARAM) |
| printf("Command 0x%02x not supported by EC.\n", cmd); |
| |
| return rv; |
| } |
| |
| *version_mask = r.version_mask; |
| return 0; |
| } |
| |
| int cmd_cmdversions(int argc, char *argv[]) |
| { |
| struct ec_params_get_cmd_versions_v1 p; |
| struct ec_response_get_cmd_versions r; |
| char *e; |
| int cmd; |
| int rv; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s <cmd>\n", argv[0]); |
| return -1; |
| } |
| cmd = strtol(argv[1], &e, 0); |
| if ((e && *e) || cmd < 0 || cmd > 0xffff) { |
| fprintf(stderr, "Bad command number.\n"); |
| return -1; |
| } |
| |
| if (cmd > 0xff) { |
| /* Ensure the EC support GET_CMD_VERSIONS v1. */ |
| rv = get_cmdversions_v0(EC_CMD_GET_CMD_VERSIONS, |
| &r.version_mask); |
| if (rv < 0) |
| return rv; |
| |
| if (!(r.version_mask & EC_VER_MASK(1))) { |
| printf("16 bits cmdversions not supported by EC.\n"); |
| return -1; |
| } |
| |
| /* Use GET_CMD_VERSIONS v1. */ |
| p.cmd = cmd; |
| rv = ec_command(EC_CMD_GET_CMD_VERSIONS, 1, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) { |
| if (rv == -EC_RES_INVALID_PARAM) |
| printf("Command 0x%02x not supported by EC.\n", |
| cmd); |
| |
| return rv; |
| } |
| } else { |
| rv = get_cmdversions_v0(cmd, &r.version_mask); |
| if (rv < 0) |
| return rv; |
| } |
| |
| printf("Command 0x%02x supports version mask 0x%08x\n", cmd, |
| r.version_mask); |
| return 0; |
| } |
| |
| /* |
| * Convert a reset cause ID to human-readable string, providing total coverage |
| * of the 'cause' space. The returned string points to static storage and must |
| * not be free()ed. |
| */ |
| static const char *reset_cause_to_str(uint16_t cause) |
| { |
| static const char *const reset_causes[] = { |
| "(reset unknown)", |
| "reset: board custom", |
| "reset: ap hang detected", |
| "reset: console command", |
| "reset: host command", |
| "reset: keyboard sysreset", |
| "reset: keyboard warm reboot", |
| "reset: debug warm reboot", |
| "reset: at AP's request", |
| "reset: during EC initialization", |
| "reset: AP watchdog", |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(reset_causes) == CHIPSET_RESET_COUNT); |
| |
| static const char *const shutdown_causes[] = { |
| "shutdown: power failure", |
| "shutdown: during EC initialization", |
| "shutdown: board custom", |
| "shutdown: battery voltage startup inhibit", |
| "shutdown: power wait asserted", |
| "shutdown: critical battery", |
| "shutdown: by console command", |
| "shutdown: entering G3", |
| "shutdown: thermal", |
| "shutdown: power button", |
| "shutdown: at AP's request", |
| "shutdown: for battery cutoff", |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(shutdown_causes) == |
| CHIPSET_SHUTDOWN_COUNT - CHIPSET_SHUTDOWN_BEGIN); |
| |
| if (cause < CHIPSET_RESET_COUNT) |
| return reset_causes[cause]; |
| |
| if (cause < CHIPSET_SHUTDOWN_BEGIN) |
| return "(reset unknown)"; |
| |
| if (cause < CHIPSET_SHUTDOWN_COUNT) |
| return shutdown_causes[cause - CHIPSET_SHUTDOWN_BEGIN]; |
| |
| return "(shutdown unknown)"; |
| } |
| |
| int cmd_uptimeinfo(int argc, char *argv[]) |
| { |
| struct ec_response_uptime_info r; |
| int rv; |
| int i; |
| int flag_count; |
| uint32_t flag; |
| static const char *const reset_flag_descs[] = { |
| #include "reset_flag_desc.inc" |
| }; |
| |
| if (argc != 1) { |
| fprintf(stderr, "uptimeinfo takes no arguments"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_GET_UPTIME_INFO, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) { |
| fprintf(stderr, "ERROR: EC_CMD_GET_UPTIME_INFO failed; %d\n", |
| rv); |
| return rv; |
| } |
| |
| printf("EC uptime: %d.%03d seconds\n", r.time_since_ec_boot_ms / 1000, |
| r.time_since_ec_boot_ms % 1000); |
| |
| printf("AP resets since EC boot: %d\n", r.ap_resets_since_ec_boot); |
| |
| printf("Most recent AP reset causes:\n"); |
| for (i = 0; i != ARRAY_SIZE(r.recent_ap_reset); ++i) { |
| if (r.recent_ap_reset[i].reset_time_ms == 0) |
| continue; |
| |
| printf("\t%d.%03d: %s\n", |
| r.recent_ap_reset[i].reset_time_ms / 1000, |
| r.recent_ap_reset[i].reset_time_ms % 1000, |
| reset_cause_to_str(r.recent_ap_reset[i].reset_cause)); |
| } |
| |
| printf("EC reset flags at last EC boot: "); |
| |
| if (!r.ec_reset_flags) { |
| printf("unknown\n"); |
| return 0; |
| } |
| |
| flag_count = 0; |
| for (flag = 0; flag < ARRAY_SIZE(reset_flag_descs); ++flag) { |
| if ((r.ec_reset_flags & BIT(flag)) != 0) { |
| if (flag_count) |
| printf(" | "); |
| printf(reset_flag_descs[flag]); |
| flag_count++; |
| } |
| } |
| |
| if (r.ec_reset_flags >= BIT(flag)) { |
| if (flag_count) |
| printf(" | "); |
| printf("no-desc"); |
| } |
| printf("\n"); |
| return 0; |
| } |
| |
| int cmd_version(int argc, char *argv[]) |
| { |
| struct ec_response_get_version_v1 r; |
| char *build_string = (char *)ec_inbuf; |
| int rv; |
| |
| if (ec_cmd_version_supported(EC_CMD_GET_VERSION, 1)) { |
| rv = ec_command(EC_CMD_GET_VERSION, 1, NULL, 0, &r, |
| sizeof(struct ec_response_get_version_v1)); |
| } else { |
| /* Fall-back to version 0 if version 1 is not supported */ |
| rv = ec_command(EC_CMD_GET_VERSION, 0, NULL, 0, &r, |
| sizeof(struct ec_response_get_version)); |
| /* These fields are not supported in version 0, ensure empty */ |
| r.cros_fwid_ro[0] = '\0'; |
| r.cros_fwid_rw[0] = '\0'; |
| } |
| if (rv < 0) { |
| fprintf(stderr, "ERROR: EC_CMD_GET_VERSION failed: %d\n", rv); |
| goto exit; |
| } |
| |
| rv = ec_command(EC_CMD_GET_BUILD_INFO, 0, NULL, 0, ec_inbuf, |
| ec_max_insize); |
| if (rv < 0) { |
| fprintf(stderr, "ERROR: EC_CMD_GET_BUILD_INFO failed: %d\n", |
| rv); |
| goto exit; |
| } |
| |
| rv = 0; |
| |
| /* Ensure versions are null-terminated before we print them */ |
| r.version_string_ro[sizeof(r.version_string_ro) - 1] = '\0'; |
| r.version_string_rw[sizeof(r.version_string_rw) - 1] = '\0'; |
| build_string[ec_max_insize - 1] = '\0'; |
| r.cros_fwid_ro[sizeof(r.cros_fwid_ro) - 1] = '\0'; |
| r.cros_fwid_rw[sizeof(r.cros_fwid_rw) - 1] = '\0'; |
| /* Print versions */ |
| printf("RO version: %s\n", r.version_string_ro); |
| if (strlen(r.cros_fwid_ro)) |
| printf("RO cros fwid: %s\n", r.cros_fwid_ro); |
| printf("RW version: %s\n", r.version_string_rw); |
| if (strlen(r.cros_fwid_rw)) |
| printf("RW cros fwid: %s\n", r.cros_fwid_rw); |
| printf("Firmware copy: %s\n", |
| (r.current_image < ARRAY_SIZE(image_names) ? |
| image_names[r.current_image] : |
| "?")); |
| printf("Build info: %s\n", build_string); |
| exit: |
| printf("Tool version: %s %s %s\n", CROS_ECTOOL_VERSION, DATE, BUILDER); |
| |
| return rv; |
| } |
| |
| int cmd_reboot_ec(int argc, char *argv[]) |
| { |
| struct ec_params_reboot_ec p; |
| int rv, i; |
| |
| if (argc < 2) { |
| /* |
| * No params specified so tell the EC to reboot immediately. |
| * That reboots the AP as well, so unlikely we'll be around |
| * to see a return code from this... |
| */ |
| sync(); |
| rv = ec_command(EC_CMD_REBOOT, 0, NULL, 0, NULL, 0); |
| return (rv < 0 ? rv : 0); |
| } |
| |
| /* Parse command */ |
| if (!strcmp(argv[1], "cancel")) |
| p.cmd = EC_REBOOT_CANCEL; |
| else if (!strcmp(argv[1], "RO")) |
| p.cmd = EC_REBOOT_JUMP_RO; |
| else if (!strcmp(argv[1], "RW")) |
| p.cmd = EC_REBOOT_JUMP_RW; |
| else if (!strcmp(argv[1], "cold")) |
| p.cmd = EC_REBOOT_COLD; |
| else if (!strcmp(argv[1], "disable-jump")) |
| p.cmd = EC_REBOOT_DISABLE_JUMP; |
| else if (!strcmp(argv[1], "hibernate")) |
| p.cmd = EC_REBOOT_HIBERNATE; |
| else if (!strcmp(argv[1], "hibernate-clear-ap-off")) { |
| p.cmd = EC_REBOOT_HIBERNATE_CLEAR_AP_OFF; |
| fprintf(stderr, "hibernate-clear-ap-off is deprecated.\n" |
| "Use hibernate and clear-ap-idle, instead.\n"); |
| } else if (!strcmp(argv[1], "cold-ap-off")) |
| p.cmd = EC_REBOOT_COLD_AP_OFF; |
| else { |
| fprintf(stderr, "Unknown command: %s\n", argv[1]); |
| return -1; |
| } |
| |
| /* Parse flags, if any */ |
| p.flags = 0; |
| for (i = 2; i < argc; i++) { |
| if (!strcmp(argv[i], "at-shutdown")) { |
| p.flags |= EC_REBOOT_FLAG_ON_AP_SHUTDOWN; |
| } else if (!strcmp(argv[i], "switch-slot")) { |
| p.flags |= EC_REBOOT_FLAG_SWITCH_RW_SLOT; |
| } else if (!strcmp(argv[i], "clear-ap-idle")) { |
| p.flags |= EC_REBOOT_FLAG_CLEAR_AP_IDLE; |
| } else { |
| fprintf(stderr, "Unknown flag: %s\n", argv[i]); |
| return -1; |
| } |
| } |
| |
| if (p.cmd != EC_REBOOT_CANCEL) |
| sync(); |
| |
| rv = ec_command(EC_CMD_REBOOT_EC, 0, &p, sizeof(p), NULL, 0); |
| return (rv < 0 ? rv : 0); |
| } |
| |
| int cmd_reboot_ap_on_g3(int argc, char *argv[]) |
| { |
| struct ec_params_reboot_ap_on_g3_v1 p; |
| int rv; |
| char *e; |
| int cmdver; |
| |
| if (argc < 2) { |
| p.reboot_ap_at_g3_delay = 0; |
| } else { |
| p.reboot_ap_at_g3_delay = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "invalid number\n"); |
| return -1; |
| } |
| } |
| if (ec_cmd_version_supported(EC_CMD_REBOOT_AP_ON_G3, 1)) |
| cmdver = 1; |
| else |
| cmdver = 0; |
| |
| rv = ec_command(EC_CMD_REBOOT_AP_ON_G3, cmdver, &p, sizeof(p), NULL, 0); |
| return (rv < 0 ? rv : 0); |
| } |
| |
| static void cmd_rgbkbd_help(char *cmd) |
| { |
| fprintf(stderr, |
| " Usage1: %s <key> <RGB> [<RGB> ...]\n" |
| " Set the color of <key> to <RGB>. Multiple colors for\n" |
| " adjacent keys can be set at once.\n" |
| "\n" |
| " Usage2: %s clear <RGB>\n" |
| " Set the color of all keys to <RGB>.\n" |
| "\n" |
| " Usage3: %s demo <num>\n" |
| " Run demo-<num>. 0: Off, 1: Flow, 2: Dot.\n" |
| "\n" |
| " Usage4: %s scale <key> <val>\n" |
| " Set the scale parameter of key_<key> to <val>.\n" |
| " <val> is a 24-bit integer where scale values are encoded\n" |
| " as R=23:16, G=15:8, B=7:0.\n" |
| "\n" |
| " Usage5: %s getconfig\n" |
| " Get the HW config supported.\n" |
| "\n", |
| cmd, cmd, cmd, cmd, cmd); |
| } |
| |
| static int cmd_rgbkbd_parse_rgb_text(const char *text, struct rgb_s *color) |
| { |
| uint32_t rgb; |
| char *e; |
| |
| rgb = strtoul(text, &e, 0); |
| if ((e && *e) || rgb > EC_RGBKBD_MAX_RGB_COLOR) { |
| fprintf(stderr, "Invalid color '%s'.\n", text); |
| return -1; |
| } |
| color->r = (rgb >> 16) & 0xff; |
| color->g = (rgb >> 8) & 0xff; |
| color->b = (rgb >> 0) & 0xff; |
| |
| return 0; |
| } |
| |
| static int cmd_rgbkbd_set_color(int argc, char *argv[]) |
| { |
| struct ec_params_rgbkbd_set_color *p; |
| int i, key, outlen; |
| char *e; |
| int rv = -1; |
| |
| outlen = sizeof(*p) + sizeof(struct rgb_s) * EC_RGBKBD_MAX_KEY_COUNT; |
| p = (struct ec_params_rgbkbd_set_color *)malloc(outlen); |
| if (p == NULL) |
| return -1; |
| memset(p, 0, outlen); |
| |
| key = strtol(argv[1], &e, 0); |
| if ((e && *e) || key >= EC_RGBKBD_MAX_KEY_COUNT) { |
| fprintf(stderr, "Invalid key ID '%s'.\n", argv[1]); |
| goto out; |
| } |
| p->start_key = key; |
| |
| if (argc - 2 > EC_RGBKBD_MAX_KEY_COUNT) { |
| fprintf(stderr, "# of colors exceed max key count.\n"); |
| goto out; |
| } |
| |
| for (i = 2; i < argc; i++) { |
| if (cmd_rgbkbd_parse_rgb_text(argv[i], &p->color[p->length])) |
| goto out; |
| p->length++; |
| } |
| |
| outlen = sizeof(*p) + sizeof(struct rgb_s) * p->length; |
| rv = ec_command(EC_CMD_RGBKBD_SET_COLOR, 0, p, outlen, NULL, 0); |
| |
| out: |
| free(p); |
| |
| return rv; |
| } |
| |
| static int cmd_rgbkbd(int argc, char *argv[]) |
| { |
| int val; |
| char *e; |
| int rv = -1; |
| struct ec_params_rgbkbd p; |
| struct ec_response_rgbkbd r; |
| |
| if (argc < 2) { |
| cmd_rgbkbd_help(argv[0]); |
| return -1; |
| } |
| |
| if (argc == 3 && !strcasecmp(argv[1], "clear")) { |
| /* Usage 2 */ |
| p.subcmd = EC_RGBKBD_SUBCMD_CLEAR; |
| if (cmd_rgbkbd_parse_rgb_text(argv[2], &p.color)) |
| return -1; |
| |
| rv = ec_command(EC_CMD_RGBKBD, 0, &p, sizeof(p), &r, sizeof(r)); |
| } else if (argc == 3 && !strcasecmp(argv[1], "demo")) { |
| /* Usage 3 */ |
| val = strtol(argv[2], &e, 0); |
| if ((e && *e) || val >= EC_RGBKBD_DEMO_COUNT) { |
| fprintf(stderr, "Invalid demo id: %s\n", argv[2]); |
| return -1; |
| } |
| p.subcmd = EC_RGBKBD_SUBCMD_DEMO; |
| p.demo = val; |
| rv = ec_command(EC_CMD_RGBKBD, 0, &p, sizeof(p), &r, sizeof(r)); |
| } else if (argc == 4 && !strcasecmp(argv[1], "scale")) { |
| /* Usage 4 */ |
| val = strtol(argv[2], &e, 0); |
| if ((e && *e) || val > EC_RGBKBD_MAX_KEY_COUNT) { |
| fprintf(stderr, "Invalid key number: %s\n", argv[2]); |
| return -1; |
| } |
| p.set_scale.key = val; |
| if (cmd_rgbkbd_parse_rgb_text(argv[3], &p.set_scale.scale)) { |
| fprintf(stderr, "Invalid scale value: %s\n", argv[3]); |
| return -1; |
| } |
| p.subcmd = EC_RGBKBD_SUBCMD_SET_SCALE; |
| rv = ec_command(EC_CMD_RGBKBD, 0, &p, sizeof(p), &r, sizeof(r)); |
| } else if (argc == 2 && !strcasecmp(argv[1], "getconfig")) { |
| /* Usage 5 */ |
| const char *type; |
| |
| p.subcmd = EC_RGBKBD_SUBCMD_GET_CONFIG; |
| rv = ec_command(EC_CMD_RGBKBD, 0, &p, sizeof(p), &r, sizeof(r)); |
| |
| if (rv < 0) |
| return rv; |
| |
| switch ((enum ec_rgbkbd_type)r.rgbkbd_type) { |
| case EC_RGBKBD_TYPE_PER_KEY: |
| type = "EC_RGBKBD_TYPE_PER_KEY"; |
| break; |
| case EC_RGBKBD_TYPE_FOUR_ZONES_40_LEDS: |
| type = "EC_RGBKBD_TYPE_FOUR_ZONES_40_LEDS"; |
| break; |
| case EC_RGBKBD_TYPE_FOUR_ZONES_12_LEDS: |
| type = "EC_RGBKBD_TYPE_FOUR_ZONES_12_LEDS"; |
| break; |
| case EC_RGBKBD_TYPE_FOUR_ZONES_4_LEDS: |
| type = "EC_RGBKBD_TYPE_FOUR_ZONES_4_LEDS"; |
| break; |
| default: |
| type = "EC_RGBKBD_TYPE_UNKNOWN"; |
| } |
| |
| printf("RGBKBD_TYPE: %s\n", type); |
| } else { |
| /* Usage 1 */ |
| rv = cmd_rgbkbd_set_color(argc, argv); |
| } |
| |
| return (rv < 0 ? rv : 0); |
| } |
| |
| int cmd_button(int argc, char *argv[]) |
| { |
| struct ec_params_button p; |
| char *e; |
| int argv_idx; |
| int button = KEYBOARD_BUTTON_COUNT; |
| int rv; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Invalid num param %d.\n", argc); |
| return -1; |
| } |
| |
| p.press_ms = 50; |
| p.btn_mask = 0; |
| |
| for (argv_idx = 1; argv_idx < argc; argv_idx++) { |
| if (!strcasecmp(argv[argv_idx], "vup")) |
| button = KEYBOARD_BUTTON_VOLUME_UP; |
| else if (!strcasecmp(argv[argv_idx], "vdown")) |
| button = KEYBOARD_BUTTON_VOLUME_DOWN; |
| else if (!strcasecmp(argv[argv_idx], "rec")) |
| button = KEYBOARD_BUTTON_RECOVERY; |
| else { |
| /* If last parameter check if it is an integer. */ |
| if (argv_idx == argc - 1) { |
| p.press_ms = strtol(argv[argv_idx], &e, 0); |
| /* If integer, break out of the loop. */ |
| if (!*e) |
| break; |
| } |
| button = KEYBOARD_BUTTON_COUNT; |
| } |
| |
| if (button == KEYBOARD_BUTTON_COUNT) { |
| fprintf(stderr, "Invalid button input.\n"); |
| return -1; |
| } |
| |
| p.btn_mask |= (1 << button); |
| } |
| if (!p.btn_mask) |
| return 0; |
| |
| rv = ec_command(EC_CMD_BUTTON, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Button(s) %d set to %d ms\n", p.btn_mask, p.press_ms); |
| return 0; |
| } |
| |
| int cmd_flash_info(int argc, char *argv[]) |
| { |
| struct ec_response_flash_info_1 r; |
| int cmdver = 1; |
| int rsize = sizeof(r); |
| int rv; |
| |
| memset(&r, 0, sizeof(r)); |
| |
| if (!ec_cmd_version_supported(EC_CMD_FLASH_INFO, cmdver)) { |
| /* Fall back to version 0 command */ |
| cmdver = 0; |
| rsize = sizeof(struct ec_response_flash_info); |
| } |
| |
| rv = ec_command(EC_CMD_FLASH_INFO, cmdver, NULL, 0, &r, rsize); |
| if (rv < 0) |
| return rv; |
| |
| printf("FlashSize %d\nWriteSize %d\nEraseSize %d\nProtectSize %d\n", |
| r.flash_size, r.write_block_size, r.erase_block_size, |
| r.protect_block_size); |
| |
| if (cmdver >= 1) { |
| /* Fields added in ver.1 available */ |
| printf("WriteIdealSize %d\nFlags 0x%x\n", r.write_ideal_size, |
| r.flags); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_rand(int argc, char *argv[]) |
| { |
| int64_t num_bytes; |
| int64_t i; |
| char *e; |
| int rv = 0; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s <num_bytes>\n", argv[0]); |
| return -1; |
| } |
| |
| num_bytes = strtol(argv[1], &e, 0); |
| if ((e && *e) || (errno == ERANGE)) { |
| fprintf(stderr, "Invalid num_bytes argument\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < num_bytes; i += ec_max_insize) { |
| uint16_t num_rand_bytes = ec_max_insize; |
| if (num_bytes - i < num_rand_bytes) |
| num_rand_bytes = num_bytes - i; |
| |
| ec::RandNumCommand rand_num_command(num_rand_bytes); |
| if (!rand_num_command.Run(comm_get_fd())) { |
| int rv = -EECRESULT - rand_num_command.Result(); |
| fprintf(stderr, "Rand Num returned with errors: %d\n", |
| rv); |
| return rv; |
| } |
| |
| rv = write(STDOUT_FILENO, |
| rand_num_command.GetRandNumData().data(), |
| num_rand_bytes); |
| if (rv != num_rand_bytes) { |
| fprintf(stderr, "Failed to write stdout\n"); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int cmd_flash_spi_info(int argc, char *argv[]) |
| { |
| struct ec_response_flash_spi_info r; |
| int rv; |
| |
| memset(&r, 0, sizeof(r)); |
| |
| /* Print SPI flash info if available */ |
| if (!ec_cmd_version_supported(EC_CMD_FLASH_SPI_INFO, 0)) { |
| printf("EC has no info (does not use SPI flash?)\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_FLASH_SPI_INFO, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("JEDECManufacturerID 0x%02x\n", r.jedec[0]); |
| printf("JEDECDeviceID 0x%02x 0x%02x\n", r.jedec[1], r.jedec[2]); |
| printf("JEDECCapacity %d\n", 1 << r.jedec[2]); |
| printf("ManufacturerID 0x%02x\n", r.mfr_dev_id[0]); |
| printf("DeviceID 0x%02x\n", r.mfr_dev_id[1]); |
| printf("StatusRegister1 0x%02x\n", r.sr1); |
| printf("StatusRegister2 0x%02x\n", r.sr2); |
| return 0; |
| } |
| |
| int cmd_flash_read(int argc, char *argv[]) |
| { |
| int offset, size; |
| int rv; |
| char *e; |
| uint8_t *buf; |
| |
| if (argc < 4) { |
| fprintf(stderr, "Usage: %s <offset> <size> <filename>\n", |
| argv[0]); |
| return -1; |
| } |
| offset = strtol(argv[1], &e, 0); |
| if ((e && *e) || offset < 0 || offset > MAX_FLASH_SIZE) { |
| fprintf(stderr, "Bad offset.\n"); |
| return -1; |
| } |
| size = strtol(argv[2], &e, 0); |
| if ((e && *e) || size <= 0 || size > MAX_FLASH_SIZE) { |
| fprintf(stderr, "Bad size.\n"); |
| return -1; |
| } |
| printf("Reading %d bytes at offset %d...\n", size, offset); |
| |
| buf = (uint8_t *)malloc(size); |
| if (!buf) { |
| fprintf(stderr, "Unable to allocate buffer.\n"); |
| return -1; |
| } |
| |
| /* Read data in chunks */ |
| rv = ec_flash_read(buf, offset, size); |
| if (rv < 0) { |
| free(buf); |
| return rv; |
| } |
| |
| rv = write_file(argv[3], (const char *)(buf), size); |
| free(buf); |
| if (rv) |
| return rv; |
| |
| printf("done.\n"); |
| return 0; |
| } |
| |
| int cmd_flash_write(int argc, char *argv[]) |
| { |
| int offset, size; |
| int rv; |
| char *e; |
| char *buf; |
| |
| if (argc < 3) { |
| fprintf(stderr, "Usage: %s <offset> <filename>\n", argv[0]); |
| return -1; |
| } |
| |
| offset = strtol(argv[1], &e, 0); |
| if ((e && *e) || offset < 0 || offset > MAX_FLASH_SIZE) { |
| fprintf(stderr, "Bad offset.\n"); |
| return -1; |
| } |
| |
| /* Read the input file */ |
| buf = read_file(argv[2], &size); |
| if (!buf) |
| return -1; |
| |
| printf("Writing to offset %d...\n", offset); |
| |
| /* Write data in chunks */ |
| rv = ec_flash_write((const uint8_t *)(buf), offset, size); |
| |
| free(buf); |
| |
| if (rv < 0) |
| return rv; |
| |
| printf("done.\n"); |
| return 0; |
| } |
| |
| int cmd_flash_erase(int argc, char *argv[]) |
| { |
| int offset, size; |
| char *e; |
| int rv; |
| bool async = false; |
| |
| if (argc < 3) { |
| fprintf(stderr, "Usage: %s <offset> <size>\n", argv[0]); |
| return -1; |
| } |
| |
| if (strcmp(argv[0], "flasheraseasync") == 0) |
| async = true; |
| |
| offset = strtol(argv[1], &e, 0); |
| if ((e && *e) || offset < 0 || offset > MAX_FLASH_SIZE) { |
| fprintf(stderr, "Bad offset.\n"); |
| return -1; |
| } |
| |
| size = strtol(argv[2], &e, 0); |
| if ((e && *e) || size <= 0 || size > MAX_FLASH_SIZE) { |
| fprintf(stderr, "Bad size.\n"); |
| return -1; |
| } |
| |
| printf("Erasing %d bytes at offset %d...\n", size, offset); |
| if (async) |
| rv = ec_flash_erase_async(offset, size); |
| else |
| rv = ec_flash_erase(offset, size); |
| if (rv < 0) |
| return rv; |
| |
| printf("done.\n"); |
| return 0; |
| } |
| |
| int cmd_flash_protect(int argc, char *argv[]) |
| { |
| /* |
| * Set up requested flags. If no flags were specified, mask will |
| * be flash_protect::Flags::kNone and nothing will change. |
| */ |
| ec::flash_protect::Flags flags = ec::flash_protect::Flags::kNone; |
| ec::flash_protect::Flags mask = ec::flash_protect::Flags::kNone; |
| |
| for (int i = 1; i < argc; i++) { |
| if (!strcasecmp(argv[i], "now")) { |
| mask |= ec::flash_protect::Flags::kAllNow; |
| flags |= ec::flash_protect::Flags::kAllNow; |
| } else if (!strcasecmp(argv[i], "enable")) { |
| mask |= ec::flash_protect::Flags::kRoAtBoot; |
| flags |= ec::flash_protect::Flags::kRoAtBoot; |
| } else if (!strcasecmp(argv[i], "disable")) |
| mask |= ec::flash_protect::Flags::kRoAtBoot; |
| } |
| |
| ec::EcCommandFactory ec_command_factory; |
| ec::EcCommandVersionSupported ec_cmd_ver_supported; |
| auto flash_protect_command = ec_command_factory.FlashProtectCommand( |
| &ec_cmd_ver_supported, flags, mask); |
| |
| if (!flash_protect_command) { |
| fprintf(stderr, "Failed to create FlashProtectCommand.\n"); |
| return -1; |
| } |
| |
| if (!flash_protect_command->Run(comm_get_fd())) { |
| int rv = -EECRESULT - flash_protect_command->Result(); |
| fprintf(stderr, "Flash protect returned with errors: %d\n", rv); |
| return rv; |
| } |
| |
| /* Print returned flags */ |
| printf("Flash protect flags: 0x%08x%s\n", |
| static_cast<int>(flash_protect_command->GetFlags()), |
| (ec::FlashProtectCommand::ParseFlags( |
| flash_protect_command->GetFlags())) |
| .c_str()); |
| printf("Valid flags: 0x%08x%s\n", |
| static_cast<int>(flash_protect_command->GetValidFlags()), |
| (ec::FlashProtectCommand::ParseFlags( |
| flash_protect_command->GetValidFlags())) |
| .c_str()); |
| printf("Writable flags: 0x%08x%s\n", |
| static_cast<int>(flash_protect_command->GetWritableFlags()), |
| |
| (ec::FlashProtectCommand::ParseFlags( |
| flash_protect_command->GetWritableFlags())) |
| .c_str()); |
| |
| /* Check if we got all the flags we asked for */ |
| if ((flash_protect_command->GetFlags() & mask) != (flags & mask)) { |
| fprintf(stderr, |
| "Unable to set requested flags " |
| "(wanted mask 0x%08x flags 0x%08x)\n", |
| static_cast<int>(mask), static_cast<int>(flags)); |
| if ((mask & ~flash_protect_command->GetWritableFlags()) != |
| ec::flash_protect::Flags::kNone) |
| fprintf(stderr, |
| "Which is expected, because writable " |
| "mask is 0x%08x.\n", |
| static_cast<int>(flash_protect_command |
| ->GetWritableFlags())); |
| |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int cmd_rw_hash_pd(int argc, char *argv[]) |
| { |
| struct ec_params_usb_pd_rw_hash_entry *p = |
| (struct ec_params_usb_pd_rw_hash_entry *)ec_outbuf; |
| int i, rv; |
| char *e; |
| uint32_t val; |
| uint8_t *rwp; |
| |
| if (argc < 7) { |
| fprintf(stderr, "Usage: %s <dev_id> <HASH[0]> ... <HASH[4]>\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p->dev_id = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad device ID\n"); |
| return -1; |
| } |
| |
| rwp = p->dev_rw_hash; |
| for (i = 2; i < 7; i++) { |
| val = strtol(argv[i], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad RW hash\n"); |
| return -1; |
| } |
| rwp[0] = (uint8_t)(val >> 0) & 0xff; |
| rwp[1] = (uint8_t)(val >> 8) & 0xff; |
| rwp[2] = (uint8_t)(val >> 16) & 0xff; |
| rwp[3] = (uint8_t)(val >> 24) & 0xff; |
| rwp += 4; |
| } |
| rv = ec_command(EC_CMD_USB_PD_RW_HASH_ENTRY, 0, p, sizeof(*p), NULL, 0); |
| |
| return rv; |
| } |
| |
| int cmd_rwsig_status(int argc, char *argv[]) |
| { |
| int rv; |
| struct ec_response_rwsig_check_status resp; |
| |
| rv = ec_command(EC_CMD_RWSIG_CHECK_STATUS, 0, NULL, 0, &resp, |
| sizeof(resp)); |
| if (rv < 0) |
| return rv; |
| |
| printf("RW signature check: %s\n", resp.status ? "OK" : "FAILED"); |
| |
| return 0; |
| } |
| |
| static int rwsig_action(const char *command) |
| { |
| struct ec_params_rwsig_action req; |
| |
| if (!strcasecmp(command, "abort")) |
| req.action = RWSIG_ACTION_ABORT; |
| else if (!strcasecmp(command, "continue")) |
| req.action = RWSIG_ACTION_CONTINUE; |
| else |
| return -1; |
| |
| return ec_command(EC_CMD_RWSIG_ACTION, 0, &req, sizeof(req), NULL, 0); |
| } |
| |
| int cmd_rwsig_action_legacy(int argc, char *argv[]) |
| { |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s [abort | continue]\n", argv[0]); |
| return -1; |
| } |
| |
| return rwsig_action(argv[1]); |
| } |
| |
| int cmd_rwsig_action(int argc, char *argv[]) |
| { |
| if (argc < 2) { |
| fprintf(stderr, "Usage: ectool rwsig action [abort | " |
| "continue]\n"); |
| return -1; |
| } |
| |
| return rwsig_action(argv[1]); |
| } |
| |
| enum rwsig_info_fields { |
| RWSIG_INFO_FIELD_SIG_ALG = BIT(0), |
| RWSIG_INFO_FIELD_KEY_VERSION = BIT(1), |
| RWSIG_INFO_FIELD_HASH_ALG = BIT(2), |
| RWSIG_INFO_FIELD_KEY_IS_VALID = BIT(3), |
| RWSIG_INFO_FIELD_KEY_ID = BIT(4), |
| RWSIG_INFO_FIELD_ALL = |
| RWSIG_INFO_FIELD_SIG_ALG | RWSIG_INFO_FIELD_KEY_VERSION | |
| RWSIG_INFO_FIELD_HASH_ALG | RWSIG_INFO_FIELD_KEY_IS_VALID | |
| RWSIG_INFO_FIELD_KEY_ID |
| }; |
| |
| static int rwsig_info(enum rwsig_info_fields fields) |
| { |
| int i; |
| int rv; |
| struct ec_response_rwsig_info r; |
| bool print_prefix = false; |
| |
| rv = ec_command(EC_CMD_RWSIG_INFO, EC_VER_RWSIG_INFO, NULL, 0, &r, |
| sizeof(r)); |
| if (rv < 0) { |
| fprintf(stderr, "rwsig info command failed\n"); |
| return -1; |
| } |
| |
| if ((fields & RWSIG_INFO_FIELD_ALL) == RWSIG_INFO_FIELD_ALL) |
| print_prefix = true; |
| |
| if (fields & RWSIG_INFO_FIELD_SIG_ALG) { |
| if (print_prefix) |
| printf("sig_alg: "); |
| |
| printf("%d\n", r.sig_alg); |
| } |
| if (fields & RWSIG_INFO_FIELD_KEY_VERSION) { |
| if (print_prefix) |
| printf("key_version: "); |
| |
| printf("%d\n", r.key_version); |
| } |
| if (fields & RWSIG_INFO_FIELD_HASH_ALG) { |
| if (print_prefix) |
| printf("hash_alg: "); |
| |
| printf("%d\n", r.hash_alg); |
| } |
| if (fields & RWSIG_INFO_FIELD_KEY_IS_VALID) { |
| if (print_prefix) |
| printf("key_is_valid: "); |
| |
| printf("%d\n", r.key_is_valid); |
| } |
| if (fields & RWSIG_INFO_FIELD_KEY_ID) { |
| if (print_prefix) |
| printf("key_id: "); |
| |
| for (i = 0; i < sizeof(r.key_id); i++) |
| printf("%02x", r.key_id[i]); |
| printf("\n"); |
| } |
| |
| return 0; |
| } |
| |
| static int cmd_rwsig_info(int argc, char *argv[]) |
| { |
| int i; |
| |
| struct rwsig_dump_cmds { |
| const char *cmd; |
| enum rwsig_info_fields field; |
| }; |
| |
| struct rwsig_dump_cmds cmd_map[] = { |
| { "sig_alg", RWSIG_INFO_FIELD_SIG_ALG }, |
| { "key_version", RWSIG_INFO_FIELD_KEY_VERSION }, |
| { "hash_alg", RWSIG_INFO_FIELD_HASH_ALG }, |
| { "key_valid", RWSIG_INFO_FIELD_KEY_IS_VALID }, |
| { "key_id", RWSIG_INFO_FIELD_KEY_ID }, |
| }; |
| |
| if (argc == 0) |
| return -1; |
| |
| if (strcmp(argv[0], "info") == 0) |
| return rwsig_info(RWSIG_INFO_FIELD_ALL); |
| |
| if (strcmp(argv[0], "dump") == 0) { |
| if (argc != 2) { |
| fprintf(stderr, |
| "Usage: rwsig dump " |
| "[sig_alg|key_version|hash_alg|key_valid|key_id]\n"); |
| return -1; |
| } |
| for (i = 0; i < ARRAY_SIZE(cmd_map); i++) |
| if (strcmp(argv[1], cmd_map[i].cmd) == 0) |
| return rwsig_info(cmd_map[i].field); |
| |
| return -1; |
| } |
| |
| return -1; |
| } |
| |
| int cmd_rwsig(int argc, char **argv) |
| { |
| struct rwsig_subcommand { |
| const char *subcommand; |
| int (*handler)(int argc, char *argv[]); |
| }; |
| |
| const struct rwsig_subcommand rwsig_subcommands[] = { |
| { "info", cmd_rwsig_info }, |
| { "dump", cmd_rwsig_info }, |
| { "action", cmd_rwsig_action }, |
| { "status", cmd_rwsig_status } |
| }; |
| |
| int i; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s <info|dump|action|status>\n", |
| argv[0]); |
| return -1; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(rwsig_subcommands); i++) |
| if (strcmp(argv[1], rwsig_subcommands[i].subcommand) == 0) |
| return rwsig_subcommands[i].handler(--argc, &argv[1]); |
| |
| return -1; |
| } |
| |
| enum sysinfo_fields { |
| SYSINFO_FIELD_NONE = 0, |
| SYSINFO_FIELD_RESET_FLAGS = BIT(0), |
| SYSINFO_FIELD_CURRENT_IMAGE = BIT(1), |
| SYSINFO_FIELD_FLAGS = BIT(2), |
| SYSINFO_INFO_FIELD_ALL = SYSINFO_FIELD_RESET_FLAGS | |
| SYSINFO_FIELD_CURRENT_IMAGE | |
| SYSINFO_FIELD_FLAGS |
| }; |
| |
| static int sysinfo(struct ec_response_sysinfo *info) |
| { |
| int rv; |
| |
| rv = ec_command(EC_CMD_SYSINFO, 0, NULL, 0, info, sizeof(*info)); |
| if (rv < 0) { |
| fprintf(stderr, "ERROR: EC_CMD_SYSINFO failed: %d\n", rv); |
| return rv; |
| } |
| |
| return 0; |
| } |
| |
| int cmd_sysinfo(int argc, char **argv) |
| { |
| struct ec_response_sysinfo r; |
| enum sysinfo_fields fields = SYSINFO_FIELD_NONE; |
| bool print_prefix = false; |
| |
| if (argc != 1 && argc != 2) |
| goto sysinfo_error_usage; |
| |
| if (argc == 1) { |
| fields = SYSINFO_INFO_FIELD_ALL; |
| print_prefix = true; |
| } else if (argc == 2) { |
| if (strcmp(argv[1], "flags") == 0) |
| fields = SYSINFO_FIELD_FLAGS; |
| else if (strcmp(argv[1], "reset_flags") == 0) |
| fields = SYSINFO_FIELD_RESET_FLAGS; |
| else if (strcmp(argv[1], "firmware_copy") == 0) |
| fields = SYSINFO_FIELD_CURRENT_IMAGE; |
| else |
| goto sysinfo_error_usage; |
| } |
| |
| memset(&r, '\0', sizeof(r)); |
| if (sysinfo(&r) != 0) |
| return -1; |
| |
| if (fields & SYSINFO_FIELD_RESET_FLAGS) { |
| if (print_prefix) |
| printf("Reset flags: "); |
| printf("0x%08x\n", r.reset_flags); |
| } |
| |
| if (fields & SYSINFO_FIELD_FLAGS) { |
| if (print_prefix) |
| printf("Flags: "); |
| printf("0x%08x\n", r.flags); |
| } |
| |
| if (fields & SYSINFO_FIELD_CURRENT_IMAGE) { |
| if (print_prefix) |
| printf("Firmware copy: "); |
| printf("%d\n", r.current_image); |
| } |
| |
| return 0; |
| |
| sysinfo_error_usage: |
| fprintf(stderr, |
| "Usage: %s " |
| "[flags|reset_flags|firmware_copy]\n", |
| argv[0]); |
| return -1; |
| } |
| |
| int cmd_rollback_info(int argc, char *argv[]) |
| { |
| struct ec_response_rollback_info_v1 r; |
| int cmdver = 1; |
| int rsize = sizeof(r); |
| int rv; |
| |
| memset(&r, 0, sizeof(r)); |
| |
| if (!ec_cmd_version_supported(EC_CMD_ROLLBACK_INFO, cmdver)) { |
| /* Fall back to version 0. Older RO firmware may not support |
| * version 1. |
| */ |
| cmdver = 0; |
| rsize = sizeof(struct ec_response_rollback_info); |
| } |
| |
| rv = ec_command(EC_CMD_ROLLBACK_INFO, cmdver, NULL, 0, &r, rsize); |
| if (rv < 0) { |
| fprintf(stderr, "ERROR: EC_CMD_ROLLBACK_INFO failed: %d\n", rv); |
| return rv; |
| } |
| |
| /* Print versions */ |
| printf("Rollback block id: %d\n", r.id); |
| printf("Rollback min version: %d\n", r.rollback_min_version); |
| printf("RW rollback version: %d\n", r.rw_rollback_version); |
| |
| if (cmdver >= 1) { |
| printf("Secret initialized: %d\n", r.is_secret_inited); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_apreset(int argc, char *argv[]) |
| { |
| sync(); |
| return ec_command(EC_CMD_AP_RESET, 0, NULL, 0, NULL, 0); |
| } |
| |
| #define FP_FRAME_INDEX_SIMPLE_IMAGE -1 |
| |
| /* |
| * Download a frame buffer from the FPMCU. |
| * |
| * Might be either the finger image or a finger template depending on 'index'. |
| * |
| * @param info a pointer to store the struct ec_response_fp_info retrieved by |
| * this command. |
| * @param index the specific frame to retrieve, might be: |
| * -1 (aka FP_FRAME_INDEX_SIMPLE_IMAGE) for the a single grayscale image. |
| * 0 (aka FP_FRAME_INDEX_RAW_IMAGE) for the full vendor raw finger image. |
| * 1..n for a finger template. |
| * |
| * @returns a vector<uint8_t> containing the the requested frame. |
| */ |
| static std::unique_ptr<std::vector<uint8_t> > |
| fp_download_frame(struct SensorImage &sensor_image, |
| struct TemplateInfo &template_info, int index, |
| uint8_t capture_type = FP_CAPTURE_VENDOR_FORMAT) |
| { |
| ec::EcCommandFactory ec_command_factory; |
| ec::EcCommandVersionSupported ec_cmd_ver_supported; |
| auto fp_info_command = |
| ec_command_factory.FpInfoCommand(&ec_cmd_ver_supported); |
| |
| if (!fp_info_command || !fp_info_command->Run(comm_get_fd())) { |
| fprintf(stderr, "Failed to Run FpInfoCommand.\n"); |
| return nullptr; |
| } |
| |
| template_info = *fp_info_command->template_info(); |
| auto images = fp_info_command->sensor_image(); |
| if (images.size() == 1) { |
| sensor_image = images[0]; |
| } else { |
| bool found = false; |
| for (const auto &image : fp_info_command->sensor_image()) |
| if (image.fp_capture_type.has_value() && |
| *image.fp_capture_type == capture_type) { |
| sensor_image = image; |
| found = true; |
| break; |
| } |
| if (!found) { |
| fprintf(stderr, |
| "Could not find fp image frame params.\n"); |
| return nullptr; |
| } |
| } |
| |
| /* |
| * TODO(b/450381837): Remove raw/simple parameters and use fp_info |
| * version 2 for frame size. |
| */ |
| size_t size; |
| if (index == FP_FRAME_INDEX_SIMPLE_IMAGE) { |
| size = (size_t)sensor_image.width * |
| DIV_ROUND_UP(sensor_image.bpp, 8) * sensor_image.height; |
| |
| index = FP_FRAME_INDEX_RAW_IMAGE; |
| } else if (index == FP_FRAME_INDEX_RAW_IMAGE) { |
| size = sensor_image.frame_size; |
| } else { |
| size = template_info.size; |
| } |
| |
| auto frame_cmd = ec::FpFrameCommand::Create(index, size, ec_max_insize); |
| if (!frame_cmd) { |
| fprintf(stderr, "Fp Frame command given invalid params\n"); |
| return nullptr; |
| } |
| if (!frame_cmd->Run(comm_get_fd())) { |
| const char *error_str = strerror(errno); |
| std::string result_str = frame_cmd->ResultString(); |
| if (frame_cmd->Result() != ec::kEcCommandUninitializedResult) { |
| error_str = result_str.c_str(); |
| } |
| fprintf(stderr, "Fp Frame command failed with error: %s\n", |
| error_str); |
| return nullptr; |
| } |
| |
| return frame_cmd->frame(); |
| } |
| |
| int cmd_fp_mode(int argc, char *argv[]) |
| { |
| struct ec_params_fp_mode p; |
| struct ec_response_fp_mode r; |
| uint32_t mode = 0; |
| uint32_t capture_type = FP_CAPTURE_SIMPLE_IMAGE; |
| int i, rv; |
| |
| if (argc == 1) |
| mode = FP_MODE_DONT_CHANGE; |
| for (i = 1; i < argc; i++) { |
| /* modes */ |
| if (!strncmp(argv[i], "deepsleep", 9)) |
| mode |= FP_MODE_DEEPSLEEP; |
| else if (!strncmp(argv[i], "fingerdown", 10)) |
| mode |= FP_MODE_FINGER_DOWN; |
| else if (!strncmp(argv[i], "fingerup", 8)) |
| mode |= FP_MODE_FINGER_UP; |
| else if (!strncmp(argv[i], "enroll", 6)) |
| mode |= FP_MODE_ENROLL_IMAGE | FP_MODE_ENROLL_SESSION; |
| else if (!strncmp(argv[i], "match", 5)) |
| mode |= FP_MODE_MATCH; |
| else if (!strncmp(argv[i], "reset_sensor", 12)) |
| mode = FP_MODE_RESET_SENSOR; |
| else if (!strncmp(argv[i], "reset", 5)) |
| mode = 0; |
| else if (!strncmp(argv[i], "maintenance", 11)) |
| mode |= FP_MODE_SENSOR_MAINTENANCE; |
| else if (!strncmp(argv[i], "capture", 7)) |
| mode |= FP_MODE_CAPTURE; |
| /* capture types */ |
| else if (!strncmp(argv[i], "vendor", 6)) |
| capture_type = FP_CAPTURE_VENDOR_FORMAT; |
| else if (!strncmp(argv[i], "pattern0", 8)) |
| capture_type = FP_CAPTURE_PATTERN0; |
| else if (!strncmp(argv[i], "pattern1", 8)) |
| capture_type = FP_CAPTURE_PATTERN1; |
| else if (!strncmp(argv[i], "qual", 4)) |
| capture_type = FP_CAPTURE_QUALITY_TEST; |
| else if (!strncmp(argv[i], "test_reset", 10)) |
| capture_type = FP_CAPTURE_RESET_TEST; |
| else if (!strncmp(argv[i], "test_defect_pixel", 17)) |
| capture_type = FP_CAPTURE_DEFECT_PXL_TEST; |
| else if (!strncmp(argv[i], "test_abnormal", 13)) |
| capture_type = FP_CAPTURE_ABNORMAL_TEST; |
| else if (!strncmp(argv[i], "test_noise", 10)) |
| capture_type = FP_CAPTURE_NOISE_TEST; |
| } |
| if (mode & FP_MODE_CAPTURE) |
| mode |= capture_type << FP_MODE_CAPTURE_TYPE_SHIFT; |
| |
| p.mode = mode; |
| rv = ec_command(EC_CMD_FP_MODE, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("FP mode: (0x%x) ", r.mode); |
| if (r.mode & FP_MODE_DEEPSLEEP) |
| printf("deepsleep "); |
| if (r.mode & FP_MODE_FINGER_DOWN) |
| printf("finger-down "); |
| if (r.mode & FP_MODE_FINGER_UP) |
| printf("finger-up "); |
| if (r.mode & FP_MODE_ENROLL_SESSION) |
| printf("enroll%s ", |
| r.mode & FP_MODE_ENROLL_IMAGE ? "+image" : ""); |
| if (r.mode & FP_MODE_MATCH) |
| printf("match "); |
| if (r.mode & FP_MODE_CAPTURE) |
| printf("capture "); |
| printf("\n"); |
| return 0; |
| } |
| |
| int cmd_fp_seed(int argc, char *argv[]) |
| { |
| struct ec_params_fp_seed p; |
| char *seed; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <seed>\n", argv[0]); |
| return 1; |
| } |
| seed = argv[1]; |
| if (strlen(seed) != FP_CONTEXT_TPM_BYTES) { |
| printf("Invalid seed '%s' is %zd bytes long instead of %d.\n", |
| seed, strlen(seed), FP_CONTEXT_TPM_BYTES); |
| return 1; |
| } |
| printf("Setting seed '%s'\n", seed); |
| p.struct_version = FP_TEMPLATE_FORMAT_VERSION; |
| memcpy(p.seed, seed, FP_CONTEXT_TPM_BYTES); |
| |
| return ec_command(EC_CMD_FP_SEED, 0, &p, sizeof(p), NULL, 0); |
| } |
| |
| int cmd_fp_stats(int argc, char *argv[]) |
| { |
| struct ec_response_fp_stats r; |
| int rv; |
| unsigned long long ts; |
| |
| rv = ec_command(EC_CMD_FP_STATS, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| ts = (uint64_t)r.overall_t0.hi << 32 | r.overall_t0.lo; |
| printf("FP stats (t0=%llu us):\n", ts); |
| printf("Last capture time: "); |
| if (r.timestamps_invalid & FPSTATS_CAPTURE_INV) |
| printf("Invalid\n"); |
| else |
| printf("%d us\n", r.capture_time_us); |
| |
| printf("Last matching time: "); |
| if (r.timestamps_invalid & FPSTATS_MATCHING_INV) |
| printf("Invalid\n"); |
| else |
| printf("%d us (finger: %d)\n", r.matching_time_us, |
| r.template_matched); |
| |
| printf("Last overall time: "); |
| if (r.timestamps_invalid) |
| printf("Invalid\n"); |
| else |
| printf("%d us\n", r.overall_time_us); |
| |
| return 0; |
| } |
| |
| int cmd_fp_info(int argc, char *argv[]) |
| { |
| ec::EcCommandFactory ec_command_factory; |
| ec::EcCommandVersionSupported ec_cmd_ver_supported; |
| auto fp_info_command = |
| ec_command_factory.FpInfoCommand(&ec_cmd_ver_supported); |
| |
| if (!fp_info_command) { |
| fprintf(stderr, "Failed to create FpInfoCommand.\n"); |
| return -1; |
| } |
| |
| if (!fp_info_command->Run(comm_get_fd())) { |
| int rv = -EECRESULT - fp_info_command->Result(); |
| fprintf(stderr, "Fp info command returned with errors: %d\n", |
| rv); |
| return rv; |
| } |
| |
| printf("%s", fp_info_command->ParseSensorInfo().c_str()); |
| |
| return 0; |
| } |
| |
| static int cmd_fp_context(int argc, char *argv[]) |
| { |
| struct ec_params_fp_context_v1 p; |
| int rv; |
| int tries = 20; /* Wait at most two seconds */ |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s <context>\n", argv[0]); |
| return -1; |
| } |
| |
| /* |
| * Note that we treat the resulting "userid" as raw byte array, so we |
| * don't want to copy the NUL from the end of the string. |
| */ |
| if (strlen(argv[1]) != sizeof(p.userid)) { |
| fprintf(stderr, "Context must be exactly %zu bytes\n", |
| sizeof(p.userid)); |
| return -1; |
| } |
| |
| p.action = FP_CONTEXT_ASYNC; |
| memcpy(p.userid, argv[1], sizeof(p.userid)); |
| |
| rv = ec_command(EC_CMD_FP_CONTEXT, 1, &p, sizeof(p), NULL, 0); |
| |
| if (rv != EC_RES_SUCCESS) |
| goto out; |
| |
| while (tries--) { |
| usleep(100000); |
| |
| p.action = FP_CONTEXT_GET_RESULT; |
| rv = ec_command(EC_CMD_FP_CONTEXT, 1, &p, sizeof(p), NULL, 0); |
| |
| if (rv == EC_RES_SUCCESS) { |
| printf("Set context successfully\n"); |
| return EC_RES_SUCCESS; |
| } |
| |
| /* Abort if EC returns an error other than EC_RES_BUSY. */ |
| if (rv <= -EECRESULT && rv != -EECRESULT - EC_RES_BUSY) |
| goto out; |
| } |
| |
| rv = -EECRESULT - EC_RES_TIMEOUT; |
| |
| out: |
| fprintf(stderr, "Failed to reset context: %d\n", rv); |
| return rv; |
| } |
| |
| int cmd_fp_enc_status(int argc, char *argv[]) |
| { |
| int rv; |
| |
| ec::FpEncryptionStatusCommand fp_encryptionstatus_command; |
| |
| if (!fp_encryptionstatus_command.Run(comm_get_fd())) { |
| int rv = -EECRESULT - fp_encryptionstatus_command.Result(); |
| fprintf(stderr, |
| "FP Encryption Status returned with errors: %d\n", rv); |
| return rv; |
| } |
| printf("FPMCU encryption status: 0x%08x%s\n", |
| fp_encryptionstatus_command.GetStatus(), |
| (ec::FpEncryptionStatusCommand::ParseFlags( |
| fp_encryptionstatus_command.GetStatus())) |
| .c_str()); |
| printf("Valid flags: 0x%08x%s\n", |
| fp_encryptionstatus_command.GetValidFlags(), |
| (ec::FpEncryptionStatusCommand::ParseFlags( |
| fp_encryptionstatus_command.GetValidFlags())) |
| .c_str()); |
| |
| rv = 0; |
| |
| return rv; |
| } |
| |
| int cmd_fp_frame(int argc, char *argv[]) |
| { |
| struct SensorImage sensor_image{}; |
| struct TemplateInfo template_info{}; |
| |
| int idx = FP_FRAME_INDEX_SIMPLE_IMAGE; |
| uint8_t capture_type = FP_CAPTURE_VENDOR_FORMAT; |
| bool capture_type_arg_seen = false; |
| |
| static const std::map<std::string, uint8_t> capture_type_map = { |
| { "vendor", FP_CAPTURE_VENDOR_FORMAT }, |
| { "pattern0", FP_CAPTURE_PATTERN0 }, |
| { "pattern1", FP_CAPTURE_PATTERN1 }, |
| { "qual", FP_CAPTURE_QUALITY_TEST }, |
| { "test_reset", FP_CAPTURE_RESET_TEST }, |
| { "test_defect_pixel", FP_CAPTURE_DEFECT_PXL_TEST }, |
| { "test_abnormal", FP_CAPTURE_ABNORMAL_TEST }, |
| { "test_noise", FP_CAPTURE_NOISE_TEST } |
| }; |
| |
| for (int i = 1; i < argc; i++) { |
| std::string arg = argv[i]; |
| if (arg == "raw") { |
| idx = FP_FRAME_INDEX_RAW_IMAGE; |
| } else { |
| auto it = capture_type_map.find(arg); |
| if (it != capture_type_map.end()) { |
| if (capture_type_arg_seen) { |
| fprintf(stderr, |
| "Error: Multiple capture types specified ('%s'). Please specify" |
| " only one.\n ", |
| arg.c_str()); |
| return -1; |
| } |
| capture_type = it->second; |
| capture_type_arg_seen = true; |
| } else { |
| fprintf(stderr, |
| "Warning: Ignoring unknown argument '%s'\n", |
| arg.c_str()); |
| } |
| } |
| } |
| |
| auto fp_frame = fp_download_frame(sensor_image, template_info, idx, |
| capture_type); |
| if (!fp_frame) { |
| fprintf(stderr, "Failed to get FP sensor frame\n"); |
| return -1; |
| } |
| |
| if (idx == FP_FRAME_INDEX_RAW_IMAGE) { |
| assert(fp_frame->size() == sensor_image.frame_size); |
| fwrite(fp_frame->data(), sensor_image.frame_size, 1, stdout); |
| return 0; |
| } |
| |
| auto frame_to_pgm = ec::FpFrameCommand::FrameToPgm( |
| *fp_frame, |
| { .bpp = sensor_image.bpp, |
| .width = static_cast<uint16_t>(sensor_image.width), |
| .height = static_cast<uint16_t>(sensor_image.height) }); |
| |
| if (!frame_to_pgm.has_value()) { |
| fprintf(stderr, "Error: Failed to convert frame to PGM.\n"); |
| return -1; |
| } |
| fprintf(stdout, "%s", frame_to_pgm->c_str()); |
| return 0; |
| } |
| |
| int cmd_fp_template(int argc, char *argv[]) |
| { |
| struct SensorImage sensor_image{}; |
| struct TemplateInfo template_info{}; |
| struct ec_params_fp_template *p = |
| (struct ec_params_fp_template *)(ec_outbuf); |
| /* TODO(b/78544921): removing 32 bits is a workaround for the MCU bug */ |
| int max_chunk = ec_max_outsize - |
| offsetof(struct ec_params_fp_template, data) - 4; |
| int idx = -1; |
| char *e; |
| int size; |
| char *buffer = NULL; |
| uint32_t offset = 0; |
| int rv = 0; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s [<infile>|<index>]\n", argv[0]); |
| return -1; |
| } |
| |
| idx = strtol(argv[1], &e, 0); |
| if (!(e && *e)) { |
| auto fp_frame = |
| fp_download_frame(sensor_image, template_info, idx + 1); |
| if (!fp_frame) { |
| fprintf(stderr, "Failed to get FP template %d\n", idx); |
| return -1; |
| } |
| assert(fp_frame->size() == template_info.size); |
| fwrite(fp_frame->data(), template_info.size, 1, stdout); |
| return 0; |
| } |
| /* not an index, is it a filename ? */ |
| buffer = read_file(argv[1], &size); |
| if (!buffer) { |
| fprintf(stderr, "Invalid parameter: %s\n", argv[1]); |
| return -1; |
| } |
| printf("sending template from: %s (%d bytes)\n", argv[1], size); |
| while (size) { |
| uint32_t tlen = MIN(max_chunk, size); |
| |
| p->offset = offset; |
| p->size = tlen; |
| size -= tlen; |
| if (!size) |
| p->size |= FP_TEMPLATE_COMMIT; |
| memcpy(p->data, buffer + offset, tlen); |
| rv = ec_command(EC_CMD_FP_TEMPLATE, 0, p, |
| tlen + offsetof(struct ec_params_fp_template, |
| data), |
| NULL, 0); |
| if (rv < 0) |
| break; |
| offset += tlen; |
| } |
| if (rv < 0) |
| fprintf(stderr, "Failed with %d\n", rv); |
| else |
| rv = 0; |
| free(buffer); |
| return rv; |
| } |
| |
| /** |
| * determine if in GFU mode or not. |
| * |
| * NOTE, Sends HOST commands that modify ec_outbuf contents. |
| * |
| * @opos return value of GFU mode object position or zero if not found |
| * @port port number to query |
| * @return 1 if in GFU mode, 0 if not, -1 if error |
| */ |
| static int in_gfu_mode(int *opos, int port) |
| { |
| int i; |
| struct ec_params_usb_pd_get_mode_request *p = |
| (struct ec_params_usb_pd_get_mode_request *)ec_outbuf; |
| struct ec_params_usb_pd_get_mode_response *r = |
| (struct ec_params_usb_pd_get_mode_response *)ec_inbuf; |
| p->port = port; |
| p->svid_idx = 0; |
| do { |
| ec_command(EC_CMD_USB_PD_GET_AMODE, 0, p, sizeof(*p), ec_inbuf, |
| ec_max_insize); |
| if (!r->svid || (r->svid == USB_VID_GOOGLE)) |
| break; |
| p->svid_idx++; |
| } while (p->svid_idx < SVID_DISCOVERY_MAX); |
| |
| if (r->svid != USB_VID_GOOGLE) { |
| fprintf(stderr, "Google VID not returned\n"); |
| return -1; |
| } |
| |
| *opos = 0; /* invalid ... must be 1 thru 6 */ |
| for (i = 0; i < VDO_MAX_OBJECTS; i++) { |
| if (r->vdo[i] == MODE_GOOGLE_FU) { |
| *opos = i + 1; |
| break; |
| } |
| } |
| |
| return r->opos == *opos; |
| } |
| |
| /** |
| * Enter GFU mode. |
| * |
| * NOTE, Sends HOST commands that modify ec_outbuf contents. |
| * |
| * @port port number to enter GFU on. |
| * @return 1 if entered GFU mode, 0 if not, -1 if error |
| */ |
| static int enter_gfu_mode(int port) |
| { |
| int opos; |
| struct ec_params_usb_pd_set_mode_request *p = |
| (struct ec_params_usb_pd_set_mode_request *)ec_outbuf; |
| int gfu_mode = in_gfu_mode(&opos, port); |
| |
| if (gfu_mode < 0) { |
| fprintf(stderr, "Failed to query GFU mode support\n"); |
| return 0; |
| } else if (!gfu_mode) { |
| if (!opos) { |
| fprintf(stderr, "Invalid object position %d\n", opos); |
| return 0; |
| } |
| p->port = port; |
| p->svid = USB_VID_GOOGLE; |
| p->opos = opos; |
| p->cmd = PD_ENTER_MODE; |
| |
| ec_command(EC_CMD_USB_PD_SET_AMODE, 0, p, sizeof(*p), NULL, 0); |
| usleep(500000); /* sleep to allow time for set mode */ |
| gfu_mode = in_gfu_mode(&opos, port); |
| } |
| return gfu_mode; |
| } |
| |
| int cmd_pd_device_info(int argc, char *argv[]) |
| { |
| int i, rv, port; |
| char *e; |
| struct ec_params_usb_pd_info_request *p = |
| (struct ec_params_usb_pd_info_request *)ec_outbuf; |
| struct ec_params_usb_pd_rw_hash_entry *r0 = |
| (struct ec_params_usb_pd_rw_hash_entry *)ec_inbuf; |
| struct ec_params_usb_pd_discovery_entry *r1; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s <port>\n", argv[0]); |
| return -1; |
| } |
| |
| port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port\n"); |
| return -1; |
| } |
| |
| p->port = port; |
| r1 = (struct ec_params_usb_pd_discovery_entry *)ec_inbuf; |
| rv = ec_command(EC_CMD_USB_PD_DISCOVERY, 0, p, sizeof(*p), r1, |
| sizeof(*r1)); |
| if (rv < 0) |
| return rv; |
| |
| if (!r1->vid) |
| printf("Port:%d has no discovered device\n", port); |
| else { |
| printf("Port:%d ptype:%d vid:0x%04x pid:0x%04x\n", port, |
| r1->ptype, r1->vid, r1->pid); |
| } |
| |
| if (enter_gfu_mode(port) != 1) { |
| fprintf(stderr, "Failed to enter GFU mode\n"); |
| return -1; |
| } |
| |
| p->port = port; |
| rv = ec_command(EC_CMD_USB_PD_DEV_INFO, 0, p, sizeof(*p), r0, |
| sizeof(*r0)); |
| if (rv < 0) |
| return rv; |
| |
| if (!r0->dev_id) |
| printf("Port:%d has no valid device\n", port); |
| else { |
| uint8_t *rwp = r0->dev_rw_hash; |
| printf("Port:%d DevId:%d.%d Hash:", port, |
| HW_DEV_ID_MAJ(r0->dev_id), HW_DEV_ID_MIN(r0->dev_id)); |
| for (i = 0; i < 5; i++) { |
| printf(" 0x%02x%02x%02x%02x", rwp[3], rwp[2], rwp[1], |
| rwp[0]); |
| rwp += 4; |
| } |
| printf(" CurImg:%s\n", image_names[r0->current_image]); |
| } |
| |
| return rv; |
| } |
| |
| int cmd_flash_pd(int argc, char *argv[]) |
| { |
| struct ec_params_usb_pd_fw_update *p = |
| (struct ec_params_usb_pd_fw_update *)ec_outbuf; |
| int i, dev_id, port; |
| int rv, fsize, step = 96; |
| char *e; |
| char *buf; |
| char *data = (char *)p + sizeof(*p); |
| |
| if (argc < 4) { |
| fprintf(stderr, "Usage: %s <dev_id> <port> <filename>\n", |
| argv[0]); |
| return -1; |
| } |
| |
| dev_id = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad device ID\n"); |
| return -1; |
| } |
| |
| port = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port\n"); |
| return -1; |
| } |
| |
| if (enter_gfu_mode(port) != 1) { |
| fprintf(stderr, "Failed to enter GFU mode\n"); |
| return -1; |
| } |
| |
| /* Read the input file */ |
| buf = read_file(argv[3], &fsize); |
| if (!buf) |
| return -1; |
| |
| /* Erase the current RW RSA signature */ |
| fprintf(stderr, "Erasing expected RW hash\n"); |
| p->dev_id = dev_id; |
| p->port = port; |
| p->cmd = USB_PD_FW_ERASE_SIG; |
| p->size = 0; |
| rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0, p, p->size + sizeof(*p), |
| NULL, 0); |
| |
| if (rv < 0) |
| goto pd_flash_error; |
| |
| /* Reboot */ |
| fprintf(stderr, "Rebooting\n"); |
| p->dev_id = dev_id; |
| p->port = port; |
| p->cmd = USB_PD_FW_REBOOT; |
| p->size = 0; |
| rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0, p, p->size + sizeof(*p), |
| NULL, 0); |
| |
| if (rv < 0) |
| goto pd_flash_error; |
| |
| usleep(3000000); /* 3sec to reboot and get CC line idle */ |
| |
| /* re-enter GFU after reboot */ |
| if (enter_gfu_mode(port) != 1) { |
| fprintf(stderr, "Failed to enter GFU mode\n"); |
| goto pd_flash_error; |
| } |
| |
| /* Erase RW flash */ |
| fprintf(stderr, "Erasing RW flash\n"); |
| p->dev_id = dev_id; |
| p->port = port; |
| p->cmd = USB_PD_FW_FLASH_ERASE; |
| p->size = 0; |
| rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0, p, p->size + sizeof(*p), |
| NULL, 0); |
| |
| /* 3 secs should allow ample time for 2KB page erases at 40ms */ |
| usleep(3000000); |
| |
| if (rv < 0) |
| goto pd_flash_error; |
| |
| /* Write RW flash */ |
| fprintf(stderr, "Writing RW flash\n"); |
| p->dev_id = dev_id; |
| p->port = port; |
| p->cmd = USB_PD_FW_FLASH_WRITE; |
| p->size = step; |
| |
| for (i = 0; i < fsize; i += step) { |
| p->size = MIN(fsize - i, step); |
| memcpy(data, buf + i, p->size); |
| rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0, p, |
| p->size + sizeof(*p), NULL, 0); |
| if (rv < 0) |
| goto pd_flash_error; |
| |
| /* |
| * TODO(crosbug.com/p/33905) throttle so EC doesn't watchdog on |
| * other tasks. Remove once issue resolved. |
| */ |
| usleep(10000); |
| } |
| |
| /* 100msec to guarantee writes finish */ |
| usleep(100000); |
| |
| /* Reboot into new RW */ |
| fprintf(stderr, "Rebooting PD into new RW\n"); |
| p->cmd = USB_PD_FW_REBOOT; |
| p->size = 0; |
| rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0, p, p->size + sizeof(*p), |
| NULL, 0); |
| |
| if (rv < 0) |
| goto pd_flash_error; |
| |
| free(buf); |
| fprintf(stderr, "Complete\n"); |
| return 0; |
| |
| pd_flash_error: |
| free(buf); |
| fprintf(stderr, "PD flash error\n"); |
| return -1; |
| } |
| |
| int cmd_pd_set_amode(int argc, char *argv[]) |
| { |
| char *e; |
| struct ec_params_usb_pd_set_mode_request *p = |
| (struct ec_params_usb_pd_set_mode_request *)ec_outbuf; |
| |
| if (argc < 5) { |
| fprintf(stderr, "Usage: %s <port> <svid> <opos> <cmd>\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p->port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port\n"); |
| return -1; |
| } |
| |
| p->svid = strtol(argv[2], &e, 0); |
| if ((e && *e) || !p->svid) { |
| fprintf(stderr, "Bad svid\n"); |
| return -1; |
| } |
| |
| p->opos = strtol(argv[3], &e, 0); |
| if ((e && *e) || !p->opos) { |
| fprintf(stderr, "Bad opos\n"); |
| return -1; |
| } |
| |
| p->cmd = strtol(argv[4], &e, 0); |
| if ((e && *e) || (p->cmd >= PD_MODE_CMD_COUNT)) { |
| fprintf(stderr, "Bad cmd\n"); |
| return -1; |
| } |
| return ec_command(EC_CMD_USB_PD_SET_AMODE, 0, p, sizeof(*p), NULL, 0); |
| } |
| |
| int cmd_pd_get_amode(int argc, char *argv[]) |
| { |
| int i; |
| char *e; |
| struct ec_params_usb_pd_get_mode_request *p = |
| (struct ec_params_usb_pd_get_mode_request *)ec_outbuf; |
| struct ec_params_usb_pd_get_mode_response *r = |
| (struct ec_params_usb_pd_get_mode_response *)ec_inbuf; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s <port>\n", argv[0]); |
| return -1; |
| } |
| |
| p->port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port\n"); |
| return -1; |
| } |
| |
| p->svid_idx = 0; |
| do { |
| ec_command(EC_CMD_USB_PD_GET_AMODE, 0, p, sizeof(*p), ec_inbuf, |
| ec_max_insize); |
| if (!r->svid) |
| break; |
| printf("%cSVID:0x%04x ", (r->opos) ? '*' : ' ', r->svid); |
| for (i = 0; i < VDO_MAX_OBJECTS; i++) { |
| printf("%c0x%08x ", |
| (r->opos && (r->opos == i + 1)) ? '*' : ' ', |
| r->vdo[i]); |
| } |
| printf("\n"); |
| p->svid_idx++; |
| } while (p->svid_idx < SVID_DISCOVERY_MAX); |
| return -1; |
| } |
| |
| /* The I/O asm funcs exist only on x86. */ |
| #if defined(__i386__) || defined(__x86_64__) |
| #include <sys/io.h> |
| |
| int cmd_serial_test(int argc, char *argv[]) |
| { |
| const char *c = "COM2 sample serial output from host!\r\n"; |
| |
| printf("Writing sample serial output to COM2\n"); |
| |
| while (*c) { |
| /* Wait for space in transmit FIFO */ |
| while (!(inb(0x2fd) & 0x20)) |
| ; |
| |
| /* Put the next character */ |
| outb(*c++, 0x2f8); |
| } |
| |
| printf("done.\n"); |
| return 0; |
| } |
| |
| int cmd_port_80_flood(int argc, char *argv[]) |
| { |
| int i; |
| |
| for (i = 0; i < 256; i++) |
| outb(i, 0x80); |
| return 0; |
| } |
| #else |
| int cmd_serial_test(int argc, char *argv[]) |
| { |
| printf("x86 specific command\n"); |
| return -1; |
| } |
| |
| int cmd_port_80_flood(int argc, char *argv[]) |
| { |
| printf("x86 specific command\n"); |
| return -1; |
| } |
| #endif |
| |
| static void cmd_smart_discharge_usage(const char *command) |
| { |
| printf("Usage: %s [hours_to_zero [hibern] [cutoff]]\n", command); |
| printf("\n"); |
| printf("Set/Get smart discharge parameters\n"); |
| printf("hours_to_zero: Desired hours for state of charge to zero\n"); |
| printf("hibern: Discharge rate in hibernation (uA)\n"); |
| printf("cutoff: Discharge rate in battery cutoff (uA)\n"); |
| } |
| |
| int cmd_smart_discharge(int argc, char *argv[]) |
| { |
| struct ec_params_smart_discharge *p = |
| (struct ec_params_smart_discharge *)(ec_outbuf); |
| struct ec_response_smart_discharge *r = |
| (struct ec_response_smart_discharge *)(ec_inbuf); |
| uint32_t cap; |
| char *e; |
| int rv; |
| |
| if (argc > 1) { |
| if (strcmp(argv[1], "help") == 0) { |
| cmd_smart_discharge_usage(argv[0]); |
| return 0; |
| } |
| p->flags = EC_SMART_DISCHARGE_FLAGS_SET; |
| p->hours_to_zero = strtol(argv[1], &e, 0); |
| if (p->hours_to_zero < 0 || (e && *e)) { |
| perror("Bad value for [hours_to_zero]"); |
| return -1; |
| } |
| if (argc == 4) { |
| p->drate.hibern = strtol(argv[2], &e, 0); |
| if (p->drate.hibern < 0 || (e && *e)) { |
| perror("Bad value for [hibern]"); |
| return -1; |
| } |
| p->drate.cutoff = strtol(argv[3], &e, 0); |
| if (p->drate.cutoff < 0 || (e && *e)) { |
| perror("Bad value for [cutoff]"); |
| return -1; |
| } |
| } else if (argc != 2) { |
| /* If argc != 4, it has to be 2. */ |
| perror("Invalid number of parameters"); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_SMART_DISCHARGE, 0, p, sizeof(*p), r, |
| sizeof(*r)); |
| if (rv < 0) { |
| perror("ERROR: EC_CMD_SMART_DISCHARGE failed"); |
| return rv; |
| } |
| |
| cap = read_mapped_mem32(EC_MEMMAP_BATT_LFCC); |
| if (!is_battery_range(cap)) { |
| perror("WARN: Failed to read battery capacity"); |
| cap = 0; |
| } |
| |
| printf("%-27s %5d h\n", "Hours to zero capacity:", r->hours_to_zero); |
| printf("%-27s %5d mAh (%d %%)\n", "Stay-up threshold:", r->dzone.stayup, |
| cap > 0 ? r->dzone.stayup * 100 / cap : -1); |
| printf("%-27s %5d mAh (%d %%)\n", "Cutoff threshold:", r->dzone.cutoff, |
| cap > 0 ? r->dzone.cutoff * 100 / cap : -1); |
| printf("%-27s %5d uA\n", "Hibernate discharge rate:", r->drate.hibern); |
| printf("%-27s %5d uA\n", "Cutoff discharge rate:", r->drate.cutoff); |
| |
| return 0; |
| } |
| |
| /* |
| * This boolean variable and handler are used for |
| * catching signals that translate into a quit/shutdown |
| * of a runtime loop. |
| * This is used in cmd_stress_test. |
| */ |
| static bool sig_quit; |
| static void sig_quit_handler(int sig) |
| { |
| sig_quit = true; |
| } |
| |
| int cmd_stress_test(int argc, char *argv[]) |
| { |
| int i; |
| bool reboot = false; |
| time_t now; |
| time_t start_time, last_update_time; |
| unsigned int rand_seed = 0; |
| uint64_t round = 1, attempt = 1; |
| uint64_t failures = 0; |
| |
| const int max_sleep_usec = 1000; /* 1ms */ |
| const int loop_update_interval = 10000; |
| |
| for (i = 1; i < argc; i++) { |
| if (strcmp(argv[i], "help") == 0) { |
| printf("Usage: %s [reboot] [help]\n", argv[0]); |
| printf("Stress tests the host command interface by" |
| " repeatedly issuing common host commands.\n"); |
| printf("The intent is to expose errors in kernel<->mcu" |
| " communication, such as exceeding timeouts.\n"); |
| printf("\n"); |
| printf("reboot - Reboots the target before" |
| " starting the stress test.\n"); |
| printf(" This may force restart the host," |
| " if the main ec is the target.\n"); |
| return 0; |
| } else if (strcmp(argv[i], "reboot") == 0) { |
| reboot = true; |
| } else { |
| fprintf(stderr, "Error - Unknown argument '%s'\n", |
| argv[i]); |
| return 1; |
| } |
| } |
| |
| printf("Stress test tool version: %s %s %s\n", CROS_ECTOOL_VERSION, |
| DATE, BUILDER); |
| |
| start_time = time(NULL); |
| last_update_time = start_time; |
| printf("Start time: %s\n", ctime(&start_time)); |
| |
| if (reboot) { |
| printf("Issuing ec reboot. Expect a few early failed" |
| " ioctl messages.\n"); |
| sync(); |
| ec_command(EC_CMD_REBOOT, 0, NULL, 0, NULL, 0); |
| sleep(2); |
| } |
| |
| sig_quit = false; |
| signal(SIGINT, sig_quit_handler); |
| while (!sig_quit) { |
| int rv; |
| struct ec_response_get_version ver_r; |
| char *build_string = (char *)ec_inbuf; |
| struct ec_params_flash_protect flash_p; |
| struct ec_response_flash_protect flash_r; |
| struct ec_params_hello hello_p; |
| struct ec_response_hello hello_r; |
| |
| /* Request EC Version Strings */ |
| rv = ec_command(EC_CMD_GET_VERSION, 0, NULL, 0, &ver_r, |
| sizeof(ver_r)); |
| if (rv < 0) { |
| failures++; |
| perror("ERROR: EC_CMD_GET_VERSION failed"); |
| } |
| ver_r.version_string_ro[sizeof(ver_r.version_string_ro) - 1] = |
| '\0'; |
| ver_r.version_string_rw[sizeof(ver_r.version_string_rw) - 1] = |
| '\0'; |
| if (strlen(ver_r.version_string_ro) == 0) { |
| failures++; |
| fprintf(stderr, "RO version string is empty\n"); |
| } |
| if (strlen(ver_r.version_string_rw) == 0) { |
| failures++; |
| fprintf(stderr, "RW version string is empty\n"); |
| } |
| |
| usleep(rand_r(&rand_seed) % max_sleep_usec); |
| |
| /* Request EC Build String */ |
| rv = ec_command(EC_CMD_GET_BUILD_INFO, 0, NULL, 0, ec_inbuf, |
| ec_max_insize); |
| if (rv < 0) { |
| failures++; |
| perror("ERROR: EC_CMD_GET_BUILD_INFO failed"); |
| } |
| build_string[ec_max_insize - 1] = '\0'; |
| if (strlen(build_string) == 0) { |
| failures++; |
| fprintf(stderr, "Build string is empty\n"); |
| } |
| |
| usleep(rand_r(&rand_seed) % max_sleep_usec); |
| |
| /* Request Flash Protect Status */ |
| rv = ec_command(EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT, |
| &flash_p, sizeof(flash_p), &flash_r, |
| sizeof(flash_r)); |
| if (rv < 0) { |
| failures++; |
| perror("ERROR: EC_CMD_FLASH_PROTECT failed"); |
| } |
| |
| usleep(rand_r(&rand_seed) % max_sleep_usec); |
| |
| /* Request Hello */ |
| hello_p.in_data = 0xa0b0c0d0; |
| rv = ec_command(EC_CMD_HELLO, 0, &hello_p, sizeof(hello_p), |
| &hello_r, sizeof(hello_r)); |
| if (rv < 0) { |
| failures++; |
| perror("ERROR: EC_CMD_HELLO failed"); |
| } |
| if (hello_r.out_data != HELLO_RESP(hello_p.in_data)) { |
| failures++; |
| fprintf(stderr, "Hello response was invalid.\n"); |
| } |
| |
| usleep(rand_r(&rand_seed) % max_sleep_usec); |
| |
| if ((attempt % loop_update_interval) == 0) { |
| now = time(NULL); |
| printf("Update: attempt %" PRIu64 " round %" PRIu64 |
| " | took %.f seconds\n", |
| attempt, round, difftime(now, last_update_time)); |
| last_update_time = now; |
| } |
| |
| if (attempt++ == UINT64_MAX) |
| round++; |
| } |
| printf("\n"); |
| |
| now = time(NULL); |
| printf("End time: %s\n", ctime(&now)); |
| printf("Total runtime: %.f seconds\n", |
| difftime(time(NULL), start_time)); |
| printf("Total failures: %" PRIu64 "\n", failures); |
| return 0; |
| } |
| |
| int read_mapped_temperature(int id) |
| { |
| int rv; |
| |
| if (!read_mapped_mem8(EC_MEMMAP_THERMAL_VERSION)) { |
| /* |
| * The temp_sensor_init() is not called, which implies no |
| * temp sensor is defined. |
| */ |
| rv = EC_TEMP_SENSOR_NOT_PRESENT; |
| } else if (id < EC_TEMP_SENSOR_ENTRIES) |
| rv = read_mapped_mem8(EC_MEMMAP_TEMP_SENSOR + id); |
| else if (read_mapped_mem8(EC_MEMMAP_THERMAL_VERSION) >= 2) |
| rv = read_mapped_mem8(EC_MEMMAP_TEMP_SENSOR_B + id - |
| EC_TEMP_SENSOR_ENTRIES); |
| else { |
| /* Sensor in second bank, but second bank isn't supported */ |
| rv = EC_TEMP_SENSOR_NOT_PRESENT; |
| } |
| return rv; |
| } |
| |
| static int get_temp_ratio(int temp, int fan_off, int fan_max) |
| { |
| if (temp < fan_off) |
| return 0; |
| if (temp > fan_max) |
| return 100; |
| return 100 * (temp - fan_off) / (fan_max - fan_off); |
| } |
| |
| static int cmd_temperature_print(int id, int mtemp) |
| { |
| struct ec_response_temp_sensor_get_info temp_r; |
| struct ec_params_temp_sensor_get_info temp_p; |
| struct ec_params_thermal_get_threshold_v1 p; |
| struct ec_thermal_config r; |
| int rc; |
| int temp = mtemp + EC_TEMP_SENSOR_OFFSET; |
| |
| temp_p.id = id; |
| rc = ec_command(EC_CMD_TEMP_SENSOR_GET_INFO, 0, &temp_p, sizeof(temp_p), |
| &temp_r, sizeof(temp_r)); |
| if (rc < 0) |
| return rc; |
| |
| p.sensor_num = id; |
| rc = ec_command(EC_CMD_THERMAL_GET_THRESHOLD, 1, &p, sizeof(p), &r, |
| sizeof(r)); |
| |
| printf("%-20s %d K (= %d C)", temp_r.sensor_name, temp, K_TO_C(temp)); |
| |
| if (rc >= 0) |
| /* |
| * Check for fan_off == fan_max when their |
| * values are either zero or non-zero |
| */ |
| if (r.temp_fan_off == r.temp_fan_max) |
| printf(" N/A (fan_off=%d K, fan_max=%d K)", |
| r.temp_fan_off, r.temp_fan_max); |
| else |
| printf(" %10d%% (%d K and %d K)", |
| get_temp_ratio(temp, r.temp_fan_off, |
| r.temp_fan_max), |
| r.temp_fan_off, r.temp_fan_max); |
| else |
| printf("%20s(rc=%d)", "error", rc); |
| |
| return 0; |
| } |
| |
| int cmd_temperature(int argc, char *argv[]) |
| { |
| int mtemp; |
| int id; |
| char *e; |
| const char header[] = "--sensor name -------- temperature " |
| "-------- ratio (fan_off and fan_max) --\n"; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <sensorid> | all\n", argv[0]); |
| return -1; |
| } |
| |
| if (strcmp(argv[1], "all") == 0) { |
| fprintf(stdout, header); |
| for (id = 0; id < EC_MAX_TEMP_SENSOR_ENTRIES; id++) { |
| mtemp = read_mapped_temperature(id); |
| switch (mtemp) { |
| case EC_TEMP_SENSOR_NOT_PRESENT: |
| break; |
| case EC_TEMP_SENSOR_ERROR: |
| fprintf(stderr, "Sensor %d error\n", id); |
| break; |
| case EC_TEMP_SENSOR_NOT_POWERED: |
| fprintf(stderr, "Sensor %d disabled\n", id); |
| break; |
| case EC_TEMP_SENSOR_NOT_CALIBRATED: |
| fprintf(stderr, "Sensor %d not calibrated\n", |
| id); |
| break; |
| default: |
| cmd_temperature_print(id, mtemp); |
| printf("\n"); |
| } |
| } |
| return 0; |
| } |
| |
| id = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad sensor ID.\n"); |
| return -1; |
| } |
| |
| if (id < 0 || id >= EC_MAX_TEMP_SENSOR_ENTRIES) { |
| printf("Sensor ID invalid.\n"); |
| return -1; |
| } |
| |
| printf("Reading temperature..."); |
| mtemp = read_mapped_temperature(id); |
| |
| switch (mtemp) { |
| case EC_TEMP_SENSOR_NOT_PRESENT: |
| printf("Sensor not present\n"); |
| return -1; |
| case EC_TEMP_SENSOR_ERROR: |
| printf("Error\n"); |
| return -1; |
| case EC_TEMP_SENSOR_NOT_POWERED: |
| printf("Sensor disabled/unpowered\n"); |
| return -1; |
| case EC_TEMP_SENSOR_NOT_CALIBRATED: |
| fprintf(stderr, "Sensor not calibrated\n"); |
| return -1; |
| default: |
| fprintf(stdout, "\n"); |
| fprintf(stdout, header); |
| return cmd_temperature_print(id, mtemp); |
| } |
| } |
| |
| int cmd_temp_sensor_info(int argc, char *argv[]) |
| { |
| struct ec_params_temp_sensor_get_info p; |
| struct ec_response_temp_sensor_get_info r; |
| int rv; |
| char *e; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <sensorid> | all\n", argv[0]); |
| return -1; |
| } |
| |
| if (strcmp(argv[1], "all") == 0) { |
| for (p.id = 0; p.id < EC_MAX_TEMP_SENSOR_ENTRIES; p.id++) { |
| if (read_mapped_temperature(p.id) == |
| EC_TEMP_SENSOR_NOT_PRESENT) |
| continue; |
| rv = ec_command(EC_CMD_TEMP_SENSOR_GET_INFO, 0, &p, |
| sizeof(p), &r, sizeof(r)); |
| if (rv < 0) |
| continue; |
| printf("%d: %d %s\n", p.id, r.sensor_type, |
| r.sensor_name); |
| } |
| return 0; |
| } |
| |
| p.id = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad sensor ID.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_TEMP_SENSOR_GET_INFO, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Sensor name: %s\n", r.sensor_name); |
| printf("Sensor type: %d\n", r.sensor_type); |
| |
| return 0; |
| } |
| |
| int cmd_thermal_get_threshold_v0(int argc, char *argv[]) |
| { |
| struct ec_params_thermal_get_threshold p; |
| struct ec_response_thermal_get_threshold r; |
| char *e; |
| int rv; |
| |
| if (argc != 3) { |
| fprintf(stderr, "Usage: %s <sensortypeid> <thresholdid>\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p.sensor_type = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad sensor type ID.\n"); |
| return -1; |
| } |
| |
| p.threshold_id = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad threshold ID.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_THERMAL_GET_THRESHOLD, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Threshold %d for sensor type %d is %d K.\n", p.threshold_id, |
| p.sensor_type, r.value); |
| |
| return 0; |
| } |
| |
| int cmd_thermal_set_threshold_v0(int argc, char *argv[]) |
| { |
| struct ec_params_thermal_set_threshold p; |
| char *e; |
| int rv; |
| |
| if (argc != 4) { |
| fprintf(stderr, |
| "Usage: %s <sensortypeid> <thresholdid> <value>\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p.sensor_type = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad sensor type ID.\n"); |
| return -1; |
| } |
| |
| p.threshold_id = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad threshold ID.\n"); |
| return -1; |
| } |
| |
| p.value = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad threshold value.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_THERMAL_SET_THRESHOLD, 0, &p, sizeof(p), NULL, |
| 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Threshold %d for sensor type %d set to %d.\n", p.threshold_id, |
| p.sensor_type, p.value); |
| |
| return 0; |
| } |
| |
| int cmd_thermal_get_threshold_v1(int argc, char *argv[]) |
| { |
| struct ec_params_thermal_get_threshold_v1 p; |
| struct ec_thermal_config r; |
| struct ec_params_temp_sensor_get_info pi; |
| struct ec_response_temp_sensor_get_info ri; |
| int rv; |
| int i; |
| |
| printf("sensor warn high halt fan_off fan_max name\n"); |
| for (i = 0; i < EC_MAX_TEMP_SENSOR_ENTRIES; i++) { |
| if (read_mapped_temperature(i) == EC_TEMP_SENSOR_NOT_PRESENT) |
| continue; |
| |
| /* ask for one */ |
| p.sensor_num = i; |
| rv = ec_command(EC_CMD_THERMAL_GET_THRESHOLD, 1, &p, sizeof(p), |
| &r, sizeof(r)); |
| if (rv <= 0) /* stop on first failure */ |
| break; |
| |
| /* ask for its name, too */ |
| pi.id = i; |
| rv = ec_command(EC_CMD_TEMP_SENSOR_GET_INFO, 0, &pi, sizeof(pi), |
| &ri, sizeof(ri)); |
| |
| /* print what we know */ |
| printf(" %2d %3d %3d %3d %3d %3d %s\n", i, |
| r.temp_host[EC_TEMP_THRESH_WARN], |
| r.temp_host[EC_TEMP_THRESH_HIGH], |
| r.temp_host[EC_TEMP_THRESH_HALT], r.temp_fan_off, |
| r.temp_fan_max, rv > 0 ? ri.sensor_name : "?"); |
| } |
| if (i) |
| printf("(all temps in degrees Kelvin)\n"); |
| |
| return 0; |
| } |
| |
| int cmd_thermal_set_threshold_v1(int argc, char *argv[]) |
| { |
| struct ec_params_thermal_get_threshold_v1 p; |
| struct ec_thermal_config r; |
| struct ec_params_thermal_set_threshold_v1 s; |
| int i, n, val, rv; |
| char *e; |
| |
| if (argc < 3 || argc > 7) { |
| printf("Usage: %s" |
| " sensor warn [high [shutdown [fan_off [fan_max]]]]\n", |
| argv[0]); |
| return 1; |
| } |
| |
| n = strtod(argv[1], &e); |
| if (e && *e) { |
| printf("arg %d is invalid\n", 1); |
| return 1; |
| } |
| |
| p.sensor_num = n; |
| rv = ec_command(EC_CMD_THERMAL_GET_THRESHOLD, 1, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv <= 0) |
| return rv; |
| |
| s.sensor_num = n; |
| s.cfg = r; |
| |
| for (i = 2; i < argc; i++) { |
| val = strtod(argv[i], &e); |
| if (e && *e) { |
| printf("arg %d is invalid\n", i); |
| return 1; |
| } |
| |
| if (val < 0) |
| continue; |
| switch (i) { |
| case 2: |
| case 3: |
| case 4: |
| s.cfg.temp_host[i - 2] = val; |
| break; |
| case 5: |
| s.cfg.temp_fan_off = val; |
| break; |
| case 6: |
| s.cfg.temp_fan_max = val; |
| break; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_THERMAL_SET_THRESHOLD, 1, &s, sizeof(s), NULL, |
| 0); |
| |
| return rv; |
| } |
| |
| int cmd_thermal_get_threshold(int argc, char *argv[]) |
| { |
| if (ec_cmd_version_supported(EC_CMD_THERMAL_GET_THRESHOLD, 1)) |
| return cmd_thermal_get_threshold_v1(argc, argv); |
| else if (ec_cmd_version_supported(EC_CMD_THERMAL_GET_THRESHOLD, 0)) |
| return cmd_thermal_get_threshold_v0(argc, argv); |
| |
| printf("I got nuthin.\n"); |
| return -1; |
| } |
| |
| int cmd_thermal_set_threshold(int argc, char *argv[]) |
| { |
| if (ec_cmd_version_supported(EC_CMD_THERMAL_SET_THRESHOLD, 1)) |
| return cmd_thermal_set_threshold_v1(argc, argv); |
| else if (ec_cmd_version_supported(EC_CMD_THERMAL_SET_THRESHOLD, 0)) |
| return cmd_thermal_set_threshold_v0(argc, argv); |
| |
| printf("I got nuthin.\n"); |
| return -1; |
| } |
| |
| static int get_num_fans(void) |
| { |
| int idx, rv; |
| struct ec_response_get_features r; |
| |
| /* |
| * iff the EC supports the GET_FEATURES, |
| * check whether it has fan support enabled. |
| */ |
| rv = ec_command(EC_CMD_GET_FEATURES, 0, NULL, 0, &r, sizeof(r)); |
| if (rv >= 0 && !(r.flags[0] & BIT(EC_FEATURE_PWM_FAN))) |
| return 0; |
| |
| for (idx = 0; idx < EC_FAN_SPEED_ENTRIES; idx++) { |
| rv = read_mapped_mem16(EC_MEMMAP_FAN + 2 * idx); |
| if (rv == EC_FAN_SPEED_NOT_PRESENT) |
| break; |
| } |
| |
| return idx; |
| } |
| |
| int cmd_thermal_auto_fan_ctrl(int argc, char *argv[]) |
| { |
| int rv, num_fans; |
| struct ec_params_auto_fan_ctrl_v1 p_v1; |
| char *e; |
| int cmdver = 1; |
| |
| if (!ec_cmd_version_supported(EC_CMD_THERMAL_AUTO_FAN_CTRL, cmdver) || |
| (argc == 1)) { |
| /* If no argument is provided then enable auto fan ctrl */ |
| /* for all fans by using version 0 of the host command */ |
| |
| rv = ec_command(EC_CMD_THERMAL_AUTO_FAN_CTRL, 0, NULL, 0, NULL, |
| 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Automatic fan control is now on for all fans.\n"); |
| return 0; |
| } |
| |
| if (argc > 2 || !strcmp(argv[1], "help")) { |
| printf("Usage: %s [idx]\n", argv[0]); |
| return -1; |
| } |
| |
| num_fans = get_num_fans(); |
| p_v1.fan_idx = strtol(argv[1], &e, 0); |
| if ((e && *e) || (p_v1.fan_idx >= num_fans)) { |
| fprintf(stderr, "Bad fan index.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_THERMAL_AUTO_FAN_CTRL, cmdver, &p_v1, |
| sizeof(p_v1), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Automatic fan control is now on for fan %d\n", p_v1.fan_idx); |
| |
| return 0; |
| } |
| |
| static int print_fan(int idx) |
| { |
| int rv = read_mapped_mem16(EC_MEMMAP_FAN + 2 * idx); |
| |
| switch (rv) { |
| case EC_FAN_SPEED_NOT_PRESENT: |
| return -1; |
| case EC_FAN_SPEED_STALLED_DEPRECATED: |
| printf("Fan %d stalled (RPM: %d)\n", idx, rv); |
| break; |
| default: |
| printf("Fan %d RPM: %d\n", idx, rv); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| int cmd_pwm_get_num_fans(int argc, char *argv[]) |
| { |
| int num_fans; |
| |
| num_fans = get_num_fans(); |
| |
| printf("Number of fans = %d\n", num_fans); |
| |
| return 0; |
| } |
| |
| int cmd_pwm_get_fan_rpm(int argc, char *argv[]) |
| { |
| int i, num_fans; |
| |
| num_fans = get_num_fans(); |
| if (argc < 2 || !strcasecmp(argv[1], "all")) { |
| /* Print all the fan speeds */ |
| for (i = 0; i < num_fans; i++) |
| print_fan(i); |
| } else { |
| char *e; |
| int idx; |
| |
| idx = strtol(argv[1], &e, 0); |
| if ((e && *e) || idx < 0 || idx >= num_fans) { |
| fprintf(stderr, "Bad index.\n"); |
| return -1; |
| } |
| |
| print_fan(idx); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_pwm_set_fan_rpm(int argc, char *argv[]) |
| { |
| struct ec_params_pwm_set_fan_target_rpm_v1 p_v1; |
| char *e; |
| int rv, num_fans; |
| int cmdver = 1; |
| |
| if (!ec_cmd_version_supported(EC_CMD_PWM_SET_FAN_TARGET_RPM, cmdver)) { |
| struct ec_params_pwm_set_fan_target_rpm_v0 p_v0; |
| |
| /* Fall back to command version 0 command */ |
| cmdver = 0; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <targetrpm>\n", argv[0]); |
| return -1; |
| } |
| p_v0.rpm = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad RPM.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_PWM_SET_FAN_TARGET_RPM, cmdver, &p_v0, |
| sizeof(p_v0), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Fan target RPM set for all fans.\n"); |
| return 0; |
| } |
| |
| if (argc > 3 || (argc == 2 && !strcmp(argv[1], "help")) || argc == 1) { |
| printf("Usage: %s [idx] <targetrpm>\n", argv[0]); |
| printf("'%s 0 3000' - Set fan 0 RPM to 3000\n", argv[0]); |
| printf("'%s 3000' - Set all fans RPM to 3000\n", argv[0]); |
| return -1; |
| } |
| |
| num_fans = get_num_fans(); |
| p_v1.rpm = strtol(argv[argc - 1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad RPM.\n"); |
| return -1; |
| } |
| |
| if (argc == 2) { |
| /* Reuse version 0 command if we're setting targetrpm |
| * for all fans */ |
| struct ec_params_pwm_set_fan_target_rpm_v0 p_v0; |
| |
| cmdver = 0; |
| p_v0.rpm = p_v1.rpm; |
| |
| rv = ec_command(EC_CMD_PWM_SET_FAN_TARGET_RPM, cmdver, &p_v0, |
| sizeof(p_v0), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Fan target RPM set for all fans.\n"); |
| } else { |
| p_v1.fan_idx = strtol(argv[1], &e, 0); |
| if ((e && *e) || (p_v1.fan_idx >= num_fans)) { |
| fprintf(stderr, "Bad fan index.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_PWM_SET_FAN_TARGET_RPM, cmdver, &p_v1, |
| sizeof(p_v1), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Fan %d target RPM set.\n", p_v1.fan_idx); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_pwm_get_keyboard_backlight(int argc, char *argv[]) |
| { |
| struct ec_response_pwm_get_keyboard_backlight r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT, 0, NULL, 0, &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| if (r.enabled == 1) |
| printf("Current keyboard backlight percent: %d\n", r.percent); |
| else |
| printf("Keyboard backlight disabled.\n"); |
| |
| return 0; |
| } |
| |
| int cmd_pwm_set_keyboard_backlight(int argc, char *argv[]) |
| { |
| struct ec_params_pwm_set_keyboard_backlight p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <percent>\n", argv[0]); |
| return -1; |
| } |
| p.percent = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad percent.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT, 0, &p, sizeof(p), |
| NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Keyboard backlight set.\n"); |
| return 0; |
| } |
| |
| int cmd_pwm_get_duty(int argc, char *argv[]) |
| { |
| struct ec_params_pwm_get_duty p; |
| struct ec_response_pwm_get_duty r; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <pwm_idx> | kb | disp\n", argv[0]); |
| return -1; |
| } |
| |
| if (!strcmp(argv[1], "kb")) { |
| p.pwm_type = EC_PWM_TYPE_KB_LIGHT; |
| p.index = 0; |
| } else if (!strcmp(argv[1], "disp")) { |
| p.pwm_type = EC_PWM_TYPE_DISPLAY_LIGHT; |
| p.index = 0; |
| } else { |
| p.pwm_type = EC_PWM_TYPE_GENERIC; |
| p.index = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad pwm_idx\n"); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_PWM_GET_DUTY, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Current PWM duty: %d\n", r.duty); |
| return 0; |
| } |
| |
| int cmd_pwm_set_duty(int argc, char *argv[]) |
| { |
| struct ec_params_pwm_set_duty p; |
| char *e; |
| int rv; |
| |
| if (argc != 3) { |
| fprintf(stderr, "Usage: %s <pwm_idx> | kb | disp <duty>\n", |
| argv[0]); |
| return -1; |
| } |
| |
| if (!strcmp(argv[1], "kb")) { |
| p.pwm_type = EC_PWM_TYPE_KB_LIGHT; |
| p.index = 0; |
| } else if (!strcmp(argv[1], "disp")) { |
| p.pwm_type = EC_PWM_TYPE_DISPLAY_LIGHT; |
| p.index = 0; |
| } else { |
| p.pwm_type = EC_PWM_TYPE_GENERIC; |
| p.index = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad pwm_idx\n"); |
| return -1; |
| } |
| } |
| |
| p.duty = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad duty.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_PWM_SET_DUTY, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("PWM set.\n"); |
| return 0; |
| } |
| |
| int cmd_fanduty(int argc, char *argv[]) |
| { |
| struct ec_params_pwm_set_fan_duty_v1 p_v1; |
| char *e; |
| int rv, num_fans; |
| int cmdver = 1; |
| |
| if (!ec_cmd_version_supported(EC_CMD_PWM_SET_FAN_DUTY, cmdver)) { |
| struct ec_params_pwm_set_fan_duty_v0 p_v0; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <percent>\n", argv[0]); |
| return -1; |
| } |
| p_v0.percent = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad percent arg.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_PWM_SET_FAN_DUTY, 0, &p_v0, sizeof(p_v0), |
| NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Fan duty cycle set.\n"); |
| return 0; |
| } |
| |
| if (argc > 3 || (argc == 2 && !strcmp(argv[1], "help")) || argc == 1) { |
| printf("Usage: %s [idx] <percent>\n", argv[0]); |
| printf("'%s 0 50' - Set fan 0 duty cycle to 50 percent\n", |
| argv[0]); |
| printf("'%s 30' - Set all fans duty cycle to 30 percent\n", |
| argv[0]); |
| return -1; |
| } |
| |
| num_fans = get_num_fans(); |
| p_v1.percent = strtol(argv[argc - 1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad percent arg.\n"); |
| return -1; |
| } |
| |
| if (argc == 2) { |
| /* Reuse version 0 command if we're setting duty cycle |
| * for all fans */ |
| struct ec_params_pwm_set_fan_duty_v0 p_v0; |
| |
| cmdver = 0; |
| p_v0.percent = p_v1.percent; |
| |
| rv = ec_command(EC_CMD_PWM_SET_FAN_DUTY, cmdver, &p_v0, |
| sizeof(p_v0), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Fan duty cycle set for all fans.\n"); |
| } else { |
| p_v1.fan_idx = strtol(argv[1], &e, 0); |
| if ((e && *e) || (p_v1.fan_idx >= num_fans)) { |
| fprintf(stderr, "Bad fan index.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_PWM_SET_FAN_DUTY, cmdver, &p_v1, |
| sizeof(p_v1), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Fan %d duty cycle set.\n", p_v1.fan_idx); |
| } |
| |
| return 0; |
| } |
| |
| #define LBMSG(state) #state |
| #include "lightbar_msg_list.h" |
| static const char *const lightbar_cmds[] = { LIGHTBAR_MSG_LIST }; |
| #undef LBMSG |
| |
| /* Size of field <FLD> in structure <ST> */ |
| #define ST_FLD_SIZE(ST, FLD) sizeof(((struct ST *)0)->FLD) |
| |
| #define ST_CMD_SIZE ST_FLD_SIZE(ec_params_lightbar, cmd) |
| #define ST_PRM_SIZE(SUBCMD) \ |
| (ST_CMD_SIZE + ST_FLD_SIZE(ec_params_lightbar, SUBCMD)) |
| #define ST_RSP_SIZE(SUBCMD) ST_FLD_SIZE(ec_response_lightbar, SUBCMD) |
| |
| static const struct { |
| uint8_t insize; |
| uint8_t outsize; |
| } lb_command_paramcount[] = { |
| { ST_CMD_SIZE, ST_RSP_SIZE(dump) }, |
| { ST_CMD_SIZE, 0 }, |
| { ST_CMD_SIZE, 0 }, |
| { ST_CMD_SIZE, 0 }, |
| { ST_PRM_SIZE(set_brightness), 0 }, |
| { ST_PRM_SIZE(seq), 0 }, |
| { ST_PRM_SIZE(reg), 0 }, |
| { ST_PRM_SIZE(set_rgb), 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_seq) }, |
| { ST_PRM_SIZE(demo), 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_params_v0) }, |
| { ST_PRM_SIZE(set_params_v0), 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(version) }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_brightness) }, |
| { ST_PRM_SIZE(get_rgb), ST_RSP_SIZE(get_rgb) }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_demo) }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_params_v1) }, |
| { ST_PRM_SIZE(set_params_v1), 0 }, |
| { ST_PRM_SIZE(set_program), 0 }, |
| { ST_PRM_SIZE(manual_suspend_ctrl), 0 }, |
| { ST_CMD_SIZE, 0 }, |
| { ST_CMD_SIZE, 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_params_v2_timing) }, |
| { ST_PRM_SIZE(set_v2par_timing), 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_params_v2_tap) }, |
| { ST_PRM_SIZE(set_v2par_tap), 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_params_v2_osc) }, |
| { ST_PRM_SIZE(set_v2par_osc), 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_params_v2_bright) }, |
| { ST_PRM_SIZE(set_v2par_bright), 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_params_v2_thlds) }, |
| { ST_PRM_SIZE(set_v2par_thlds), 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_params_v2_colors) }, |
| { ST_PRM_SIZE(set_v2par_colors), 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_params_v3) }, |
| { ST_PRM_SIZE(set_program_ex), 0 }, |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(lb_command_paramcount) == LIGHTBAR_NUM_CMDS); |
| |
| #undef ST_CMD_SIZE |
| #undef ST_PRM_SIZE |
| #undef ST_RSP_SIZE |
| |
| static int lb_help(const char *cmd) |
| { |
| printf("Usage:\n"); |
| printf(" %s - dump all regs\n", cmd); |
| printf(" %s off - enter standby\n", cmd); |
| printf(" %s on - leave standby\n", cmd); |
| printf(" %s init - load default vals\n", cmd); |
| printf(" %s brightness [NUM] - get/set intensity(0-ff)\n", cmd); |
| printf(" %s seq [NUM|SEQUENCE] - run given pattern" |
| " (no arg for list)\n", |
| cmd); |
| printf(" %s CTRL REG VAL - set LED controller regs\n", cmd); |
| printf(" %s LED RED GREEN BLUE - set color manually" |
| " (LED=4 for all)\n", |
| cmd); |
| printf(" %s LED - get current LED color\n", cmd); |
| printf(" %s demo [0|1] - turn demo mode on & off\n", cmd); |
| printf(" %s params [setfile] - get params" |
| " (or set from file)\n", |
| cmd); |
| printf(" %s params2 group [setfile] - get params by group\n" |
| " (or set from file)\n", |
| cmd); |
| printf(" %s program file - load program from file\n", cmd); |
| return 0; |
| } |
| |
| static uint8_t lb_find_msg_by_name(const char *str) |
| { |
| uint8_t i; |
| for (i = 0; i < LIGHTBAR_NUM_SEQUENCES; i++) |
| if (!strcasecmp(str, lightbar_cmds[i])) |
| return i; |
| |
| return LIGHTBAR_NUM_SEQUENCES; |
| } |
| |
| static int lb_do_cmd(enum lightbar_command cmd, struct ec_params_lightbar *in, |
| struct ec_response_lightbar *out) |
| { |
| int rv; |
| in->cmd = cmd; |
| rv = ec_command(EC_CMD_LIGHTBAR_CMD, 0, in, |
| lb_command_paramcount[cmd].insize, out, |
| lb_command_paramcount[cmd].outsize); |
| return (rv < 0 ? rv : 0); |
| } |
| |
| static int lb_show_msg_names(void) |
| { |
| int i, current_state; |
| struct ec_params_lightbar param; |
| struct ec_response_lightbar resp; |
| |
| i = lb_do_cmd(LIGHTBAR_CMD_GET_SEQ, ¶m, &resp); |
| if (i < 0) |
| return i; |
| current_state = resp.get_seq.num; |
| |
| printf("sequence names:"); |
| for (i = 0; i < LIGHTBAR_NUM_SEQUENCES; i++) |
| printf(" %s", lightbar_cmds[i]); |
| printf("\nCurrent = 0x%x %s\n", current_state, |
| lightbar_cmds[current_state]); |
| |
| return 0; |
| } |
| |
| static int lb_read_params_v0_from_file(const char *filename, |
| struct lightbar_params_v0 *p) |
| { |
| FILE *fp; |
| char buf[80]; |
| int val[4]; |
| int r = 1; |
| int line = 0; |
| int want, got; |
| int i; |
| |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| fprintf(stderr, "Can't open %s: %s\n", filename, |
| strerror(errno)); |
| return 1; |
| } |
| |
| /* We must read the correct number of params from each line */ |
| #define READ(N) \ |
| do { \ |
| line++; \ |
| want = (N); \ |
| got = -1; \ |
| if (!fgets(buf, sizeof(buf), fp)) \ |
| goto done; \ |
| got = sscanf(buf, "%i %i %i %i", &val[0], &val[1], &val[2], \ |
| &val[3]); \ |
| if (want != got) \ |
| goto done; \ |
| } while (0) |
| |
| /* Do it */ |
| READ(1); |
| p->google_ramp_up = val[0]; |
| READ(1); |
| p->google_ramp_down = val[0]; |
| READ(1); |
| p->s3s0_ramp_up = val[0]; |
| READ(1); |
| p->s0_tick_delay[0] = val[0]; |
| READ(1); |
| p->s0_tick_delay[1] = val[0]; |
| READ(1); |
| p->s0a_tick_delay[0] = val[0]; |
| READ(1); |
| p->s0a_tick_delay[1] = val[0]; |
| READ(1); |
| p->s0s3_ramp_down = val[0]; |
| READ(1); |
| p->s3_sleep_for = val[0]; |
| READ(1); |
| p->s3_ramp_up = val[0]; |
| READ(1); |
| p->s3_ramp_down = val[0]; |
| READ(1); |
| p->new_s0 = val[0]; |
| |
| READ(2); |
| p->osc_min[0] = val[0]; |
| p->osc_min[1] = val[1]; |
| READ(2); |
| p->osc_max[0] = val[0]; |
| p->osc_max[1] = val[1]; |
| READ(2); |
| p->w_ofs[0] = val[0]; |
| p->w_ofs[1] = val[1]; |
| |
| READ(2); |
| p->bright_bl_off_fixed[0] = val[0]; |
| p->bright_bl_off_fixed[1] = val[1]; |
| |
| READ(2); |
| p->bright_bl_on_min[0] = val[0]; |
| p->bright_bl_on_min[1] = val[1]; |
| |
| READ(2); |
| p->bright_bl_on_max[0] = val[0]; |
| p->bright_bl_on_max[1] = val[1]; |
| |
| READ(3); |
| p->battery_threshold[0] = val[0]; |
| p->battery_threshold[1] = val[1]; |
| p->battery_threshold[2] = val[2]; |
| |
| READ(4); |
| p->s0_idx[0][0] = val[0]; |
| p->s0_idx[0][1] = val[1]; |
| p->s0_idx[0][2] = val[2]; |
| p->s0_idx[0][3] = val[3]; |
| |
| READ(4); |
| p->s0_idx[1][0] = val[0]; |
| p->s0_idx[1][1] = val[1]; |
| p->s0_idx[1][2] = val[2]; |
| p->s0_idx[1][3] = val[3]; |
| |
| READ(4); |
| p->s3_idx[0][0] = val[0]; |
| p->s3_idx[0][1] = val[1]; |
| p->s3_idx[0][2] = val[2]; |
| p->s3_idx[0][3] = val[3]; |
| |
| READ(4); |
| p->s3_idx[1][0] = val[0]; |
| p->s3_idx[1][1] = val[1]; |
| p->s3_idx[1][2] = val[2]; |
| p->s3_idx[1][3] = val[3]; |
| |
| for (i = 0; i < ARRAY_SIZE(p->color); i++) { |
| READ(3); |
| p->color[i].r = val[0]; |
| p->color[i].g = val[1]; |
| p->color[i].b = val[2]; |
| } |
| |
| #undef READ |
| |
| /* Yay */ |
| r = 0; |
| done: |
| if (r) |
| fprintf(stderr, "problem with line %d: wanted %d, got %d\n", |
| line, want, got); |
| fclose(fp); |
| return r; |
| } |
| |
| static void lb_show_params_v0(const struct lightbar_params_v0 *p) |
| { |
| int i; |
| |
| printf("%d\t\t# .google_ramp_up\n", p->google_ramp_up); |
| printf("%d\t\t# .google_ramp_down\n", p->google_ramp_down); |
| printf("%d\t\t# .s3s0_ramp_up\n", p->s3s0_ramp_up); |
| printf("%d\t\t# .s0_tick_delay (battery)\n", p->s0_tick_delay[0]); |
| printf("%d\t\t# .s0_tick_delay (AC)\n", p->s0_tick_delay[1]); |
| printf("%d\t\t# .s0a_tick_delay (battery)\n", p->s0a_tick_delay[0]); |
| printf("%d\t\t# .s0a_tick_delay (AC)\n", p->s0a_tick_delay[1]); |
| printf("%d\t\t# .s0s3_ramp_down\n", p->s0s3_ramp_down); |
| printf("%d\t# .s3_sleep_for\n", p->s3_sleep_for); |
| printf("%d\t\t# .s3_ramp_up\n", p->s3_ramp_up); |
| printf("%d\t\t# .s3_ramp_down\n", p->s3_ramp_down); |
| printf("%d\t\t# .new_s0\n", p->new_s0); |
| printf("0x%02x 0x%02x\t# .osc_min (battery, AC)\n", p->osc_min[0], |
| p->osc_min[1]); |
| printf("0x%02x 0x%02x\t# .osc_max (battery, AC)\n", p->osc_max[0], |
| p->osc_max[1]); |
| printf("%d %d\t\t# .w_ofs (battery, AC)\n", p->w_ofs[0], p->w_ofs[1]); |
| printf("0x%02x 0x%02x\t# .bright_bl_off_fixed (battery, AC)\n", |
| p->bright_bl_off_fixed[0], p->bright_bl_off_fixed[1]); |
| printf("0x%02x 0x%02x\t# .bright_bl_on_min (battery, AC)\n", |
| p->bright_bl_on_min[0], p->bright_bl_on_min[1]); |
| printf("0x%02x 0x%02x\t# .bright_bl_on_max (battery, AC)\n", |
| p->bright_bl_on_max[0], p->bright_bl_on_max[1]); |
| printf("%d %d %d\t\t# .battery_threshold\n", p->battery_threshold[0], |
| p->battery_threshold[1], p->battery_threshold[2]); |
| printf("%d %d %d %d\t\t# .s0_idx[] (battery)\n", p->s0_idx[0][0], |
| p->s0_idx[0][1], p->s0_idx[0][2], p->s0_idx[0][3]); |
| printf("%d %d %d %d\t\t# .s0_idx[] (AC)\n", p->s0_idx[1][0], |
| p->s0_idx[1][1], p->s0_idx[1][2], p->s0_idx[1][3]); |
| printf("%d %d %d %d\t# .s3_idx[] (battery)\n", p->s3_idx[0][0], |
| p->s3_idx[0][1], p->s3_idx[0][2], p->s3_idx[0][3]); |
| printf("%d %d %d %d\t# .s3_idx[] (AC)\n", p->s3_idx[1][0], |
| p->s3_idx[1][1], p->s3_idx[1][2], p->s3_idx[1][3]); |
| for (i = 0; i < ARRAY_SIZE(p->color); i++) |
| printf("0x%02x 0x%02x 0x%02x\t# color[%d]\n", p->color[i].r, |
| p->color[i].g, p->color[i].b, i); |
| } |
| |
| static int lb_read_params_v1_from_file(const char *filename, |
| struct lightbar_params_v1 *p) |
| { |
| FILE *fp; |
| char buf[80]; |
| int val[4]; |
| int r = 1; |
| int line = 0; |
| int want, got; |
| int i; |
| |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| fprintf(stderr, "Can't open %s: %s\n", filename, |
| strerror(errno)); |
| return 1; |
| } |
| |
| /* We must read the correct number of params from each line */ |
| #define READ(N) \ |
| do { \ |
| line++; \ |
| want = (N); \ |
| got = -1; \ |
| if (!fgets(buf, sizeof(buf), fp)) \ |
| goto done; \ |
| got = sscanf(buf, "%i %i %i %i", &val[0], &val[1], &val[2], \ |
| &val[3]); \ |
| if (want != got) \ |
| goto done; \ |
| } while (0) |
| |
| /* Do it */ |
| READ(1); |
| p->google_ramp_up = val[0]; |
| READ(1); |
| p->google_ramp_down = val[0]; |
| READ(1); |
| p->s3s0_ramp_up = val[0]; |
| READ(1); |
| p->s0_tick_delay[0] = val[0]; |
| READ(1); |
| p->s0_tick_delay[1] = val[0]; |
| READ(1); |
| p->s0a_tick_delay[0] = val[0]; |
| READ(1); |
| p->s0a_tick_delay[1] = val[0]; |
| READ(1); |
| p->s0s3_ramp_down = val[0]; |
| READ(1); |
| p->s3_sleep_for = val[0]; |
| READ(1); |
| p->s3_ramp_up = val[0]; |
| READ(1); |
| p->s3_ramp_down = val[0]; |
| READ(1); |
| p->tap_tick_delay = val[0]; |
| READ(1); |
| p->tap_gate_delay = val[0]; |
| READ(1); |
| p->tap_display_time = val[0]; |
| |
| READ(1); |
| p->tap_pct_red = val[0]; |
| READ(1); |
| p->tap_pct_green = val[0]; |
| READ(1); |
| p->tap_seg_min_on = val[0]; |
| READ(1); |
| p->tap_seg_max_on = val[0]; |
| READ(1); |
| p->tap_seg_osc = val[0]; |
| READ(3); |
| p->tap_idx[0] = val[0]; |
| p->tap_idx[1] = val[1]; |
| p->tap_idx[2] = val[2]; |
| |
| READ(2); |
| p->osc_min[0] = val[0]; |
| p->osc_min[1] = val[1]; |
| READ(2); |
| p->osc_max[0] = val[0]; |
| p->osc_max[1] = val[1]; |
| READ(2); |
| p->w_ofs[0] = val[0]; |
| p->w_ofs[1] = val[1]; |
| |
| READ(2); |
| p->bright_bl_off_fixed[0] = val[0]; |
| p->bright_bl_off_fixed[1] = val[1]; |
| |
| READ(2); |
| p->bright_bl_on_min[0] = val[0]; |
| p->bright_bl_on_min[1] = val[1]; |
| |
| READ(2); |
| p->bright_bl_on_max[0] = val[0]; |
| p->bright_bl_on_max[1] = val[1]; |
| |
| READ(3); |
| p->battery_threshold[0] = val[0]; |
| p->battery_threshold[1] = val[1]; |
| p->battery_threshold[2] = val[2]; |
| |
| READ(4); |
| p->s0_idx[0][0] = val[0]; |
| p->s0_idx[0][1] = val[1]; |
| p->s0_idx[0][2] = val[2]; |
| p->s0_idx[0][3] = val[3]; |
| |
| READ(4); |
| p->s0_idx[1][0] = val[0]; |
| p->s0_idx[1][1] = val[1]; |
| p->s0_idx[1][2] = val[2]; |
| p->s0_idx[1][3] = val[3]; |
| |
| READ(4); |
| p->s3_idx[0][0] = val[0]; |
| p->s3_idx[0][1] = val[1]; |
| p->s3_idx[0][2] = val[2]; |
| p->s3_idx[0][3] = val[3]; |
| |
| READ(4); |
| p->s3_idx[1][0] = val[0]; |
| p->s3_idx[1][1] = val[1]; |
| p->s3_idx[1][2] = val[2]; |
| p->s3_idx[1][3] = val[3]; |
| |
| for (i = 0; i < ARRAY_SIZE(p->color); i++) { |
| READ(3); |
| p->color[i].r = val[0]; |
| p->color[i].g = val[1]; |
| p->color[i].b = val[2]; |
| } |
| |
| #undef READ |
| |
| /* Yay */ |
| r = 0; |
| done: |
| if (r) |
| fprintf(stderr, "problem with line %d: wanted %d, got %d\n", |
| line, want, got); |
| fclose(fp); |
| return r; |
| } |
| |
| static void lb_show_params_v1(const struct lightbar_params_v1 *p) |
| { |
| int i; |
| |
| printf("%d\t\t# .google_ramp_up\n", p->google_ramp_up); |
| printf("%d\t\t# .google_ramp_down\n", p->google_ramp_down); |
| printf("%d\t\t# .s3s0_ramp_up\n", p->s3s0_ramp_up); |
| printf("%d\t\t# .s0_tick_delay (battery)\n", p->s0_tick_delay[0]); |
| printf("%d\t\t# .s0_tick_delay (AC)\n", p->s0_tick_delay[1]); |
| printf("%d\t\t# .s0a_tick_delay (battery)\n", p->s0a_tick_delay[0]); |
| printf("%d\t\t# .s0a_tick_delay (AC)\n", p->s0a_tick_delay[1]); |
| printf("%d\t\t# .s0s3_ramp_down\n", p->s0s3_ramp_down); |
| printf("%d\t\t# .s3_sleep_for\n", p->s3_sleep_for); |
| printf("%d\t\t# .s3_ramp_up\n", p->s3_ramp_up); |
| printf("%d\t\t# .s3_ramp_down\n", p->s3_ramp_down); |
| printf("%d\t\t# .tap_tick_delay\n", p->tap_tick_delay); |
| printf("%d\t\t# .tap_gate_delay\n", p->tap_gate_delay); |
| printf("%d\t\t# .tap_display_time\n", p->tap_display_time); |
| printf("%d\t\t# .tap_pct_red\n", p->tap_pct_red); |
| printf("%d\t\t# .tap_pct_green\n", p->tap_pct_green); |
| printf("%d\t\t# .tap_seg_min_on\n", p->tap_seg_min_on); |
| printf("%d\t\t# .tap_seg_max_on\n", p->tap_seg_max_on); |
| printf("%d\t\t# .tap_seg_osc\n", p->tap_seg_osc); |
| printf("%d %d %d\t\t# .tap_idx\n", p->tap_idx[0], p->tap_idx[1], |
| p->tap_idx[2]); |
| printf("0x%02x 0x%02x\t# .osc_min (battery, AC)\n", p->osc_min[0], |
| p->osc_min[1]); |
| printf("0x%02x 0x%02x\t# .osc_max (battery, AC)\n", p->osc_max[0], |
| p->osc_max[1]); |
| printf("%d %d\t\t# .w_ofs (battery, AC)\n", p->w_ofs[0], p->w_ofs[1]); |
| printf("0x%02x 0x%02x\t# .bright_bl_off_fixed (battery, AC)\n", |
| p->bright_bl_off_fixed[0], p->bright_bl_off_fixed[1]); |
| printf("0x%02x 0x%02x\t# .bright_bl_on_min (battery, AC)\n", |
| p->bright_bl_on_min[0], p->bright_bl_on_min[1]); |
| printf("0x%02x 0x%02x\t# .bright_bl_on_max (battery, AC)\n", |
| p->bright_bl_on_max[0], p->bright_bl_on_max[1]); |
| printf("%d %d %d\t# .battery_threshold\n", p->battery_threshold[0], |
| p->battery_threshold[1], p->battery_threshold[2]); |
| printf("%d %d %d %d\t\t# .s0_idx[] (battery)\n", p->s0_idx[0][0], |
| p->s0_idx[0][1], p->s0_idx[0][2], p->s0_idx[0][3]); |
| printf("%d %d %d %d\t\t# .s0_idx[] (AC)\n", p->s0_idx[1][0], |
| p->s0_idx[1][1], p->s0_idx[1][2], p->s0_idx[1][3]); |
| printf("%d %d %d %d\t# .s3_idx[] (battery)\n", p->s3_idx[0][0], |
| p->s3_idx[0][1], p->s3_idx[0][2], p->s3_idx[0][3]); |
| printf("%d %d %d %d\t# .s3_idx[] (AC)\n", p->s3_idx[1][0], |
| p->s3_idx[1][1], p->s3_idx[1][2], p->s3_idx[1][3]); |
| for (i = 0; i < ARRAY_SIZE(p->color); i++) |
| printf("0x%02x 0x%02x 0x%02x\t# color[%d]\n", p->color[i].r, |
| p->color[i].g, p->color[i].b, i); |
| } |
| |
| static int lb_rd_timing_v2par_from_file(const char *filename, |
| struct lightbar_params_v2_timing *p) |
| { |
| FILE *fp; |
| char buf[80]; |
| int val[4]; |
| int r = 1; |
| int line = 0; |
| int want, got; |
| |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| fprintf(stderr, "Can't open %s: %s\n", filename, |
| strerror(errno)); |
| return 1; |
| } |
| |
| /* We must read the correct number of params from each line */ |
| #define READ(N) \ |
| do { \ |
| line++; \ |
| want = (N); \ |
| got = -1; \ |
| if (!fgets(buf, sizeof(buf), fp)) \ |
| goto done; \ |
| got = sscanf(buf, "%i %i %i %i", &val[0], &val[1], &val[2], \ |
| &val[3]); \ |
| if (want != got) \ |
| goto done; \ |
| } while (0) |
| |
| READ(1); |
| p->google_ramp_up = val[0]; |
| READ(1); |
| p->google_ramp_down = val[0]; |
| READ(1); |
| p->s3s0_ramp_up = val[0]; |
| READ(1); |
| p->s0_tick_delay[0] = val[0]; |
| READ(1); |
| p->s0_tick_delay[1] = val[0]; |
| READ(1); |
| p->s0a_tick_delay[0] = val[0]; |
| READ(1); |
| p->s0a_tick_delay[1] = val[0]; |
| READ(1); |
| p->s0s3_ramp_down = val[0]; |
| READ(1); |
| p->s3_sleep_for = val[0]; |
| READ(1); |
| p->s3_ramp_up = val[0]; |
| READ(1); |
| p->s3_ramp_down = val[0]; |
| READ(1); |
| p->tap_tick_delay = val[0]; |
| READ(1); |
| p->tap_gate_delay = val[0]; |
| READ(1); |
| p->tap_display_time = val[0]; |
| #undef READ |
| |
| /* Yay */ |
| r = 0; |
| done: |
| if (r) |
| fprintf(stderr, "problem with line %d: wanted %d, got %d\n", |
| line, want, got); |
| fclose(fp); |
| return r; |
| } |
| |
| static int lb_rd_tap_v2par_from_file(const char *filename, |
| struct lightbar_params_v2_tap *p) |
| { |
| FILE *fp; |
| char buf[80]; |
| int val[4]; |
| int r = 1; |
| int line = 0; |
| int want, got; |
| |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| fprintf(stderr, "Can't open %s: %s\n", filename, |
| strerror(errno)); |
| return 1; |
| } |
| |
| /* We must read the correct number of params from each line */ |
| #define READ(N) \ |
| do { \ |
| line++; \ |
| want = (N); \ |
| got = -1; \ |
| if (!fgets(buf, sizeof(buf), fp)) \ |
| goto done; \ |
| got = sscanf(buf, "%i %i %i %i", &val[0], &val[1], &val[2], \ |
| &val[3]); \ |
| if (want != got) \ |
| goto done; \ |
| } while (0) |
| |
| READ(1); |
| p->tap_pct_red = val[0]; |
| READ(1); |
| p->tap_pct_green = val[0]; |
| READ(1); |
| p->tap_seg_min_on = val[0]; |
| READ(1); |
| p->tap_seg_max_on = val[0]; |
| READ(1); |
| p->tap_seg_osc = val[0]; |
| READ(3); |
| p->tap_idx[0] = val[0]; |
| p->tap_idx[1] = val[1]; |
| p->tap_idx[2] = val[2]; |
| #undef READ |
| |
| /* Yay */ |
| r = 0; |
| done: |
| if (r) |
| fprintf(stderr, "problem with line %d: wanted %d, got %d\n", |
| line, want, got); |
| fclose(fp); |
| return r; |
| } |
| |
| static int lb_rd_osc_v2par_from_file(const char *filename, |
| struct lightbar_params_v2_oscillation *p) |
| { |
| FILE *fp; |
| char buf[80]; |
| int val[4]; |
| int r = 1; |
| int line = 0; |
| int want, got; |
| |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| fprintf(stderr, "Can't open %s: %s\n", filename, |
| strerror(errno)); |
| return 1; |
| } |
| |
| /* We must read the correct number of params from each line */ |
| #define READ(N) \ |
| do { \ |
| line++; \ |
| want = (N); \ |
| got = -1; \ |
| if (!fgets(buf, sizeof(buf), fp)) \ |
| goto done; \ |
| got = sscanf(buf, "%i %i %i %i", &val[0], &val[1], &val[2], \ |
| &val[3]); \ |
| if (want != got) \ |
| goto done; \ |
| } while (0) |
| |
| READ(2); |
| p->osc_min[0] = val[0]; |
| p->osc_min[1] = val[1]; |
| READ(2); |
| p->osc_max[0] = val[0]; |
| p->osc_max[1] = val[1]; |
| READ(2); |
| p->w_ofs[0] = val[0]; |
| p->w_ofs[1] = val[1]; |
| #undef READ |
| |
| /* Yay */ |
| r = 0; |
| done: |
| if (r) |
| fprintf(stderr, "problem with line %d: wanted %d, got %d\n", |
| line, want, got); |
| fclose(fp); |
| return r; |
| } |
| |
| static int lb_rd_bright_v2par_from_file(const char *filename, |
| struct lightbar_params_v2_brightness *p) |
| { |
| FILE *fp; |
| char buf[80]; |
| int val[4]; |
| int r = 1; |
| int line = 0; |
| int want, got; |
| |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| fprintf(stderr, "Can't open %s: %s\n", filename, |
| strerror(errno)); |
| return 1; |
| } |
| |
| /* We must read the correct number of params from each line */ |
| #define READ(N) \ |
| do { \ |
| line++; \ |
| want = (N); \ |
| got = -1; \ |
| if (!fgets(buf, sizeof(buf), fp)) \ |
| goto done; \ |
| got = sscanf(buf, "%i %i %i %i", &val[0], &val[1], &val[2], \ |
| &val[3]); \ |
| if (want != got) \ |
| goto done; \ |
| } while (0) |
| |
| READ(2); |
| p->bright_bl_off_fixed[0] = val[0]; |
| p->bright_bl_off_fixed[1] = val[1]; |
| |
| READ(2); |
| p->bright_bl_on_min[0] = val[0]; |
| p->bright_bl_on_min[1] = val[1]; |
| |
| READ(2); |
| p->bright_bl_on_max[0] = val[0]; |
| p->bright_bl_on_max[1] = val[1]; |
| #undef READ |
| |
| /* Yay */ |
| r = 0; |
| done: |
| if (r) |
| fprintf(stderr, "problem with line %d: wanted %d, got %d\n", |
| line, want, got); |
| fclose(fp); |
| return r; |
| } |
| |
| static int lb_rd_thlds_v2par_from_file(const char *filename, |
| struct lightbar_params_v2_thresholds *p) |
| { |
| FILE *fp; |
| char buf[80]; |
| int val[4]; |
| int r = 1; |
| int line = 0; |
| int want, got; |
| |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| fprintf(stderr, "Can't open %s: %s\n", filename, |
| strerror(errno)); |
| return 1; |
| } |
| |
| /* We must read the correct number of params from each line */ |
| #define READ(N) \ |
| do { \ |
| line++; \ |
| want = (N); \ |
| got = -1; \ |
| if (!fgets(buf, sizeof(buf), fp)) \ |
| goto done; \ |
| got = sscanf(buf, "%i %i %i %i", &val[0], &val[1], &val[2], \ |
| &val[3]); \ |
| if (want != got) \ |
| goto done; \ |
| } while (0) |
| |
| READ(3); |
| p->battery_threshold[0] = val[0]; |
| p->battery_threshold[1] = val[1]; |
| p->battery_threshold[2] = val[2]; |
| #undef READ |
| |
| /* Yay */ |
| r = 0; |
| done: |
| if (r) |
| fprintf(stderr, "problem with line %d: wanted %d, got %d\n", |
| line, want, got); |
| fclose(fp); |
| return r; |
| } |
| |
| static int lb_rd_colors_v2par_from_file(const char *filename, |
| struct lightbar_params_v2_colors *p) |
| { |
| FILE *fp; |
| char buf[80]; |
| int val[4]; |
| int r = 1; |
| int line = 0; |
| int want, got; |
| int i; |
| |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| fprintf(stderr, "Can't open %s: %s\n", filename, |
| strerror(errno)); |
| return 1; |
| } |
| |
| /* We must read the correct number of params from each line */ |
| #define READ(N) \ |
| do { \ |
| line++; \ |
| want = (N); \ |
| got = -1; \ |
| if (!fgets(buf, sizeof(buf), fp)) \ |
| goto done; \ |
| got = sscanf(buf, "%i %i %i %i", &val[0], &val[1], &val[2], \ |
| &val[3]); \ |
| if (want != got) \ |
| goto done; \ |
| } while (0) |
| |
| READ(4); |
| p->s0_idx[0][0] = val[0]; |
| p->s0_idx[0][1] = val[1]; |
| p->s0_idx[0][2] = val[2]; |
| p->s0_idx[0][3] = val[3]; |
| |
| READ(4); |
| p->s0_idx[1][0] = val[0]; |
| p->s0_idx[1][1] = val[1]; |
| p->s0_idx[1][2] = val[2]; |
| p->s0_idx[1][3] = val[3]; |
| |
| READ(4); |
| p->s3_idx[0][0] = val[0]; |
| p->s3_idx[0][1] = val[1]; |
| p->s3_idx[0][2] = val[2]; |
| p->s3_idx[0][3] = val[3]; |
| |
| READ(4); |
| p->s3_idx[1][0] = val[0]; |
| p->s3_idx[1][1] = val[1]; |
| p->s3_idx[1][2] = val[2]; |
| p->s3_idx[1][3] = val[3]; |
| for (i = 0; i < ARRAY_SIZE(p->color); i++) { |
| READ(3); |
| p->color[i].r = val[0]; |
| p->color[i].g = val[1]; |
| p->color[i].b = val[2]; |
| } |
| |
| #undef READ |
| |
| /* Yay */ |
| r = 0; |
| done: |
| if (r) |
| fprintf(stderr, "problem with line %d: wanted %d, got %d\n", |
| line, want, got); |
| fclose(fp); |
| return r; |
| } |
| |
| static void lb_show_v2par_timing(const struct lightbar_params_v2_timing *p) |
| { |
| printf("%d\t\t# .google_ramp_up\n", p->google_ramp_up); |
| printf("%d\t\t# .google_ramp_down\n", p->google_ramp_down); |
| printf("%d\t\t# .s3s0_ramp_up\n", p->s3s0_ramp_up); |
| printf("%d\t\t# .s0_tick_delay (battery)\n", p->s0_tick_delay[0]); |
| printf("%d\t\t# .s0_tick_delay (AC)\n", p->s0_tick_delay[1]); |
| printf("%d\t\t# .s0a_tick_delay (battery)\n", p->s0a_tick_delay[0]); |
| printf("%d\t\t# .s0a_tick_delay (AC)\n", p->s0a_tick_delay[1]); |
| printf("%d\t\t# .s0s3_ramp_down\n", p->s0s3_ramp_down); |
| printf("%d\t\t# .s3_sleep_for\n", p->s3_sleep_for); |
| printf("%d\t\t# .s3_ramp_up\n", p->s3_ramp_up); |
| printf("%d\t\t# .s3_ramp_down\n", p->s3_ramp_down); |
| printf("%d\t\t# .tap_tick_delay\n", p->tap_tick_delay); |
| printf("%d\t\t# .tap_gate_delay\n", p->tap_gate_delay); |
| printf("%d\t\t# .tap_display_time\n", p->tap_display_time); |
| } |
| |
| static void lb_show_v2par_tap(const struct lightbar_params_v2_tap *p) |
| { |
| printf("%d\t\t# .tap_pct_red\n", p->tap_pct_red); |
| printf("%d\t\t# .tap_pct_green\n", p->tap_pct_green); |
| printf("%d\t\t# .tap_seg_min_on\n", p->tap_seg_min_on); |
| printf("%d\t\t# .tap_seg_max_on\n", p->tap_seg_max_on); |
| printf("%d\t\t# .tap_seg_osc\n", p->tap_seg_osc); |
| printf("%d %d %d\t\t# .tap_idx\n", p->tap_idx[0], p->tap_idx[1], |
| p->tap_idx[2]); |
| } |
| |
| static void lb_show_v2par_osc(const struct lightbar_params_v2_oscillation *p) |
| { |
| printf("0x%02x 0x%02x\t# .osc_min (battery, AC)\n", p->osc_min[0], |
| p->osc_min[1]); |
| printf("0x%02x 0x%02x\t# .osc_max (battery, AC)\n", p->osc_max[0], |
| p->osc_max[1]); |
| printf("%d %d\t\t# .w_ofs (battery, AC)\n", p->w_ofs[0], p->w_ofs[1]); |
| } |
| |
| static void lb_show_v2par_bright(const struct lightbar_params_v2_brightness *p) |
| { |
| printf("0x%02x 0x%02x\t# .bright_bl_off_fixed (battery, AC)\n", |
| p->bright_bl_off_fixed[0], p->bright_bl_off_fixed[1]); |
| printf("0x%02x 0x%02x\t# .bright_bl_on_min (battery, AC)\n", |
| p->bright_bl_on_min[0], p->bright_bl_on_min[1]); |
| printf("0x%02x 0x%02x\t# .bright_bl_on_max (battery, AC)\n", |
| p->bright_bl_on_max[0], p->bright_bl_on_max[1]); |
| } |
| |
| static void lb_show_v2par_thlds(const struct lightbar_params_v2_thresholds *p) |
| { |
| printf("%d %d %d\t# .battery_threshold\n", p->battery_threshold[0], |
| p->battery_threshold[1], p->battery_threshold[2]); |
| } |
| |
| static void lb_show_v2par_colors(const struct lightbar_params_v2_colors *p) |
| { |
| int i; |
| |
| printf("%d %d %d %d\t\t# .s0_idx[] (battery)\n", p->s0_idx[0][0], |
| p->s0_idx[0][1], p->s0_idx[0][2], p->s0_idx[0][3]); |
| printf("%d %d %d %d\t\t# .s0_idx[] (AC)\n", p->s0_idx[1][0], |
| p->s0_idx[1][1], p->s0_idx[1][2], p->s0_idx[1][3]); |
| printf("%d %d %d %d\t# .s3_idx[] (battery)\n", p->s3_idx[0][0], |
| p->s3_idx[0][1], p->s3_idx[0][2], p->s3_idx[0][3]); |
| printf("%d %d %d %d\t# .s3_idx[] (AC)\n", p->s3_idx[1][0], |
| p->s3_idx[1][1], p->s3_idx[1][2], p->s3_idx[1][3]); |
| |
| for (i = 0; i < ARRAY_SIZE(p->color); i++) |
| printf("0x%02x 0x%02x 0x%02x\t# color[%d]\n", p->color[i].r, |
| p->color[i].g, p->color[i].b, i); |
| } |
| |
| static int lb_load_program(const char *filename, struct lightbar_program *prog) |
| { |
| FILE *fp; |
| size_t got; |
| int rc; |
| |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| fprintf(stderr, "Can't open %s: %s\n", filename, |
| strerror(errno)); |
| return 1; |
| } |
| |
| rc = fseek(fp, 0, SEEK_END); |
| if (rc) { |
| fprintf(stderr, "Couldn't find end of file %s", filename); |
| fclose(fp); |
| return 1; |
| } |
| rc = (int)ftell(fp); |
| if (rc > EC_LB_PROG_LEN) { |
| fprintf(stderr, "File %s is too long, aborting\n", filename); |
| fclose(fp); |
| return 1; |
| } |
| rewind(fp); |
| |
| memset(prog->data, 0, EC_LB_PROG_LEN); |
| got = fread(prog->data, 1, EC_LB_PROG_LEN, fp); |
| if (rc != got) |
| fprintf(stderr, "Warning: did not read entire file\n"); |
| prog->size = got; |
| fclose(fp); |
| return 0; |
| } |
| |
| static int cmd_lightbar_params_v0(int argc, char **argv) |
| { |
| struct ec_params_lightbar param; |
| struct ec_response_lightbar resp; |
| int r; |
| |
| if (argc > 2) { |
| r = lb_read_params_v0_from_file(argv[2], ¶m.set_params_v0); |
| if (r) |
| return r; |
| return lb_do_cmd(LIGHTBAR_CMD_SET_PARAMS_V0, ¶m, &resp); |
| } |
| r = lb_do_cmd(LIGHTBAR_CMD_GET_PARAMS_V0, ¶m, &resp); |
| if (!r) |
| lb_show_params_v0(&resp.get_params_v0); |
| return r; |
| } |
| |
| static int cmd_lightbar_params_v1(int argc, char **argv) |
| { |
| struct ec_params_lightbar param; |
| struct ec_response_lightbar resp; |
| int r; |
| |
| if (argc > 2) { |
| r = lb_read_params_v1_from_file(argv[2], ¶m.set_params_v1); |
| if (r) |
| return r; |
| return lb_do_cmd(LIGHTBAR_CMD_SET_PARAMS_V1, ¶m, &resp); |
| } |
| r = lb_do_cmd(LIGHTBAR_CMD_GET_PARAMS_V1, ¶m, &resp); |
| if (!r) |
| lb_show_params_v1(&resp.get_params_v1); |
| return r; |
| } |
| |
| static void lb_param_v2_help(void) |
| { |
| printf("Usage:\n"); |
| printf("lightbar params2 group [setfile]\n"); |
| printf("group list:\n"); |
| printf(" timing\n"); |
| printf(" tap\n"); |
| printf(" oscillation\n"); |
| printf(" brightness\n"); |
| printf(" thresholds\n"); |
| printf(" colors\n"); |
| |
| return; |
| } |
| |
| static int cmd_lightbar_params_v2(int argc, char **argv) |
| { |
| struct ec_params_lightbar p; |
| struct ec_response_lightbar resp; |
| int r = 0; |
| int set = 0; |
| |
| memset(&p, 0, sizeof(struct ec_params_lightbar)); |
| memset(&resp, 0, sizeof(struct ec_response_lightbar)); |
| |
| if (argc < 3) { |
| lb_param_v2_help(); |
| return 1; |
| } |
| |
| /* Set new params if provided with a setfile */ |
| if (argc > 3) |
| set = 1; |
| |
| /* Show selected v2 params */ |
| if (!strncasecmp(argv[2], "timing", 6)) { |
| if (set) { |
| r = lb_rd_timing_v2par_from_file(argv[3], |
| &p.set_v2par_timing); |
| if (r) |
| return r; |
| r = lb_do_cmd(LIGHTBAR_CMD_SET_PARAMS_V2_TIMING, &p, |
| &resp); |
| if (r) |
| return r; |
| } |
| r = lb_do_cmd(LIGHTBAR_CMD_GET_PARAMS_V2_TIMING, &p, &resp); |
| if (r) |
| return r; |
| lb_show_v2par_timing(&resp.get_params_v2_timing); |
| } else if (!strcasecmp(argv[2], "tap")) { |
| if (set) { |
| r = lb_rd_tap_v2par_from_file(argv[3], |
| &p.set_v2par_tap); |
| if (r) |
| return r; |
| r = lb_do_cmd(LIGHTBAR_CMD_SET_PARAMS_V2_TAP, &p, |
| &resp); |
| if (r) |
| return r; |
| } |
| r = lb_do_cmd(LIGHTBAR_CMD_GET_PARAMS_V2_TAP, &p, &resp); |
| if (r) |
| return r; |
| lb_show_v2par_tap(&resp.get_params_v2_tap); |
| } else if (!strncasecmp(argv[2], "oscillation", 11)) { |
| if (set) { |
| r = lb_rd_osc_v2par_from_file(argv[3], |
| &p.set_v2par_osc); |
| if (r) |
| return r; |
| r = lb_do_cmd(LIGHTBAR_CMD_SET_PARAMS_V2_OSCILLATION, |
| &p, &resp); |
| if (r) |
| return r; |
| } |
| r = lb_do_cmd(LIGHTBAR_CMD_GET_PARAMS_V2_OSCILLATION, &p, |
| &resp); |
| if (r) |
| return r; |
| lb_show_v2par_osc(&resp.get_params_v2_osc); |
| } else if (!strncasecmp(argv[2], "brightness", 10)) { |
| if (set) { |
| r = lb_rd_bright_v2par_from_file(argv[3], |
| &p.set_v2par_bright); |
| if (r) |
| return r; |
| r = lb_do_cmd(LIGHTBAR_CMD_SET_PARAMS_V2_BRIGHTNESS, &p, |
| &resp); |
| if (r) |
| return r; |
| } |
| r = lb_do_cmd(LIGHTBAR_CMD_GET_PARAMS_V2_BRIGHTNESS, &p, &resp); |
| if (r) |
| return r; |
| lb_show_v2par_bright(&resp.get_params_v2_bright); |
| } else if (!strncasecmp(argv[2], "thresholds", 10)) { |
| if (set) { |
| r = lb_rd_thlds_v2par_from_file(argv[3], |
| &p.set_v2par_thlds); |
| if (r) |
| return r; |
| r = lb_do_cmd(LIGHTBAR_CMD_SET_PARAMS_V2_THRESHOLDS, &p, |
| &resp); |
| if (r) |
| return r; |
| } |
| r = lb_do_cmd(LIGHTBAR_CMD_GET_PARAMS_V2_THRESHOLDS, &p, &resp); |
| if (r) |
| return r; |
| lb_show_v2par_thlds(&resp.get_params_v2_thlds); |
| } else if (!strncasecmp(argv[2], "colors", 6)) { |
| if (set) { |
| r = lb_rd_colors_v2par_from_file(argv[3], |
| &p.set_v2par_colors); |
| if (r) |
| return r; |
| r = lb_do_cmd(LIGHTBAR_CMD_SET_PARAMS_V2_COLORS, &p, |
| &resp); |
| if (r) |
| return r; |
| } |
| r = lb_do_cmd(LIGHTBAR_CMD_GET_PARAMS_V2_COLORS, &p, &resp); |
| if (r) |
| return r; |
| lb_show_v2par_colors(&resp.get_params_v2_colors); |
| } else { |
| lb_param_v2_help(); |
| } |
| |
| return r; |
| } |
| |
| static int cmd_lightbar(int argc, char **argv) |
| { |
| int i, r; |
| struct ec_params_lightbar param; |
| struct ec_response_lightbar resp; |
| |
| printf("warning: ectool lightbar is depricated. " |
| "Use ectool led lightbar instead.\n"); |
| |
| if (1 == argc) { /* no args = dump 'em all */ |
| r = lb_do_cmd(LIGHTBAR_CMD_DUMP, ¶m, &resp); |
| if (r) |
| return r; |
| for (i = 0; i < ARRAY_SIZE(resp.dump.vals); i++) { |
| printf(" %02x %02x %02x\n", |
| resp.dump.vals[i].reg, resp.dump.vals[i].ic0, |
| resp.dump.vals[i].ic1); |
| } |
| return 0; |
| } |
| |
| if (argc == 2 && !strcasecmp(argv[1], "init")) |
| return lb_do_cmd(LIGHTBAR_CMD_INIT, ¶m, &resp); |
| |
| if (argc == 2 && !strcasecmp(argv[1], "off")) |
| return lb_do_cmd(LIGHTBAR_CMD_OFF, ¶m, &resp); |
| |
| if (argc == 2 && !strcasecmp(argv[1], "on")) |
| return lb_do_cmd(LIGHTBAR_CMD_ON, ¶m, &resp); |
| |
| if (!strcasecmp(argv[1], "params0")) |
| return cmd_lightbar_params_v0(argc, argv); |
| |
| if (!strcasecmp(argv[1], "params1")) |
| return cmd_lightbar_params_v1(argc, argv); |
| |
| if (!strcasecmp(argv[1], "params2")) |
| return cmd_lightbar_params_v2(argc, argv); |
| |
| if (!strcasecmp(argv[1], "params")) { |
| /* Just try them both */ |
| fprintf(stderr, "trying params1 ...\n"); |
| if (0 == cmd_lightbar_params_v1(argc, argv)) |
| return 0; |
| fprintf(stderr, "trying params0 ...\n"); |
| return cmd_lightbar_params_v0(argc, argv); |
| } |
| |
| if (!strcasecmp(argv[1], "version")) { |
| r = lb_do_cmd(LIGHTBAR_CMD_VERSION, ¶m, &resp); |
| if (!r) |
| printf("version %d flags 0x%x\n", resp.version.num, |
| resp.version.flags); |
| return r; |
| } |
| |
| if (argc > 1 && !strcasecmp(argv[1], "brightness")) { |
| char *e; |
| int rv; |
| if (argc > 2) { |
| param.set_brightness.num = 0xff & |
| strtoull(argv[2], &e, 16); |
| return lb_do_cmd(LIGHTBAR_CMD_SET_BRIGHTNESS, ¶m, |
| &resp); |
| } |
| rv = lb_do_cmd(LIGHTBAR_CMD_GET_BRIGHTNESS, ¶m, &resp); |
| if (rv) |
| return rv; |
| printf("%02x\n", resp.get_brightness.num); |
| return 0; |
| } |
| |
| if (argc > 1 && !strcasecmp(argv[1], "demo")) { |
| int rv; |
| if (argc > 2) { |
| if (!strcasecmp(argv[2], "on") || argv[2][0] == '1') |
| param.demo.num = 1; |
| else if (!strcasecmp(argv[2], "off") || |
| argv[2][0] == '0') |
| param.demo.num = 0; |
| else { |
| fprintf(stderr, "Invalid arg\n"); |
| return -1; |
| } |
| return lb_do_cmd(LIGHTBAR_CMD_DEMO, ¶m, &resp); |
| } |
| |
| rv = lb_do_cmd(LIGHTBAR_CMD_GET_DEMO, ¶m, &resp); |
| if (rv) |
| return rv; |
| printf("%s\n", resp.get_demo.num ? "on" : "off"); |
| return 0; |
| } |
| |
| if (argc >= 2 && !strcasecmp(argv[1], "seq")) { |
| char *e; |
| uint8_t num; |
| if (argc == 2) |
| return lb_show_msg_names(); |
| num = 0xff & strtoull(argv[2], &e, 16); |
| if (e && *e) |
| num = lb_find_msg_by_name(argv[2]); |
| if (num >= LIGHTBAR_NUM_SEQUENCES) { |
| fprintf(stderr, "Invalid arg\n"); |
| return -1; |
| } |
| param.seq.num = num; |
| return lb_do_cmd(LIGHTBAR_CMD_SEQ, ¶m, &resp); |
| } |
| |
| if (argc >= 3 && !strcasecmp(argv[1], "program")) { |
| lb_load_program(argv[2], ¶m.set_program); |
| return lb_do_cmd(LIGHTBAR_CMD_SET_PROGRAM, ¶m, &resp); |
| } |
| |
| if (argc == 4) { |
| char *e; |
| param.reg.ctrl = 0xff & strtoull(argv[1], &e, 16); |
| param.reg.reg = 0xff & strtoull(argv[2], &e, 16); |
| param.reg.value = 0xff & strtoull(argv[3], &e, 16); |
| return lb_do_cmd(LIGHTBAR_CMD_REG, ¶m, &resp); |
| } |
| |
| if (argc == 5) { |
| char *e; |
| param.set_rgb.led = strtoull(argv[1], &e, 16); |
| param.set_rgb.red = strtoull(argv[2], &e, 16); |
| param.set_rgb.green = strtoull(argv[3], &e, 16); |
| param.set_rgb.blue = strtoull(argv[4], &e, 16); |
| return lb_do_cmd(LIGHTBAR_CMD_SET_RGB, ¶m, &resp); |
| } |
| |
| /* Only thing left is to try to read an LED value */ |
| if (argc == 2) { |
| char *e; |
| param.get_rgb.led = strtoull(argv[1], &e, 0); |
| if (!(e && *e)) { |
| r = lb_do_cmd(LIGHTBAR_CMD_GET_RGB, ¶m, &resp); |
| if (r) |
| return r; |
| printf("%02x %02x %02x\n", resp.get_rgb.red, |
| resp.get_rgb.green, resp.get_rgb.blue); |
| return 0; |
| } |
| } |
| |
| return lb_help(argv[0]); |
| } |
| |
| /* Create an array to store sizes of motion sense param and response structs. */ |
| #define ST_CMD_SIZE ST_FLD_SIZE(ec_params_motion_sense, cmd) |
| #define ST_PRM_SIZE(SUBCMD) \ |
| (ST_CMD_SIZE + ST_FLD_SIZE(ec_params_motion_sense, SUBCMD)) |
| #define ST_RSP_SIZE(SUBCMD) ST_FLD_SIZE(ec_response_motion_sense, SUBCMD) |
| #define ST_BOTH_SIZES(SUBCMD) { ST_PRM_SIZE(SUBCMD), ST_RSP_SIZE(SUBCMD) } |
| |
| /* |
| * For ectool only, assume no more than 16 sensors. More advanced |
| * implementation would allocate the right amount of memory depending on the |
| * number of sensors. |
| */ |
| #define ECTOOL_MAX_SENSOR 16 |
| |
| static const struct { |
| uint8_t outsize; |
| uint8_t insize; |
| } ms_command_sizes[] = { |
| { ST_PRM_SIZE(dump), |
| ST_RSP_SIZE(dump) + sizeof(struct ec_response_motion_sensor_data) * |
| ECTOOL_MAX_SENSOR }, |
| ST_BOTH_SIZES(info_4), |
| ST_BOTH_SIZES(ec_rate), |
| ST_BOTH_SIZES(sensor_odr), |
| ST_BOTH_SIZES(sensor_range), |
| ST_BOTH_SIZES(kb_wake_angle), |
| ST_BOTH_SIZES(data), |
| { ST_CMD_SIZE, |
| ST_RSP_SIZE(fifo_info) + sizeof(uint16_t) * ECTOOL_MAX_SENSOR }, |
| ST_BOTH_SIZES(fifo_flush), |
| ST_BOTH_SIZES(fifo_read), |
| ST_BOTH_SIZES(perform_calib), |
| ST_BOTH_SIZES(sensor_offset), |
| ST_BOTH_SIZES(list_activities), |
| { ST_PRM_SIZE(set_activity), 0 }, |
| { ST_CMD_SIZE, ST_RSP_SIZE(lid_angle) }, |
| ST_BOTH_SIZES(fifo_int_enable), |
| ST_BOTH_SIZES(spoof), |
| ST_BOTH_SIZES(tablet_mode_threshold), |
| ST_BOTH_SIZES(sensor_scale), |
| ST_BOTH_SIZES(online_calib_read), |
| ST_BOTH_SIZES(get_activity), |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(ms_command_sizes) == MOTIONSENSE_NUM_CMDS); |
| |
| #undef ST_CMD_SIZE |
| #undef ST_PRM_SIZE |
| #undef ST_RSP_SIZE |
| #undef ST_BOTH_SIZES |
| |
| static int ms_help(const char *cmd) |
| { |
| printf("Usage:\n"); |
| printf(" %s - dump all motion data\n", |
| cmd); |
| printf(" %s active - print active flag\n", cmd); |
| printf(" %s info [NUM] - print sensor info\n", cmd); |
| printf(" %s ec_rate NUM [RATE_MS] - set/get sample rate\n", |
| cmd); |
| printf(" %s odr NUM [ODR [ROUNDUP]] - set/get sensor ODR\n", cmd); |
| printf(" %s range NUM [RANGE [ROUNDUP]] - set/get sensor range\n", |
| cmd); |
| printf(" %s offset NUM [X Y Z [TEMP]] - set/get sensor offset\n", |
| cmd); |
| printf(" %s kb_wake NUM - set/get KB wake ang\n", |
| cmd); |
| printf(" %s fifo_info - print fifo info\n", cmd); |
| printf(" %s fifo_int_enable [0/1] - enable/disable/get fifo " |
| "interrupt status\n", |
| cmd); |
| printf(" %s fifo_read MAX_DATA - read fifo data\n", cmd); |
| printf(" %s fifo_flush NUM - trigger fifo interrupt\n", |
| cmd); |
| printf(" %s list_activities - list supported " |
| "activities\n", |
| cmd); |
| printf(" %s set_activity ACT EN - enable/disable activity\n", |
| cmd); |
| printf(" %s get_activity ACT - get activity status\n", |
| cmd); |
| printf(" %s lid_angle - print lid angle\n", cmd); |
| printf(" %s spoof NUM [0/1] [X Y Z] - enable/disable spoofing\n", |
| cmd); |
| printf(" %s spoof NUM activity ACT [0/1] [STATE] - enable/disable " |
| "activity spoofing\n", |
| cmd); |
| printf(" %s tablet_mode_angle ANG HYS - set/get tablet mode " |
| "angle\n", |
| cmd); |
| printf(" %s calibrate NUM - run sensor calibration\n", |
| cmd); |
| |
| return 0; |
| } |
| |
| static void motionsense_display_activities(uint32_t activities) |
| { |
| if (activities & BIT(MOTIONSENSE_ACTIVITY_SIG_MOTION)) |
| printf("%d: Significant motion\n", |
| MOTIONSENSE_ACTIVITY_SIG_MOTION); |
| if (activities & BIT(MOTIONSENSE_ACTIVITY_DOUBLE_TAP)) |
| printf("%d: Double tap\n", MOTIONSENSE_ACTIVITY_DOUBLE_TAP); |
| if (activities & BIT(MOTIONSENSE_ACTIVITY_ORIENTATION)) |
| printf("%d: Orientation\n", MOTIONSENSE_ACTIVITY_ORIENTATION); |
| if (activities & BIT(MOTIONSENSE_ACTIVITY_BODY_DETECTION)) |
| printf("%d: Body Detection\n", |
| MOTIONSENSE_ACTIVITY_BODY_DETECTION); |
| } |
| |
| static int cmd_motionsense(int argc, char **argv) |
| { |
| int i, rv, status_only = (argc == 2); |
| struct ec_params_motion_sense param; |
| /* The largest size using resp as a response buffer */ |
| std::unique_ptr<uint8_t[]> resp_buffer_ptr = |
| std::make_unique<uint8_t[]>( |
| ms_command_sizes[MOTIONSENSE_CMD_DUMP].insize); |
| struct ec_response_motion_sense *resp = |
| (struct ec_response_motion_sense *)resp_buffer_ptr.get(); |
| char *e; |
| /* |
| * Warning: the following strings printed out are read in an |
| * autotest. Do not change string without consulting autotest |
| * for kernel_CrosECSysfsAccel. |
| */ |
| const char *motion_status_string[2][2] = { |
| { "Motion sensing inactive", "0" }, |
| { "Motion sensing active", "1" }, |
| }; |
| /* No motionsense command has more than 7 args. */ |
| if (argc > 7) |
| return ms_help(argv[0]); |
| |
| if ((argc == 1) || (argc == 2 && !strcasecmp(argv[1], "active"))) { |
| param.cmd = MOTIONSENSE_CMD_DUMP; |
| param.dump.max_sensor_count = ECTOOL_MAX_SENSOR; |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv > 0) { |
| printf("%s\n", motion_status_string[!!( |
| resp->dump.module_flags & |
| MOTIONSENSE_MODULE_FLAG_ACTIVE)] |
| [status_only]); |
| if (status_only) |
| return 0; |
| |
| if (resp->dump.sensor_count > ECTOOL_MAX_SENSOR) { |
| printf("Too many sensors to handle: %d", |
| resp->dump.sensor_count); |
| return -1; |
| } |
| for (i = 0; i < resp->dump.sensor_count; i++) { |
| /* |
| * Warning: the following string printed out |
| * is read by an autotest. Do not change string |
| * without consulting autotest for |
| * kernel_CrosECSysfsAccel. |
| */ |
| printf("Sensor %d: ", i); |
| if (resp->dump.sensor[i].flags & |
| MOTIONSENSE_SENSOR_FLAG_PRESENT) |
| printf("%d\t%d\t%d\n", |
| resp->dump.sensor[i].data[0], |
| resp->dump.sensor[i].data[1], |
| resp->dump.sensor[i].data[2]); |
| else |
| printf("None\n"); |
| } |
| return 0; |
| } else { |
| return rv; |
| } |
| } |
| |
| if ((argc == 2 || argc == 3) && !strcasecmp(argv[1], "info")) { |
| int version = 0; |
| int loop_start; |
| int loop_end; |
| int i; |
| |
| rv = get_latest_cmd_version(EC_CMD_MOTION_SENSE_CMD, &version); |
| if (rv < 0) |
| return rv; |
| |
| if (argc == 2) { |
| param.cmd = MOTIONSENSE_CMD_DUMP; |
| param.dump.max_sensor_count = ECTOOL_MAX_SENSOR; |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1, ¶m, |
| ms_command_sizes[param.cmd].outsize, |
| resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv < 0) |
| return rv; |
| if (resp->dump.sensor_count > ECTOOL_MAX_SENSOR) |
| return -1; |
| |
| loop_start = 0; |
| loop_end = resp->dump.sensor_count; |
| } else { |
| loop_start = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| loop_end = loop_start + 1; |
| } |
| param.cmd = MOTIONSENSE_CMD_INFO; |
| |
| for (i = loop_start; i < loop_end; i++) { |
| param.sensor_odr.sensor_num = i; |
| |
| if (argc == 2) { |
| if (i != loop_start) |
| printf("\n"); |
| printf("Index: %d\n", i); |
| } |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, version, |
| ¶m, |
| ms_command_sizes[param.cmd].outsize, |
| resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv < 0) { |
| /* |
| * Return the error code to a higher level if |
| * we're querying about a specific sensor; else |
| * just print the error. |
| */ |
| if (argc == 3) |
| return rv; |
| |
| printf("Error: %d\n", rv); |
| continue; |
| } |
| printf("Type: "); |
| switch (resp->info.type) { |
| case MOTIONSENSE_TYPE_ACCEL: |
| printf("accel\n"); |
| break; |
| case MOTIONSENSE_TYPE_GYRO: |
| printf("gyro\n"); |
| break; |
| case MOTIONSENSE_TYPE_MAG: |
| printf("magnetometer\n"); |
| break; |
| case MOTIONSENSE_TYPE_LIGHT: |
| printf("light\n"); |
| break; |
| case MOTIONSENSE_TYPE_LIGHT_RGB: |
| printf("rgb light\n"); |
| break; |
| case MOTIONSENSE_TYPE_PROX: |
| printf("proximity\n"); |
| break; |
| case MOTIONSENSE_TYPE_ACTIVITY: |
| printf("activity\n"); |
| break; |
| case MOTIONSENSE_TYPE_BARO: |
| printf("barometer\n"); |
| break; |
| case MOTIONSENSE_TYPE_SYNC: |
| printf("sync\n"); |
| break; |
| default: |
| printf("unknown\n"); |
| } |
| |
| printf("Location: "); |
| switch (resp->info.location) { |
| case MOTIONSENSE_LOC_BASE: |
| printf("base\n"); |
| break; |
| case MOTIONSENSE_LOC_LID: |
| printf("lid\n"); |
| break; |
| case MOTIONSENSE_LOC_CAMERA: |
| printf("camera\n"); |
| break; |
| default: |
| printf("unknown\n"); |
| } |
| |
| printf("Chip: "); |
| switch (resp->info.chip) { |
| case MOTIONSENSE_CHIP_KXCJ9: |
| printf("kxcj9\n"); |
| break; |
| case MOTIONSENSE_CHIP_LSM6DS0: |
| printf("lsm6ds0\n"); |
| break; |
| case MOTIONSENSE_CHIP_BMI160: |
| printf("bmi160\n"); |
| break; |
| case MOTIONSENSE_CHIP_SI1141: |
| printf("si1141\n"); |
| break; |
| case MOTIONSENSE_CHIP_KX022: |
| printf("kx022\n"); |
| break; |
| case MOTIONSENSE_CHIP_L3GD20H: |
| printf("l3gd20h\n"); |
| break; |
| case MOTIONSENSE_CHIP_BMA255: |
| printf("bma255\n"); |
| break; |
| case MOTIONSENSE_CHIP_BMP280: |
| printf("bmp280\n"); |
| break; |
| case MOTIONSENSE_CHIP_OPT3001: |
| printf("opt3001\n"); |
| break; |
| case MOTIONSENSE_CHIP_CM32183: |
| printf("cm32183\n"); |
| break; |
| case MOTIONSENSE_CHIP_CM36781: |
| printf("cm36781\n"); |
| break; |
| case MOTIONSENSE_CHIP_BH1730: |
| printf("bh1730\n"); |
| break; |
| case MOTIONSENSE_CHIP_GPIO: |
| printf("gpio\n"); |
| break; |
| case MOTIONSENSE_CHIP_LIS2DH: |
| printf("lis2dh\n"); |
| break; |
| case MOTIONSENSE_CHIP_LSM6DSM: |
| printf("lsm6dsm\n"); |
| break; |
| case MOTIONSENSE_CHIP_LIS2DE: |
| printf("lis2de\n"); |
| break; |
| case MOTIONSENSE_CHIP_LIS2MDL: |
| printf("lis2mdl\n"); |
| break; |
| case MOTIONSENSE_CHIP_LSM6DS3: |
| printf("lsm6ds3\n"); |
| break; |
| case MOTIONSENSE_CHIP_LSM6DSO: |
| printf("lsm6dso\n"); |
| break; |
| case MOTIONSENSE_CHIP_LNG2DM: |
| printf("lng2dm\n"); |
| break; |
| case MOTIONSENSE_CHIP_TCS3400: |
| printf("tcs3400\n"); |
| break; |
| case MOTIONSENSE_CHIP_LIS2DW12: |
| printf("lis2dw12\n"); |
| break; |
| case MOTIONSENSE_CHIP_LIS2DWL: |
| printf("lis2dwl\n"); |
| break; |
| case MOTIONSENSE_CHIP_LIS2DS: |
| printf("lis2ds\n"); |
| break; |
| case MOTIONSENSE_CHIP_BMI260: |
| printf("bmi260\n"); |
| break; |
| case MOTIONSENSE_CHIP_ICM426XX: |
| printf("icm426xx\n"); |
| break; |
| case MOTIONSENSE_CHIP_ICM42607: |
| printf("icm42607\n"); |
| break; |
| case MOTIONSENSE_CHIP_BMI323: |
| printf("bmi323\n"); |
| break; |
| case MOTIONSENSE_CHIP_BMA422: |
| printf("bma422\n"); |
| break; |
| case MOTIONSENSE_CHIP_BMI220: |
| printf("bmi220\n"); |
| break; |
| case MOTIONSENSE_CHIP_VEML3328: |
| printf("veml3328\n"); |
| break; |
| default: |
| printf("unknown\n"); |
| } |
| |
| if (version >= 3) { |
| printf("Min Frequency: %d mHz\n", |
| resp->info_3.min_frequency); |
| printf("Max Frequency: %d mHz\n", |
| resp->info_3.max_frequency); |
| printf("FIFO Max Event Count: %d\n", |
| resp->info_3.fifo_max_event_count); |
| } |
| if (version >= 4) { |
| printf("Flags: %d\n", |
| resp->info_4.flags); |
| } |
| } |
| |
| return 0; |
| } |
| |
| if (argc > 2 && !strcasecmp(argv[1], "ec_rate")) { |
| param.cmd = MOTIONSENSE_CMD_EC_RATE; |
| param.ec_rate.data = EC_MOTION_SENSE_NO_VALUE; |
| param.sensor_odr.sensor_num = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| if (argc == 4) { |
| param.ec_rate.data = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[3]); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| |
| if (rv < 0) |
| return rv; |
| |
| printf("%d\n", resp->ec_rate.ret); |
| return 0; |
| } |
| |
| if (argc > 2 && !strcasecmp(argv[1], "odr")) { |
| param.cmd = MOTIONSENSE_CMD_SENSOR_ODR; |
| param.sensor_odr.data = EC_MOTION_SENSE_NO_VALUE; |
| param.sensor_odr.roundup = 1; |
| |
| param.sensor_odr.sensor_num = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| |
| if (argc >= 4) { |
| param.sensor_odr.data = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[3]); |
| return -1; |
| } |
| } |
| |
| if (argc == 5) { |
| param.sensor_odr.roundup = strtol(argv[4], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[4]); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| |
| if (rv < 0) |
| return rv; |
| |
| printf("%d\n", resp->sensor_odr.ret); |
| return 0; |
| } |
| |
| if (argc > 2 && !strcasecmp(argv[1], "range")) { |
| param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE; |
| param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE; |
| param.sensor_range.roundup = 1; |
| |
| param.sensor_range.sensor_num = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| |
| if (argc >= 4) { |
| param.sensor_range.data = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[3]); |
| return -1; |
| } |
| } |
| |
| if (argc == 5) { |
| param.sensor_odr.roundup = strtol(argv[4], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[4]); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| |
| if (rv < 0) |
| return rv; |
| |
| printf("%d\n", resp->sensor_range.ret); |
| return 0; |
| } |
| |
| if (argc < 4 && !strcasecmp(argv[1], "kb_wake")) { |
| param.cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE; |
| param.kb_wake_angle.data = EC_MOTION_SENSE_NO_VALUE; |
| |
| if (argc == 3) { |
| param.kb_wake_angle.data = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| |
| if (rv < 0) |
| return rv; |
| |
| printf("%d\n", resp->kb_wake_angle.ret); |
| return 0; |
| } |
| |
| if (argc < 5 && !strcasecmp(argv[1], "tablet_mode_angle")) { |
| param.cmd = MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE; |
| /* |
| * EC_MOTION_SENSE_NO_VALUE indicates to the EC that host is |
| * attempting to only read the current values. |
| */ |
| param.tablet_mode_threshold.lid_angle = |
| EC_MOTION_SENSE_NO_VALUE; |
| param.tablet_mode_threshold.hys_degree = |
| EC_MOTION_SENSE_NO_VALUE; |
| |
| if (argc == 4) { |
| param.tablet_mode_threshold.lid_angle = |
| strtol(argv[2], &e, 0); |
| |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| |
| param.tablet_mode_threshold.hys_degree = |
| strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[3]); |
| return -1; |
| } |
| } else if (argc != 2) { |
| return ms_help(argv[0]); |
| } |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| |
| if (rv < 0) |
| return rv; |
| |
| printf("tablet_mode_angle=%d hys=%d\n", |
| resp->tablet_mode_threshold.lid_angle, |
| resp->tablet_mode_threshold.hys_degree); |
| |
| return 0; |
| } |
| |
| if (argc == 2 && !strcasecmp(argv[1], "fifo_info")) { |
| int sensor_count; |
| |
| param.cmd = MOTIONSENSE_CMD_DUMP; |
| param.dump.max_sensor_count = 0; |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv < 0) |
| return rv; |
| sensor_count = resp->dump.sensor_count; |
| |
| param.cmd = MOTIONSENSE_CMD_FIFO_INFO; |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv < 0) |
| return rv; |
| |
| printf("Size: %d\n", resp->fifo_info.size); |
| printf("Count: %d\n", resp->fifo_info.count); |
| printf("Timestamp:%" PRIx32 "\n", resp->fifo_info.timestamp); |
| printf("Total lost: %d\n", resp->fifo_info.total_lost); |
| for (i = 0; i < sensor_count; i++) { |
| int lost = resp->fifo_info.lost[i]; |
| if (lost != 0) |
| printf("Lost %d: %d\n", i, lost); |
| } |
| return 0; |
| } |
| |
| if (argc >= 2 && !strcasecmp(argv[1], "fifo_int_enable")) { |
| param.cmd = MOTIONSENSE_CMD_FIFO_INT_ENABLE; |
| if (argc == 3) |
| param.fifo_int_enable.enable = strtol(argv[2], &e, 0); |
| else |
| param.fifo_int_enable.enable = EC_MOTION_SENSE_NO_VALUE; |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv < 0) |
| return rv; |
| |
| printf("%d\n", resp->fifo_int_enable.ret); |
| return 0; |
| } |
| |
| if (argc == 3 && !strcasecmp(argv[1], "fifo_read")) { |
| /* large number to test fragmentation */ |
| struct { |
| uint32_t number_data; |
| struct ec_response_motion_sensor_data data[512]; |
| } fifo_read_buffer = { |
| .number_data = UINT32_MAX, |
| }; |
| int print_data = 0, max_data = strtol(argv[2], &e, 0); |
| |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| while (fifo_read_buffer.number_data != 0 && |
| print_data < max_data) { |
| struct ec_response_motion_sensor_data *vector; |
| param.cmd = MOTIONSENSE_CMD_FIFO_READ; |
| param.fifo_read.max_data_vector = |
| MIN(ARRAY_SIZE(fifo_read_buffer.data), |
| max_data - print_data); |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2, ¶m, |
| ms_command_sizes[param.cmd].outsize, |
| &fifo_read_buffer, ec_max_insize); |
| if (rv < 0) |
| return rv; |
| |
| print_data += fifo_read_buffer.number_data; |
| for (i = 0; i < fifo_read_buffer.number_data; i++) { |
| vector = &fifo_read_buffer.data[i]; |
| if (vector->flags & |
| (MOTIONSENSE_SENSOR_FLAG_TIMESTAMP | |
| MOTIONSENSE_SENSOR_FLAG_FLUSH)) { |
| printf("Timestamp:%" PRIx32 "%s\n", |
| vector->timestamp, |
| (vector->flags & MOTIONSENSE_SENSOR_FLAG_FLUSH ? |
| " - Flush" : |
| "")); |
| } else { |
| printf("Sensor %d: %d\t%d\t%d " |
| "(as uint16: %u\t%u\t%u)\n", |
| vector->sensor_num, |
| vector->data[0], vector->data[1], |
| vector->data[2], vector->data[0], |
| vector->data[1], |
| vector->data[2]); |
| } |
| } |
| } |
| return 0; |
| } |
| if (argc == 3 && !strcasecmp(argv[1], "fifo_flush")) { |
| param.cmd = MOTIONSENSE_CMD_FIFO_FLUSH; |
| |
| param.sensor_odr.sensor_num = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| |
| return rv < 0 ? rv : 0; |
| } |
| |
| if (argc == 3 && !strcasecmp(argv[1], "calibrate")) { |
| param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB; |
| param.perform_calib.enable = 1; |
| param.perform_calib.sensor_num = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| |
| if (rv < 0) |
| return rv; |
| |
| printf("--- Calibrated well ---\n"); |
| printf("New offset vector: X:%d, Y:%d, Z:%d\n", |
| resp->perform_calib.offset[0], |
| resp->perform_calib.offset[1], |
| resp->perform_calib.offset[2]); |
| if (resp->perform_calib.temp == |
| EC_MOTION_SENSE_INVALID_CALIB_TEMP) |
| printf("Temperature at calibration unknown\n"); |
| else |
| printf("Temperature at calibration: %d.%02d C\n", |
| resp->perform_calib.temp / 100, |
| resp->perform_calib.temp % 100); |
| return 0; |
| } |
| |
| if (argc >= 3 && !strcasecmp(argv[1], "offset")) { |
| param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET; |
| param.sensor_offset.flags = 0; |
| param.sensor_offset.temp = EC_MOTION_SENSE_INVALID_CALIB_TEMP; |
| |
| param.sensor_offset.sensor_num = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| |
| if (argc >= 4) { |
| /* Regarded as a command to set offset */ |
| if (argc >= 6 && argc < 8) { |
| /* Set offset : X, Y, Z */ |
| param.sensor_offset.flags = |
| MOTION_SENSE_SET_OFFSET; |
| for (i = 0; i < 3; i++) { |
| param.sensor_offset.offset[i] = |
| strtol(argv[3 + i], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", |
| argv[3 + i]); |
| return -1; |
| } |
| } |
| if (argc == 7) { |
| /* Set offset : Temperature */ |
| param.sensor_offset.temp = |
| strtol(argv[6], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", |
| argv[6]); |
| return -1; |
| } |
| } |
| } else { |
| return ms_help(argv[0]); |
| } |
| } |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 1, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| |
| if (rv < 0) |
| return rv; |
| |
| printf("Offset vector: X:%d, Y:%d, Z:%d\n", |
| resp->sensor_offset.offset[0], |
| resp->sensor_offset.offset[1], |
| resp->sensor_offset.offset[2]); |
| if (resp->sensor_offset.temp == |
| EC_MOTION_SENSE_INVALID_CALIB_TEMP) |
| printf("temperature at calibration unknown\n"); |
| else |
| printf("temperature at calibration: %d.%02d C\n", |
| resp->sensor_offset.temp / 100, |
| resp->sensor_offset.temp % 100); |
| return 0; |
| } |
| |
| if (argc == 2 && !strcasecmp(argv[1], "list_activities")) { |
| param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES; |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv < 0) |
| return rv; |
| |
| printf("Enabled:\n"); |
| motionsense_display_activities(resp->list_activities.enabled); |
| printf("Disabled:\n"); |
| motionsense_display_activities(resp->list_activities.disabled); |
| return 0; |
| } |
| if (argc == 4 && !strcasecmp(argv[1], "set_activity")) { |
| param.cmd = MOTIONSENSE_CMD_SET_ACTIVITY; |
| param.set_activity.activity = strtol(argv[2], &e, 0); |
| param.set_activity.enable = strtol(argv[3], &e, 0); |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv < 0) |
| return rv; |
| return 0; |
| } |
| if (argc == 3 && !strcasecmp(argv[1], "get_activity")) { |
| param.cmd = MOTIONSENSE_CMD_GET_ACTIVITY; |
| param.get_activity.activity = strtol(argv[2], &e, 0); |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv < 0) |
| return rv; |
| printf("State: %d\n", resp->get_activity.state); |
| return 0; |
| } |
| if (argc == 2 && !strcasecmp(argv[1], "lid_angle")) { |
| param.cmd = MOTIONSENSE_CMD_LID_ANGLE; |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv < 0) |
| return rv; |
| |
| printf("Lid angle: "); |
| if (resp->lid_angle.value == LID_ANGLE_UNRELIABLE) |
| printf("unreliable\n"); |
| else |
| printf("%d\n", resp->lid_angle.value); |
| |
| return 0; |
| } |
| |
| if (argc >= 3 && !strcasecmp(argv[1], "spoof")) { |
| param.cmd = MOTIONSENSE_CMD_SPOOF; |
| /* By default, just query the current spoof status. */ |
| param.spoof.spoof_enable = MOTIONSENSE_SPOOF_MODE_QUERY; |
| param.spoof.sensor_id = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", argv[2]); |
| return -1; |
| } |
| /* spoof activity state */ |
| if (argc >= 5 && !strcasecmp(argv[3], "activity")) { |
| int enable = 0; |
| |
| param.spoof.activity_num = strtol(argv[4], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Base %s arg.\n", argv[4]); |
| return -1; |
| } |
| if (argc >= 6) { |
| enable = strtol(argv[5], &e, 0); |
| if ((e && *e) || (enable != 0 && enable != 1)) { |
| fprintf(stderr, "Bad %s arg.\n", |
| argv[5]); |
| return -1; |
| } |
| } |
| if ((enable == 1) && (argc == 6)) { |
| /* Enable spoofing, but lock to current state */ |
| param.spoof.spoof_enable = |
| MOTIONSENSE_SPOOF_MODE_LOCK_CURRENT; |
| } else if ((enable == 1) && (argc == 7)) { |
| /* Enable spoofing, but use provided state */ |
| int state = strtol(argv[6], &e, 0); |
| |
| if ((e && *e) || (state != 0 && state != 1)) { |
| fprintf(stderr, "Bad %s arg.\n", |
| argv[6]); |
| return -1; |
| } |
| param.spoof.activity_state = state; |
| param.spoof.spoof_enable = |
| MOTIONSENSE_SPOOF_MODE_CUSTOM; |
| } else if ((enable == 0) && (argc == 6)) { |
| param.spoof.spoof_enable = |
| MOTIONSENSE_SPOOF_MODE_DISABLE; |
| } else if (argc != 5) { |
| return ms_help(argv[0]); |
| } |
| /* spoof accel data */ |
| } else if (argc >= 4) { |
| int enable, i; |
| int16_t val; |
| |
| enable = strtol(argv[3], &e, 0); |
| if ((e && *e) || (enable != 0 && enable != 1)) { |
| fprintf(stderr, "Bad %s arg.\n", argv[3]); |
| return -1; |
| } |
| |
| if ((enable == 1) && (argc == 4)) { |
| /* |
| * Enable spoofing, but lock to current sensor |
| * values. |
| */ |
| param.spoof.spoof_enable = |
| MOTIONSENSE_SPOOF_MODE_LOCK_CURRENT; |
| } else if ((enable == 1) && (argc == 7)) { |
| /* |
| * Enable spoofing, but use provided component |
| * values. |
| */ |
| param.spoof.spoof_enable = |
| MOTIONSENSE_SPOOF_MODE_CUSTOM; |
| for (i = 0; i < 3; i++) { |
| val = strtol(argv[4 + i], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad %s arg.\n", |
| argv[4 + i]); |
| return -1; |
| } |
| param.spoof.components[i] = val; |
| } |
| } else if (enable == 0) { |
| param.spoof.spoof_enable = |
| MOTIONSENSE_SPOOF_MODE_DISABLE; |
| } else { |
| return ms_help(argv[0]); |
| } |
| } |
| |
| rv = ec_command(EC_CMD_MOTION_SENSE_CMD, 2, ¶m, |
| ms_command_sizes[param.cmd].outsize, resp, |
| ms_command_sizes[param.cmd].insize); |
| if (rv < 0) |
| return rv; |
| |
| if (param.spoof.spoof_enable == MOTIONSENSE_SPOOF_MODE_QUERY) |
| /* |
| * Response is the current spoof status of the |
| * sensor. |
| */ |
| printf("Sensor %d spoof mode is %s.\n", |
| param.spoof.sensor_id, |
| resp->spoof.ret ? "enabled" : "disabled"); |
| |
| return 0; |
| } |
| |
| return ms_help(argv[0]); |
| } |
| |
| static int cmd_watchdog_info(int argc, char *argv[]) |
| { |
| struct ec_params_hostcmd_watchdog_info p; |
| struct ec_response_hostcmd_watchdog_info r; |
| int rv; |
| |
| if (argc == 1) { |
| p.reset_stats = false; |
| } else if (argc == 2) { |
| if (strcasecmp(argv[1], "reset_stats") == 0) |
| p.reset_stats = true; |
| else |
| goto usage; |
| } else { |
| goto usage; |
| } |
| |
| rv = ec_command(EC_CMD_HOSTCMD_WATCHDOG_INFO, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Watchdog Period: %d ms\n", r.watchdog_period_ms); |
| printf("Watchdog Warning Period: %d ms\n", |
| r.watchdog_warning_period_ms); |
| printf("Watchdog Reload Period Nominal: %d ms\n", |
| r.watchdog_reload_period_nominal_ms); |
| printf("Watchdog Stats Elapsed Time: %" PRId64 ".%03" PRId64 " s\n", |
| r.watchdog_stats_elapsed_ms / 1000, |
| r.watchdog_stats_elapsed_ms % 1000); |
| printf("Watchdog Reload Count: %u\n", r.watchdog_reload_count); |
| printf("Watchdog Reload Period Max: %d ms @ %" PRId64 ".%03" PRId64 |
| " s\n", |
| r.watchdog_reload_period_max_ms, |
| r.watchdog_reload_period_max_ts_ms / 1000, |
| r.watchdog_reload_period_max_ts_ms % 1000); |
| printf("Watchdog Reload Period Average: %" PRId64 " ms\n", |
| r.watchdog_reload_count > 0 ? |
| r.watchdog_stats_elapsed_ms / r.watchdog_reload_count : |
| 0); |
| if (p.reset_stats) |
| printf("Watchdog stats reset.\n"); |
| return 0; |
| |
| usage: |
| fprintf(stderr, "Usage: %s [reset_stats]\n", argv[0]); |
| return -1; |
| } |
| |
| int cmd_next_event(int argc, char *argv[]) |
| { |
| uint8_t *rdata = (uint8_t *)ec_inbuf; |
| int rv; |
| int i; |
| |
| rv = ec_command(EC_CMD_GET_NEXT_EVENT, 0, NULL, 0, rdata, |
| ec_max_insize); |
| if (rv < 0) |
| return rv; |
| |
| printf("Next event is 0x%02x\n", rdata[0]); |
| if (rv > 1) { |
| printf("Event data:\n"); |
| for (i = 1; i < rv; ++i) { |
| printf("%02x ", rdata[i]); |
| if (!(i & 0xf)) |
| printf("\n"); |
| } |
| printf("\n"); |
| } |
| |
| return 0; |
| } |
| |
| static int find_led_color_by_name(const char *color) |
| { |
| int i; |
| |
| for (i = 0; i < EC_LED_COLOR_COUNT; ++i) |
| if (!strcasecmp(color, led_color_names[i])) |
| return i; |
| |
| return -1; |
| } |
| |
| static int find_led_id_by_name(const char *led) |
| { |
| int i; |
| |
| for (i = 0; i < EC_LED_ID_COUNT; ++i) |
| if (!strcasecmp(led, led_names[i])) |
| return i; |
| |
| return -1; |
| } |
| |
| int cmd_led(int argc, char *argv[]) |
| { |
| struct ec_params_led_control p; |
| struct ec_response_led_control r; |
| char *e, *ptr; |
| int rv, i, j; |
| |
| memset(p.brightness, 0, sizeof(p.brightness)); |
| p.flags = 0; |
| |
| if (argc < 3) { |
| fprintf(stderr, |
| "Usage: %s <name> <query | auto | " |
| "off | <color> | <color>=<value>...>\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p.led_id = find_led_id_by_name(argv[1]); |
| if (p.led_id == (uint8_t)-1) { |
| fprintf(stderr, "Bad LED name: %s\n", argv[1]); |
| fprintf(stderr, "Valid LED names: "); |
| for (i = 0; i < EC_LED_ID_COUNT; i++) |
| fprintf(stderr, "%s ", led_names[i]); |
| fprintf(stderr, "\n"); |
| return -1; |
| } |
| |
| if (!strcasecmp(argv[2], "query")) { |
| p.flags = EC_LED_FLAGS_QUERY; |
| rv = ec_command(EC_CMD_LED_CONTROL, 1, &p, sizeof(p), &r, |
| sizeof(r)); |
| printf("Brightness range for LED %d:\n", p.led_id); |
| if (rv < 0) { |
| fprintf(stderr, "Error: Unsupported LED.\n"); |
| return rv; |
| } |
| for (i = 0; i < EC_LED_COLOR_COUNT; ++i) |
| printf("\t%s\t: 0x%x\n", led_color_names[i], |
| r.brightness_range[i]); |
| return 0; |
| } |
| |
| if (!strcasecmp(argv[2], "off")) { |
| /* Brightness initialized to 0 for each color. */ |
| } else if (!strcasecmp(argv[2], "auto")) { |
| p.flags = EC_LED_FLAGS_AUTO; |
| } else if ((i = find_led_color_by_name(argv[2])) != -1) { |
| p.brightness[i] = 0xff; |
| } else { |
| for (i = 2; i < argc; ++i) { |
| ptr = strtok(argv[i], "="); |
| j = find_led_color_by_name(ptr); |
| if (j == -1) { |
| fprintf(stderr, "Bad color name: %s\n", ptr); |
| fprintf(stderr, "Valid colors: "); |
| for (j = 0; j < EC_LED_COLOR_COUNT; j++) |
| fprintf(stderr, "%s ", |
| led_color_names[j]); |
| fprintf(stderr, "\n"); |
| return -1; |
| } |
| ptr = strtok(NULL, "="); |
| if (ptr == NULL) { |
| fprintf(stderr, "Missing brightness value\n"); |
| return -1; |
| } |
| p.brightness[j] = strtol(ptr, &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad brightness: %s\n", ptr); |
| return -1; |
| } |
| } |
| } |
| |
| rv = ec_command(EC_CMD_LED_CONTROL, 1, &p, sizeof(p), &r, sizeof(r)); |
| return (rv < 0 ? rv : 0); |
| } |
| |
| int cmd_usb_charge_set_mode(int argc, char *argv[]) |
| { |
| struct ec_params_usb_charge_set_mode p; |
| char *e; |
| int rv; |
| |
| if (argc != 3 && argc != 4) { |
| fprintf(stderr, |
| "Usage: %s <port_id> <mode_id> [<inhibit_charge>]\n", |
| argv[0]); |
| return -1; |
| } |
| p.usb_port_id = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port ID.\n"); |
| return -1; |
| } |
| p.mode = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad mode ID.\n"); |
| return -1; |
| } |
| p.inhibit_charge = 0; |
| if (argc == 4) { |
| p.inhibit_charge = strtol(argv[3], &e, 0); |
| if ((e && *e) || |
| (p.inhibit_charge != 0 && p.inhibit_charge != 1)) { |
| fprintf(stderr, "Bad value\n"); |
| return -1; |
| } |
| } |
| |
| printf("Setting port %d to mode %d inhibit_charge %d...\n", |
| p.usb_port_id, p.mode, p.inhibit_charge); |
| |
| rv = ec_command(EC_CMD_USB_CHARGE_SET_MODE, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("USB charging mode set.\n"); |
| return 0; |
| } |
| |
| int cmd_usb_mux(int argc, char *argv[]) |
| { |
| struct ec_params_usb_mux p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <mux>\n", argv[0]); |
| return -1; |
| } |
| |
| p.mux = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad mux value.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_USB_MUX, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Set USB mux to 0x%x.\n", p.mux); |
| |
| return 0; |
| } |
| |
| int cmd_usb_pd(int argc, char *argv[]) |
| { |
| const char *role_str[] = { "", "toggle", "toggle-off", |
| "sink", "source", "freeze" }; |
| const char *mux_str[] = { "", "none", "usb", "dp", "dock", "auto" }; |
| const char *swap_str[] = { "", "dr_swap", "pr_swap", "vconn_swap" }; |
| struct ec_params_usb_pd_control p; |
| struct ec_response_usb_pd_control_v2 *r_v2 = |
| (struct ec_response_usb_pd_control_v2 *)ec_inbuf; |
| struct ec_response_usb_pd_control_v1 *r_v1 = |
| (struct ec_response_usb_pd_control_v1 *)ec_inbuf; |
| struct ec_response_usb_pd_control *r = |
| (struct ec_response_usb_pd_control *)ec_inbuf; |
| int rv, i, j; |
| int option_ok; |
| char *e; |
| int cmdver; |
| |
| BUILD_ASSERT(ARRAY_SIZE(role_str) == USB_PD_CTRL_ROLE_COUNT); |
| BUILD_ASSERT(ARRAY_SIZE(mux_str) == USB_PD_CTRL_MUX_COUNT); |
| BUILD_ASSERT(ARRAY_SIZE(swap_str) == USB_PD_CTRL_SWAP_COUNT); |
| p.role = USB_PD_CTRL_ROLE_NO_CHANGE; |
| p.mux = USB_PD_CTRL_MUX_NO_CHANGE; |
| p.swap = USB_PD_CTRL_SWAP_NONE; |
| |
| if (argc < 2) { |
| fprintf(stderr, "No port specified.\n"); |
| return -1; |
| } |
| |
| p.port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Invalid param (port)\n"); |
| return -1; |
| } |
| |
| for (i = 2; i < argc; ++i) { |
| option_ok = 0; |
| if (!strcmp(argv[i], "auto")) { |
| if (argc != 3) { |
| fprintf(stderr, "\"auto\" may not be used " |
| "with other options.\n"); |
| return -1; |
| } |
| p.role = USB_PD_CTRL_ROLE_TOGGLE_ON; |
| p.mux = USB_PD_CTRL_MUX_AUTO; |
| continue; |
| } |
| |
| for (j = 0; j < ARRAY_SIZE(role_str); ++j) { |
| if (!strcmp(argv[i], role_str[j])) { |
| if (p.role != USB_PD_CTRL_ROLE_NO_CHANGE) { |
| fprintf(stderr, |
| "Only one role allowed.\n"); |
| return -1; |
| } |
| p.role = j; |
| option_ok = 1; |
| break; |
| } |
| } |
| if (option_ok) |
| continue; |
| |
| for (j = 0; j < ARRAY_SIZE(mux_str); ++j) { |
| if (!strcmp(argv[i], mux_str[j])) { |
| if (p.mux != USB_PD_CTRL_MUX_NO_CHANGE) { |
| fprintf(stderr, |
| "Only one mux type allowed.\n"); |
| return -1; |
| } |
| p.mux = j; |
| option_ok = 1; |
| break; |
| } |
| } |
| if (option_ok) |
| continue; |
| |
| for (j = 0; j < ARRAY_SIZE(swap_str); ++j) { |
| if (!strcmp(argv[i], swap_str[j])) { |
| if (p.swap != USB_PD_CTRL_SWAP_NONE) { |
| fprintf(stderr, |
| "Only one swap type allowed.\n"); |
| return -1; |
| } |
| p.swap = j; |
| option_ok = 1; |
| break; |
| } |
| } |
| |
| if (!option_ok) { |
| fprintf(stderr, "Unknown option: %s\n", argv[i]); |
| return -1; |
| } |
| } |
| |
| if (ec_cmd_version_supported(EC_CMD_USB_PD_CONTROL, 2)) |
| cmdver = 2; |
| else if (ec_cmd_version_supported(EC_CMD_USB_PD_CONTROL, 1)) |
| cmdver = 1; |
| else |
| cmdver = 0; |
| |
| rv = ec_command(EC_CMD_USB_PD_CONTROL, cmdver, &p, sizeof(p), ec_inbuf, |
| ec_max_insize); |
| |
| if (rv < 0 || argc != 2) |
| return (rv < 0) ? rv : 0; |
| |
| if (cmdver == 0) { |
| printf("Port C%d is %sabled, Role:%s Polarity:CC%d State:%d\n", |
| p.port, (r->enabled) ? "en" : "dis", |
| r->role == PD_ROLE_SOURCE ? "SRC" : "SNK", |
| r->polarity + 1, r->state); |
| } else { |
| printf("Port C%d: %s, %s State:%s\n" |
| "Role:%s %s%s, Polarity:CC%d\n", |
| p.port, |
| (r_v1->enabled & PD_CTRL_RESP_ENABLED_COMMS) ? |
| "enabled" : |
| "disabled", |
| (r_v1->enabled & PD_CTRL_RESP_ENABLED_CONNECTED) ? |
| "connected" : |
| "disconnected", |
| r_v1->state, |
| |
| (r_v1->role & PD_CTRL_RESP_ROLE_POWER) ? "SRC" : "SNK", |
| (r_v1->role & PD_CTRL_RESP_ROLE_DATA) ? "DFP" : "UFP", |
| (r_v1->role & PD_CTRL_RESP_ROLE_VCONN) ? " VCONN" : "", |
| r_v1->polarity + 1); |
| |
| if (cmdver == 2) { |
| printf("CC State:"); |
| if (r_v2->cc_state == PD_CC_NONE) |
| printf("None"); |
| else if (r_v2->cc_state == PD_CC_UFP_AUDIO_ACC) |
| printf("UFP Audio accessory"); |
| else if (r_v2->cc_state == PD_CC_UFP_DEBUG_ACC) |
| printf("UFP Debug accessory"); |
| else if (r_v2->cc_state == PD_CC_UFP_ATTACHED) |
| printf("UFP attached"); |
| else if (r_v2->cc_state == PD_CC_DFP_DEBUG_ACC) |
| printf("DFP Debug accessory"); |
| else if (r_v2->cc_state == PD_CC_DFP_ATTACHED) |
| printf("DFP attached"); |
| else |
| printf("UNKNOWN"); |
| printf("\n"); |
| |
| if (r_v2->dp_mode) { |
| printf("DP pin mode:"); |
| if (r_v2->dp_mode == MODE_DP_PIN_A) |
| printf("A"); |
| else if (r_v2->dp_mode == MODE_DP_PIN_B) |
| printf("B"); |
| else if (r_v2->dp_mode == MODE_DP_PIN_C) |
| printf("C"); |
| else if (r_v2->dp_mode == MODE_DP_PIN_D) |
| printf("D"); |
| else if (r_v2->dp_mode == MODE_DP_PIN_E) |
| printf("E"); |
| else if (r_v2->dp_mode == MODE_DP_PIN_F) |
| printf("F"); |
| else |
| printf("UNKNOWN"); |
| printf("\n"); |
| } |
| |
| printf("Cable type:%s\n", |
| r_v2->control_flags & USB_PD_CTRL_ACTIVE_CABLE ? |
| "Active" : |
| "Passive"); |
| |
| printf("TBT Adapter type:%s\n", |
| r_v2->control_flags & |
| USB_PD_CTRL_TBT_LEGACY_ADAPTER ? |
| "Legacy" : |
| "Gen3"); |
| |
| printf("Optical Cable:%s\n", |
| r_v2->control_flags & USB_PD_CTRL_OPTICAL_CABLE ? |
| "True" : |
| "False"); |
| |
| printf("Link LSRX Communication:%s-directional\n", |
| r_v2->control_flags & |
| USB_PD_CTRL_ACTIVE_LINK_UNIDIR ? |
| "Uni" : |
| "Bi"); |
| |
| printf("TBT Cable Speed:"); |
| switch (r_v2->cable_speed) { |
| case TBT_SS_U31_GEN1: |
| printf("TBT Gen1"); |
| break; |
| case TBT_SS_U32_GEN1_GEN2: |
| printf("TBT Gen1 and TBT Gen2"); |
| break; |
| case TBT_SS_TBT_GEN3: |
| printf("TBT Gen3"); |
| break; |
| default: |
| printf("UNKNOWN"); |
| } |
| printf("\n"); |
| |
| printf("Rounded support: 3rd Gen %srounded support\n", |
| r_v2->cable_gen ? "and 4th Gen " : ""); |
| } |
| /* If connected to a PD device, then print port partner info */ |
| if ((r_v1->enabled & PD_CTRL_RESP_ENABLED_CONNECTED) && |
| (r_v1->enabled & PD_CTRL_RESP_ENABLED_PD_CAPABLE)) |
| printf("PD Partner Capabilities:\n%s%s%s%s", |
| (r_v1->role & PD_CTRL_RESP_ROLE_DR_POWER) ? |
| " DR power\n" : |
| "", |
| (r_v1->role & PD_CTRL_RESP_ROLE_DR_DATA) ? |
| " DR data\n" : |
| "", |
| (r_v1->role & PD_CTRL_RESP_ROLE_USB_COMM) ? |
| " USB capable\n" : |
| "", |
| (r_v1->role & PD_CTRL_RESP_ROLE_UNCONSTRAINED) ? |
| " Unconstrained power\n" : |
| ""); |
| } |
| return 0; |
| } |
| |
| static int usb_pd_dps_control(bool en) |
| { |
| struct ec_params_usb_pd_dps_control p; |
| |
| p.enable = en; |
| int rv = ec_command(EC_CMD_USB_PD_DPS_CONTROL, 0, &p, sizeof(p), NULL, |
| 0); |
| if (rv < 0) |
| return rv; |
| |
| return 0; |
| } |
| |
| static int usb_pd_dps_status(void) |
| { |
| struct ec_response_usb_pd_dps_status r; |
| |
| int rv = |
| ec_command(EC_CMD_USB_PD_DPS_STATUS, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| if (!r.is_enabled) { |
| printf("DPS Disabled\n"); |
| return 0; |
| } |
| |
| if (r.port < 0) { |
| printf("No charger attached\n"); |
| return 0; |
| } |
| |
| printf("C%d DPS Enabled\n" |
| "Requested: %dmV/%dmA\n" |
| "Measured: %dmV/%dmA/%dmW\n" |
| "Efficient: %dmV\n" |
| "Batt: %dmv\n" |
| "PDMaxMV: %dmV\n", |
| r.port, r.requested_voltage, r.requested_current, |
| r.input_voltage, r.input_current, r.input_power, |
| r.efficient_voltage, r.battery_voltage, r.max_voltage); |
| |
| return 0; |
| } |
| |
| int cmd_usb_pd_dps(int argc, char *argv[]) |
| { |
| /* |
| * Set up requested flags. If no flags were specified, p.mask will |
| * be 0 and nothing will change. |
| */ |
| if (argc <= 1) { |
| fprintf(stderr, "Usage: %s [enable|disable|status]\n", argv[0]); |
| return -1; |
| } |
| |
| if (!strcasecmp(argv[1], "enable")) { |
| return usb_pd_dps_control(true); |
| } else if (!strcasecmp(argv[1], "disable")) { |
| return usb_pd_dps_control(false); |
| } else if (!strcasecmp(argv[1], "status")) { |
| return usb_pd_dps_status(); |
| } else { |
| fprintf(stderr, "Usage: %s [enable|disable|status]\n", argv[0]); |
| return -1; |
| } |
| } |
| |
| static void print_pd_power_info(struct ec_response_usb_pd_power_info *r) |
| { |
| switch (r->role) { |
| case USB_PD_PORT_POWER_DISCONNECTED: |
| printf("Disconnected"); |
| break; |
| case USB_PD_PORT_POWER_SOURCE: |
| printf("SRC"); |
| break; |
| case USB_PD_PORT_POWER_SINK: |
| printf("SNK"); |
| break; |
| case USB_PD_PORT_POWER_SINK_NOT_CHARGING: |
| printf("SNK (not charging)"); |
| break; |
| default: |
| printf("Unknown"); |
| } |
| |
| if ((r->role == USB_PD_PORT_POWER_SOURCE) && (r->meas.current_max)) |
| printf(" %dmA", r->meas.current_max); |
| |
| if ((r->role == USB_PD_PORT_POWER_DISCONNECTED) || |
| (r->role == USB_PD_PORT_POWER_SOURCE)) { |
| printf("\n"); |
| return; |
| } |
| |
| printf(r->dualrole ? " DRP" : " Charger"); |
| switch (r->type) { |
| case USB_CHG_TYPE_PD: |
| printf(" PD"); |
| break; |
| case USB_CHG_TYPE_C: |
| printf(" Type-C"); |
| break; |
| case USB_CHG_TYPE_PROPRIETARY: |
| printf(" Proprietary"); |
| break; |
| case USB_CHG_TYPE_BC12_DCP: |
| printf(" DCP"); |
| break; |
| case USB_CHG_TYPE_BC12_CDP: |
| printf(" CDP"); |
| break; |
| case USB_CHG_TYPE_BC12_SDP: |
| printf(" SDP"); |
| break; |
| case USB_CHG_TYPE_OTHER: |
| printf(" Other"); |
| break; |
| case USB_CHG_TYPE_VBUS: |
| printf(" VBUS"); |
| break; |
| case USB_CHG_TYPE_UNKNOWN: |
| printf(" Unknown"); |
| break; |
| } |
| printf(" %dmV / %dmA, max %dmV / %dmA", r->meas.voltage_now, |
| r->meas.current_lim, r->meas.voltage_max, r->meas.current_max); |
| if (r->max_power) |
| printf(" / %dmW", r->max_power / 1000); |
| printf("\n"); |
| } |
| |
| /** |
| * @brief Helper function for getting the number of USB-PD ports in the system |
| * |
| * @param num_ports[out] Output parameter for number of ports |
| * @return 0 on success |
| * @return negative error code on failure |
| */ |
| static int get_num_pd_ports(int *num_ports) |
| { |
| struct ec_response_usb_pd_ports r; |
| int rv; |
| |
| assert(num_ports); |
| |
| rv = ec_command(EC_CMD_USB_PD_PORTS, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) { |
| return rv; |
| } |
| |
| *num_ports = r.num_ports; |
| |
| return 0; |
| } |
| |
| int cmd_usb_pd_mux_info(int argc, char *argv[]) |
| { |
| struct ec_params_usb_pd_mux_info p; |
| struct ec_response_usb_pd_mux_info r; |
| int num_ports, rv, i; |
| bool tsv = false; |
| |
| if (argc == 2 && (strncmp(argv[1], "tsv", 4) == 0)) { |
| tsv = true; |
| } else if (argc >= 2) { |
| fprintf(stderr, "Usage: %s [tsv]\n", argv[0]); |
| return -1; |
| } |
| |
| rv = get_num_pd_ports(&num_ports); |
| if (rv < 0) |
| return rv; |
| |
| for (i = 0; i < num_ports; i++) { |
| p.port = i; |
| rv = ec_command(EC_CMD_USB_PD_MUX_INFO, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| if (tsv) { |
| /* |
| * Machine-readable tab-separated values. This set of |
| * values is append-only. Columns should not be removed |
| * or repurposed. Update the documentation above if new |
| * columns are added. |
| */ |
| printf("%d\t", i); |
| printf("%d\t", !!(r.flags & USB_PD_MUX_USB_ENABLED)); |
| printf("%d\t", !!(r.flags & USB_PD_MUX_DP_ENABLED)); |
| printf("%s\t", r.flags & USB_PD_MUX_POLARITY_INVERTED ? |
| "INVERTED" : |
| "NORMAL"); |
| printf("%d\t", !!(r.flags & USB_PD_MUX_HPD_IRQ)); |
| printf("%d\n", !!(r.flags & USB_PD_MUX_HPD_LVL)); |
| } else { |
| /* Human-readable mux info. */ |
| printf("Port %d: ", i); |
| printf("USB=%d ", !!(r.flags & USB_PD_MUX_USB_ENABLED)); |
| printf("DP=%d ", !!(r.flags & USB_PD_MUX_DP_ENABLED)); |
| printf("POLARITY=%s ", |
| r.flags & USB_PD_MUX_POLARITY_INVERTED ? |
| "INVERTED" : |
| "NORMAL"); |
| printf("HPD_IRQ=%d ", !!(r.flags & USB_PD_MUX_HPD_IRQ)); |
| printf("HPD_LVL=%d ", !!(r.flags & USB_PD_MUX_HPD_LVL)); |
| printf("SAFE=%d ", !!(r.flags & USB_PD_MUX_SAFE_MODE)); |
| printf("TBT=%d ", |
| !!(r.flags & USB_PD_MUX_TBT_COMPAT_ENABLED)); |
| printf("USB4=%d ", |
| !!(r.flags & USB_PD_MUX_USB4_ENABLED)); |
| printf("\n"); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void print_usb_pd_port_caps(int port_num, |
| const struct ec_response_get_pd_port_caps *r) |
| { |
| printf("Port %02d: ", port_num); |
| |
| printf("power_role_cap="); |
| switch (r->pd_power_role_cap) { |
| case EC_PD_POWER_ROLE_SOURCE: |
| printf("source-only "); |
| break; |
| case EC_PD_POWER_ROLE_SINK: |
| printf("sink-only "); |
| break; |
| case EC_PD_POWER_ROLE_DUAL: |
| printf("dual-role "); |
| break; |
| default: |
| printf("unknown(%u) ", r->pd_power_role_cap); |
| break; |
| } |
| |
| printf("try_power_role_cap="); |
| switch (r->pd_try_power_role_cap) { |
| case EC_PD_TRY_POWER_ROLE_NONE: |
| printf("none "); |
| break; |
| case EC_PD_TRY_POWER_ROLE_SINK: |
| printf("trysink "); |
| break; |
| case EC_PD_TRY_POWER_ROLE_SOURCE: |
| printf("trysrc "); |
| break; |
| default: |
| printf("unknown(%u) ", r->pd_try_power_role_cap); |
| break; |
| } |
| |
| printf("data_role_cap="); |
| switch (r->pd_data_role_cap) { |
| case EC_PD_DATA_ROLE_DFP: |
| printf("dfp-only "); |
| break; |
| case EC_PD_DATA_ROLE_UFP: |
| printf("ufp-only "); |
| break; |
| case EC_PD_DATA_ROLE_DUAL: |
| printf("dual-role "); |
| break; |
| default: |
| printf("unknown(%u) ", r->pd_data_role_cap); |
| break; |
| } |
| |
| printf("port_location="); |
| switch (r->pd_port_location) { |
| case EC_PD_PORT_LOCATION_UNKNOWN: |
| printf("unknown "); |
| break; |
| case EC_PD_PORT_LOCATION_LEFT: |
| printf("left "); |
| break; |
| case EC_PD_PORT_LOCATION_RIGHT: |
| printf("right "); |
| break; |
| case EC_PD_PORT_LOCATION_BACK: |
| printf("back "); |
| break; |
| case EC_PD_PORT_LOCATION_FRONT: |
| printf("front "); |
| break; |
| case EC_PD_PORT_LOCATION_LEFT_FRONT: |
| printf("left-front "); |
| break; |
| case EC_PD_PORT_LOCATION_LEFT_BACK: |
| printf("left-back "); |
| break; |
| case EC_PD_PORT_LOCATION_RIGHT_FRONT: |
| printf("right-front "); |
| break; |
| case EC_PD_PORT_LOCATION_RIGHT_BACK: |
| printf("right-back "); |
| break; |
| case EC_PD_PORT_LOCATION_BACK_LEFT: |
| printf("back-left "); |
| break; |
| case EC_PD_PORT_LOCATION_BACK_RIGHT: |
| printf("back-right "); |
| break; |
| default: |
| printf("other(%u) ", r->pd_port_location); |
| break; |
| } |
| |
| printf("\n"); |
| } |
| |
| int cmd_usb_pd_port_caps(int argc, char *argv[]) |
| { |
| struct ec_params_get_pd_port_caps p; |
| struct ec_response_get_pd_port_caps *r = |
| (struct ec_response_get_pd_port_caps *)ec_inbuf; |
| int num_ports, rv; |
| char *e; |
| |
| if (argc < 2) { |
| /* Print all ports' capabilities */ |
| rv = get_num_pd_ports(&num_ports); |
| if (rv < 0) |
| return rv; |
| |
| for (int i = 0; i < num_ports; i++) { |
| p.port = i; |
| |
| rv = ec_command(EC_CMD_GET_PD_PORT_CAPS, 0, &p, |
| sizeof(p), ec_inbuf, ec_max_insize); |
| if (rv < 0) { |
| return rv; |
| } |
| |
| print_usb_pd_port_caps(i, r); |
| } |
| |
| return 0; |
| } |
| |
| /* Print a specific port's capabilities */ |
| p.port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port number\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_GET_PD_PORT_CAPS, 0, &p, sizeof(p), ec_inbuf, |
| ec_max_insize); |
| if (rv < 0) { |
| return rv; |
| } |
| |
| print_usb_pd_port_caps(p.port, r); |
| |
| return 0; |
| } |
| |
| int cmd_usb_pd_power(int argc, char *argv[]) |
| { |
| struct ec_params_usb_pd_power_info p; |
| struct ec_response_usb_pd_power_info *r = |
| (struct ec_response_usb_pd_power_info *)ec_inbuf; |
| int num_ports, i, rv; |
| char *e; |
| |
| rv = get_num_pd_ports(&num_ports); |
| if (rv < 0) |
| return rv; |
| |
| if (argc < 2) { |
| for (i = 0; i < num_ports; i++) { |
| p.port = i; |
| rv = ec_command(EC_CMD_USB_PD_POWER_INFO, 0, &p, |
| sizeof(p), ec_inbuf, ec_max_insize); |
| if (rv < 0) |
| return rv; |
| |
| printf("Port %d: ", i); |
| print_pd_power_info(r); |
| } |
| } else { |
| p.port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port.\n"); |
| return -1; |
| } |
| rv = ec_command(EC_CMD_USB_PD_POWER_INFO, 0, &p, sizeof(p), |
| ec_inbuf, ec_max_insize); |
| if (rv < 0) |
| return rv; |
| |
| printf("Port %d: ", p.port); |
| print_pd_power_info(r); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_kbpress(int argc, char *argv[]) |
| { |
| struct ec_params_mkbp_simulate_key p; |
| char *e; |
| int rv; |
| |
| if (argc != 4) { |
| fprintf(stderr, "Usage: %s <row> <col> <0|1>\n", argv[0]); |
| return -1; |
| } |
| p.row = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad row.\n"); |
| return -1; |
| } |
| p.col = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad column.\n"); |
| return -1; |
| } |
| p.pressed = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad pressed flag.\n"); |
| return -1; |
| } |
| |
| printf("%s row %d col %d.\n", p.pressed ? "Pressing" : "Releasing", |
| p.row, p.col); |
| |
| rv = ec_command(EC_CMD_MKBP_SIMULATE_KEY, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| printf("Done.\n"); |
| return 0; |
| } |
| |
| int cmd_keyboard_factory_test(int argc, char *argv[]) |
| { |
| struct ec_response_keyboard_factory_test r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_KEYBOARD_FACTORY_TEST, 0, NULL, 0, &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| if (r.shorted != 0) |
| printf("Keyboard %d and %d pin are shorted.\n", |
| r.shorted & 0x00ff, r.shorted >> 8); |
| else |
| printf("Keyboard factory test passed.\n"); |
| |
| return 0; |
| } |
| |
| const char *action_key_names[] = { |
| [TK_ABSENT] = "Absent", |
| [TK_BACK] = "Back", |
| [TK_FORWARD] = "Forward", |
| [TK_REFRESH] = "Refresh", |
| [TK_FULLSCREEN] = "Fullscreen", |
| [TK_OVERVIEW] = "Overview", |
| [TK_BRIGHTNESS_DOWN] = "Brightness Down", |
| [TK_BRIGHTNESS_UP] = "Brightness Up", |
| [TK_VOL_MUTE] = "Volume Mute", |
| [TK_VOL_DOWN] = "Volume Down", |
| [TK_VOL_UP] = "Volume Up", |
| [TK_SNAPSHOT] = "Snapshot", |
| [TK_PRIVACY_SCRN_TOGGLE] = "Privacy Screen Toggle", |
| [TK_KBD_BKLIGHT_DOWN] = "Keyboard Backlight Down", |
| [TK_KBD_BKLIGHT_UP] = "Keyboard Backlight Up", |
| [TK_PLAY_PAUSE] = "Play/Pause", |
| [TK_NEXT_TRACK] = "Next Track", |
| [TK_PREV_TRACK] = "Previous Track", |
| [TK_KBD_BKLIGHT_TOGGLE] = "Keyboard Backlight Toggle", |
| [TK_MICMUTE] = "Microphone Mute", |
| [TK_MENU] = "Menu", |
| [TK_DICTATE] = "Dictation", |
| [TK_ACCESSIBILITY] = "Accessibility", |
| [TK_DONOTDISTURB] = "Do Not Disturb", |
| [TK_HOME] = "Home", |
| }; |
| |
| BUILD_ASSERT(ARRAY_SIZE(action_key_names) == TK_COUNT); |
| |
| int cmd_keyboard_get_config(int argc, char *argv[]) |
| { |
| struct ec_response_keybd_config r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_GET_KEYBD_CONFIG, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Vivaldi key:\n"); |
| for (int i = 0; i < r.num_top_row_keys; ++i) { |
| const char *name = NULL; |
| if (r.action_keys[i] < TK_COUNT) { |
| name = action_key_names[r.action_keys[i]]; |
| } |
| if (name == NULL) { |
| name = "Unknown Key"; |
| } |
| printf("%2i: %s (%d)\n", i, name, r.action_keys[i]); |
| } |
| printf("Capabilities: %0#x", r.capabilities); |
| if (r.capabilities & KEYBD_CAP_FUNCTION_KEYS) { |
| printf(" FUNCTION_KEYS"); |
| } |
| if (r.capabilities & KEYBD_CAP_NUMERIC_KEYPAD) { |
| printf(" NUMERIC_KEYPAD"); |
| } |
| if (r.capabilities & KEYBD_CAP_SCRNLOCK_KEY) { |
| printf(" SCRNLOCK_KEY"); |
| } |
| printf("\n"); |
| |
| return 0; |
| } |
| |
| int cmd_panic_log(int argc, char *argv[]) |
| { |
| int rv; |
| struct ec_params_panic_log_info info_params = { 0 }; |
| struct ec_response_panic_log_info info_response; |
| |
| if (argc != 2) { |
| goto usage; |
| } |
| |
| if (!ec_cmd_version_supported(EC_CMD_PANIC_LOG_INFO, 0)) { |
| fprintf(stderr, "Panic log not supported\n"); |
| return -1; |
| } |
| |
| if (!strcmp(argv[1], "info")) { |
| rv = ec_command(EC_CMD_PANIC_LOG_INFO, 0, &info_params, |
| sizeof(info_params), &info_response, |
| sizeof(info_response)); |
| if (rv < 0) { |
| fprintf(stderr, "Error getting panic log info\n"); |
| return rv; |
| } |
| printf("Valid: %d\n", info_response.valid); |
| printf("Frozen: %d\n", info_response.frozen); |
| printf("Length: %d\n", info_response.length); |
| printf("Capacity: %d\n", info_response.capacity); |
| printf("Version: %d\n", info_response.version); |
| } else if (!strcmp(argv[1], "freeze")) { |
| info_params.freeze = 1; |
| rv = ec_command(EC_CMD_PANIC_LOG_INFO, 0, &info_params, |
| sizeof(info_params), &info_response, |
| sizeof(info_response)); |
| if (rv < 0) { |
| fprintf(stderr, "Error freezing panic log\n"); |
| return rv; |
| } |
| if (info_response.frozen) |
| printf("Panic log already frozen\n"); |
| else |
| printf("Panic log frozen\n"); |
| } else if (!strcmp(argv[1], "unfreeze")) { |
| info_params.unfreeze = 1; |
| rv = ec_command(EC_CMD_PANIC_LOG_INFO, 0, &info_params, |
| sizeof(info_params), &info_response, |
| sizeof(info_response)); |
| if (rv < 0) { |
| fprintf(stderr, "Error unfreezing panic log\n"); |
| return rv; |
| } |
| if (!info_response.frozen) |
| printf("Panic log already unfrozen\n"); |
| else |
| printf("Panic log unfrozen\n"); |
| } else if (!strcmp(argv[1], "reset")) { |
| info_params.reset = 1; |
| rv = ec_command(EC_CMD_PANIC_LOG_INFO, 0, &info_params, |
| sizeof(info_params), &info_response, |
| sizeof(info_response)); |
| if (rv < 0) { |
| fprintf(stderr, "Error resetting panic log\n"); |
| return rv; |
| } |
| printf("Panic log reset\n"); |
| } else if (!strcmp(argv[1], "dump")) { |
| char *response = (char *)ec_inbuf; |
| int response_max; |
| struct ec_params_panic_log_read read_params; |
| struct ec_response_get_protocol_info protocol_info_response; |
| |
| /* Determine the max response packet size */ |
| rv = ec_command(EC_CMD_GET_PROTOCOL_INFO, 0, NULL, 0, |
| &protocol_info_response, |
| sizeof(protocol_info_response)); |
| if (rv < 0) { |
| fprintf(stderr, "Error getting protocol info\n"); |
| return rv; |
| } |
| response_max = protocol_info_response.max_response_packet_size; |
| |
| info_params.freeze = 1; |
| rv = ec_command(EC_CMD_PANIC_LOG_INFO, 0, &info_params, |
| sizeof(info_params), &info_response, |
| sizeof(info_response)); |
| if (rv < 0) { |
| fprintf(stderr, "Error getting panic log info\n"); |
| return rv; |
| } |
| while (read_params.offset < info_response.length) { |
| /* Limit response size by one byte for null terminator |
| */ |
| int response_size = |
| ec_command(EC_CMD_PANIC_LOG_READ, 0, |
| &read_params, sizeof(read_params), |
| response, response_max - 1); |
| if (response_size < 0) { |
| fprintf(stderr, "Error reading panic log\n"); |
| return rv; |
| } |
| if (response_size == 0) |
| break; |
| /* Ensure null terminated */ |
| response[response_size] = '\0'; |
| fputs(response, stdout); |
| read_params.offset += response_size; |
| } |
| fputs("\n", stdout); |
| /* Restore frozen state */ |
| if (!info_response.frozen) { |
| info_params = { 0 }; |
| info_params.unfreeze = 1; |
| rv = ec_command(EC_CMD_PANIC_LOG_INFO, 0, &info_params, |
| sizeof(info_params), &info_response, |
| sizeof(info_response)); |
| if (rv < 0) { |
| fprintf(stderr, |
| "Error unfreezing panic log after dumping\n"); |
| return rv; |
| } |
| } |
| |
| } else { |
| goto usage; |
| } |
| |
| return 0; |
| |
| usage: |
| fprintf(stderr, "Usage: %s [info | dump | freeze | unfreeze | reset]\n", |
| argv[0]); |
| return -1; |
| } |
| |
| int cmd_panic_info(int argc, char *argv[]) |
| { |
| int rv; |
| /* ec_params_get_panic_info_v2 is a superset of |
| * ec_params_get_panic_info_v1 */ |
| struct ec_params_get_panic_info_v2 params = { |
| /* By default, reading the panic info will set |
| * PANIC_DATA_FLAG_OLD_HOSTCMD. Prefer to leave this |
| * flag untouched when supported. |
| */ |
| .preserve_old_hostcmd_flag = 1, |
| .read_offset = 0, |
| }; |
| std::vector<unsigned char> data; |
| |
| if (ec_cmd_version_supported(EC_CMD_GET_PANIC_INFO, 2)) { |
| const int max_read_count = 100; |
| int read_count = 0; |
| while (true) { |
| if (read_count++ >= max_read_count) { |
| printf("ERROR: timeout reading panic info.\n"); |
| return -ETIMEDOUT; |
| } |
| rv = ec_command(EC_CMD_GET_PANIC_INFO, 2, ¶ms, |
| sizeof(params), ec_inbuf, |
| ec_max_insize); |
| /* Read until no more data is returned */ |
| if (rv <= 0) |
| break; |
| data.insert(data.end(), |
| static_cast<unsigned char *>(ec_inbuf), |
| static_cast<unsigned char *>(ec_inbuf) + |
| rv); |
| params.read_offset += rv; |
| }; |
| } else if (ec_cmd_version_supported(EC_CMD_GET_PANIC_INFO, 1)) { |
| rv = ec_command(EC_CMD_GET_PANIC_INFO, 1, ¶ms, |
| sizeof(params), ec_inbuf, ec_max_insize); |
| data.assign(static_cast<unsigned char *>(ec_inbuf), |
| static_cast<unsigned char *>(ec_inbuf) + rv); |
| } else { |
| rv = ec_command(EC_CMD_GET_PANIC_INFO, 0, NULL, 0, ec_inbuf, |
| ec_max_insize); |
| data.assign(static_cast<unsigned char *>(ec_inbuf), |
| static_cast<unsigned char *>(ec_inbuf) + rv); |
| } |
| |
| if (rv < 0) |
| return rv; |
| |
| if (data.empty()) { |
| printf("No panic data.\n"); |
| return 0; |
| } |
| |
| auto result = ec::ParsePanicInfo(data); |
| |
| if (!result.has_value()) { |
| fprintf(stderr, "%s", result.error().c_str()); |
| return 1; |
| } |
| printf("%s", result.value().c_str()); |
| |
| return 0; |
| } |
| |
| int cmd_power_info(int argc, char *argv[]) |
| { |
| struct ec_response_power_info_v1 r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_POWER_INFO, 1, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Power source:\t"); |
| switch (r.system_power_source) { |
| case POWER_SOURCE_UNKNOWN: |
| printf("Unknown\n"); |
| break; |
| case POWER_SOURCE_BATTERY: |
| printf("Battery\n"); |
| break; |
| case POWER_SOURCE_AC: |
| printf("AC\n"); |
| break; |
| case POWER_SOURCE_AC_BATTERY: |
| printf("AC + battery\n"); |
| break; |
| } |
| |
| printf("Battery state-of-charge: %d%%\n", r.battery_soc); |
| printf("Max AC power: %d Watts\n", r.ac_adapter_100pct); |
| printf("Battery 1Cd rate: %d\n", r.battery_1cd); |
| printf("RoP Avg: %d Watts\n", r.rop_avg); |
| printf("RoP Peak: %d Watts\n", r.rop_peak); |
| printf("Battery DBPT support level: %d\n", |
| r.intel.batt_dbpt_support_level); |
| printf("Battery DBPT Max Peak Power: %d Watts\n", |
| r.intel.batt_dbpt_max_peak_power); |
| printf("Battery DBPT Sus Peak Power: %d Watts\n", |
| r.intel.batt_dbpt_sus_peak_power); |
| return 0; |
| } |
| |
| int cmd_pse(int argc, char *argv[]) |
| { |
| struct ec_params_pse p; |
| struct ec_response_pse_status r; |
| int rsize = 0; |
| char *e; |
| int rv; |
| |
| if (argc < 2 || argc > 3 || !strcmp(argv[1], "help")) { |
| printf("Usage: %s <port> [<subcmd>]\n", argv[0]); |
| printf("'pse <port> [status]' - Get port status\n"); |
| printf("'pse <port> disable' - Disable port\n"); |
| printf("'pse <port> enable' - Enable port\n"); |
| return -1; |
| } |
| |
| p.port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port.\n"); |
| return -1; |
| } |
| |
| if (argc == 2 || !strcmp(argv[2], "status")) { |
| p.cmd = EC_PSE_STATUS; |
| rsize = sizeof(r); |
| } else if (!strcmp(argv[2], "disable")) { |
| p.cmd = EC_PSE_DISABLE; |
| } else if (!strcmp(argv[2], "enable")) { |
| p.cmd = EC_PSE_ENABLE; |
| } else { |
| fprintf(stderr, "Unknown command: %s\n", argv[2]); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_PSE, 0, &p, sizeof(p), &r, rsize); |
| if (rv < 0) |
| return rv; |
| |
| if (p.cmd == EC_PSE_STATUS) { |
| const char *status; |
| |
| switch (r.status) { |
| case EC_PSE_STATUS_DISABLED: |
| status = "disabled"; |
| break; |
| case EC_PSE_STATUS_ENABLED: |
| status = "enabled"; |
| break; |
| case EC_PSE_STATUS_POWERED: |
| status = "powered"; |
| break; |
| default: |
| status = "unknown"; |
| break; |
| } |
| |
| printf("Port %d: %s\n", p.port, status); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_pstore_info(int argc, char *argv[]) |
| { |
| struct ec_response_pstore_info r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_PSTORE_INFO, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("PstoreSize %d\nAccessSize %d\n", r.pstore_size, r.access_size); |
| return 0; |
| } |
| |
| int cmd_pstore_read(int argc, char *argv[]) |
| { |
| struct ec_params_pstore_read p; |
| uint8_t rdata[EC_PSTORE_SIZE_MAX]; |
| int offset, size; |
| int rv; |
| int i; |
| char *e; |
| char *buf; |
| |
| if (argc < 4) { |
| fprintf(stderr, "Usage: %s <offset> <size> <filename>\n", |
| argv[0]); |
| return -1; |
| } |
| offset = strtol(argv[1], &e, 0); |
| if ((e && *e) || offset < 0 || offset > 0x10000) { |
| fprintf(stderr, "Bad offset.\n"); |
| return -1; |
| } |
| size = strtol(argv[2], &e, 0); |
| if ((e && *e) || size <= 0 || size > 0x10000) { |
| fprintf(stderr, "Bad size.\n"); |
| return -1; |
| } |
| printf("Reading %d bytes at offset %d...\n", size, offset); |
| |
| buf = (char *)malloc(size); |
| if (!buf) { |
| fprintf(stderr, "Unable to allocate buffer.\n"); |
| return -1; |
| } |
| |
| /* Read data in chunks */ |
| for (i = 0; i < size; i += EC_PSTORE_SIZE_MAX) { |
| p.offset = offset + i; |
| p.size = MIN(size - i, EC_PSTORE_SIZE_MAX); |
| rv = ec_command(EC_CMD_PSTORE_READ, 0, &p, sizeof(p), rdata, |
| sizeof(rdata)); |
| if (rv < 0) { |
| fprintf(stderr, "Read error at offset %d\n", i); |
| free(buf); |
| return rv; |
| } |
| memcpy(buf + i, rdata, p.size); |
| } |
| |
| rv = write_file(argv[3], buf, size); |
| free(buf); |
| if (rv) |
| return rv; |
| |
| printf("done.\n"); |
| return 0; |
| } |
| |
| int cmd_pstore_write(int argc, char *argv[]) |
| { |
| struct ec_params_pstore_write p; |
| int offset, size; |
| int rv; |
| int i; |
| char *e; |
| char *buf; |
| |
| if (argc < 3) { |
| fprintf(stderr, "Usage: %s <offset> <filename>\n", argv[0]); |
| return -1; |
| } |
| offset = strtol(argv[1], &e, 0); |
| if ((e && *e) || offset < 0 || offset > 0x10000) { |
| fprintf(stderr, "Bad offset.\n"); |
| return -1; |
| } |
| |
| /* Read the input file */ |
| buf = read_file(argv[2], &size); |
| if (!buf) |
| return -1; |
| |
| printf("Writing to offset %d...\n", offset); |
| |
| /* Write data in chunks */ |
| for (i = 0; i < size; i += EC_PSTORE_SIZE_MAX) { |
| p.offset = offset + i; |
| p.size = MIN(size - i, EC_PSTORE_SIZE_MAX); |
| memcpy(p.data, buf + i, p.size); |
| rv = ec_command(EC_CMD_PSTORE_WRITE, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) { |
| fprintf(stderr, "Write error at offset %d\n", i); |
| free(buf); |
| return rv; |
| } |
| } |
| |
| free(buf); |
| printf("done.\n"); |
| return 0; |
| } |
| |
| int cmd_host_event_get_raw(int argc, char *argv[]) |
| { |
| uint32_t events = read_mapped_mem32(EC_MEMMAP_HOST_EVENTS); |
| |
| if (events & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INVALID)) { |
| printf("Current host events: invalid\n"); |
| return -1; |
| } |
| |
| printf("Current host events: 0x%08x\n", events); |
| return 0; |
| } |
| |
| int cmd_host_event_get_b(int argc, char *argv[]) |
| { |
| struct ec_response_host_event_mask r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_HOST_EVENT_GET_B, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| if (rv < sizeof(r)) { |
| fprintf(stderr, "Insufficient data received.\n"); |
| return -1; |
| } |
| |
| if (r.mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INVALID)) { |
| printf("Current host events-B: invalid\n"); |
| return -1; |
| } |
| |
| printf("Current host events-B: 0x%08x\n", r.mask); |
| return 0; |
| } |
| |
| int cmd_host_event_get_smi_mask(int argc, char *argv[]) |
| { |
| struct ec_response_host_event_mask r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_HOST_EVENT_GET_SMI_MASK, 0, NULL, 0, &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Current host event SMI mask: 0x%08x\n", r.mask); |
| return 0; |
| } |
| |
| int cmd_host_event_get_sci_mask(int argc, char *argv[]) |
| { |
| struct ec_response_host_event_mask r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_HOST_EVENT_GET_SCI_MASK, 0, NULL, 0, &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Current host event SCI mask: 0x%08x\n", r.mask); |
| return 0; |
| } |
| |
| int cmd_host_event_get_wake_mask(int argc, char *argv[]) |
| { |
| struct ec_response_host_event_mask r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_HOST_EVENT_GET_WAKE_MASK, 0, NULL, 0, &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Current host event wake mask: 0x%08x\n", r.mask); |
| return 0; |
| } |
| |
| int cmd_host_event_set_smi_mask(int argc, char *argv[]) |
| { |
| struct ec_params_host_event_mask p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <mask>\n", argv[0]); |
| return -1; |
| } |
| p.mask = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad mask.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_HOST_EVENT_SET_SMI_MASK, 0, &p, sizeof(p), NULL, |
| 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Mask set.\n"); |
| return 0; |
| } |
| |
| int cmd_host_event_set_sci_mask(int argc, char *argv[]) |
| { |
| struct ec_params_host_event_mask p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <mask>\n", argv[0]); |
| return -1; |
| } |
| p.mask = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad mask.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_HOST_EVENT_SET_SCI_MASK, 0, &p, sizeof(p), NULL, |
| 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Mask set.\n"); |
| return 0; |
| } |
| |
| int cmd_host_event_set_wake_mask(int argc, char *argv[]) |
| { |
| struct ec_params_host_event_mask p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <mask>\n", argv[0]); |
| return -1; |
| } |
| p.mask = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad mask.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_HOST_EVENT_SET_WAKE_MASK, 0, &p, sizeof(p), NULL, |
| 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Mask set.\n"); |
| return 0; |
| } |
| |
| int cmd_host_event_clear(int argc, char *argv[]) |
| { |
| struct ec_params_host_event_mask p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <mask>\n", argv[0]); |
| return -1; |
| } |
| p.mask = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad mask.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_HOST_EVENT_CLEAR, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Host events cleared.\n"); |
| return 0; |
| } |
| |
| int cmd_host_event_clear_b(int argc, char *argv[]) |
| { |
| struct ec_params_host_event_mask p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <mask>\n", argv[0]); |
| return -1; |
| } |
| p.mask = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad mask.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_HOST_EVENT_CLEAR_B, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Host events-B cleared.\n"); |
| return 0; |
| } |
| |
| int cmd_switches(int argc, char *argv[]) |
| { |
| uint8_t s = read_mapped_mem8(EC_MEMMAP_SWITCHES); |
| printf("Current switches: 0x%02x\n", s); |
| printf("Lid switch: %s\n", |
| (s & EC_SWITCH_LID_OPEN ? "OPEN" : "CLOSED")); |
| printf("Power button: %s\n", |
| (s & EC_SWITCH_POWER_BUTTON_PRESSED ? "DOWN" : "UP")); |
| printf("Dedicated recovery: %sABLED\n", |
| (s & EC_SWITCH_DEDICATED_RECOVERY ? "EN" : "DIS")); |
| |
| return 0; |
| } |
| |
| int cmd_tabletmode(int argc, char *argv[]) |
| { |
| struct ec_params_set_tablet_mode p; |
| |
| if (argc != 2) |
| return EC_ERROR_PARAM_COUNT; |
| |
| memset(&p, 0, sizeof(p)); |
| /* |+1| to also make sure the strings the same length. */ |
| if (strncmp(argv[1], "on", strlen("on") + 1) == 0) { |
| p.tablet_mode = TABLET_MODE_FORCE_TABLET; |
| } else if (strncmp(argv[1], "off", strlen("off") + 1) == 0) { |
| p.tablet_mode = TABLET_MODE_FORCE_CLAMSHELL; |
| } else if (strncmp(argv[1], "reset", strlen("reset") + 1) == 0) { |
| // Match tablet mode to the current HW orientation. |
| p.tablet_mode = TABLET_MODE_DEFAULT; |
| } else { |
| return EC_ERROR_PARAM1; |
| } |
| |
| int rv = ec_command(EC_CMD_SET_TABLET_MODE, 0, &p, sizeof(p), NULL, 0); |
| rv = (rv < 0 ? rv : 0); |
| |
| if (rv < 0) { |
| fprintf(stderr, "Failed to set tablet mode, rv=%d\n", rv); |
| } else { |
| printf("\n"); |
| printf("SUCCESS. The tablet mode has been set.\n"); |
| } |
| return rv; |
| } |
| |
| int cmd_wireless(int argc, char *argv[]) |
| { |
| char *e; |
| int rv; |
| int now_flags; |
| |
| if (argc < 2) { |
| fprintf(stderr, |
| "Usage: %s <flags> [<mask> [<susflags> <susmask>]]\n", |
| argv[0]); |
| fprintf(stderr, " 0x1 = WLAN radio\n" |
| " 0x2 = Bluetooth radio\n" |
| " 0x4 = WWAN power\n" |
| " 0x8 = WLAN power\n"); |
| return -1; |
| } |
| |
| now_flags = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad flags.\n"); |
| return -1; |
| } |
| |
| if (argc < 3) { |
| /* Old-style - current flags only */ |
| struct ec_params_switch_enable_wireless_v0 p; |
| |
| p.enabled = now_flags; |
| rv = ec_command(EC_CMD_SWITCH_ENABLE_WIRELESS, 0, &p, sizeof(p), |
| NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Success.\n"); |
| } else { |
| /* New-style - masks and suspend flags */ |
| struct ec_params_switch_enable_wireless_v1 p; |
| struct ec_response_switch_enable_wireless_v1 r; |
| |
| memset(&p, 0, sizeof(p)); |
| |
| p.now_flags = now_flags; |
| |
| p.now_mask = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad mask.\n"); |
| return -1; |
| } |
| |
| if (argc > 4) { |
| p.suspend_flags = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad suspend flags.\n"); |
| return -1; |
| } |
| |
| p.suspend_mask = strtol(argv[4], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad suspend mask.\n"); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_SWITCH_ENABLE_WIRELESS, |
| EC_VER_SWITCH_ENABLE_WIRELESS, &p, sizeof(p), |
| &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Now=0x%x, suspend=0x%x\n", r.now_flags, |
| r.suspend_flags); |
| } |
| |
| return 0; |
| } |
| |
| static void cmd_locate_chip_help(const char *const cmd) |
| { |
| fprintf(stderr, |
| "Usage: %s <type> <index>\n" |
| " <type> is one of:\n" |
| " 0: CBI_EEPROM\n" |
| " 1: TCPCs\n" |
| " <index> instance # of <type>\n", |
| cmd); |
| } |
| |
| static const char *bus_type[] = { "I2C", "EMBEDDED" }; |
| |
| int cmd_locate_chip(int argc, char *argv[]) |
| { |
| struct ec_params_locate_chip p; |
| struct ec_response_locate_chip r = { 0 }; |
| char *e; |
| int rv; |
| |
| if (argc != 3) { |
| cmd_locate_chip_help(argv[0]); |
| return -1; |
| } |
| |
| p.type = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad type.\n"); |
| cmd_locate_chip_help(argv[0]); |
| return -1; |
| } |
| |
| p.index = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad index.\n"); |
| cmd_locate_chip_help(argv[0]); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_LOCATE_CHIP, 0, &p, sizeof(p), &r, sizeof(r)); |
| |
| if (rv == -EC_RES_INVALID_PARAM - EECRESULT) { |
| fprintf(stderr, "Bus type %d not supported.\n", p.type); |
| return rv; |
| } |
| |
| if (rv == -EC_RES_UNAVAILABLE - EECRESULT) { |
| fprintf(stderr, "Chip not found\n"); |
| return rv; |
| } |
| |
| if (rv == -EC_RES_OVERFLOW - EECRESULT) { |
| fprintf(stderr, "Index too large\n"); |
| return rv; |
| } |
| |
| if (rv < 0) |
| return rv; |
| |
| if (r.bus_type >= EC_BUS_TYPE_COUNT || |
| r.bus_type >= ARRAY_SIZE(bus_type)) { |
| fprintf(stderr, "Unknown bus type (%d)\n", r.bus_type); |
| return -1; |
| } |
| |
| /* |
| * When changing the format of this print, make sure FAFT |
| * (firmware_ECCbiEeprom) still passes. It may silently skip the test. |
| */ |
| printf("Bus: %s; Port: %d; Address: 0x%02x (7-bit format)\n", |
| bus_type[r.bus_type], r.i2c_info.port, |
| I2C_STRIP_FLAGS(r.i2c_info.addr_flags)); |
| |
| return 0; |
| } |
| |
| int cmd_lcd_backlight(int argc, char *argv[]) |
| { |
| struct ec_params_switch_enable_backlight p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <0|1>\n", argv[0]); |
| return -1; |
| } |
| p.enabled = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad value.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_SWITCH_ENABLE_BKLIGHT, 0, &p, sizeof(p), NULL, |
| 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Success.\n"); |
| return 0; |
| } |
| |
| int cmd_poe(int argc, char *argv[]) |
| { |
| struct ec_params_switch_enable_poe p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <0|1>\n", argv[0]); |
| return -1; |
| } |
| p.enabled = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad value.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_SWITCH_ENABLE_POE, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Success.\n"); |
| return 0; |
| } |
| |
| static void cmd_basestate_help(void) |
| { |
| fprintf(stderr, "Usage: ectool basestate [attach | detach | reset]\n"); |
| } |
| |
| int cmd_basestate(int argc, char *argv[]) |
| { |
| struct ec_params_set_base_state p; |
| |
| if (argc != 2) { |
| cmd_basestate_help(); |
| return -1; |
| } |
| |
| if (!strncmp(argv[1], "attach", 6)) { |
| p.cmd = EC_SET_BASE_STATE_ATTACH; |
| } else if (!strncmp(argv[1], "detach", 6)) { |
| p.cmd = EC_SET_BASE_STATE_DETACH; |
| } else if (!strncmp(argv[1], "reset", 5)) { |
| p.cmd = EC_SET_BASE_STATE_RESET; |
| } else { |
| cmd_basestate_help(); |
| return -1; |
| } |
| |
| return ec_command(EC_CMD_SET_BASE_STATE, 0, &p, sizeof(p), NULL, 0); |
| } |
| |
| int cmd_ext_power_limit(int argc, char *argv[]) |
| { |
| /* Version 1 is used, no support for obsolete version 0 */ |
| struct ec_params_external_power_limit_v1 p; |
| char *e; |
| |
| if (argc != 3) { |
| fprintf(stderr, "Usage: %s <max_current_mA> <max_voltage_mV>\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p.current_lim = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad param1.\n"); |
| return -1; |
| } |
| |
| p.voltage_lim = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad param2.\n"); |
| return -1; |
| } |
| |
| /* Send version 1 of command */ |
| return ec_command(EC_CMD_EXTERNAL_POWER_LIMIT, 1, &p, sizeof(p), NULL, |
| 0); |
| } |
| |
| static void cmd_charge_current_limit_help(const char *cmd) |
| { |
| fprintf(stderr, |
| "\n" |
| " Usage: %s <max_current_mA>\n" |
| " Set the maximum battery charging current.\n" |
| " Usage: %s <max_current_mA> [battery_SoC]\n" |
| " Set the maximum battery charging current and the minimum battery\n" |
| " SoC at which it will apply. Setting [battery_SoC] is only \n" |
| " supported in v1.\n" |
| "\n", |
| cmd, cmd); |
| } |
| |
| int cmd_charge_current_limit(int argc, char *argv[]) |
| { |
| struct ec_params_current_limit_v1 p1; |
| uint32_t limit; |
| uint8_t battery_soc; |
| char *e; |
| |
| /* |
| * v0: max_current_ma (argc == 2) |
| * v1: max_current_ma [battery_soc] (argc == 2 or 3) |
| */ |
| if (!ec_cmd_version_supported(EC_CMD_CHARGE_CURRENT_LIMIT, 1)) { |
| if (argc != 2) { |
| cmd_charge_current_limit_help(argv[0]); |
| return -1; |
| } |
| } else { |
| if (argc < 2 || argc > 3) { |
| cmd_charge_current_limit_help(argv[0]); |
| return -1; |
| } |
| } |
| |
| /* max_current_ma */ |
| limit = strtoull(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "ERROR: Bad limit value: %s\n", argv[1]); |
| return -1; |
| } |
| |
| if (argc == 2) { |
| struct ec_params_current_limit p0; |
| |
| p0.limit = limit; |
| return ec_command(EC_CMD_CHARGE_CURRENT_LIMIT, 0, &p0, |
| sizeof(p0), NULL, 0); |
| } |
| |
| /* argc==3 for battery_soc */ |
| battery_soc = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "ERROR: Bad battery SoC value: %s\n", argv[2]); |
| return -1; |
| } |
| |
| p1.limit = limit; |
| p1.battery_soc = battery_soc; |
| return ec_command(EC_CMD_CHARGE_CURRENT_LIMIT, 1, &p1, sizeof(p1), NULL, |
| 0); |
| } |
| |
| static void cmd_charge_control_help(const char *cmd, const char *msg) |
| { |
| if (msg) |
| fprintf(stderr, "ERROR: %s\n", msg); |
| |
| fprintf(stderr, |
| "\n" |
| " Usage: %s\n" |
| " Get current settings.\n" |
| " Usage: %s normal|idle|discharge\n" |
| " Set charge mode (and disable battery sustainer).\n" |
| " Usage: %s normal <lower> <upper> [<flags>]\n" |
| " Enable battery sustainer. <lower> and <upper> are battery SoC\n" |
| " between which EC tries to keep the battery level.\n" |
| " <flags> are supported in v3+\n." |
| "\n", |
| cmd, cmd, cmd); |
| } |
| |
| int cmd_charge_control(int argc, char *argv[]) |
| { |
| struct ec_params_charge_control p = {}; |
| struct ec_response_charge_control r; |
| int version; |
| const char *const charge_mode_text[] = EC_CHARGE_MODE_TEXT; |
| char *e; |
| int rv; |
| |
| if (ec_cmd_version_supported(EC_CMD_CHARGE_CONTROL, 3)) { |
| version = 3; |
| } else if (ec_cmd_version_supported(EC_CMD_CHARGE_CONTROL, 2)) { |
| if (argc > 4) { |
| cmd_charge_control_help(argv[0], |
| "<flags> not supported by EC"); |
| return -1; |
| } |
| version = 2; |
| } else { |
| if (argc != 2) { |
| cmd_charge_control_help( |
| argv[0], "Bad arguments or EC is too old"); |
| return -1; |
| } |
| version = 1; |
| } |
| |
| if (argc == 1) { |
| p.cmd = EC_CHARGE_CONTROL_CMD_GET; |
| rv = ec_command(EC_CMD_CHARGE_CONTROL, version, &p, sizeof(p), |
| &r, sizeof(r)); |
| if (rv < 0) { |
| fprintf(stderr, "Command failed.\n"); |
| return rv; |
| } |
| printf("Charge mode = %s (%d)\n", |
| r.mode < ARRAY_SIZE(charge_mode_text) ? |
| charge_mode_text[r.mode] : |
| "UNDEFINED", |
| r.mode); |
| printf("Battery sustainer = %s (%d%% ~ %d%%)\n", |
| (r.sustain_soc.lower != -1 && |
| r.sustain_soc.upper != -1) ? |
| "on" : |
| "off", |
| r.sustain_soc.lower, r.sustain_soc.upper); |
| return 0; |
| } |
| |
| p.cmd = EC_CHARGE_CONTROL_CMD_SET; |
| if (!strcasecmp(argv[1], "normal")) { |
| p.mode = CHARGE_CONTROL_NORMAL; |
| if (argc == 2) { |
| p.sustain_soc.lower = -1; |
| p.sustain_soc.upper = -1; |
| } else if (argc > 3) { |
| p.sustain_soc.lower = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| cmd_charge_control_help( |
| argv[0], "Bad character in <lower>"); |
| return -1; |
| } |
| p.sustain_soc.upper = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| cmd_charge_control_help( |
| argv[0], "Bad character in <upper>"); |
| return -1; |
| } |
| if (argc == 5) { |
| p.flags = strtoul(argv[4], &e, 0); |
| if (e && *e) { |
| cmd_charge_control_help( |
| argv[0], |
| "Bad character in <flags>"); |
| return -1; |
| } |
| } |
| } else { |
| cmd_charge_control_help(argv[0], "Bad arguments"); |
| return -1; |
| } |
| } else if (!strcasecmp(argv[1], "idle")) { |
| if (argc != 2) { |
| cmd_charge_control_help(argv[0], "Bad arguments"); |
| return -1; |
| } |
| p.mode = CHARGE_CONTROL_IDLE; |
| } else if (!strcasecmp(argv[1], "discharge")) { |
| if (argc != 2) { |
| cmd_charge_control_help(argv[0], "Bad arguments"); |
| return -1; |
| } |
| p.mode = CHARGE_CONTROL_DISCHARGE; |
| } else { |
| cmd_charge_control_help(argv[0], "Bad sub-command"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_CHARGE_CONTROL, version, &p, sizeof(p), NULL, 0); |
| if (rv < 0) { |
| fprintf(stderr, "Is AC connected?\n"); |
| return rv; |
| } |
| |
| switch (p.mode) { |
| case CHARGE_CONTROL_NORMAL: |
| printf("Charge state machine is in normal mode%s.\n", |
| (p.sustain_soc.lower == -1 || |
| p.sustain_soc.upper == -1) ? |
| "" : |
| " with sustainer enabled"); |
| break; |
| case CHARGE_CONTROL_IDLE: |
| printf("Charge state machine force idle.\n"); |
| break; |
| case CHARGE_CONTROL_DISCHARGE: |
| printf("Charge state machine force discharge.\n"); |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| static void print_bool(const char *name, bool value) |
| { |
| printf("%s = %s\n", name, value ? "true" : "false"); |
| } |
| |
| static int cmd_chargesplash(int argc, char **argv) |
| { |
| static struct { |
| const char *name; |
| enum ec_chargesplash_cmd cmd; |
| } actions[] = { |
| { "state", EC_CHARGESPLASH_GET_STATE }, |
| { "request", EC_CHARGESPLASH_REQUEST }, |
| { "lockout", EC_CHARGESPLASH_LOCKOUT }, |
| { "reset", EC_CHARGESPLASH_RESET }, |
| }; |
| struct ec_params_chargesplash params; |
| struct ec_response_chargesplash resp; |
| |
| if (argc != 2) { |
| goto usage; |
| } |
| |
| for (int i = 0; i < ARRAY_SIZE(actions); i++) { |
| if (!strcasecmp(actions[i].name, argv[1])) { |
| params.cmd = actions[i].cmd; |
| if (ec_command(EC_CMD_CHARGESPLASH, 0, ¶ms, |
| sizeof(params), &resp, |
| sizeof(resp)) < 0) { |
| fprintf(stderr, "Host command failed\n"); |
| return -1; |
| } |
| |
| print_bool("requested", resp.requested); |
| print_bool("display_initialized", |
| resp.display_initialized); |
| print_bool("locked_out", resp.locked_out); |
| return 0; |
| } |
| } |
| |
| usage: |
| fprintf(stderr, "Usage: %s <state|request|lockout|reset>", argv[0]); |
| return -1; |
| } |
| |
| #define ST_CMD_SIZE ST_FLD_SIZE(ec_params_charge_state, cmd) |
| #define ST_PRM_SIZE(SUBCMD) \ |
| (ST_CMD_SIZE + ST_FLD_SIZE(ec_params_charge_state, SUBCMD)) |
| #define ST_RSP_SIZE(SUBCMD) ST_FLD_SIZE(ec_response_charge_state, SUBCMD) |
| |
| /* Table of subcommand sizes for EC_CMD_CHARGE_STATE */ |
| static const struct { |
| uint8_t to_ec_size; |
| uint8_t from_ec_size; |
| } cs_paramcount[] = { |
| /* Order must match enum charge_state_command */ |
| { ST_CMD_SIZE, ST_RSP_SIZE(get_state) }, |
| { ST_PRM_SIZE(get_param), ST_RSP_SIZE(get_param) }, |
| { ST_PRM_SIZE(set_param), 0 }, |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(cs_paramcount) == CHARGE_STATE_NUM_CMDS); |
| |
| #undef ST_CMD_SIZE |
| #undef ST_PRM_SIZE |
| #undef ST_RSP_SIZE |
| |
| static int cs_do_cmd(struct ec_params_charge_state *to_ec, |
| struct ec_response_charge_state *from_ec) |
| { |
| int rv; |
| int cmd = to_ec->cmd; |
| |
| rv = ec_command(EC_CMD_CHARGE_STATE, 0, to_ec, |
| cs_paramcount[cmd].to_ec_size, from_ec, |
| cs_paramcount[cmd].from_ec_size); |
| |
| return (rv < 0 ? 1 : 0); |
| } |
| |
| static const char *const base_params[] = { |
| "chg_voltage", |
| "chg_current", |
| "chg_input_current", |
| "chg_status", |
| "chg_option", |
| "limit_power", |
| "chg_voltage_min", |
| "chg_voltage_max", |
| "chg_voltage_step", |
| "chg_current_min", |
| "chg_current_max", |
| "chg_current_step", |
| "chg_input_current_min", |
| "chg_input_current_max", |
| "chg_input_current_step", |
| "chg_minimum_charging_mv", |
| "chg_is_charger_sufficient", |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(base_params) == CS_NUM_BASE_PARAMS); |
| |
| static int cmd_charge_state(int argc, char **argv) |
| { |
| struct ec_params_charge_state param; |
| struct ec_response_charge_state resp; |
| uint32_t p, v; |
| int i, r; |
| char *e; |
| |
| if (argc > 1 && !strcasecmp(argv[1], "show")) { |
| param.cmd = CHARGE_STATE_CMD_GET_STATE; |
| r = cs_do_cmd(¶m, &resp); |
| if (r) |
| return r; |
| printf("ac = %d\n", resp.get_state.ac); |
| printf("chg_voltage = %dmV\n", resp.get_state.chg_voltage); |
| printf("chg_current = %dmA\n", resp.get_state.chg_current); |
| printf("chg_input_current = %dmA\n", |
| resp.get_state.chg_input_current); |
| printf("batt_state_of_charge = %d%%\n", |
| resp.get_state.batt_state_of_charge); |
| return 0; |
| } |
| |
| if (argc > 1 && !strcasecmp(argv[1], "param")) { |
| switch (argc) { |
| case 3: |
| if (!strcasecmp(argv[2], "help")) |
| break; |
| param.cmd = CHARGE_STATE_CMD_GET_PARAM; |
| p = strtoull(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad param: %s\n", argv[2]); |
| return -1; |
| } |
| param.get_param.param = p; |
| r = cs_do_cmd(¶m, &resp); |
| if (r) |
| return r; |
| v = resp.get_param.value; |
| if (p < CS_NUM_BASE_PARAMS) |
| printf("%d (0x%x) # %s\n", v, v, |
| base_params[p]); |
| else |
| printf("%d (0x%x)\n", v, v); |
| return 0; |
| case 4: |
| param.cmd = CHARGE_STATE_CMD_SET_PARAM; |
| p = strtoull(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad param: %s\n", argv[2]); |
| return -1; |
| } |
| v = strtoull(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad value: %s\n", argv[3]); |
| return -1; |
| } |
| param.set_param.param = p; |
| param.set_param.value = v; |
| return cs_do_cmd(¶m, &resp); |
| } |
| |
| printf("base params:\n"); |
| for (i = 0; i < CS_NUM_BASE_PARAMS; i++) |
| printf(" %d %s\n", i, base_params[i]); |
| printf("custom profile params:\n"); |
| printf(" 0x%x - 0x%x\n", CS_PARAM_CUSTOM_PROFILE_MIN, |
| CS_PARAM_CUSTOM_PROFILE_MAX); |
| |
| return 0; |
| } |
| |
| printf("Usage:\n"); |
| printf(" %s show - show current state\n", argv[0]); |
| printf(" %s param NUM [VALUE] - get/set param NUM\n", argv[0]); |
| printf(" %s param help - show known param NUMs\n", argv[0]); |
| return 0; |
| } |
| |
| int cmd_gpio_get(int argc, char *argv[]) |
| { |
| struct ec_params_gpio_get_v1 p_v1; |
| struct ec_response_gpio_get_v1 r_v1; |
| int i, rv, subcmd, num_gpios; |
| int cmdver = 1; |
| |
| if (!ec_cmd_version_supported(EC_CMD_GPIO_GET, cmdver)) { |
| struct ec_params_gpio_get p; |
| struct ec_response_gpio_get r; |
| |
| /* Fall back to version 0 command */ |
| cmdver = 0; |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <GPIO name>\n", argv[0]); |
| return -1; |
| } |
| |
| if (strlen(argv[1]) + 1 > sizeof(p.name)) { |
| fprintf(stderr, "GPIO name too long.\n"); |
| return -1; |
| } |
| strcpy(p.name, argv[1]); |
| |
| rv = ec_command(EC_CMD_GPIO_GET, cmdver, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("GPIO %s = %d\n", p.name, r.val); |
| return 0; |
| } |
| |
| if (argc > 2 || (argc == 2 && !strcmp(argv[1], "help"))) { |
| printf("Usage: %s [<subcmd> <GPIO name>]\n", argv[0]); |
| printf("'gpioget <GPIO_NAME>' - Get value by name\n"); |
| printf("'gpioget count' - Get count of GPIOS\n"); |
| printf("'gpioget all' - Get info for all GPIOs\n"); |
| return -1; |
| } |
| |
| /* Keeping it consistent with console command behavior */ |
| if (argc == 1) |
| subcmd = EC_GPIO_GET_INFO; |
| else if (!strcmp(argv[1], "count")) |
| subcmd = EC_GPIO_GET_COUNT; |
| else if (!strcmp(argv[1], "all")) |
| subcmd = EC_GPIO_GET_INFO; |
| else |
| subcmd = EC_GPIO_GET_BY_NAME; |
| |
| if (subcmd == EC_GPIO_GET_BY_NAME) { |
| p_v1.subcmd = EC_GPIO_GET_BY_NAME; |
| if (strlen(argv[1]) + 1 > sizeof(p_v1.get_value_by_name.name)) { |
| fprintf(stderr, "GPIO name too long.\n"); |
| return -1; |
| } |
| strcpy(p_v1.get_value_by_name.name, argv[1]); |
| |
| rv = ec_command(EC_CMD_GPIO_GET, cmdver, &p_v1, sizeof(p_v1), |
| &r_v1, sizeof(r_v1)); |
| |
| if (rv < 0) |
| return rv; |
| |
| printf("GPIO %s = %d\n", p_v1.get_value_by_name.name, |
| r_v1.get_value_by_name.val); |
| return 0; |
| } |
| |
| /* Need GPIO count for EC_GPIO_GET_COUNT or EC_GPIO_GET_INFO */ |
| p_v1.subcmd = EC_GPIO_GET_COUNT; |
| rv = ec_command(EC_CMD_GPIO_GET, cmdver, &p_v1, sizeof(p_v1), &r_v1, |
| sizeof(r_v1)); |
| if (rv < 0) |
| return rv; |
| |
| if (subcmd == EC_GPIO_GET_COUNT) { |
| printf("GPIO COUNT = %d\n", r_v1.get_count.val); |
| return 0; |
| } |
| |
| /* subcmd EC_GPIO_GET_INFO */ |
| num_gpios = r_v1.get_count.val; |
| p_v1.subcmd = EC_GPIO_GET_INFO; |
| |
| for (i = 0; i < num_gpios; i++) { |
| p_v1.get_info.index = i; |
| |
| rv = ec_command(EC_CMD_GPIO_GET, cmdver, &p_v1, sizeof(p_v1), |
| &r_v1, sizeof(r_v1)); |
| if (rv < 0) |
| return rv; |
| |
| printf("%2d %-32s 0x%04X\n", r_v1.get_info.val, |
| r_v1.get_info.name, r_v1.get_info.flags); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_gpio_set(int argc, char *argv[]) |
| { |
| struct ec_params_gpio_set p; |
| char *e; |
| int rv; |
| |
| if (argc != 3) { |
| fprintf(stderr, "Usage: %s <GPIO name> <0 | 1>\n", argv[0]); |
| return -1; |
| } |
| |
| if (strlen(argv[1]) + 1 > sizeof(p.name)) { |
| fprintf(stderr, "GPIO name too long.\n"); |
| return -1; |
| } |
| strcpy(p.name, argv[1]); |
| |
| p.val = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad value.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_GPIO_SET, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("GPIO %s set to %d\n", p.name, p.val); |
| return 0; |
| } |
| |
| void print_battery_flags(int flags) |
| { |
| printf(" Flags 0x%02x", flags); |
| if (flags & EC_BATT_FLAG_AC_PRESENT) |
| printf(" AC_PRESENT"); |
| if (flags & EC_BATT_FLAG_BATT_PRESENT) |
| printf(" BATT_PRESENT"); |
| if (flags & EC_BATT_FLAG_DISCHARGING) |
| printf(" DISCHARGING"); |
| if (flags & EC_BATT_FLAG_CHARGING) |
| printf(" CHARGING"); |
| if (flags & EC_BATT_FLAG_LEVEL_CRITICAL) |
| printf(" LEVEL_CRITICAL"); |
| if (flags & EC_BATT_FLAG_CUT_OFF) |
| printf(" CUT_OFF"); |
| printf("\n"); |
| } |
| |
| static int get_battery_command_print_info( |
| uint8_t index, |
| const struct ec_response_battery_static_info_v2 *const static_r, |
| const struct ec_response_battery_dynamic_info *dynamic_r) |
| { |
| struct ec_params_battery_dynamic_info dynamic_p = { |
| .index = index, |
| }; |
| struct ec_response_battery_dynamic_info d; |
| bool dynamic_from_cmd = false; |
| int rv; |
| |
| if (!dynamic_r) { |
| dynamic_from_cmd = true; |
| rv = ec_command(EC_CMD_BATTERY_GET_DYNAMIC, 0, &dynamic_p, |
| sizeof(dynamic_p), &d, sizeof(d)); |
| if (rv < 0) |
| return -1; |
| dynamic_r = &d; |
| } |
| |
| printf("Battery %d info:\n", index); |
| |
| if (dynamic_r->flags & EC_BATT_FLAG_INVALID_DATA) { |
| printf(" Invalid data (not present?)\n"); |
| return -1; |
| } |
| |
| if (!is_string_printable(static_r->manufacturer)) |
| goto cmd_error; |
| printf(" Manufacturer: %s\n", static_r->manufacturer); |
| |
| if (!is_string_printable(static_r->device_name)) |
| goto cmd_error; |
| printf(" Device name: %s\n", static_r->device_name); |
| |
| if (!is_string_printable(static_r->chemistry)) |
| goto cmd_error; |
| printf(" Chemistry : %s\n", static_r->chemistry); |
| |
| if (!is_string_printable(static_r->serial)) |
| goto cmd_error; |
| printf(" Serial number: %s\n", static_r->serial); |
| |
| if (!is_battery_range(static_r->design_capacity)) |
| goto cmd_error; |
| printf(" Design capacity: %u mAh\n", static_r->design_capacity); |
| |
| if (!is_battery_range(dynamic_r->full_capacity)) |
| goto cmd_error; |
| printf(" Last full charge: %u mAh\n", dynamic_r->full_capacity); |
| |
| if (!is_battery_range(static_r->design_voltage)) |
| goto cmd_error; |
| printf(" Design output voltage %u mV\n", static_r->design_voltage); |
| |
| if (!is_battery_range(static_r->cycle_count)) |
| goto cmd_error; |
| printf(" Cycle count %u\n", static_r->cycle_count); |
| |
| if (!is_battery_range(dynamic_r->actual_voltage)) |
| goto cmd_error; |
| printf(" Present voltage %u mV\n", dynamic_r->actual_voltage); |
| |
| /* current can be negative */ |
| printf(" Present current %d mA\n", dynamic_r->actual_current); |
| |
| if (!is_battery_range(dynamic_r->remaining_capacity)) |
| goto cmd_error; |
| printf(" Remaining capacity %u mAh\n", |
| dynamic_r->remaining_capacity); |
| |
| if (dynamic_from_cmd) { |
| if (!is_battery_range(dynamic_r->desired_voltage)) |
| goto cmd_error; |
| printf(" Desired voltage %u mV\n", |
| dynamic_r->desired_voltage); |
| } |
| |
| if (dynamic_from_cmd) { |
| if (!is_battery_range(dynamic_r->desired_current)) |
| goto cmd_error; |
| printf(" Desired current %u mA\n", |
| dynamic_r->desired_current); |
| } |
| |
| print_battery_flags(dynamic_r->flags); |
| return 0; |
| |
| cmd_error: |
| fprintf(stderr, "Bad battery info value.\n"); |
| return -1; |
| } |
| |
| static int get_battery_command_v2(uint8_t index) |
| { |
| struct ec_params_battery_static_info static_p = { |
| .index = index, |
| }; |
| struct ec_response_battery_static_info_v2 static_r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_BATTERY_GET_STATIC, 2, &static_p, |
| sizeof(static_p), &static_r, sizeof(static_r)); |
| if (rv < 0) { |
| fprintf(stderr, "CMD_BATTERY_GET_STATIC v2 failed: %d\n", rv); |
| return -1; |
| } |
| |
| return get_battery_command_print_info(index, &static_r, NULL); |
| } |
| |
| static int get_battery_command_v1(uint8_t index) |
| { |
| struct ec_params_battery_static_info static_p{ |
| .index = index, |
| }; |
| struct ec_response_battery_static_info_v1 static_r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_BATTERY_GET_STATIC, 1, &static_p, |
| sizeof(static_p), &static_r, sizeof(static_r)); |
| if (rv < 0) { |
| fprintf(stderr, "CMD_BATTERY_GET_STATIC v1 failed: %d\n", rv); |
| return -1; |
| } |
| |
| /* Translate v1 response into v2 to display it */ |
| struct ec_response_battery_static_info_v2 static_v2 = { |
| .design_capacity = static_r.design_capacity, |
| .design_voltage = static_r.design_voltage, |
| .cycle_count = static_r.cycle_count, |
| }; |
| strncpy(static_v2.manufacturer, static_r.manufacturer_ext, |
| sizeof(static_v2.manufacturer) - 1); |
| strncpy(static_v2.device_name, static_r.model_ext, |
| sizeof(static_v2.device_name) - 1); |
| strncpy(static_v2.serial, static_r.serial_ext, |
| sizeof(static_v2.serial) - 1); |
| strncpy(static_v2.chemistry, static_r.type_ext, |
| sizeof(static_v2.chemistry) - 1); |
| |
| return get_battery_command_print_info(index, &static_v2, NULL); |
| } |
| static int get_battery_command_v0(uint8_t index) |
| { |
| struct ec_params_battery_static_info static_p = { |
| .index = index, |
| }; |
| struct ec_response_battery_static_info static_r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_BATTERY_GET_STATIC, 0, &static_p, |
| sizeof(static_p), &static_r, sizeof(static_r)); |
| if (rv < 0) { |
| fprintf(stderr, "CMD_BATTERY_GET_STATIC v0 failed: %d\n", rv); |
| return -1; |
| } |
| |
| /* Translate v0 response into v2 to display it */ |
| struct ec_response_battery_static_info_v2 static_v2 = { |
| .design_capacity = static_r.design_capacity, |
| .design_voltage = static_r.design_voltage, |
| .cycle_count = static_r.cycle_count, |
| }; |
| strncpy(static_v2.manufacturer, static_r.manufacturer, |
| sizeof(static_v2.manufacturer) - 1); |
| strncpy(static_v2.device_name, static_r.model, |
| sizeof(static_v2.device_name) - 1); |
| strncpy(static_v2.serial, static_r.serial, |
| sizeof(static_v2.serial) - 1); |
| strncpy(static_v2.chemistry, static_r.type, |
| sizeof(static_v2.chemistry) - 1); |
| |
| return get_battery_command_print_info(index, &static_v2, NULL); |
| } |
| |
| static int get_battery_info_from_memmap(void) |
| { |
| struct ec_response_battery_static_info_v2 s2 = {}; |
| struct ec_response_battery_dynamic_info d = {}; |
| int val; |
| |
| val = read_mapped_mem8(EC_MEMMAP_BATTERY_VERSION); |
| if (val < 1) { |
| fprintf(stderr, "Battery version %d is not supported\n", val); |
| return -1; |
| } |
| |
| d.flags = read_mapped_mem8(EC_MEMMAP_BATT_FLAG); |
| |
| read_mapped_string(EC_MEMMAP_BATT_MFGR, s2.manufacturer, |
| sizeof(s2.manufacturer)); |
| |
| read_mapped_string(EC_MEMMAP_BATT_MODEL, s2.device_name, |
| sizeof(s2.device_name)); |
| |
| read_mapped_string(EC_MEMMAP_BATT_TYPE, s2.chemistry, |
| sizeof(s2.chemistry)); |
| |
| read_mapped_string(EC_MEMMAP_BATT_SERIAL, s2.serial, sizeof(s2.serial)); |
| |
| s2.design_capacity = read_mapped_mem32(EC_MEMMAP_BATT_DCAP); |
| |
| d.full_capacity = read_mapped_mem32(EC_MEMMAP_BATT_LFCC); |
| |
| s2.design_voltage = read_mapped_mem32(EC_MEMMAP_BATT_DVLT); |
| |
| s2.cycle_count = read_mapped_mem32(EC_MEMMAP_BATT_CCNT); |
| |
| d.actual_voltage = read_mapped_mem32(EC_MEMMAP_BATT_VOLT); |
| |
| d.actual_current = read_mapped_mem32(EC_MEMMAP_BATT_RATE); |
| if (d.flags & EC_BATT_FLAG_DISCHARGING) |
| d.actual_current *= -1; |
| |
| d.remaining_capacity = read_mapped_mem32(EC_MEMMAP_BATT_CAP); |
| |
| if (get_battery_command_print_info(0, &s2, &d)) { |
| fprintf(stderr, |
| "Bad battery info value. Check protocol version.\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int cmd_battery(int argc, char *argv[]) |
| { |
| char *e; |
| int index = 0; |
| |
| if (argc > 2) { |
| fprintf(stderr, "Usage: %s [index]\n", argv[0]); |
| return -1; |
| } else if (argc == 2) { |
| index = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad battery index.\n"); |
| return -1; |
| } |
| } |
| |
| /* |
| * Prefer to use newer hostcmd versions if supported because these allow |
| * us to read longer strings, and always use hostcmd for non-primary |
| * batteries because memmap doesn't export that data. |
| */ |
| uint32_t versions; |
| ec_get_cmd_versions(EC_CMD_BATTERY_GET_STATIC, &versions); |
| |
| if (versions & EC_VER_MASK(2)) |
| return get_battery_command_v2(index); |
| else if (versions & EC_VER_MASK(1)) |
| return get_battery_command_v1(index); |
| else if (index > 0) |
| return get_battery_command_v0(index); |
| else |
| return get_battery_info_from_memmap(); |
| } |
| |
| int cmd_battery_cut_off(int argc, char *argv[]) |
| { |
| struct ec_params_battery_cutoff p; |
| int cmd_version; |
| int rv; |
| |
| memset(&p, 0, sizeof(p)); |
| if (ec_cmd_version_supported(EC_CMD_BATTERY_CUT_OFF, 1)) { |
| cmd_version = 1; |
| if (argc > 1) { |
| if (!strcasecmp(argv[1], "at-shutdown")) { |
| p.flags = EC_BATTERY_CUTOFF_FLAG_AT_SHUTDOWN; |
| } else { |
| fprintf(stderr, "Bad parameter: %s\n", argv[1]); |
| return -1; |
| } |
| } |
| } else { |
| /* Fall back to version 0 command */ |
| cmd_version = 0; |
| if (argc > 1) { |
| if (!strcasecmp(argv[1], "at-shutdown")) { |
| fprintf(stderr, "Explicit 'at-shutdown' "); |
| fprintf(stderr, "parameter not supported.\n"); |
| } else { |
| fprintf(stderr, "Bad parameter: %s\n", argv[1]); |
| } |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_BATTERY_CUT_OFF, cmd_version, &p, sizeof(p), |
| NULL, 0); |
| rv = (rv < 0 ? rv : 0); |
| |
| if (rv < 0) { |
| fprintf(stderr, "Failed to cut off battery, rv=%d\n", rv); |
| fprintf(stderr, |
| "It is expected if the rv is -%d " |
| "(EC_RES_INVALID_COMMAND) if the battery " |
| "doesn't support cut-off function.\n", |
| EC_RES_INVALID_COMMAND); |
| } else { |
| printf("\n"); |
| printf("SUCCESS. The battery has arranged a cut-off.\n"); |
| |
| if (cmd_version == 1 && |
| (p.flags & EC_BATTERY_CUTOFF_FLAG_AT_SHUTDOWN)) |
| printf("The battery will be cut off after shutdown.\n"); |
| else |
| printf("The system should be shutdown immediately.\n"); |
| |
| printf("\n"); |
| } |
| return rv; |
| } |
| |
| int cmd_battery_vendor_param(int argc, char *argv[]) |
| { |
| struct ec_params_battery_vendor_param p; |
| struct ec_response_battery_vendor_param r; |
| char *e; |
| int rv; |
| |
| if (argc < 3) |
| goto cmd_battery_vendor_param_usage; |
| |
| if (!strcasecmp(argv[1], "get")) |
| p.mode = BATTERY_VENDOR_PARAM_MODE_GET; |
| else if (!strcasecmp(argv[1], "set")) |
| p.mode = BATTERY_VENDOR_PARAM_MODE_SET; |
| else |
| goto cmd_battery_vendor_param_usage; |
| |
| p.param = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Invalid param.\n"); |
| goto cmd_battery_vendor_param_usage; |
| } |
| |
| if (p.mode == BATTERY_VENDOR_PARAM_MODE_SET) { |
| if (argc != 4) { |
| fprintf(stderr, "Missing value.\n"); |
| goto cmd_battery_vendor_param_usage; |
| } |
| |
| p.value = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Invalid value.\n"); |
| goto cmd_battery_vendor_param_usage; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_BATTERY_VENDOR_PARAM, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| |
| if (rv < 0) |
| return rv; |
| |
| printf("0x%08x\n", r.value); |
| |
| return 0; |
| |
| cmd_battery_vendor_param_usage: |
| fprintf(stderr, |
| "Usage:\t %s get <param>\n" |
| "\t %s set <param> <value>\n", |
| argv[0], argv[0]); |
| return -1; |
| } |
| |
| static void batt_conf_dump(const struct board_batt_params *conf, |
| const char *manuf_name, const char *device_name, |
| uint8_t struct_version, bool is_human_readable) |
| { |
| const struct fuel_gauge_info *fg = &conf->fuel_gauge; |
| const struct ship_mode_info *ship = &conf->fuel_gauge.ship_mode; |
| const struct sleep_mode_info *sleep = &conf->fuel_gauge.sleep_mode; |
| const struct fet_info *fet = &conf->fuel_gauge.fet; |
| const struct battery_info *info = &conf->batt_info; |
| const char *comma = is_human_readable ? "," : ""; |
| |
| printf("{\n"); /* Start of root */ |
| printf("\t\"%s,%s\": {\n", manuf_name, device_name); |
| printf("\t\t\"struct_version\": \"0x%02x\",\n", struct_version); |
| printf("\t\t\"fuel_gauge\": {\n"); |
| printf("\t\t\t\"flags\": \"0x%x\",\n", fg->flags); |
| |
| printf("\t\t\t\"ship_mode\": {\n"); |
| printf("\t\t\t\t\"reg_addr\": \"0x%02x\",\n", ship->reg_addr); |
| printf("\t\t\t\t\"reg_data\": [ \"0x%04x\", \"0x%04x\" ]%s\n", |
| ship->reg_data[0], ship->reg_data[1], comma); |
| printf("\t\t\t},\n"); |
| |
| printf("\t\t\t\"sleep_mode\": {\n"); |
| printf("\t\t\t\t\"reg_addr\": \"0x%02x\",\n", sleep->reg_addr); |
| printf("\t\t\t\t\"reg_data\": \"0x%04x\"%s\n", sleep->reg_data, comma); |
| printf("\t\t\t},\n"); |
| |
| printf("\t\t\t\"fet\": {\n"); |
| printf("\t\t\t\t\"reg_addr\": \"0x%02x\",\n", fet->reg_addr); |
| printf("\t\t\t\t\"reg_mask\": \"0x%04x\",\n", fet->reg_mask); |
| printf("\t\t\t\t\"disconnect_val\": \"0x%04x\",\n", |
| fet->disconnect_val); |
| printf("\t\t\t\t\"cfet_mask\": \"0x%04x\",\n", fet->cfet_mask); |
| printf("\t\t\t\t\"cfet_off_val\": \"0x%04x\"%s\n", fet->cfet_off_val, |
| comma); |
| printf("\t\t\t}%s\n", comma); |
| |
| printf("\t\t},\n"); /* end of fuel_gauge */ |
| |
| printf("\t\t\"batt_info\": {\n"); |
| printf("\t\t\t\"voltage_max\": %d,\n", info->voltage_max); |
| printf("\t\t\t\"voltage_normal\": %d,\n", info->voltage_normal); |
| printf("\t\t\t\"voltage_min\": %d,\n", info->voltage_min); |
| printf("\t\t\t\"precharge_voltage\": %d,\n", info->precharge_voltage); |
| printf("\t\t\t\"precharge_current\": %d,\n", info->precharge_current); |
| printf("\t\t\t\"start_charging_min_c\": %d,\n", |
| info->start_charging_min_c); |
| printf("\t\t\t\"start_charging_max_c\": %d,\n", |
| info->start_charging_max_c); |
| printf("\t\t\t\"charging_min_c\": %d,\n", info->charging_min_c); |
| printf("\t\t\t\"charging_max_c\": %d,\n", info->charging_max_c); |
| printf("\t\t\t\"discharging_min_c\": %d,\n", info->discharging_min_c); |
| printf("\t\t\t\"discharging_max_c\": %d%s\n", info->discharging_max_c, |
| comma); |
| printf("\t\t}%s\n", comma); /* end of batt_info */ |
| |
| printf("\t}%s\n", comma); /* end of board_batt_params */ |
| printf("}\n"); /* End of root */ |
| } |
| |
| static void batt_conf_dump_in_c(const struct board_batt_params *conf, |
| const char *manuf_name, const char *device_name, |
| uint8_t struct_version) |
| { |
| const struct fuel_gauge_info *fg = &conf->fuel_gauge; |
| const struct ship_mode_info *ship = &conf->fuel_gauge.ship_mode; |
| const struct sleep_mode_info *sleep = &conf->fuel_gauge.sleep_mode; |
| const struct fet_info *fet = &conf->fuel_gauge.fet; |
| const struct battery_info *info = &conf->batt_info; |
| |
| printf("// struct_version = 0x%02x\n", struct_version); |
| printf(".manuf_name = \"%s\",\n", manuf_name); |
| printf(".device_name = \"%s\",\n", device_name); |
| |
| printf(".config = {\n"); |
| printf("\t.fuel_gauge = {\n"); |
| printf("\t\t.flags = 0x%x,\n", fg->flags); |
| |
| printf("\t\t.ship_mode = {\n"); |
| printf("\t\t\t.reg_addr = 0x%02x,\n", ship->reg_addr); |
| printf("\t\t\t.reg_data = { 0x%04x, 0x%04x },\n", ship->reg_data[0], |
| ship->reg_data[1]); |
| printf("\t\t},\n"); |
| |
| printf("\t\t.sleep_mode = {\n"); |
| printf("\t\t\t.reg_addr = 0x%02x,\n", sleep->reg_addr); |
| printf("\t\t\t.reg_data = 0x%04x,\n", sleep->reg_data); |
| printf("\t\t},\n"); |
| |
| printf("\t\t.fet = {\n"); |
| printf("\t\t\t.reg_addr = 0x%02x,\n", fet->reg_addr); |
| printf("\t\t\t.reg_mask = 0x%04x,\n", fet->reg_mask); |
| printf("\t\t\t.disconnect_val = 0x%04x,\n", fet->disconnect_val); |
| printf("\t\t\t.cfet_mask = 0x%04x,\n", fet->cfet_mask); |
| printf("\t\t\t.cfet_off_val = 0x%04x,\n", fet->cfet_off_val); |
| printf("\t\t},\n"); |
| |
| printf("\t},\n"); /* end of fuel_gauge */ |
| |
| printf("\t.batt_info = {\n"); |
| printf("\t\t.voltage_max = %d,\n", info->voltage_max); |
| printf("\t\t.voltage_normal = %d,\n", info->voltage_normal); |
| printf("\t\t.voltage_min = %d,\n", info->voltage_min); |
| printf("\t\t.precharge_voltage= %d,\n", info->precharge_voltage); |
| printf("\t\t.precharge_current = %d,\n", info->precharge_current); |
| printf("\t\t.start_charging_min_c = %d,\n", info->start_charging_min_c); |
| printf("\t\t.start_charging_max_c = %d,\n", info->start_charging_max_c); |
| printf("\t\t.charging_min_c = %d,\n", info->charging_min_c); |
| printf("\t\t.charging_max_c = %d,\n", info->charging_max_c); |
| printf("\t\t.discharging_min_c = %d,\n", info->discharging_min_c); |
| printf("\t\t.discharging_max_c = %d,\n", info->discharging_max_c); |
| printf("\t},\n"); /* end of batt_info */ |
| |
| printf("},\n"); /* end of board_batt_params */ |
| } |
| |
| static int read_u32_from_json(base::DictValue *dict, const char *key, |
| uint32_t *value) |
| { |
| std::string *str = dict->FindString(key); |
| char *e; |
| |
| if (str == nullptr) { |
| if (verbose) |
| printf("Key '%s' not found. Ignored.\n", key); |
| return 0; |
| } |
| |
| *value = strtoul(str->c_str(), &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Failed to parse '%s: %s'\n", key, |
| str->c_str()); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int read_u16_from_json(base::DictValue *dict, const char *key, |
| uint16_t *value) |
| { |
| std::string *str = dict->FindString(key); |
| char *e; |
| |
| if (str == nullptr) { |
| if (verbose) |
| printf("Key '%s' not found. Ignored.\n", key); |
| return 0; |
| } |
| |
| *value = strtoul(str->c_str(), &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Failed to parse '%s: %s'\n", key, |
| str->c_str()); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int read_u8_from_json(base::DictValue *dict, const char *key, |
| uint8_t *value) |
| { |
| const std::string *str = dict->FindString(key); |
| char *e; |
| |
| if (str == nullptr) { |
| if (verbose) |
| printf("Key '%s' not found. Ignored.\n", key); |
| return 0; |
| } |
| |
| *value = strtoul(str->c_str(), &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Failed to parse '%s: %s'\n", key, |
| str->c_str()); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int read_battery_config_from_json(base::DictValue *root_dict, |
| struct board_batt_params *config) |
| { |
| int i; |
| char *e; |
| |
| base::DictValue *fuel_gauge = root_dict->FindDict("fuel_gauge"); |
| if (fuel_gauge == nullptr) { |
| fprintf(stderr, "Error. fuel_gauge not found.\n"); |
| return -1; |
| } |
| if (read_u32_from_json(fuel_gauge, "flags", &config->fuel_gauge.flags)) |
| return -1; |
| if (read_u32_from_json(fuel_gauge, "board_flags", |
| &config->fuel_gauge.board_flags)) |
| return -1; |
| |
| base::DictValue *ship_mode = fuel_gauge->FindDict("ship_mode"); |
| if (ship_mode != nullptr) { |
| struct ship_mode_info *sm = &config->fuel_gauge.ship_mode; |
| |
| if (read_u8_from_json(ship_mode, "reg_addr", &sm->reg_addr)) |
| return -1; |
| |
| base::ListValue *reg_data = ship_mode->FindList("reg_data"); |
| for (i = 0; i < reg_data->size() && i < SHIP_MODE_WRITES; ++i) { |
| const std::string *str = (*reg_data)[i].GetIfString(); |
| sm->reg_data[i] = strtoul(str->c_str(), &e, 0); |
| if (e && *e) { |
| fprintf(stderr, |
| "Failed to parse reg_data: %s\n", |
| str->c_str()); |
| return -1; |
| } |
| }; |
| } |
| |
| base::DictValue *sleep_mode = fuel_gauge->FindDict("sleep_mode"); |
| if (sleep_mode != nullptr) { |
| struct sleep_mode_info *sm = &config->fuel_gauge.sleep_mode; |
| |
| if (read_u8_from_json(sleep_mode, "reg_addr", &sm->reg_addr)) |
| return -1; |
| if (read_u16_from_json(sleep_mode, "reg_data", &sm->reg_data)) |
| return -1; |
| } |
| |
| base::DictValue *fet = fuel_gauge->FindDict("fet"); |
| if (fet != nullptr) { |
| struct fet_info *fi = &config->fuel_gauge.fet; |
| |
| if (read_u8_from_json(fet, "reg_addr", &fi->reg_addr)) |
| return -1; |
| if (read_u16_from_json(fet, "reg_mask", &fi->reg_mask)) |
| return -1; |
| if (read_u16_from_json(fet, "disconnect_val", |
| &fi->disconnect_val)) |
| return -1; |
| if (read_u16_from_json(fet, "cfet_mask", &fi->cfet_mask)) |
| return -1; |
| if (read_u16_from_json(fet, "cfet_off_val", &fi->cfet_off_val)) |
| return -1; |
| } |
| |
| base::DictValue *batt_info = root_dict->FindDict("batt_info"); |
| if (batt_info == nullptr) { |
| fprintf(stderr, "Error. batt_info not found.\n"); |
| return -1; |
| } |
| |
| struct battery_info *bi = &config->batt_info; |
| std::optional<int> voltage_max = batt_info->FindInt("voltage_max"); |
| std::optional<int> voltage_normal = |
| batt_info->FindInt("voltage_normal"); |
| std::optional<int> voltage_min = batt_info->FindInt("voltage_min"); |
| std::optional<int> precharge_voltage = |
| batt_info->FindInt("precharge_voltage"); |
| std::optional<int> precharge_current = |
| batt_info->FindInt("precharge_current"); |
| std::optional<int> start_charging_min_c = |
| batt_info->FindInt("start_charging_min_c"); |
| std::optional<int> start_charging_max_c = |
| batt_info->FindInt("start_charging_max_c"); |
| std::optional<int> charging_min_c = |
| batt_info->FindInt("charging_min_c"); |
| std::optional<int> charging_max_c = |
| batt_info->FindInt("charging_max_c"); |
| std::optional<int> discharging_min_c = |
| batt_info->FindInt("discharging_min_c"); |
| std::optional<int> discharging_max_c = |
| batt_info->FindInt("discharging_max_c"); |
| std::optional<int> vendor_param_start = |
| batt_info->FindInt("vendor_param_start"); |
| bi->voltage_max = voltage_max.value(); |
| bi->voltage_normal = voltage_normal.value(); |
| bi->voltage_min = voltage_min.value(); |
| bi->precharge_voltage = precharge_voltage.value(); |
| bi->precharge_current = precharge_current.value(); |
| bi->start_charging_min_c = start_charging_min_c.value(); |
| bi->start_charging_max_c = start_charging_max_c.value(); |
| bi->charging_min_c = charging_min_c.value(); |
| bi->charging_max_c = charging_max_c.value(); |
| bi->discharging_min_c = discharging_min_c.value(); |
| bi->discharging_max_c = discharging_max_c.value(); |
| if (vendor_param_start != std::nullopt) |
| bi->vendor_param_start = vendor_param_start.value(); |
| |
| return 0; |
| } |
| |
| static void cmd_battery_config_help(const char *cmd) |
| { |
| fprintf(stderr, |
| "\n" |
| "Usage: %s get [-c/-j/-h] [<index>]\n" |
| " Print active battery config in one of following formats:\n" |
| " JSON5 (-h), JSON (-j), C-struct (-c). Default output format is\n" |
| " JSON5 (-h).\n" |
| " If <index> is specified, a config is read from CBI.\n" |
| "\n" |
| "Usage: %s set <json_file> <manuf_name> <device_name> [<index>]\n" |
| " Copy battery config from file to CBI.\n" |
| "\n" |
| " json_file: Path to JSON file containing battery configs\n" |
| " manuf_name: Manufacturer's name. Up to 31 chars.\n" |
| " device_name: Battery's name. Up to 31 chars.\n" |
| " index: Index of config in CBI to be get or set.\n" |
| "\n" |
| " Run `ectool battery` for <manuf_name> and <device_name>\n" |
| "\n" |
| "Usage: %s search <json_file> <manuf_name> <device_name> [<index>]\n" |
| " Search for a matching battery config in the config file.\n", |
| cmd, cmd, cmd); |
| } |
| |
| static int cmd_battery_config_get(int argc, char *argv[]) |
| { |
| struct batt_conf_header *head; |
| struct board_batt_params conf; |
| char manuf_name[SBS_MAX_STR_OBJ_SIZE]; |
| char device_name[SBS_MAX_STR_OBJ_SIZE]; |
| uint8_t *p; |
| int expected; |
| bool in_json = true; |
| bool in_json_human = false; |
| int rv; |
| int c; |
| int index = -1; |
| |
| while ((c = getopt(argc, argv, "chj")) != -1) { |
| switch (c) { |
| case 'c': |
| in_json = false; |
| break; |
| case 'j': |
| in_json_human = false; |
| break; |
| case 'h': |
| in_json_human = true; |
| break; |
| case '?': |
| /* getopt prints error message. */ |
| cmd_battery_config_help("bcfg"); |
| return -1; |
| default: |
| return -1; |
| } |
| } |
| |
| if (optind < argc) { |
| char *e; |
| index = strtol(argv[optind], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad index: '%s'\n", argv[optind]); |
| return -1; |
| } |
| optind++; |
| } |
| |
| if (optind < argc) { |
| fprintf(stderr, "Unknown argument '%s'\n", argv[optind]); |
| cmd_battery_config_help("bcfg"); |
| return -1; |
| } |
| |
| if (index < 0) { |
| rv = ec_command(EC_CMD_BATTERY_CONFIG, 0, NULL, 0, ec_inbuf, |
| ec_max_insize); |
| } else { |
| struct ec_params_get_cbi pa = { 0 }; |
| |
| pa.tag = index + CBI_TAG_BATTERY_CONFIG; |
| rv = ec_command(EC_CMD_GET_CROS_BOARD_INFO, 0, &pa, sizeof(pa), |
| ec_inbuf, ec_max_insize); |
| if (rv == -EC_RES_INVALID_PARAM - EECRESULT) |
| fprintf(stderr, "Config[%d] not found in CBI.\n", |
| index); |
| } |
| if (rv < 0) |
| return rv; |
| |
| head = (struct batt_conf_header *)ec_inbuf; |
| if (head->struct_version > EC_BATTERY_CONFIG_STRUCT_VERSION) { |
| fprintf(stderr, |
| "Struct version mismatch. Supported: 0x00 ~ 0x%02x.\n", |
| EC_BATTERY_CONFIG_STRUCT_VERSION); |
| return -1; |
| } |
| |
| /* Now we know it's ok to read the rest of the header. */ |
| |
| expected = sizeof(*head) + head->manuf_name_size + |
| head->device_name_size + sizeof(struct board_batt_params); |
| if (rv != expected) { |
| fprintf(stderr, "Size mismatch: %d (expected=%d)\n", rv, |
| expected); |
| fprintf(stderr, ".manuf_name_size = %d\n", |
| head->manuf_name_size); |
| fprintf(stderr, ".device_name_size = %d\n", |
| head->device_name_size); |
| return -1; |
| } |
| |
| /* Now we know it's ok to parse the payload. */ |
| p = (uint8_t *)head; |
| p += sizeof(*head); |
| memset(manuf_name, 0, sizeof(manuf_name)); |
| memcpy(manuf_name, p, head->manuf_name_size); |
| p += head->manuf_name_size; |
| memset(device_name, 0, sizeof(device_name)); |
| memcpy(device_name, p, head->device_name_size); |
| p += head->device_name_size; |
| memcpy(&conf, p, sizeof(conf)); |
| if (in_json) { |
| batt_conf_dump(&conf, manuf_name, device_name, |
| head->struct_version, in_json_human); |
| } else { |
| batt_conf_dump_in_c(&conf, manuf_name, device_name, |
| head->struct_version); |
| } |
| |
| return 0; |
| } |
| |
| static int cmd_battery_config_set(int argc, char *argv[], bool search_only) |
| { |
| FILE *fp = NULL; |
| int size; |
| char *json = NULL; |
| const char *json_file; |
| const char *manuf_name; |
| const char *device_name; |
| char identifier[SBS_MAX_STR_OBJ_SIZE * 2]; |
| struct board_batt_params config; |
| struct ec_params_set_cbi *p = (struct ec_params_set_cbi *)ec_outbuf; |
| struct batt_conf_header *header = (struct batt_conf_header *)p->data; |
| uint8_t *d = (uint8_t *)header; |
| uint8_t struct_version = EC_BATTERY_CONFIG_STRUCT_VERSION; |
| int rv; |
| int index = 0; |
| |
| if (argc < 4 || 5 < argc) { |
| fprintf(stderr, "Invalid number of arguments.\n"); |
| cmd_battery_config_help("bcfg"); |
| return -1; |
| } else if (argc == 5) { |
| char *e; |
| index = strtol(argv[4], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad index: '%s'\n", argv[4]); |
| return -1; |
| } |
| } |
| |
| json_file = argv[1]; |
| manuf_name = argv[2]; |
| device_name = argv[3]; |
| |
| if (strlen(manuf_name) > SBS_MAX_STR_SIZE) { |
| fprintf(stderr, "manuf_name is too long."); |
| return -1; |
| } |
| |
| if (strlen(device_name) > SBS_MAX_STR_SIZE) { |
| fprintf(stderr, "device_name is too long."); |
| return -1; |
| } |
| |
| fp = fopen(json_file, "rb"); |
| if (!fp) { |
| fprintf(stderr, "Can't open %s: %s\n", json_file, |
| strerror(errno)); |
| return -1; |
| } |
| fseek(fp, 0, SEEK_END); |
| size = ftell(fp); |
| rewind(fp); |
| |
| json = (char *)malloc(size); |
| if (!json) { |
| fprintf(stderr, "Failed to allocate memory.\n"); |
| fclose(fp); |
| return -1; |
| } |
| |
| if (fread(json, 1, size, fp) != size) { |
| fprintf(stderr, "Failed to read %s\n", json_file); |
| fclose(fp); |
| free(json); |
| return -1; |
| } |
| |
| fclose(fp); |
| |
| std::optional<base::Value> root = |
| base::JSONReader::Read(json, base::JSON_PARSE_RFC); |
| if (root == std::nullopt) { |
| fprintf(stderr, "File %s isn't properly formed JSON file.\n", |
| json_file); |
| free(json); |
| return -1; |
| } |
| base::DictValue *dict = root->GetIfDict(); |
| if (dict == nullptr) { |
| fprintf(stderr, "Failed to get dictionary from JSON file.\n"); |
| free(json); |
| return -1; |
| } |
| if (read_u8_from_json(dict, "struct_version", &struct_version)) |
| return -1; |
| |
| /* Clear the dst to ensure it'll be null-terminated. */ |
| memset(identifier, 0, sizeof(identifier)); |
| sprintf(identifier, "%s,%s", manuf_name, device_name); |
| base::DictValue *root_dict = nullptr; |
| int num_matches = 0; |
| |
| for (const auto &identifier_in_json : *dict) { |
| if (!strncasecmp(identifier, identifier_in_json.first.c_str(), |
| identifier_in_json.first.size())) { |
| root_dict = identifier_in_json.second.GetIfDict(); |
| ++num_matches; |
| } |
| } |
| |
| if (num_matches == 0) { |
| fprintf(stderr, "No config found for '%s' in %s\n", identifier, |
| json_file); |
| free(json); |
| return -1; |
| } else if (num_matches > 1) { |
| fprintf(stderr, "Multiple (%d) configs found for '%s' in %s\n", |
| num_matches, identifier, json_file); |
| free(json); |
| return -1; |
| } |
| |
| if (read_u8_from_json(root_dict, "struct_version", &struct_version)) |
| return -1; |
| |
| if (search_only) { |
| printf("Battery config with identifier: '%s' is found at %s\n", |
| identifier, json_file); |
| return 0; |
| } |
| |
| /* Clear config to ensure unspecified (optional) fields are 0. */ |
| memset(&config, 0, sizeof(config)); |
| if (read_battery_config_from_json(root_dict, &config)) |
| return -1; |
| |
| /* Data is ready. Create a packet for EC_CMD_SET_CROS_BOARD_INFO. */ |
| memset(p, 0, ec_max_outsize); |
| header->struct_version = struct_version; |
| header->manuf_name_size = strlen(manuf_name); |
| header->device_name_size = strlen(device_name); |
| d += sizeof(*header); |
| memcpy(d, manuf_name, header->manuf_name_size); |
| d += header->manuf_name_size; |
| memcpy(d, device_name, header->device_name_size); |
| d += header->device_name_size; |
| memcpy(d, &config, sizeof(config)); |
| |
| p->tag = index + CBI_TAG_BATTERY_CONFIG; |
| p->size = sizeof(struct batt_conf_header) + header->manuf_name_size + |
| header->device_name_size + sizeof(config); |
| size = sizeof(*p); |
| size += p->size; |
| |
| rv = ec_command(EC_CMD_SET_CROS_BOARD_INFO, 0, p, size, NULL, 0); |
| if (rv < 0) { |
| if (rv == -EC_RES_ACCESS_DENIED - EECRESULT) |
| fprintf(stderr, "Failed. CBI is write-protected.\n"); |
| else |
| fprintf(stderr, "Error code: %d\n", rv); |
| } else { |
| printf("Successfully wrote battery config in CBI\n"); |
| } |
| |
| free(json); |
| |
| return rv; |
| } |
| |
| static int cmd_battery_config(int argc, char *argv[]) |
| { |
| if (argc > 1 && !strcasecmp(argv[1], "get")) |
| return cmd_battery_config_get(--argc, ++argv); |
| else if (argc > 1 && !strcasecmp(argv[1], "set")) |
| return cmd_battery_config_set(--argc, ++argv, false); |
| else if (argc > 1 && !strcasecmp(argv[1], "search")) |
| return cmd_battery_config_set(--argc, ++argv, true); |
| |
| fprintf(stderr, "Invalid sub-command '%s'\n", |
| argv[1] ? argv[1] : "(null)"); |
| cmd_battery_config_help(argv[0]); |
| return -1; |
| } |
| |
| int cmd_board_version(int argc, char *argv[]) |
| { |
| struct ec_response_board_version response; |
| int rv; |
| |
| rv = ec_command(EC_CMD_GET_BOARD_VERSION, 0, NULL, 0, &response, |
| sizeof(response)); |
| if (rv < 0) |
| return rv; |
| |
| printf("%d\n", response.board_version); |
| return rv; |
| } |
| |
| int cmd_boottime(int argc, char *argv[]) |
| { |
| struct ec_response_get_boot_time response; |
| int rv; |
| |
| rv = ec_command(EC_CMD_GET_BOOT_TIME, 0, NULL, 0, &response, |
| sizeof(response)); |
| if (rv < 0) |
| return rv; |
| |
| printf("arail: %" PRIu64 "\n", response.timestamp[ARAIL]); |
| printf("rsmrst: %" PRIu64 "\n", response.timestamp[RSMRST]); |
| printf("espirst: %" PRIu64 "\n", response.timestamp[ESPIRST]); |
| printf("pltrst_low: %" PRIu64 "\n", response.timestamp[PLTRST_LOW]); |
| printf("pltrst_high: %" PRIu64 "\n", response.timestamp[PLTRST_HIGH]); |
| printf("cnt: %" PRIu16 "\n", response.cnt); |
| printf("ec_cur_time: %" PRIu64 "\n", response.timestamp[EC_CUR_TIME]); |
| return rv; |
| } |
| |
| #define CBI_FIELD_NAME_ENTRY(tag) [CBI_TAG_##tag] = STRINGIFY(tag) |
| |
| static const char *const cbi_field_name[] = { |
| CBI_FIELD_NAME_ENTRY(BOARD_VERSION), |
| CBI_FIELD_NAME_ENTRY(OEM_ID), |
| CBI_FIELD_NAME_ENTRY(SKU_ID), |
| CBI_FIELD_NAME_ENTRY(DRAM_PART_NUM), |
| CBI_FIELD_NAME_ENTRY(OEM_NAME), |
| CBI_FIELD_NAME_ENTRY(MODEL_ID), |
| CBI_FIELD_NAME_ENTRY(FW_CONFIG), |
| CBI_FIELD_NAME_ENTRY(PCB_SUPPLIER), |
| CBI_FIELD_NAME_ENTRY(SSFC), |
| CBI_FIELD_NAME_ENTRY(REWORK_ID), |
| CBI_FIELD_NAME_ENTRY(FACTORY_CALIBRATION_DATA), |
| CBI_FIELD_NAME_ENTRY(COMMON_CONTROL), |
| CBI_FIELD_NAME_ENTRY(BATTERY_CONFIG), |
| CBI_FIELD_NAME_ENTRY(BATTERY_CONFIG_15), |
| CBI_FIELD_NAME_ENTRY(PROVISION_MATRIX_VERSION), |
| CBI_FIELD_NAME_ENTRY(UFSC), |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(cbi_field_name) == CBI_TAG_COUNT); |
| |
| static int cmd_cbi_is_string_field(enum cbi_data_tag tag) |
| { |
| return tag == CBI_TAG_DRAM_PART_NUM || tag == CBI_TAG_OEM_NAME; |
| } |
| |
| static int cmd_cbi_is_binary_field(enum cbi_data_tag tag) |
| { |
| return (CBI_TAG_BATTERY_CONFIG <= tag && |
| tag <= CBI_TAG_BATTERY_CONFIG_15) || |
| tag == CBI_TAG_UFSC; |
| } |
| |
| static const char *get_cbi_tag_type_string(enum cbi_data_tag tag) |
| { |
| if (cmd_cbi_is_string_field(tag)) |
| return " (string)"; |
| if (cmd_cbi_is_binary_field(tag)) |
| return " (hex)"; |
| return ""; |
| } |
| |
| static void cmd_cbi_help(char *cmd) |
| { |
| fprintf(stderr, |
| " Usage: %s get <tag> [get_flag]\n" |
| " Usage: %s set <tag> <value> <size> [set_flag]\n" |
| " Usage: %s set <tag> <string/hex> <*> [set_flag]\n" |
| " Usage: %s remove <tag> [set_flag]\n" |
| " <tag> is one of:\n", |
| cmd, cmd, cmd, cmd); |
| for (int tag = CBI_TAG_BOARD_VERSION; tag <= CBI_TAG_COMMON_CONTROL; |
| ++tag) { |
| fprintf(stderr, " %2d: %s%s\n", tag, cbi_field_name[tag], |
| get_cbi_tag_type_string( |
| static_cast<enum cbi_data_tag>(tag))); |
| } |
| fprintf(stderr, " [%d:%d]: BATTERY_CONFIG_[0:15] (hex)\n", |
| CBI_TAG_BATTERY_CONFIG, CBI_TAG_BATTERY_CONFIG_15); |
| for (int tag = CBI_TAG_PROVISION_MATRIX_VERSION; tag < CBI_TAG_COUNT; |
| ++tag) { |
| fprintf(stderr, " %2d: %s%s\n", tag, cbi_field_name[tag], |
| get_cbi_tag_type_string( |
| static_cast<enum cbi_data_tag>(tag))); |
| } |
| fprintf(stderr, |
| " <size> is the size of the data in byte. It should be zero for\n" |
| " string types.\n" |
| " <value/string> is an integer or a string to be set\n" |
| " <*> is unused but must be present (e.g. '0')\n" |
| " <hex> is a hex string\n" |
| " [get_flag] is combination of:\n" |
| " 01b: Invalidate cache and reload data from EEPROM\n" |
| " [set_flag] is combination of:\n" |
| " 10b: Set all fields to defaults first\n"); |
| } |
| |
| /* |
| * Write value to CBI |
| * |
| * TODO: Support asynchronous write |
| */ |
| static int cmd_cbi(int argc, char *argv[]) |
| { |
| enum cbi_data_tag tag; |
| char *e; |
| int rv; |
| int i; |
| |
| if (argc < 3) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_cbi_help(argv[0]); |
| return -1; |
| } |
| |
| /* Tag */ |
| tag = (enum cbi_data_tag)(strtol(argv[2], &e, 0)); |
| if (e && *e) { |
| fprintf(stderr, "Bad tag\n"); |
| return -1; |
| } |
| |
| if (!strcasecmp(argv[1], "get")) { |
| struct ec_params_get_cbi p = { 0 }; |
| |
| p.tag = tag; |
| if (argc > 3) { |
| p.flag = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad flag\n"); |
| return -1; |
| } |
| } |
| rv = ec_command(EC_CMD_GET_CROS_BOARD_INFO, 0, &p, sizeof(p), |
| ec_inbuf, ec_max_insize); |
| if (rv < 0) { |
| fprintf(stderr, "Error code: %d\n", rv); |
| return rv; |
| } |
| if (rv < sizeof(uint8_t)) { |
| fprintf(stderr, "Invalid size: %d\n", rv); |
| return -1; |
| } |
| if (cmd_cbi_is_string_field(tag)) { |
| printf("%.*s", rv, (const char *)ec_inbuf); |
| } else if (cmd_cbi_is_binary_field(tag)) { |
| const uint8_t *const buf = |
| (const uint8_t *const)(ec_inbuf); |
| for (i = 0; i < rv; i++) |
| printf("%02x", buf[i]); |
| } else { |
| const uint8_t *const buffer = |
| (const uint8_t *const)(ec_inbuf); |
| uint64_t int_value = 0; |
| for (i = 0; i < rv; i++) |
| int_value |= (uint64_t)buffer[i] << (i * 8); |
| |
| printf("As uint: %llu (0x%llx)\n", |
| (unsigned long long)int_value, |
| (unsigned long long)int_value); |
| printf("As binary:"); |
| for (i = 0; i < rv; i++) { |
| if (i % 32 == 31) |
| printf("\n"); |
| printf(" %02x", buffer[i]); |
| } |
| } |
| printf("\n"); |
| return 0; |
| } else if (!strcasecmp(argv[1], "set")) { |
| struct ec_params_set_cbi *p = |
| (struct ec_params_set_cbi *)ec_outbuf; |
| void *val_ptr; |
| uint64_t val = 0; |
| size_t size; |
| uint8_t bad_size = 0; |
| uint8_t *buf = NULL; |
| |
| if (argc < 5) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_cbi_help(argv[0]); |
| return -1; |
| } |
| memset(p, 0, ec_max_outsize); |
| p->tag = tag; |
| |
| if (cmd_cbi_is_string_field(tag)) { |
| val_ptr = argv[3]; |
| size = strlen((char *)(val_ptr)) + 1; |
| } else if (cmd_cbi_is_binary_field(tag)) { |
| const char *p = argv[3]; |
| |
| size = strlen(p); |
| if (size % 2) { |
| fprintf(stderr, |
| "\n<hex> length must be even.\n"); |
| return -1; |
| } |
| |
| size /= 2; |
| buf = (uint8_t *)malloc(size); |
| if (!buf) { |
| fprintf(stderr, |
| "\nFailed to allocate buffer.\n"); |
| return -1; |
| } |
| for (i = 0; i < size; i++) { |
| char t[3] = {}; |
| |
| memcpy(t, p, 2); |
| buf[i] = strtoul(t, &e, 16); |
| if (e && *e) { |
| fprintf(stderr, "\nBad value: '%s'\n", |
| t); |
| free(buf); |
| return -1; |
| } |
| p += 2; |
| } |
| val_ptr = buf; |
| } else { |
| val = strtoul(argv[3], &e, 0); |
| /* strtoul sets an errno for invalid input. If the value |
| * read is out of range of representable values by an |
| * unsigned long int, the function returns ULONG_MAX |
| * or ULONG_MIN and the errno is set to ERANGE. |
| */ |
| if ((e && *e) || errno == ERANGE) { |
| fprintf(stderr, "Bad value\n"); |
| return -1; |
| } |
| size = strtol(argv[4], &e, 0); |
| if (tag == CBI_TAG_REWORK_ID) { |
| if ((e && *e) || size < 1 || size > 8 || |
| (size < 8 && val >= (1ull << size * 8))) |
| bad_size = 1; |
| } else { |
| if ((e && *e) || size < 1 || 4 < size || |
| val >= (1ull << size * 8)) |
| bad_size = 1; |
| } |
| if (bad_size == 1) { |
| fprintf(stderr, "Bad size: %zu\n", size); |
| return -1; |
| } |
| |
| val_ptr = &val; |
| } |
| |
| if (size > ec_max_outsize - sizeof(*p)) { |
| fprintf(stderr, "Size exceeds parameter buffer: %zu\n", |
| size); |
| return -1; |
| } |
| /* Little endian */ |
| memcpy(p->data, val_ptr, size); |
| free(buf); |
| val_ptr = NULL; |
| p->size = size; |
| if (argc > 5) { |
| p->flag = strtol(argv[5], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad flag\n"); |
| return -1; |
| } |
| } |
| rv = ec_command(EC_CMD_SET_CROS_BOARD_INFO, 0, p, |
| sizeof(*p) + size, NULL, 0); |
| if (rv < 0) { |
| if (rv == -EC_RES_ACCESS_DENIED - EECRESULT) |
| fprintf(stderr, |
| "Write-protect is enabled or " |
| "EC explicitly refused to change the " |
| "requested field.\n"); |
| else |
| fprintf(stderr, "Error code: %d\n", rv); |
| return rv; |
| } |
| return 0; |
| } else if (!strcasecmp(argv[1], "remove")) { |
| struct ec_params_set_cbi p = { 0 }; |
| |
| p.tag = tag; |
| p.size = 0; |
| if (argc > 3) { |
| p.flag = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad flag\n"); |
| return -1; |
| } |
| } |
| rv = ec_command(EC_CMD_SET_CROS_BOARD_INFO, 0, &p, sizeof(p), |
| NULL, 0); |
| if (rv < 0) { |
| if (rv == -EC_RES_ACCESS_DENIED - EECRESULT) |
| fprintf(stderr, |
| "Write-protect is enabled or " |
| "EC explicitly refused to change the " |
| "requested field.\n"); |
| else |
| fprintf(stderr, "Error code: %d\n", rv); |
| return rv; |
| } |
| return 0; |
| } |
| |
| fprintf(stderr, "Invalid sub command: %s\n", argv[1]); |
| cmd_cbi_help(argv[0]); |
| |
| return -1; |
| } |
| |
| static void cmd_cbi_bin_help(char *cmd) |
| { |
| fprintf(stderr, |
| " Usage: %s read <file> <size>\n" |
| " read from cbi in flash or EEPROM into file.\n" |
| " Usage: %s write <file> <size>\n" |
| " write from file into cbi flash or EEPROM.\n", |
| cmd, cmd); |
| } |
| |
| /* |
| * Read or Write to CBI binary |
| */ |
| static int cmd_cbi_bin(int argc, char *argv[]) |
| { |
| char *e; |
| int rv; |
| constexpr int packet_max_size = 64; |
| |
| if (argc != 4) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_cbi_bin_help(argv[0]); |
| return -1; |
| } |
| |
| if (!strcasecmp(argv[1], "read")) { |
| struct ec_params_get_cbi_bin p = { 0 }; |
| int i; |
| |
| FILE *fp = fopen(argv[2], "wb"); |
| |
| if (!fp) { |
| fprintf(stderr, "\nCan't open %s: %s\n", argv[2], |
| strerror(errno)); |
| return -1; |
| } |
| |
| int size = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad size\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < size; i += packet_max_size) { |
| p.size = MIN(packet_max_size, size - i); |
| p.offset = i; |
| |
| rv = ec_command(EC_CMD_CBI_BIN_READ, 0, &p, sizeof(p), |
| ec_inbuf, ec_max_insize); |
| if (rv < 0) { |
| fprintf(stderr, "Error code: %d\n", rv); |
| return rv; |
| } |
| if (rv < sizeof(uint8_t)) { |
| fprintf(stderr, "Invalid size: %d\n", rv); |
| return -1; |
| } |
| fwrite((uint8_t *)ec_inbuf, sizeof(uint8_t), p.size, |
| fp); |
| } |
| |
| fclose(fp); |
| printf("Read successful.\n"); |
| return 0; |
| } else if (!strcasecmp(argv[1], "write")) { |
| struct ec_params_set_cbi_bin *p = |
| (struct ec_params_set_cbi_bin *)ec_outbuf; |
| uint8_t buffer[packet_max_size]; |
| int i; |
| |
| FILE *fp = fopen(argv[2], "rb"); |
| |
| if (!fp) { |
| fprintf(stderr, "\nCan't open %s: %s\n", argv[2], |
| strerror(errno)); |
| return -1; |
| } |
| |
| int size = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad size\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < size; i += packet_max_size) { |
| memset(p, 0, ec_max_outsize); |
| |
| p->size = MIN(packet_max_size, size - i); |
| p->offset = i; |
| if (p->offset == 0) { |
| p->flags |= EC_CBI_BIN_BUFFER_CLEAR; |
| } |
| if (p->size + p->offset == size) { |
| p->flags |= EC_CBI_BIN_BUFFER_WRITE; |
| } |
| |
| uint16_t read_len = |
| fread(buffer, sizeof(*buffer), p->size, fp); |
| |
| /* |
| * p->data is 0 initialized so if size is bigger than |
| * file length the extra length is padded with 0. |
| */ |
| memcpy(p->data, buffer, read_len); |
| |
| rv = ec_command(EC_CMD_CBI_BIN_WRITE, 0, p, |
| sizeof(*p) + p->size, NULL, 0); |
| if (rv < 0) { |
| if (rv == -EC_RES_ACCESS_DENIED - EECRESULT) |
| fprintf(stderr, |
| "Write-protect is enabled or " |
| "EC explicitly refused to change the " |
| "requested field.\n"); |
| else |
| fprintf(stderr, "Error code: %d\n", rv); |
| return rv; |
| } |
| } |
| |
| fclose(fp); |
| printf("Write successful.\n"); |
| return 0; |
| } |
| |
| fprintf(stderr, "Invalid sub command: %s\n", argv[1]); |
| cmd_cbi_bin_help(argv[0]); |
| |
| return -1; |
| } |
| |
| int cmd_chipinfo(int argc, char *argv[]) |
| { |
| struct ec_response_get_chip_info info; |
| int rv; |
| |
| printf("Chip info:\n"); |
| |
| rv = ec_command(EC_CMD_GET_CHIP_INFO, 0, NULL, 0, &info, sizeof(info)); |
| if (rv < 0) |
| return rv; |
| printf(" vendor: %s\n", info.vendor); |
| printf(" name: %s\n", info.name); |
| printf(" revision: %s\n", info.revision); |
| |
| return 0; |
| } |
| |
| int cmd_proto_info(int argc, char *argv[]) |
| { |
| struct ec_response_get_protocol_info info; |
| int rv; |
| int i; |
| |
| printf("Protocol info:\n"); |
| |
| rv = ec_command(EC_CMD_GET_PROTOCOL_INFO, 0, NULL, 0, &info, |
| sizeof(info)); |
| if (rv < 0) { |
| fprintf(stderr, "Protocol info unavailable. EC probably only " |
| "supports protocol version 2.\n"); |
| return rv; |
| } |
| |
| printf(" protocol versions:"); |
| for (i = 0; i < 32; i++) { |
| if (info.protocol_versions & BIT(i)) |
| printf(" %d", i); |
| } |
| printf("\n"); |
| |
| printf(" max request: %4d bytes\n", info.max_request_packet_size); |
| printf(" max response: %4d bytes\n", info.max_response_packet_size); |
| printf(" flags: 0x%08x\n", info.flags); |
| if (info.flags & EC_PROTOCOL_INFO_IN_PROGRESS_SUPPORTED) |
| printf(" EC_RES_IN_PROGRESS supported\n"); |
| return 0; |
| } |
| |
| static int ec_hash_help(const char *cmd) |
| { |
| printf("Usage:\n"); |
| printf(" %s - get last hash\n", cmd); |
| printf(" %s abort - abort hashing\n", cmd); |
| printf(" %s start [<offset> <size> [<nonce>]] - start hashing\n", cmd); |
| printf(" %s recalc [<offset> <size> [<nonce>]] - sync rehash\n", cmd); |
| printf("\n" |
| "If <offset> is RO or RW, offset and size are computed\n" |
| "automatically for the EC-RO or EC-RW firmware image.\n"); |
| |
| return 0; |
| } |
| |
| static int ec_hash_print(const struct ec_response_vboot_hash *r) |
| { |
| int i; |
| |
| if (r->status == EC_VBOOT_HASH_STATUS_BUSY) { |
| printf("status: busy\n"); |
| return 0; |
| } else if (r->status == EC_VBOOT_HASH_STATUS_NONE) { |
| printf("status: unavailable\n"); |
| return 0; |
| } else if (r->status != EC_VBOOT_HASH_STATUS_DONE) { |
| printf("status: %d\n", r->status); |
| return 0; |
| } |
| |
| printf("status: done\n"); |
| if (r->hash_type == EC_VBOOT_HASH_TYPE_SHA256) |
| printf("type: SHA-256\n"); |
| else |
| printf("type: %d\n", r->hash_type); |
| |
| printf("offset: 0x%08x\n", r->offset); |
| printf("size: 0x%08x\n", r->size); |
| |
| printf("hash: "); |
| for (i = 0; i < r->digest_size; i++) |
| printf("%02x", r->hash_digest[i]); |
| printf("\n"); |
| return 0; |
| } |
| |
| int cmd_ec_hash(int argc, char *argv[]) |
| { |
| struct ec_params_vboot_hash p; |
| struct ec_response_vboot_hash r; |
| char *e; |
| int rv; |
| |
| memset(&p, 0, sizeof(p)); |
| if (argc < 2) { |
| /* Get hash status */ |
| p.cmd = EC_VBOOT_HASH_GET; |
| rv = ec_command(EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| return ec_hash_print(&r); |
| } |
| |
| if (argc == 2 && !strcasecmp(argv[1], "abort")) { |
| /* Abort hash calculation */ |
| p.cmd = EC_VBOOT_HASH_ABORT; |
| rv = ec_command(EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| return (rv < 0 ? rv : 0); |
| } |
| |
| /* The only other commands are start and recalc */ |
| if (!strcasecmp(argv[1], "start")) |
| p.cmd = EC_VBOOT_HASH_START; |
| else if (!strcasecmp(argv[1], "recalc")) |
| p.cmd = EC_VBOOT_HASH_RECALC; |
| else |
| return ec_hash_help(argv[0]); |
| |
| p.hash_type = EC_VBOOT_HASH_TYPE_SHA256; |
| |
| if (argc < 3) { |
| fprintf(stderr, "Must specify offset\n"); |
| return -1; |
| } |
| |
| if (!strcasecmp(argv[2], "ro")) { |
| p.offset = EC_VBOOT_HASH_OFFSET_RO; |
| p.size = 0; |
| printf("Hashing EC-RO...\n"); |
| } else if (!strcasecmp(argv[2], "rw")) { |
| p.offset = EC_VBOOT_HASH_OFFSET_ACTIVE; |
| p.size = 0; |
| printf("Hashing EC-RW...\n"); |
| } else if (argc < 4) { |
| fprintf(stderr, "Must specify size\n"); |
| return -1; |
| } else { |
| p.offset = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad offset.\n"); |
| return -1; |
| } |
| p.size = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad size.\n"); |
| return -1; |
| } |
| printf("Hashing %d bytes at offset %d...\n", p.size, p.offset); |
| } |
| |
| if (argc == 5) { |
| /* |
| * Technically nonce can be any binary data up to 64 bytes, |
| * but this command only supports a 32-bit value. |
| */ |
| uint32_t nonce = strtol(argv[4], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad nonce integer.\n"); |
| return -1; |
| } |
| memcpy(p.nonce_data, &nonce, sizeof(nonce)); |
| p.nonce_size = sizeof(nonce); |
| } else |
| p.nonce_size = 0; |
| |
| rv = ec_command(EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| /* Start command doesn't wait for hashing to finish */ |
| if (p.cmd == EC_VBOOT_HASH_START) |
| return 0; |
| |
| /* Recalc command does wait around, so a result is ready now */ |
| return ec_hash_print(&r); |
| } |
| |
| int cmd_rtc_get(int argc, char *argv[]) |
| { |
| struct ec_response_rtc r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_RTC_GET_VALUE, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Current time: 0x%08x (%d)\n", r.time, r.time); |
| return 0; |
| } |
| |
| int cmd_rtc_set(int argc, char *argv[]) |
| { |
| struct ec_params_rtc p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <time>\n", argv[0]); |
| return -1; |
| } |
| p.time = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad time.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_RTC_SET_VALUE, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Time set.\n"); |
| return 0; |
| } |
| |
| int cmd_rtc_set_alarm(int argc, char *argv[]) |
| { |
| struct ec_params_rtc p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <sec>\n", argv[0]); |
| return -1; |
| } |
| p.time = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad time.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_RTC_SET_ALARM, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| if (p.time == 0) |
| printf("Disabling alarm.\n"); |
| else |
| printf("Alarm set to go off in %d secs.\n", p.time); |
| return 0; |
| } |
| |
| int cmd_rtc_get_alarm(int argc, char *argv[]) |
| { |
| struct ec_response_rtc r; |
| int rv; |
| |
| rv = ec_command(EC_CMD_RTC_GET_ALARM, 0, NULL, 0, &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| if (r.time == 0) |
| printf("Alarm not set\n"); |
| else |
| printf("Alarm to go off in %d secs\n", r.time); |
| return 0; |
| } |
| |
| int cmd_console(int argc, char *argv[]) |
| { |
| char *out = (char *)ec_inbuf; |
| int rv; |
| |
| /* Snapshot the EC console */ |
| rv = ec_command(EC_CMD_CONSOLE_SNAPSHOT, 0, NULL, 0, NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| /* Loop and read from the snapshot until it's done */ |
| while (1) { |
| rv = ec_command(EC_CMD_CONSOLE_READ, 0, NULL, 0, ec_inbuf, |
| ec_max_insize); |
| if (rv < 0) |
| return rv; |
| |
| /* Empty response means done */ |
| if (!rv || !*out) |
| break; |
| |
| /* Make sure output is null-terminated, then dump it */ |
| out[ec_max_insize - 1] = '\0'; |
| fputs(out, stdout); |
| } |
| printf("\n"); |
| return 0; |
| } |
| |
| static int cmd_console_print(int argc, char *argv[]) |
| { |
| char *msg; |
| int msg_len; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <msg>\n", argv[0]); |
| return -1; |
| } |
| msg = argv[1]; |
| msg_len = strlen(msg); |
| if (msg_len > ec_max_outsize - 1) { |
| /* Truncate message */ |
| msg_len = ec_max_outsize - 1; |
| msg[msg_len] = '\0'; |
| fprintf(stderr, |
| "Message exceeds max-length(%d), truncating to '%s'\n", |
| ec_max_outsize - 1, msg); |
| } |
| return ec_command(EC_CMD_CONSOLE_PRINT, 0, msg, msg_len + 1, NULL, 0); |
| } |
| |
| int cmd_set_alarm_slp_s0_dbg(int argc, char *argv[]) |
| { |
| struct ec_params_set_alarm_slp_s0_dbg p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <sec>\n", argv[0]); |
| return -1; |
| } |
| p.time = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad time.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_SET_ALARM_SLP_S0_DBG, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| if (p.time == 0) |
| printf("Disabling alarm for SLP S0 Debug.\n"); |
| else |
| printf("Wake host in %d secs for SLP_S0 Debug.\n", p.time); |
| return 0; |
| } |
| |
| int cmd_sleep_signal_transitions(int argc, char *argv[]) |
| { |
| struct ec_response_host_sleep_signal_transitions resp; |
| if (ec_command(EC_CMD_HOST_SLEEP_SIGNAL_TRANSITIONS, 0, NULL, 0, &resp, |
| sizeof(resp)) < 0) { |
| fprintf(stderr, "Host command failed\n"); |
| return -1; |
| } |
| printf("0x%x\n", resp.sleep_signal_transitions); |
| |
| return 0; |
| } |
| |
| struct param_info { |
| const char *name; /* name of this parameter */ |
| const char *help; /* help message */ |
| int size; /* size in bytes */ |
| int offset; /* offset within structure */ |
| }; |
| |
| #define FIELD(fname, field, help_str) \ |
| { \ |
| .name = fname, \ |
| .help = help_str, \ |
| .size = sizeof(((struct ec_mkbp_config *)NULL)->field), \ |
| .offset = __builtin_offsetof(struct ec_mkbp_config, field), \ |
| } |
| |
| static const struct param_info keyconfig_params[] = { |
| FIELD("scan_period", scan_period_us, "period between scans"), |
| FIELD("poll_timeout", poll_timeout_us, |
| "revert to irq mode after no activity for this long"), |
| FIELD("min_post_scan_delay", min_post_scan_delay_us, |
| "minimum post-scan delay before starting a new scan"), |
| FIELD("output_settle", output_settle_us, |
| "delay to wait for output to settle"), |
| FIELD("debounce_down", debounce_down_us, |
| "time for debounce on key down"), |
| FIELD("debounce_up", debounce_up_us, "time for debounce on key up"), |
| FIELD("fifo_max_depth", fifo_max_depth, |
| "maximum depth to allow for fifo (0 = disable)"), |
| FIELD("flags", flags, "0 to disable scanning, 1 to enable"), |
| }; |
| |
| static const struct param_info *find_field(const struct param_info *params, |
| int count, const char *name, |
| unsigned int *nump) |
| { |
| const struct param_info *param; |
| int i; |
| |
| for (i = 0, param = params; i < count; i++, param++) { |
| if (0 == strcmp(param->name, name)) { |
| if (nump) |
| *nump = i; |
| return param; |
| } |
| } |
| |
| fprintf(stderr, "Unknown parameter '%s'\n", name); |
| return NULL; |
| } |
| |
| static int get_value(const struct param_info *param, const char *config) |
| { |
| const char *field; |
| |
| field = config + param->offset; |
| switch (param->size) { |
| case 1: |
| return *(uint8_t *)field; |
| case 2: |
| return *(uint16_t *)field; |
| case 4: |
| return *(uint32_t *)field; |
| default: |
| fprintf(stderr, "Internal error: unknown size %d\n", |
| param->size); |
| } |
| |
| return -1; |
| } |
| |
| static int show_fields(struct ec_mkbp_config *config, int argc, char *argv[]) |
| { |
| const struct param_info *param; |
| uint32_t mask; |
| int i; |
| |
| if (!argc) { |
| mask = -1U; /* show all fields */ |
| } else { |
| mask = 0; |
| while (argc > 0) { |
| unsigned int num; |
| |
| param = find_field(keyconfig_params, |
| ARRAY_SIZE(keyconfig_params), |
| argv[0], &num); |
| if (!param) |
| return -1; |
| mask |= 1 << num; |
| argc--; |
| argv++; |
| } |
| } |
| |
| param = keyconfig_params; |
| for (i = 0; i < ARRAY_SIZE(keyconfig_params); i++, param++) { |
| if (mask & BIT(i)) { |
| fprintf(stderr, "%-12s %u\n", param->name, |
| get_value(param, (char *)config)); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int cmd_kbinfo(int argc, char *argv[]) |
| { |
| struct ec_params_mkbp_info info = { |
| .info_type = EC_MKBP_INFO_KBD, |
| }; |
| struct ec_response_mkbp_info resp; |
| int rv; |
| |
| if (argc > 1) { |
| fprintf(stderr, "Too many args\n"); |
| return -1; |
| } |
| rv = ec_command(EC_CMD_MKBP_INFO, 0, &info, sizeof(info), &resp, |
| sizeof(resp)); |
| if (rv < 0) |
| return rv; |
| |
| printf("Matrix rows: %d\n", resp.rows); |
| printf("Matrix columns: %d\n", resp.cols); |
| |
| return 0; |
| } |
| |
| static int cmd_keyconfig(int argc, char *argv[]) |
| { |
| struct ec_params_mkbp_set_config req; |
| int cmd; |
| int rv; |
| |
| if (argc < 2) { |
| const struct param_info *param; |
| int i; |
| |
| fprintf(stderr, |
| "Usage: %s get [<param>] - print params\n" |
| "\t%s set [<param>> <value>]\n" |
| " Available params are: (all time values are in us)", |
| argv[0], argv[0]); |
| |
| param = keyconfig_params; |
| for (i = 0; i < ARRAY_SIZE(keyconfig_params); i++, param++) { |
| fprintf(stderr, "%-12s %s\n", param->name, |
| param->name); |
| } |
| return -1; |
| } |
| |
| /* Get the command */ |
| if (0 == strcmp(argv[1], "get")) { |
| cmd = EC_CMD_MKBP_GET_CONFIG; |
| } else if (0 == strcmp(argv[1], "set")) { |
| cmd = EC_CMD_MKBP_SET_CONFIG; |
| } else { |
| fprintf(stderr, "Invalid command '%s\n", argv[1]); |
| return -1; |
| } |
| |
| switch (cmd) { |
| case EC_CMD_MKBP_GET_CONFIG: |
| /* Read the existing config */ |
| rv = ec_command(cmd, 0, NULL, 0, &req, sizeof(req)); |
| if (rv < 0) |
| return rv; |
| show_fields(&req.config, argc - 2, argv + 2); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static void cmd_memory_dump_usage(const char *command_name) |
| { |
| fprintf(stderr, |
| "Usage: %s [<address> [<size>]]\n" |
| " Prints the memory available for dumping in hexdump canonical format.\n" |
| " <address> is a 32-bit address offset. Defaults to 0x0.\n" |
| " <size> is the number of bytes to print after <address>." |
| " Defaults to end of RAM.\n" |
| "Usage: %s info\n" |
| " Prints metadata about the memory available for dumping\n", |
| command_name, command_name); |
| } |
| |
| static int cmd_memory_dump(int argc, char *argv[]) |
| { |
| int rv; |
| char *e; |
| bool just_info; |
| int response_max; |
| const char *command_name = argv[0]; |
| void *read_mem_response = NULL; |
| uint32_t requested_address_start = 0; |
| uint32_t requested_address_end = UINT32_MAX; |
| /* Simple local structs for storing a memory dump */ |
| struct mem_segment { |
| uint32_t addr_start; |
| uint32_t addr_end; |
| uint32_t size; |
| uint8_t *mem; |
| struct mem_segment *next; |
| }; |
| uint16_t entry_count; |
| struct mem_segment *segments = NULL; |
| /* The real root is root.next, all other root fields are unused */ |
| struct mem_segment root; |
| struct mem_segment *seg; |
| struct ec_response_memory_dump_get_metadata metadata_response; |
| struct ec_response_get_protocol_info protocol_info_response; |
| |
| if (argc > 3 || (argc == 2 && strcmp(argv[1], "help") == 0)) { |
| cmd_memory_dump_usage(command_name); |
| return -1; |
| } |
| if (argc == 2 && strcmp(argv[1], "info") == 0) { |
| just_info = true; |
| } |
| if (argc >= 2 && !just_info) { |
| /* Parse requested address argument */ |
| requested_address_start = strtoul(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad argument '%s'\n", argv[1]); |
| cmd_memory_dump_usage(command_name); |
| return -1; |
| } |
| } |
| if (argc == 3 && !just_info) { |
| /* Parse requested size argument */ |
| uint32_t requested_size = strtoul(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad argument '%s'\n", argv[2]); |
| cmd_memory_dump_usage(command_name); |
| return -1; |
| } |
| /* Cap max address at UINT32_MAX */ |
| requested_address_end = |
| MIN((uint64_t)requested_address_start + requested_size, |
| (uint64_t)UINT32_MAX); |
| } |
| |
| rv = ec_command(EC_CMD_GET_PROTOCOL_INFO, 0, NULL, 0, |
| &protocol_info_response, |
| sizeof(protocol_info_response)); |
| if (rv < 0) { |
| fprintf(stderr, "Protocol info unavailable.\n"); |
| goto cmd_memory_dump_cleanup; |
| } |
| response_max = protocol_info_response.max_response_packet_size; |
| read_mem_response = malloc(response_max); |
| |
| /* Fetch memory dump metadata */ |
| rv = ec_command(EC_CMD_MEMORY_DUMP_GET_METADATA, 0, NULL, 0, |
| &metadata_response, sizeof(metadata_response)); |
| if (rv < 0) { |
| fprintf(stderr, "Failed to get memory dump metadata.\n"); |
| goto cmd_memory_dump_cleanup; |
| } |
| entry_count = metadata_response.memory_dump_entry_count; |
| if (entry_count == 0) { |
| fprintf(stderr, "Memory dump is empty.\n"); |
| rv = -1; |
| goto cmd_memory_dump_cleanup; |
| } |
| segments = (struct mem_segment *)calloc(entry_count, |
| sizeof(struct mem_segment)); |
| if (segments == NULL) { |
| fprintf(stderr, "calloc failed\n"); |
| rv = -1; |
| goto cmd_memory_dump_cleanup; |
| } |
| |
| /* Fetch all memory segments */ |
| for (uint16_t entry_index = 0; entry_index < entry_count; |
| entry_index++) { |
| seg = &segments[entry_index]; |
| struct ec_params_memory_dump_get_entry_info entry_info_params = { |
| .memory_dump_entry_index = entry_index |
| }; |
| struct ec_response_memory_dump_get_entry_info |
| entry_info_response; |
| |
| rv = ec_command(EC_CMD_MEMORY_DUMP_GET_ENTRY_INFO, 0, |
| &entry_info_params, sizeof(entry_info_params), |
| &entry_info_response, |
| sizeof(entry_info_response)); |
| if (rv < 0) { |
| fprintf(stderr, |
| "Failed to get memory dump info for entry %d.\n", |
| entry_index); |
| goto cmd_memory_dump_cleanup; |
| } |
| |
| uint32_t entry_address_end = |
| entry_info_response.address + entry_info_response.size; |
| |
| /* Check if entry is even in bounds of the requested range */ |
| if (entry_info_response.address >= requested_address_end || |
| entry_address_end <= requested_address_start) |
| continue; |
| |
| /* Clip memory segment boundaries based on requested range */ |
| seg->addr_start = MAX(entry_info_response.address, |
| requested_address_start); |
| seg->addr_end = MIN(entry_address_end, requested_address_end); |
| if (seg->addr_end - seg->addr_start <= 0) |
| continue; |
| seg->size = seg->addr_end - seg->addr_start; |
| |
| if (just_info) { |
| printf("%-3d: %x-%x (%d bytes)\n", entry_index, |
| seg->addr_start, seg->addr_end, seg->size); |
| continue; |
| } |
| |
| seg->mem = (uint8_t *)malloc(seg->size); |
| if (seg->mem == NULL) { |
| fprintf(stderr, "malloc failed\n"); |
| rv = -1; |
| goto cmd_memory_dump_cleanup; |
| } |
| |
| /* Keep fetching until entire segment is copied */ |
| uint32_t offset = 0; |
| while (offset < seg->size) { |
| struct ec_params_memory_dump_read_memory |
| read_mem_params = { |
| .memory_dump_entry_index = entry_index, |
| .address = seg->addr_start + offset, |
| .size = seg->size - offset, |
| }; |
| |
| rv = ec_command(EC_CMD_MEMORY_DUMP_READ_MEMORY, 0, |
| &read_mem_params, |
| sizeof(read_mem_params), |
| read_mem_response, response_max); |
| if (rv <= 0) { |
| fprintf(stderr, |
| "Failed to read memory at %x.\n", |
| read_mem_params.address); |
| rv = -1; |
| goto cmd_memory_dump_cleanup; |
| } |
| |
| if (!memcpy(seg->mem + offset, read_mem_response, rv)) { |
| fprintf(stderr, "memcpy failed\n"); |
| rv = -1; |
| goto cmd_memory_dump_cleanup; |
| } |
| |
| offset += rv; |
| }; |
| |
| /* Sort segments in ascending order of starting address */ |
| struct mem_segment *current = &root; |
| for (int i = 0; current->next && i < entry_count; i++) { |
| if (seg->addr_start < current->next->addr_start) { |
| /* Insert segment before current->next */ |
| seg->next = current->next; |
| current->next = seg; |
| break; |
| } |
| current = current->next; |
| } |
| current->next = seg; |
| } |
| |
| if (just_info) { |
| rv = 0; |
| goto cmd_memory_dump_cleanup; |
| } |
| |
| /* Merge overlapping or touching segments */ |
| seg = root.next; |
| for (int i = 0; seg && seg->next && i < entry_count; i++) { |
| if (seg->addr_end < seg->next->addr_start) { |
| /* No overlap */ |
| seg = seg->next; |
| continue; |
| } |
| uint32_t overlap = seg->addr_end - seg->next->addr_start; |
| uint32_t new_size = seg->size + seg->next->size - overlap; |
| if (new_size != seg->next->addr_end - seg->addr_start) { |
| fprintf(stderr, "Segment size is not aligned\n"); |
| rv = -1; |
| goto cmd_memory_dump_cleanup; |
| } |
| seg->mem = (uint8_t *)realloc(seg->mem, new_size); |
| if (seg->mem == NULL) { |
| fprintf(stderr, "realloc failed\n"); |
| rv = -1; |
| goto cmd_memory_dump_cleanup; |
| } |
| if (!memcpy(seg->mem + seg->size, seg->next->mem + overlap, |
| seg->next->size - overlap)) { |
| fprintf(stderr, "Merging segments failed\n"); |
| rv = -1; |
| goto cmd_memory_dump_cleanup; |
| } |
| seg->addr_end = seg->next->addr_end; |
| seg->size = new_size; |
| seg->next = seg->next->next; |
| } |
| |
| /* Print dump in hexdump cononical format */ |
| seg = root.next; |
| for (int i = 0; seg && i < entry_count; i++) { |
| hexdump_canonical(seg->mem, seg->size, seg->addr_start); |
| /* Extra newline to delinate segments */ |
| printf("\n"); |
| seg = seg->next; |
| } |
| rv = 0; |
| cmd_memory_dump_cleanup: |
| free(read_mem_response); |
| if (segments) { |
| for (int i = 0; i < entry_count; i++) |
| free(segments[i].mem); |
| free(segments); |
| } |
| return rv; |
| } |
| |
| static const char *const mkbp_button_strings[] = { |
| [EC_MKBP_POWER_BUTTON] = "Power", |
| [EC_MKBP_VOL_UP] = "Volume up", |
| [EC_MKBP_VOL_DOWN] = "Volume down", |
| [EC_MKBP_RECOVERY] = "Recovery", |
| }; |
| |
| static const char *const mkbp_switch_strings[] = { |
| [EC_MKBP_LID_OPEN] = "Lid open", |
| [EC_MKBP_TABLET_MODE] = "Tablet mode", |
| [EC_MKBP_BASE_ATTACHED] = "Base attached", |
| }; |
| |
| static int cmd_mkbp_get(int argc, char *argv[]) |
| { |
| struct ec_params_mkbp_info p; |
| union ec_response_get_next_data r; |
| int rv; |
| int i; |
| uint32_t supported; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s <buttons|switches>\n", argv[0]); |
| return -1; |
| } |
| |
| if (strncmp(argv[1], "button", 6) == 0) { |
| p.event_type = EC_MKBP_EVENT_BUTTON; |
| } else if (strncmp(argv[1], "switch", 6) == 0) { |
| p.event_type = EC_MKBP_EVENT_SWITCH; |
| } else { |
| fprintf(stderr, "Invalid param: '%s'\n", argv[1]); |
| return -1; |
| } |
| |
| p.info_type = EC_MKBP_INFO_SUPPORTED; |
| rv = ec_command(EC_CMD_MKBP_INFO, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| if (p.event_type == EC_MKBP_EVENT_BUTTON) |
| supported = r.buttons; |
| else if (p.event_type == EC_MKBP_EVENT_SWITCH) |
| supported = r.switches; |
| else |
| return -1; |
| |
| p.info_type = EC_MKBP_INFO_CURRENT; |
| rv = ec_command(EC_CMD_MKBP_INFO, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| if (p.event_type == EC_MKBP_EVENT_BUTTON) { |
| printf("MKBP buttons state: 0x%04x (supported: 0x%04x)\n", |
| r.buttons, supported); |
| for (i = 0; i < ARRAY_SIZE(mkbp_button_strings); i++) { |
| if (supported & BIT(i) && mkbp_button_strings[i]) { |
| printf("%s: %s\n", mkbp_button_strings[i], |
| r.buttons & BIT(i) ? "ON" : "OFF"); |
| supported &= ~BIT(i); |
| } |
| } |
| if (supported) |
| printf("Unknown buttons: 0x%04x\n", supported); |
| } else if (p.event_type == EC_MKBP_EVENT_SWITCH) { |
| printf("MKBP switches state: 0x%04x (supported: 0x%04x)\n", |
| r.switches, supported); |
| for (i = 0; i < ARRAY_SIZE(mkbp_switch_strings); i++) { |
| if (supported & BIT(i) && mkbp_switch_strings[i]) { |
| printf("%s: %s\n", mkbp_switch_strings[i], |
| r.switches & BIT(i) ? "ON" : "OFF"); |
| supported &= ~BIT(i); |
| } |
| } |
| if (supported) |
| printf("Unknown switches: 0x%04x\n", supported); |
| } |
| |
| return 0; |
| } |
| |
| static int cmd_mkbp_wake_mask(int argc, char *argv[]) |
| { |
| struct ec_params_mkbp_event_wake_mask p; |
| struct ec_response_mkbp_event_wake_mask r; |
| int rv; |
| |
| if (argc < 3) { |
| fprintf(stderr, |
| "Usage: %s get <event|hostevent>\n" |
| "\t%s set <event|hostevent> <mask>\n", |
| argv[0], argv[0]); |
| return -1; |
| } |
| |
| /* Determine if the user want to get or set the wake mask. */ |
| if (strncmp(argv[1], "get", 3) == 0) { |
| p.action = GET_WAKE_MASK; |
| } else if (strncmp(argv[1], "set", 3) == 0) { |
| p.action = SET_WAKE_MASK; |
| } else { |
| fprintf(stderr, "Invalid param: '%s'\n", argv[1]); |
| return -1; |
| } |
| |
| /* Determine which mask is of interest. */ |
| if (strncmp(argv[2], "event", 5) == 0) { |
| p.mask_type = EC_MKBP_EVENT_WAKE_MASK; |
| } else if (strncmp(argv[2], "hostevent", 9) == 0) { |
| p.mask_type = EC_MKBP_HOST_EVENT_WAKE_MASK; |
| } else { |
| fprintf(stderr, "Invalid param: '%s'\n", argv[2]); |
| return -1; |
| } |
| |
| if (p.action == SET_WAKE_MASK) { |
| char *e; |
| |
| if (argc < 4) { |
| fprintf(stderr, "Missing mask value!"); |
| return -1; |
| } |
| |
| p.new_wake_mask = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad mask: '%s'", argv[1]); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_MKBP_WAKE_MASK, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) { |
| if (rv == -EECRESULT - EC_RES_INVALID_PARAM) { |
| fprintf(stderr, |
| "Unknown mask, or mask is not in use. " |
| "You may need to enable the " |
| "CONFIG_MKBP_%s_WAKEUP_MASK option in the EC.\n", |
| p.mask_type == EC_MKBP_EVENT_WAKE_MASK ? |
| "EVENT" : |
| "HOSTEVENT"); |
| } |
| return rv; |
| } |
| |
| if (p.action == GET_WAKE_MASK) |
| printf("MBKP %s wake mask: 0x%08x\n", argv[2], r.wake_mask); |
| else if (p.action == SET_WAKE_MASK) |
| printf("MKBP %s wake mask set.\n", argv[2]); |
| |
| return 0; |
| } |
| |
| /* Index is already checked. argv[0] is first param value */ |
| static int cmd_tmp006cal_v0(int idx, int argc, char *argv[]) |
| { |
| struct ec_params_tmp006_get_calibration pg; |
| struct ec_response_tmp006_get_calibration_v0 rg; |
| struct ec_params_tmp006_set_calibration_v0 ps; |
| float val; |
| char *e; |
| int i, rv; |
| |
| /* Get current values */ |
| pg.index = idx; |
| rv = ec_command(EC_CMD_TMP006_GET_CALIBRATION, 0, &pg, sizeof(pg), &rg, |
| sizeof(rg)); |
| if (rv < 0) |
| return rv; |
| |
| if (!argc) { |
| /* If no new values are given, just print what we have */ |
| printf("S0: %e\n", rg.s0); |
| printf("b0: %e\n", rg.b0); |
| printf("b1: %e\n", rg.b1); |
| printf("b2: %e\n", rg.b2); |
| return EC_SUCCESS; |
| } |
| |
| /* Prepare to reuse the current values */ |
| memset(&ps, 0, sizeof(ps)); |
| ps.index = idx; |
| ps.s0 = rg.s0; |
| ps.b0 = rg.b0; |
| ps.b1 = rg.b1; |
| ps.b2 = rg.b2; |
| |
| /* Parse up to four args, skipping any that are just "-" */ |
| for (i = 0; i < argc && i < 4; i++) { |
| if (!strcmp(argv[i], "-")) |
| continue; |
| val = strtod(argv[i], &e); |
| if (e && *e) { |
| fprintf(stderr, |
| "Bad arg \"%s\". Use \"-\" to skip a param.\n", |
| argv[i]); |
| return -1; |
| } |
| switch (i) { |
| case 0: |
| ps.s0 = val; |
| break; |
| case 1: |
| ps.b0 = val; |
| break; |
| case 2: |
| ps.b1 = val; |
| break; |
| case 3: |
| ps.b2 = val; |
| break; |
| } |
| } |
| |
| /* Set 'em */ |
| return ec_command(EC_CMD_TMP006_SET_CALIBRATION, 0, &ps, sizeof(ps), |
| NULL, 0); |
| } |
| |
| /* Index is already checked. argv[0] is first param value */ |
| static int cmd_tmp006cal_v1(int idx, int argc, char *argv[]) |
| { |
| struct ec_params_tmp006_get_calibration pg; |
| struct ec_response_tmp006_get_calibration_v1 *rg = |
| (struct ec_response_tmp006_get_calibration_v1 *)(ec_inbuf); |
| struct ec_params_tmp006_set_calibration_v1 *ps = |
| (struct ec_params_tmp006_set_calibration_v1 *)(ec_outbuf); |
| float val; |
| char *e; |
| int i, rv, cmdsize; |
| |
| /* Algorithm 1 parameter names */ |
| static const char *const alg1_pname[] = { |
| "s0", "a1", "a2", "b0", "b1", "b2", |
| "c2", "d0", "d1", "ds", "e0", "e1", |
| }; |
| |
| /* Get current values */ |
| pg.index = idx; |
| rv = ec_command(EC_CMD_TMP006_GET_CALIBRATION, 1, &pg, sizeof(pg), rg, |
| ec_max_insize); |
| if (rv < 0) |
| return rv; |
| |
| if (!argc) { |
| /* If no new values are given, just print what we have */ |
| printf("algorithm: %d\n", rg->algorithm); |
| printf("params:\n"); |
| /* We only know about alg 1 at the moment */ |
| if (rg->algorithm == 1) |
| for (i = 0; i < rg->num_params; i++) |
| printf(" %s %e\n", alg1_pname[i], rg->val[i]); |
| else |
| for (i = 0; i < rg->num_params; i++) |
| printf(" param%d %e\n", i, rg->val[i]); |
| return EC_SUCCESS; |
| } |
| |
| /* Prepare to reuse the current values */ |
| memset(ps, 0, ec_max_outsize); |
| ps->index = idx; |
| ps->algorithm = rg->algorithm; |
| ps->num_params = rg->num_params; |
| for (i = 0; i < rg->num_params; i++) |
| ps->val[i] = rg->val[i]; |
| |
| /* Parse the args, skipping any that are just "-" */ |
| for (i = 0; i < argc && i < rg->num_params; i++) { |
| if (!strcmp(argv[i], "-")) |
| continue; |
| val = strtod(argv[i], &e); |
| if (e && *e) { |
| fprintf(stderr, |
| "Bad arg \"%s\". Use \"-\" to skip a param.\n", |
| argv[i]); |
| return -1; |
| } |
| ps->val[i] = val; |
| } |
| |
| /* Set 'em */ |
| cmdsize = sizeof(*ps) + ps->num_params * sizeof(ps->val[0]); |
| return ec_command(EC_CMD_TMP006_SET_CALIBRATION, 1, ps, cmdsize, NULL, |
| 0); |
| } |
| |
| int cmd_tmp006cal(int argc, char *argv[]) |
| { |
| char *e; |
| int idx; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Must specify tmp006 index.\n"); |
| return -1; |
| } |
| |
| idx = strtol(argv[1], &e, 0); |
| if ((e && *e) || idx < 0 || idx > 255) { |
| fprintf(stderr, "Bad index.\n"); |
| return -1; |
| } |
| |
| /* Pass just the params (if any) to the helper function */ |
| argc -= 2; |
| argv += 2; |
| |
| if (ec_cmd_version_supported(EC_CMD_TMP006_GET_CALIBRATION, 1)) |
| return cmd_tmp006cal_v1(idx, argc, argv); |
| |
| if (ec_cmd_version_supported(EC_CMD_TMP006_GET_CALIBRATION, 0)) |
| return cmd_tmp006cal_v0(idx, argc, argv); |
| |
| printf("The EC is being stupid\n"); |
| return -1; |
| } |
| |
| int cmd_tmp006raw(int argc, char *argv[]) |
| { |
| struct ec_params_tmp006_get_raw p; |
| struct ec_response_tmp006_get_raw r; |
| char *e; |
| int idx; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Must specify tmp006 index.\n"); |
| return -1; |
| } |
| |
| idx = strtol(argv[1], &e, 0); |
| if ((e && *e) || idx < 0 || idx > 255) { |
| fprintf(stderr, "Bad index.\n"); |
| return -1; |
| } |
| |
| p.index = idx; |
| |
| rv = ec_command(EC_CMD_TMP006_GET_RAW, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("T: %d.%02d K\n", r.t / 100, r.t % 100); |
| printf("V: %d nV\n", r.v); |
| return EC_SUCCESS; |
| } |
| |
| static int cmd_hang_detect(int argc, char *argv[]) |
| { |
| struct ec_params_hang_detect req; |
| struct ec_response_hang_detect resp; |
| int rv; |
| char *e; |
| |
| memset(&req, 0, sizeof(req)); |
| |
| if (argc == 2 && !strcasecmp(argv[1], "reload")) { |
| req.command = EC_HANG_DETECT_CMD_RELOAD; |
| return ec_command(EC_CMD_HANG_DETECT, 0, &req, sizeof(req), |
| NULL, 0); |
| } |
| |
| if (argc == 2 && !strcasecmp(argv[1], "cancel")) { |
| req.command = EC_HANG_DETECT_CMD_CANCEL; |
| return ec_command(EC_CMD_HANG_DETECT, 0, &req, sizeof(req), |
| NULL, 0); |
| } |
| |
| if (argc == 3 && !strcasecmp(argv[1], "set_timeout")) { |
| req.command = EC_HANG_DETECT_CMD_SET_TIMEOUT; |
| |
| req.reboot_timeout_sec = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad reboot timeout.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_HANG_DETECT, 0, &req, sizeof(req), NULL, |
| 0); |
| if (rv < 0) |
| printf("Couldn't set reboot timeout (rv=%d)\n", rv); |
| else |
| printf("reboot_timeout=%d s\n", req.reboot_timeout_sec); |
| return rv; |
| } |
| |
| if (argc == 2 && !strcasecmp(argv[1], "get_status")) { |
| req.command = EC_HANG_DETECT_CMD_GET_STATUS; |
| rv = ec_command(EC_CMD_HANG_DETECT, 0, &req, sizeof(req), &resp, |
| sizeof(resp)); |
| |
| if (rv < 0) |
| printf("Couldn't get boot status (rv=%d)\n", rv); |
| else |
| printf("boot status=%d\n", resp.status); |
| return rv; |
| } |
| |
| if (argc == 2 && !strcasecmp(argv[1], "clear_status")) { |
| req.command = EC_HANG_DETECT_CMD_CLEAR_STATUS; |
| return ec_command(EC_CMD_HANG_DETECT, 0, &req, sizeof(req), |
| NULL, 0); |
| } |
| |
| fprintf(stderr, |
| "args: reload|cancel|set_timeout <reboot_sec>|get_status|clear_status\n"); |
| return -1; |
| } |
| |
| enum port_80_event { |
| PORT_80_EVENT_RESUME = 0x1001, /* S3->S0 transition */ |
| PORT_80_EVENT_RESET = 0x1002, /* RESET transition */ |
| }; |
| |
| int cmd_port80_read(int argc, char *argv[]) |
| { |
| struct ec_params_port80_read p; |
| int cmdver = 1, rv; |
| int i, head, tail; |
| uint16_t *history; |
| uint32_t writes, history_size; |
| struct ec_response_port80_read rsp; |
| int printed = 0; |
| |
| if (!ec_cmd_version_supported(EC_CMD_PORT80_READ, cmdver)) { |
| /* fall back to last boot */ |
| struct ec_response_port80_last_boot r; |
| rv = ec_command(EC_CMD_PORT80_LAST_BOOT, 0, NULL, 0, &r, |
| sizeof(r)); |
| fprintf(stderr, "Last boot %2x\n", r.code); |
| printf("done.\n"); |
| return 0; |
| } |
| |
| /* read writes and history_size */ |
| p.subcmd = EC_PORT80_GET_INFO; |
| rv = ec_command(EC_CMD_PORT80_READ, cmdver, &p, sizeof(p), &rsp, |
| sizeof(rsp)); |
| if (rv < 0) { |
| fprintf(stderr, "Read error at writes\n"); |
| return rv; |
| } |
| writes = rsp.get_info.writes; |
| history_size = rsp.get_info.history_size; |
| |
| history = (uint16_t *)(malloc(history_size * sizeof(uint16_t))); |
| if (!history) { |
| fprintf(stderr, "Unable to allocate buffer.\n"); |
| return -1; |
| } |
| /* As the history buffer is quite large, we read data in chunks, with |
| size in bytes of EC_PORT80_SIZE_MAX in each chunk. |
| Incrementing offset until all history buffer has been read. To |
| simplify the design, chose HISTORY_LEN is always multiple of |
| EC_PORT80_SIZE_MAX. |
| |
| offset: entry offset from the beginning of history buffer. |
| num_entries: number of entries requested. |
| */ |
| p.subcmd = EC_PORT80_READ_BUFFER; |
| for (i = 0; i < history_size; i += EC_PORT80_SIZE_MAX) { |
| p.read_buffer.offset = i; |
| p.read_buffer.num_entries = EC_PORT80_SIZE_MAX; |
| rv = ec_command(EC_CMD_PORT80_READ, cmdver, &p, sizeof(p), &rsp, |
| sizeof(rsp)); |
| if (rv < 0) { |
| fprintf(stderr, "Read error at offset %d\n", i); |
| free(history); |
| return rv; |
| } |
| memcpy((void *)(history + i), rsp.data.codes, |
| EC_PORT80_SIZE_MAX * sizeof(uint16_t)); |
| } |
| |
| head = writes; |
| if (head > history_size) |
| tail = head - history_size; |
| else |
| tail = 0; |
| |
| fprintf(stderr, "Port 80 writes"); |
| for (i = tail; i < head; i++) { |
| int e = history[i % history_size]; |
| switch (e) { |
| case PORT_80_EVENT_RESUME: |
| fprintf(stderr, "\n(S3->S0)"); |
| printed = 0; |
| break; |
| case PORT_80_EVENT_RESET: |
| fprintf(stderr, "\n(RESET)"); |
| printed = 0; |
| break; |
| default: |
| if (!(printed++ % 20)) |
| fprintf(stderr, "\n "); |
| fprintf(stderr, " %02x", e); |
| } |
| } |
| fprintf(stderr, " <--new\n"); |
| |
| free(history); |
| printf("done.\n"); |
| return 0; |
| } |
| |
| int cmd_force_lid_open(int argc, char *argv[]) |
| { |
| struct ec_params_force_lid_open p; |
| char *e; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s <0|1>\n", argv[0]); |
| return -1; |
| } |
| p.enabled = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad value.\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_FORCE_LID_OPEN, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| printf("Success.\n"); |
| return 0; |
| } |
| |
| int cmd_powerbtn_press(int argc, char *argv[]) |
| { |
| struct ec_params_power_button_press p = { 0 }; |
| char *e; |
| int rv; |
| |
| if (argc > 1) { |
| p.first_press_delay_ms = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad value.\n"); |
| return -1; |
| } |
| } |
| if (argc > 2) { |
| p.first_press_duration_ms = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad value.\n"); |
| return -1; |
| } |
| } |
| if (argc > 3) { |
| p.second_press_delay_ms = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad value.\n"); |
| return -1; |
| } |
| } |
| if (argc > 4) { |
| p.second_press_duration_ms = strtol(argv[4], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad value.\n"); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_POWER_BUTTON_PRESS, 0, &p, sizeof(p), NULL, 0); |
| if (rv < 0) |
| return rv; |
| printf("Success.\n"); |
| return 0; |
| } |
| |
| int cmd_charge_port_override(int argc, char *argv[]) |
| { |
| struct ec_params_charge_port_override p; |
| char *e; |
| int rv; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s <port# | dontcharge | off>\n", |
| argv[0]); |
| return -1; |
| } |
| |
| if (!strcasecmp(argv[1], "dontcharge")) |
| p.override_port = OVERRIDE_DONT_CHARGE; |
| else if (!strcasecmp(argv[1], "off")) |
| p.override_port = OVERRIDE_OFF; |
| else { |
| p.override_port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad parameter.\n"); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_PD_CHARGE_PORT_OVERRIDE, 0, &p, sizeof(p), NULL, |
| 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Override port set to %d\n", p.override_port); |
| return 0; |
| } |
| |
| static void cmd_pchg_help(char *cmd) |
| { |
| fprintf(stderr, |
| " Usage1: %s\n" |
| " Print the number of ports.\n" |
| "\n" |
| " Usage2: %s <port>\n" |
| " Print the status of <port>.\n" |
| "\n" |
| " Usage3: %s <port> reset [mode]\n" |
| " Reset <port> to [mode]. [mode]: 'normal'.\n" |
| "\n" |
| " Usage4: %s <port> update <version> <addr1> <file1> <addr2> <file2> ...\n" |
| " Update firmware of <port>.\n" |
| "\n" |
| " Usage5: %s <port> passthru <on/off> ...\n" |
| " Enable passthru mode for <port>.\n", |
| cmd, cmd, cmd, cmd, cmd); |
| } |
| |
| static int cmd_pchg_info(const struct ec_response_pchg *res) |
| { |
| static const char *const pchg_state_text[] = EC_PCHG_STATE_TEXT; |
| |
| BUILD_ASSERT(ARRAY_SIZE(pchg_state_text) == PCHG_STATE_COUNT); |
| |
| printf("State: %s (%d)\n", |
| res->state < PCHG_STATE_COUNT ? pchg_state_text[res->state] : |
| "UNDEF", |
| res->state); |
| printf("Battery: %u%%\n", res->battery_percentage); |
| printf("Errors: 0x%x\n", res->error); |
| printf("FW Version: 0x%x\n", res->fw_version); |
| printf("Dropped events: %u\n", res->dropped_event_count); |
| return 0; |
| } |
| |
| static int cmd_pchg_wait_event(int port, uint32_t expected) |
| { |
| struct ec_response_get_next_event_v1 event; |
| const long timeout = 5000; |
| uint32_t *e = &event.data.host_event; |
| int rv; |
| |
| rv = wait_event(EC_MKBP_EVENT_PCHG, &event, sizeof(event), timeout); |
| if (rv < 0) |
| return rv; |
| |
| if (EC_MKBP_PCHG_EVENT_TO_PORT(*e) == port) { |
| if (*e & EC_MKBP_PCHG_UPDATE_ERROR) { |
| fprintf(stderr, "\nReceived update error\n"); |
| return -1; |
| } |
| if (*e & expected) |
| return 0; |
| } |
| |
| fprintf(stderr, "\nExpected event=0x%x but received 0x%x\n", expected, |
| *e); |
| return -1; |
| } |
| |
| static int cmd_pchg_update_open(int port, uint32_t version, |
| uint32_t *block_size, uint32_t *crc) |
| { |
| struct ec_params_pchg_update *pu = |
| (struct ec_params_pchg_update *)(ec_outbuf); |
| struct ec_response_pchg_update *r = |
| (struct ec_response_pchg_update *)(ec_inbuf); |
| struct ec_params_pchg p; |
| struct ec_response_pchg_v2 rv2; |
| int rv; |
| |
| /* Open session. */ |
| pu->port = port; |
| pu->cmd = EC_PCHG_UPDATE_CMD_OPEN; |
| pu->version = version; |
| rv = ec_command(EC_CMD_PCHG_UPDATE, 0, pu, sizeof(*pu), r, sizeof(*r)); |
| if (rv < 0) { |
| fprintf(stderr, "\nFailed to open update session: %d\n", rv); |
| return rv; |
| } |
| |
| if (r->block_size + sizeof(*pu) > ec_max_outsize) { |
| fprintf(stderr, "\nBlock size (%d) is too large.\n", |
| r->block_size); |
| return -1; |
| } |
| |
| rv = cmd_pchg_wait_event(port, EC_MKBP_PCHG_DEVICE_EVENT); |
| if (rv) |
| return rv; |
| |
| p.port = port; |
| rv = ec_command(EC_CMD_PCHG, 2, &p, sizeof(p), &rv2, sizeof(rv2)); |
| if (rv == -EC_RES_INVALID_VERSION - EECRESULT) |
| /* We can use v2 because it's a superset of v1. */ |
| rv = ec_command(EC_CMD_PCHG, 1, &p, sizeof(p), &rv2, |
| sizeof(struct ec_response_pchg)); |
| if (rv < 0) { |
| fprintf(stderr, "EC_CMD_PCHG failed: %d\n", rv); |
| return rv; |
| } |
| if (rv2.state != PCHG_STATE_DOWNLOAD) { |
| fprintf(stderr, "Failed to reset to download mode: %d\n", rv); |
| return -1; |
| } |
| |
| rv = cmd_pchg_wait_event(port, EC_MKBP_PCHG_UPDATE_OPENED); |
| if (rv) |
| return rv; |
| |
| printf("Opened update session (port=%d ver=0x%x bsize=%d):\n", port, |
| version, r->block_size); |
| |
| *block_size = r->block_size; |
| crc32_ctx_init(crc); |
| |
| return 0; |
| } |
| |
| static int cmd_pchg_update_write(int port, uint32_t address, |
| const char *filename, uint32_t block_size, |
| uint32_t *crc) |
| { |
| struct ec_params_pchg_update *p = |
| (struct ec_params_pchg_update *)(ec_outbuf); |
| FILE *fp; |
| size_t len, total; |
| int progress = 0; |
| int rv; |
| |
| fp = fopen(filename, "rb"); |
| if (!fp) { |
| fprintf(stderr, "\nCan't open %s: %s\n", filename, |
| strerror(errno)); |
| return -1; |
| } |
| |
| fseek(fp, 0L, SEEK_END); |
| total = ftell(fp); |
| rewind(fp); |
| printf("Writing %s (%zu bytes).\n", filename, total); |
| |
| p->cmd = EC_PCHG_UPDATE_CMD_WRITE; |
| p->addr = address; |
| |
| /* Write firmware in blocks. */ |
| len = fread(p->data, 1, block_size, fp); |
| while (len > 0) { |
| int previous_progress = progress; |
| int i; |
| |
| crc32_ctx_hash(crc, p->data, len); |
| p->size = len; |
| rv = ec_command(EC_CMD_PCHG_UPDATE, 0, p, sizeof(*p) + len, |
| NULL, 0); |
| if (rv < 0) { |
| fprintf(stderr, "\nFailed to write FW: %d\n", rv); |
| fclose(fp); |
| return rv; |
| } |
| |
| rv = cmd_pchg_wait_event(port, EC_MKBP_PCHG_WRITE_COMPLETE); |
| if (rv) |
| return rv; |
| |
| p->addr += len; |
| progress = (p->addr - address) * 100 / total; |
| for (i = 0; i < progress - previous_progress; i++) { |
| printf("*"); |
| fflush(stdout); |
| } |
| |
| len = fread(p->data, 1, block_size, fp); |
| } |
| |
| printf("\n"); |
| fclose(fp); |
| |
| return 0; |
| } |
| |
| static int cmd_pchg_update_close(int port, uint32_t *crc) |
| { |
| struct ec_params_pchg_update *p = |
| (struct ec_params_pchg_update *)(ec_outbuf); |
| int rv; |
| |
| p->cmd = EC_PCHG_UPDATE_CMD_CLOSE; |
| p->crc32 = crc32_ctx_result(crc); |
| rv = ec_command(EC_CMD_PCHG_UPDATE, 0, p, sizeof(*p), NULL, 0); |
| |
| if (rv < 0) { |
| fprintf(stderr, "\nFailed to close update session: %d\n", rv); |
| return rv; |
| } |
| |
| rv = cmd_pchg_wait_event(port, EC_MKBP_PCHG_UPDATE_CLOSED); |
| if (rv) |
| return rv; |
| |
| printf("Firmware was updated successfully (CRC32=0x%x).\n", p->crc32); |
| |
| return 0; |
| } |
| |
| static int cmd_pchg(int argc, char *argv[]) |
| { |
| const size_t max_input_files = 8; |
| int port, port_count; |
| struct ec_response_pchg_count rcnt; |
| struct ec_params_pchg p; |
| struct ec_response_pchg r; |
| char *e; |
| int rv; |
| |
| rv = ec_command(EC_CMD_PCHG_COUNT, 0, NULL, 0, &rcnt, sizeof(rcnt)); |
| if (rv < 0) { |
| fprintf(stderr, "\nFailed to get port count: %d\n", rv); |
| return rv; |
| } |
| port_count = rcnt.port_count; |
| |
| if (argc == 1) { |
| /* Usage.1 */ |
| printf("%d\n", port_count); |
| return 0; |
| } |
| |
| port = strtol(argv[1], &e, 0); |
| if ((e && *e) || port >= port_count) { |
| fprintf(stderr, "\nBad port index: %s\n", argv[1]); |
| cmd_pchg_help(argv[0]); |
| return -1; |
| } |
| |
| p.port = port; |
| rv = ec_command(EC_CMD_PCHG, 1, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) { |
| fprintf(stderr, "\nError code: %d\n", rv); |
| return rv; |
| } |
| |
| if (argc == 2) { |
| /* Usage.2 */ |
| return cmd_pchg_info(&r); |
| } else if (argc >= 3 && !strcmp(argv[2], "reset")) { |
| /* Usage.3 */ |
| struct ec_params_pchg_update u; |
| |
| u.port = port; |
| |
| if (argc == 3) { |
| u.cmd = EC_PCHG_UPDATE_CMD_RESET; |
| } else if (argc == 4 && !strcmp(argv[3], "normal")) { |
| u.cmd = EC_PCHG_UPDATE_CMD_RESET_TO_NORMAL; |
| } else { |
| fprintf(stderr, "\nInvalid mode: '%s'\n", argv[3]); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_PCHG_UPDATE, 0, &u, sizeof(u), NULL, 0); |
| if (rv < 0) { |
| fprintf(stderr, "\nFailed to reset port %d: %d\n", port, |
| rv); |
| cmd_pchg_help(argv[0]); |
| return rv; |
| } |
| printf("Reset port %d complete.\n", port); |
| return 0; |
| } else if (argc >= 6 && !strcmp(argv[2], "update")) { |
| /* |
| * Usage.4: |
| * argv[3]: <version> |
| * argv[4]: <addr1> |
| * argv[5]: <file1> |
| * argv[6]: <addr2> |
| * argv[7]: <file2> |
| * ... |
| */ |
| uint32_t address, version; |
| uint32_t block_size = 0; |
| uint32_t crc; |
| int i; |
| |
| if (argc > 4 + max_input_files * 2) { |
| fprintf(stderr, "\nToo many input files.\n"); |
| return -1; |
| } |
| |
| version = strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "\nBad version: %s.\n", argv[3]); |
| cmd_pchg_help(argv[0]); |
| return -1; |
| } |
| |
| rv = cmd_pchg_update_open(port, version, &block_size, &crc); |
| if (rv < 0 || block_size == 0) { |
| fprintf(stderr, "\nFailed to open update session: %d\n", |
| rv); |
| return -1; |
| } |
| |
| /* Write files one by one. */ |
| for (i = 4; i + 1 < argc; i += 2) { |
| address = strtol(argv[i], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "\nBad address: %s\n", argv[i]); |
| cmd_pchg_help(argv[0]); |
| return -1; |
| } |
| rv = cmd_pchg_update_write(port, address, argv[i + 1], |
| block_size, &crc); |
| if (rv < 0) { |
| fprintf(stderr, |
| "\nFailed to write file '%s': %d", |
| argv[i + i], rv); |
| return -1; |
| } |
| } |
| |
| rv = cmd_pchg_update_close(port, &crc); |
| if (rv < 0) { |
| fprintf(stderr, "\nFailed to close update session: %d", |
| rv); |
| return -1; |
| } |
| |
| return 0; |
| } else if (argc >= 4 && !strcmp(argv[2], "passthru")) { |
| /* |
| * Usage 5 |
| */ |
| struct ec_params_pchg_update u; |
| int onoff; |
| |
| if (!parse_bool(argv[3], &onoff)) { |
| fprintf(stderr, "\nInvalid arg: '%s'\n", argv[3]); |
| return -1; |
| } |
| |
| u.port = port; |
| u.cmd = EC_PCHG_UPDATE_CMD_ENABLE_PASSTHRU; |
| |
| rv = ec_command(EC_CMD_PCHG_UPDATE, 0, &u, sizeof(u), NULL, 0); |
| if (rv < 0) { |
| fprintf(stderr, "\nFailed to enable pass-through: %d\n", |
| rv); |
| return rv; |
| } |
| |
| printf("Pass-through is %s for port %d\n", |
| onoff ? "enabled" : "disabled", port); |
| return 0; |
| } |
| |
| fprintf(stderr, "Invalid parameter\n\n"); |
| cmd_pchg_help(argv[0]); |
| |
| return -1; |
| } |
| |
| int cmd_pd_log(int argc, char *argv[]) |
| { |
| union { |
| struct ec_response_pd_log r; |
| uint32_t words[8]; /* space for the payload */ |
| } u; |
| struct mcdp_info minfo; |
| struct ec_response_usb_pd_power_info pinfo; |
| int rv; |
| unsigned long long milliseconds; |
| unsigned int seconds; |
| time_t now; |
| struct tm ltime; |
| char time_str[64]; |
| |
| while (1) { |
| now = time(NULL); |
| rv = ec_command(EC_CMD_PD_GET_LOG_ENTRY, 0, NULL, 0, &u, |
| sizeof(u)); |
| if (rv < 0) |
| return rv; |
| |
| if (u.r.type == PD_EVENT_NO_ENTRY) { |
| printf("--- END OF LOG ---\n"); |
| break; |
| } |
| |
| /* the timestamp is in 1024th of seconds */ |
| milliseconds = |
| ((uint64_t)u.r.timestamp << PD_LOG_TIMESTAMP_SHIFT) / |
| 1000; |
| /* the timestamp is the number of milliseconds in the past */ |
| seconds = (milliseconds + 999) / 1000; |
| milliseconds -= seconds * 1000; |
| now -= seconds; |
| localtime_r(&now, <ime); |
| strftime(time_str, sizeof(time_str), "%F %T", <ime); |
| printf("%s.%03lld P%d ", time_str, -milliseconds, |
| PD_LOG_PORT(u.r.size_port)); |
| if (u.r.type == PD_EVENT_MCU_CHARGE) { |
| if (u.r.data & CHARGE_FLAGS_OVERRIDE) |
| printf("override "); |
| if (u.r.data & CHARGE_FLAGS_DELAYED_OVERRIDE) |
| printf("pending_override "); |
| memcpy(&pinfo.meas, u.r.payload, |
| sizeof(struct usb_chg_measures)); |
| pinfo.dualrole = !!(u.r.data & CHARGE_FLAGS_DUAL_ROLE); |
| pinfo.role = u.r.data & CHARGE_FLAGS_ROLE_MASK; |
| pinfo.type = (u.r.data & CHARGE_FLAGS_TYPE_MASK) >> |
| CHARGE_FLAGS_TYPE_SHIFT; |
| pinfo.max_power = 0; |
| print_pd_power_info(&pinfo); |
| } else if (u.r.type == PD_EVENT_MCU_CONNECT) { |
| printf("New connection\n"); |
| } else if (u.r.type == PD_EVENT_MCU_BOARD_CUSTOM) { |
| printf("Board-custom event\n"); |
| } else if (u.r.type == PD_EVENT_ACC_RW_FAIL) { |
| printf("RW signature check failed\n"); |
| } else if (u.r.type == PD_EVENT_PS_FAULT) { |
| static const char *const fault_names[] = { |
| "---", "OCP", "fast OCP", "OVP", "Discharge" |
| }; |
| const char *fault = u.r.data < ARRAY_SIZE(fault_names) ? |
| fault_names[u.r.data] : |
| "???"; |
| printf("Power supply fault: %s\n", fault); |
| } else if (u.r.type == PD_EVENT_VIDEO_DP_MODE) { |
| printf("DP mode %sabled\n", |
| (u.r.data == 1) ? "en" : "dis"); |
| } else if (u.r.type == PD_EVENT_VIDEO_CODEC) { |
| memcpy(&minfo, u.r.payload, sizeof(struct mcdp_info)); |
| printf("HDMI info: family:%04x chipid:%04x " |
| "irom:%d.%d.%d fw:%d.%d.%d\n", |
| MCDP_FAMILY(minfo.family), |
| MCDP_CHIPID(minfo.chipid), minfo.irom.major, |
| minfo.irom.minor, minfo.irom.build, |
| minfo.fw.major, minfo.fw.minor, minfo.fw.build); |
| } else { /* Unknown type */ |
| int i; |
| printf("Event %02x (%04x) [", u.r.type, u.r.data); |
| for (i = 0; i < PD_LOG_SIZE(u.r.size_port); i++) |
| printf("%02x ", u.r.payload[i]); |
| printf("]\n"); |
| } |
| } |
| |
| return 0; |
| } |
| |
| int cmd_pd_control(int argc, char *argv[]) |
| { |
| struct ec_params_pd_control p; |
| int rv; |
| |
| if (argc < 2) { |
| fprintf(stderr, "Missing parameter\n"); |
| return -1; |
| } |
| |
| /* Parse command */ |
| if (!strcmp(argv[1], "reset")) |
| p.subcmd = PD_RESET; |
| else if (!strcmp(argv[1], "suspend")) |
| p.subcmd = PD_SUSPEND; |
| else if (!strcmp(argv[1], "resume")) |
| p.subcmd = PD_RESUME; |
| else if (!strcmp(argv[1], "disable")) |
| p.subcmd = PD_CONTROL_DISABLE; |
| else if (!strcmp(argv[1], "on") || !strcmp(argv[1], "chip_on")) |
| p.subcmd = PD_CHIP_ON; |
| else { |
| fprintf(stderr, "Unknown command: %s\n", argv[1]); |
| return -1; |
| } |
| |
| if (argc == 2) { |
| p.chip = 0; |
| } else { |
| char *e; |
| p.chip = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port number '%s'.\n", argv[2]); |
| return -1; |
| } |
| } |
| |
| rv = ec_command(EC_CMD_PD_CONTROL, 0, &p, sizeof(p), NULL, 0); |
| return (rv < 0 ? rv : 0); |
| } |
| |
| int cmd_pd_chip_info(int argc, char *argv[]) |
| { |
| struct ec_params_pd_chip_info p; |
| struct ec_response_pd_chip_info_v3 r; |
| char *e; |
| int rv; |
| int cmdver; |
| |
| if (argc < 2 || 3 < argc) { |
| fprintf(stderr, |
| "Usage: %s <port> [<live>]\n" |
| "live parameter can take values 0 or 1\n" |
| "0 -> Return hard-coded value for VID/PID and\n" |
| " cached value for Firmware Version\n" |
| "1 -> Return live chip value for VID/PID/FW Version\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p.port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port number.\n"); |
| return -1; |
| } |
| |
| p.live = 0; |
| if (argc == 3) { |
| p.live = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "invalid arg \"%s\"\n", argv[2]); |
| return -1; |
| } |
| } |
| |
| rv = ec_get_highest_supported_cmd_version(EC_CMD_PD_CHIP_INFO, &cmdver); |
| if (rv) |
| return rv; |
| |
| /* Protect against the EC supporting a higher HC version than ectool. |
| * This should be incremented as ectool support for newer versions is |
| * implemented (specific response struct type and decoding logic below) |
| */ |
| const int highest_supported_version = 3; |
| if (cmdver > highest_supported_version) { |
| cmdver = highest_supported_version; |
| } |
| |
| rv = ec_command(EC_CMD_PD_CHIP_INFO, cmdver, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("vendor_id: 0x%x\n", r.vendor_id); |
| printf("product_id: 0x%x\n", r.product_id); |
| printf("device_id: 0x%x\n", r.device_id); |
| |
| if (r.fw_version_number != -1) { |
| printf("fw_version: 0x%" PRIx64 "\n", r.fw_version_number); |
| |
| printf("fw_version_dec: %u.%u.%u.%u.%u.%u.%u.%u\n", |
| r.fw_version_string[7], r.fw_version_string[6], |
| r.fw_version_string[5], r.fw_version_string[4], |
| r.fw_version_string[3], r.fw_version_string[2], |
| r.fw_version_string[1], r.fw_version_string[0]); |
| } else |
| printf("fw_version: UNSUPPORTED\n"); |
| |
| if (cmdver >= 1) |
| printf("min_req_fw_version: 0x%" PRIx64 "\n", |
| r.min_req_fw_version_number); |
| else |
| printf("min_req_fw_version: UNSUPPORTED\n"); |
| |
| if (cmdver >= 2) { |
| printf("fw_update_flags: 0x%04x\n", r.fw_update_flags); |
| printf("fw_name_str: '%s'\n", r.fw_name_str); |
| } else { |
| printf("fw_update_flags: UNSUPPORTED\n"); |
| printf("fw_name_str: UNSUPPORTED\n"); |
| } |
| |
| if (cmdver >= 3) { |
| printf("driver_name: '%s'\n", r.driver_name); |
| } else { |
| printf("driver_name: UNSUPPORTED\n"); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_pd_write_log(int argc, char *argv[]) |
| { |
| struct ec_params_pd_write_log_entry p; |
| char *e; |
| |
| if (argc < 3) { |
| fprintf(stderr, "Usage: %s <log_type> <port>\n", argv[0]); |
| return -1; |
| } |
| |
| if (!strcasecmp(argv[1], "charge")) |
| p.type = PD_EVENT_MCU_CHARGE; |
| else { |
| p.type = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad log_type parameter.\n"); |
| return -1; |
| } |
| } |
| |
| p.port = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port parameter.\n"); |
| return -1; |
| } |
| |
| return ec_command(EC_CMD_PD_WRITE_LOG_ENTRY, 0, &p, sizeof(p), NULL, 0); |
| } |
| |
| int cmd_typec_control(int argc, char *argv[]) |
| { |
| struct ec_params_typec_control p; |
| long conversion_result; |
| char *endptr; |
| int rv; |
| |
| if (argc < 3) { |
| fprintf(stderr, |
| "Usage: %s <port> <command> [args]\n" |
| " <port> is the type-c port to query\n" |
| " <command> is one of:\n" |
| " 0: Exit modes\n" |
| " 1: Clear events\n" |
| " args: <event mask>\n" |
| " 2: Enter mode\n" |
| " args: <0: DP, 1:TBT, 2:USB4>\n" |
| " 3: Set TBT UFP Reply\n" |
| " args: <0: NAK, 1: ACK>\n" |
| " 4: Set USB mux mode\n" |
| " args: <mux_index> <mux_mode>\n" |
| " <mux_mode> is one of: dp, dock, usb, tbt,\n" |
| " usb4, none, safe\n" |
| " 5: Enable bist share mode\n" |
| " args: <0: DISABLE, 1: ENABLE>\n" |
| " 6: Send VDM REQ\n" |
| " args: <tx_type vdm_hdr [vdo...]>\n" |
| " <tx_type> is 0 - SOP, 1 - SOP', 2 - SOP''\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p.port = strtol(argv[1], &endptr, 0); |
| if (endptr && *endptr) { |
| fprintf(stderr, "Bad port\n"); |
| return -1; |
| } |
| |
| p.command = strtol(argv[2], &endptr, 0); |
| if (endptr && *endptr) { |
| fprintf(stderr, "Bad command\n"); |
| return -1; |
| } |
| |
| switch (p.command) { |
| case TYPEC_CONTROL_COMMAND_CLEAR_EVENTS: |
| if (argc < 4) { |
| fprintf(stderr, "Missing event mask\n"); |
| return -1; |
| } |
| |
| p.clear_events_mask = strtol(argv[3], &endptr, 0); |
| if (endptr && *endptr) { |
| fprintf(stderr, "Bad event mask\n"); |
| return -1; |
| } |
| break; |
| case TYPEC_CONTROL_COMMAND_ENTER_MODE: |
| if (argc < 4) { |
| fprintf(stderr, "Missing mode\n"); |
| return -1; |
| } |
| |
| conversion_result = strtol(argv[3], &endptr, 0); |
| if ((endptr && *endptr) || conversion_result > UINT8_MAX || |
| conversion_result < 0) { |
| fprintf(stderr, "Bad mode\n"); |
| return -1; |
| } |
| p.mode_to_enter = conversion_result; |
| break; |
| case TYPEC_CONTROL_COMMAND_TBT_UFP_REPLY: |
| if (argc < 4) { |
| fprintf(stderr, "Missing reply\n"); |
| return -1; |
| } |
| |
| conversion_result = strtol(argv[3], &endptr, 0); |
| if ((endptr && *endptr) || conversion_result > UINT8_MAX || |
| conversion_result < 0) { |
| fprintf(stderr, "Bad reply\n"); |
| return -1; |
| } |
| p.tbt_ufp_reply = conversion_result; |
| break; |
| case TYPEC_CONTROL_COMMAND_USB_MUX_SET: |
| if (argc < 5) { |
| fprintf(stderr, "Missing index or mode\n"); |
| return -1; |
| } |
| |
| conversion_result = strtol(argv[3], &endptr, 0); |
| if ((endptr && *endptr) || conversion_result > UINT8_MAX || |
| conversion_result < 0) { |
| fprintf(stderr, "Bad index\n"); |
| return -1; |
| } |
| p.mux_params.mux_index = conversion_result; |
| if (!strcmp(argv[4], "dp")) { |
| p.mux_params.mux_flags = USB_PD_MUX_DP_ENABLED; |
| } else if (!strcmp(argv[4], "dock")) { |
| p.mux_params.mux_flags = USB_PD_MUX_DOCK; |
| } else if (!strcmp(argv[4], "usb")) { |
| p.mux_params.mux_flags = USB_PD_MUX_USB_ENABLED; |
| } else if (!strcmp(argv[4], "tbt")) { |
| p.mux_params.mux_flags = USB_PD_MUX_TBT_COMPAT_ENABLED; |
| } else if (!strcmp(argv[4], "usb4")) { |
| p.mux_params.mux_flags = USB_PD_MUX_USB4_ENABLED; |
| } else if (!strcmp(argv[4], "none")) { |
| p.mux_params.mux_flags = USB_PD_MUX_NONE; |
| } else if (!strcmp(argv[4], "safe")) { |
| p.mux_params.mux_flags = USB_PD_MUX_SAFE_MODE; |
| } else { |
| fprintf(stderr, "Bad mux mode\n"); |
| return -1; |
| } |
| break; |
| case TYPEC_CONTROL_COMMAND_BIST_SHARE_MODE: |
| if (argc < 4) { |
| fprintf(stderr, "Missing reply\n"); |
| return -1; |
| } |
| |
| conversion_result = strtol(argv[3], &endptr, 0); |
| if ((endptr && *endptr) || conversion_result > UINT8_MAX || |
| conversion_result < 0) { |
| fprintf(stderr, "Bad index\n"); |
| return -1; |
| } |
| p.bist_share_mode = conversion_result; |
| break; |
| case TYPEC_CONTROL_COMMAND_SEND_VDM_REQ: |
| if (argc < 5) { |
| fprintf(stderr, "Missing VDM header and type\n"); |
| return -1; |
| } |
| if (argc > 4 + VDO_MAX_SIZE) { |
| fprintf(stderr, "Too many VDOs\n"); |
| return -1; |
| } |
| |
| conversion_result = strtol(argv[3], &endptr, 0); |
| if ((endptr && *endptr) || conversion_result > UINT8_MAX || |
| conversion_result < 0) { |
| fprintf(stderr, "Bad SOP* type\n"); |
| return -1; |
| } |
| p.vdm_req_params.partner_type = conversion_result; |
| |
| int vdm_index; |
| for (vdm_index = 0; vdm_index < argc - 4; vdm_index++) { |
| uint32_t vdm_entry = |
| strtoul(argv[vdm_index + 4], &endptr, 0); |
| if (endptr && *endptr) { |
| fprintf(stderr, "Bad VDO\n"); |
| return -1; |
| } |
| p.vdm_req_params.vdm_data[vdm_index] = vdm_entry; |
| } |
| p.vdm_req_params.vdm_data_objects = vdm_index; |
| } |
| |
| rv = ec_command(EC_CMD_TYPEC_CONTROL, 0, &p, sizeof(p), ec_inbuf, |
| ec_max_insize); |
| if (rv < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| int cmd_typec_discovery(int argc, char *argv[]) |
| { |
| struct ec_params_typec_discovery p; |
| struct ec_response_typec_discovery *r = |
| (struct ec_response_typec_discovery *)ec_inbuf; |
| char *e; |
| int rv, i, j; |
| |
| if (argc < 3) { |
| fprintf(stderr, |
| "Usage: %s <port> <type>\n" |
| " <port> is the type-c port to query\n" |
| " <type> is one of:\n" |
| " 0: SOP\n" |
| " 1: SOP prime\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p.port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad port\n"); |
| return -1; |
| } |
| |
| p.partner_type = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad type\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_TYPEC_DISCOVERY, 0, &p, sizeof(p), ec_inbuf, |
| ec_max_insize); |
| if (rv < 0) |
| return -1; |
| |
| if (r->identity_count == 0) { |
| printf("No identity discovered\n"); |
| return 0; |
| } |
| |
| printf("Identity VDOs:\n"); |
| for (i = 0; i < r->identity_count; i++) |
| printf("0x%08x\n", r->discovery_vdo[i]); |
| |
| if (r->svid_count == 0) { |
| printf("No SVIDs discovered\n"); |
| return 0; |
| } |
| |
| for (i = 0; i < r->svid_count; i++) { |
| printf("SVID 0x%04x Modes:\n", r->svids[i].svid); |
| for (j = 0; j < r->svids[i].mode_count; j++) |
| printf("0x%08x\n", r->svids[i].mode_vdo[j]); |
| } |
| |
| return 0; |
| } |
| |
| /* Print shared fields of sink and source cap PDOs */ |
| static inline void print_pdo_fixed(uint32_t pdo) |
| { |
| printf(" Fixed: %dmV %dmA %s%s%s%s", PDO_FIXED_VOLTAGE(pdo), |
| PDO_FIXED_CURRENT(pdo), pdo & PDO_FIXED_DUAL_ROLE ? "DRP " : "", |
| pdo & PDO_FIXED_UNCONSTRAINED ? "UP " : "", |
| pdo & PDO_FIXED_COMM_CAP ? "USB " : "", |
| pdo & PDO_FIXED_DATA_SWAP ? "DRD" : ""); |
| } |
| |
| static inline void print_pdo_battery(uint32_t pdo) |
| { |
| printf(" Battery: max %dmV min %dmV max %dmW\n", |
| PDO_BATT_MAX_VOLTAGE(pdo), PDO_BATT_MIN_VOLTAGE(pdo), |
| PDO_BATT_MAX_POWER(pdo)); |
| } |
| |
| static inline void print_pdo_variable(uint32_t pdo) |
| { |
| printf(" Variable: max %dmV min %dmV max %dmA\n", |
| PDO_VAR_MAX_VOLTAGE(pdo), PDO_VAR_MIN_VOLTAGE(pdo), |
| PDO_VAR_MAX_CURRENT(pdo)); |
| } |
| |
| static inline void print_pdo_augmented(uint32_t pdo) |
| { |
| printf(" Augmented: max %dmV min %dmV max %dmA\n", |
| PDO_AUG_MAX_VOLTAGE(pdo), PDO_AUG_MIN_VOLTAGE(pdo), |
| PDO_AUG_MAX_CURRENT(pdo)); |
| } |
| |
| int cmd_typec_status(int argc, char *argv[]) |
| { |
| struct ec_params_typec_status p; |
| struct ec_response_typec_status *r = |
| (struct ec_response_typec_status *)ec_inbuf; |
| char *endptr; |
| int rv, i; |
| const char *desc; |
| |
| if (argc != 2) { |
| fprintf(stderr, |
| "Usage: %s <port>\n" |
| " <port> is the type-c port to query\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p.port = strtol(argv[1], &endptr, 0); |
| if (endptr && *endptr) { |
| fprintf(stderr, "Bad port\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_TYPEC_STATUS, 0, &p, sizeof(p), ec_inbuf, |
| ec_max_insize); |
| if (rv == -EC_RES_INVALID_COMMAND - EECRESULT) |
| /* Fall back to PD_CONTROL to support older ECs */ |
| return cmd_usb_pd(argc, argv); |
| else if (rv < 0) |
| return -1; |
| |
| printf("Port C%d: %s, %s State:%s\n" |
| "Role:%s %s%s, Polarity:CC%d\n", |
| p.port, r->pd_enabled ? "enabled" : "disabled", |
| r->dev_connected ? "connected" : "disconnected", r->tc_state, |
| (r->power_role == PD_ROLE_SOURCE) ? "SRC" : "SNK", |
| (r->data_role == PD_ROLE_DFP) ? "DFP" : |
| (r->data_role == PD_ROLE_UFP) ? "UFP" : |
| "", |
| (r->vconn_role == PD_ROLE_VCONN_SRC) ? " VCONN" : "", |
| (r->polarity % 2 + 1)); |
| |
| switch (r->cc_state) { |
| case PD_CC_NONE: |
| desc = "None"; |
| break; |
| case PD_CC_UFP_AUDIO_ACC: |
| desc = "UFP Audio accessory"; |
| break; |
| case PD_CC_UFP_DEBUG_ACC: |
| desc = "UFP Debug accessory"; |
| break; |
| case PD_CC_UFP_ATTACHED: |
| desc = "UFP attached"; |
| break; |
| case PD_CC_DFP_DEBUG_ACC: |
| desc = "DFP Debug accessory"; |
| break; |
| case PD_CC_DFP_ATTACHED: |
| desc = "DFP attached"; |
| break; |
| default: |
| desc = "UNKNOWN"; |
| break; |
| } |
| printf("CC State: %s\n", desc); |
| |
| if (r->dp_pin) { |
| switch (r->dp_pin) { |
| case MODE_DP_PIN_A: |
| desc = "A"; |
| break; |
| case MODE_DP_PIN_B: |
| desc = "B"; |
| break; |
| case MODE_DP_PIN_C: |
| desc = "C"; |
| break; |
| case MODE_DP_PIN_D: |
| desc = "D"; |
| break; |
| case MODE_DP_PIN_E: |
| desc = "E"; |
| break; |
| case MODE_DP_PIN_F: |
| desc = "F"; |
| break; |
| default: |
| desc = "UNKNOWN"; |
| break; |
| } |
| printf("DP pin mode: %s\n", desc); |
| } |
| |
| if (r->mux_state) { |
| printf("MUX: USB=%d DP=%d POLARITY=%s HPD_IRQ=%d HPD_LVL=%d\n" |
| " SAFE=%d TBT=%d USB4=%d\n", |
| !!(r->mux_state & USB_PD_MUX_USB_ENABLED), |
| !!(r->mux_state & USB_PD_MUX_DP_ENABLED), |
| (r->mux_state & USB_PD_MUX_POLARITY_INVERTED) ? |
| "INVERTED" : |
| "NORMAL", |
| !!(r->mux_state & USB_PD_MUX_HPD_IRQ), |
| !!(r->mux_state & USB_PD_MUX_HPD_LVL), |
| !!(r->mux_state & USB_PD_MUX_SAFE_MODE), |
| !!(r->mux_state & USB_PD_MUX_TBT_COMPAT_ENABLED), |
| !!(r->mux_state & USB_PD_MUX_USB4_ENABLED)); |
| } |
| |
| printf("Port events: 0x%08x\n", r->events); |
| |
| if (r->sop_revision) |
| printf("SOP PD Rev: %d.%d\n", |
| PD_STATUS_REV_GET_MAJOR(r->sop_revision), |
| PD_STATUS_REV_GET_MINOR(r->sop_revision)); |
| |
| if (r->sop_prime_revision) |
| printf("SOP' PD Rev: %d.%d\n", |
| PD_STATUS_REV_GET_MAJOR(r->sop_prime_revision), |
| PD_STATUS_REV_GET_MINOR(r->sop_prime_revision)); |
| |
| for (i = 0; i < r->source_cap_count; i++) { |
| /* |
| * Bits 31:30 always indicate the type of PDO |
| * |
| * Table 6-7 PD Rev 3.0 Ver 2.0 |
| */ |
| uint32_t pdo = r->source_cap_pdos[i]; |
| int pdo_type = pdo & PDO_TYPE_MASK; |
| |
| if (i == 0) |
| printf("Source Capabilities:\n"); |
| |
| if (pdo_type == PDO_TYPE_FIXED) { |
| print_pdo_fixed(pdo); |
| printf("\n"); |
| } else if (pdo_type == PDO_TYPE_BATTERY) { |
| print_pdo_battery(pdo); |
| } else if (pdo_type == PDO_TYPE_VARIABLE) { |
| print_pdo_variable(pdo); |
| } else { |
| print_pdo_augmented(pdo); |
| } |
| } |
| |
| for (i = 0; i < r->sink_cap_count; i++) { |
| /* |
| * Bits 31:30 always indicate the type of PDO |
| * |
| * Table 6-7 PD Rev 3.0 Ver 2.0 |
| */ |
| uint32_t pdo = r->sink_cap_pdos[i]; |
| int pdo_type = pdo & PDO_TYPE_MASK; |
| |
| if (i == 0) |
| printf("Sink Capabilities:\n"); |
| |
| if (pdo_type == PDO_TYPE_FIXED) { |
| print_pdo_fixed(pdo); |
| /* Note: FRS bits are reserved in PD 2.0 spec */ |
| printf("%s\n", |
| pdo & PDO_FIXED_FRS_CURR_MASK ? "FRS" : ""); |
| } else if (pdo_type == PDO_TYPE_BATTERY) { |
| print_pdo_battery(pdo); |
| } else if (pdo_type == PDO_TYPE_VARIABLE) { |
| print_pdo_variable(pdo); |
| } else { |
| print_pdo_augmented(pdo); |
| } |
| } |
| |
| return 0; |
| } |
| |
| int cmd_typec_vdm_response(int argc, char *argv[]) |
| { |
| struct ec_params_typec_vdm_response p; |
| struct ec_response_typec_vdm_response *r = |
| (ec_response_typec_vdm_response *)ec_inbuf; |
| char *endptr; |
| int rv, i; |
| |
| if (argc != 2) { |
| fprintf(stderr, |
| "Usage: %s <port>\n" |
| " <port> is the type-c port to query\n", |
| argv[0]); |
| return -1; |
| } |
| |
| p.port = strtol(argv[1], &endptr, 0); |
| if (endptr && *endptr) { |
| fprintf(stderr, "Bad port\n"); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_TYPEC_VDM_RESPONSE, 0, &p, sizeof(p), ec_inbuf, |
| ec_max_insize); |
| if (rv < 0) |
| return -1; |
| |
| if (r->vdm_data_objects > 0 && r->vdm_response_err == EC_RES_SUCCESS) { |
| printf("VDM response from partner: %d", r->partner_type); |
| for (i = 0; i < r->vdm_data_objects; i++) |
| printf("\n 0x%08x", r->vdm_response[i]); |
| printf("\n"); |
| } else { |
| printf("No VDM response found (err: %d)\n", |
| r->vdm_response_err); |
| } |
| |
| if (r->vdm_attention_objects > 0) { |
| printf("VDM Attention:"); |
| for (i = 0; i < r->vdm_attention_objects; i++) |
| printf("\n 0x%08x", r->vdm_attention[i]); |
| printf("\n"); |
| printf("%d Attention messages remaining\n", |
| r->vdm_attention_left); |
| } else { |
| printf("No VDM Attention found"); |
| } |
| |
| return 0; |
| } |
| |
| int cmd_tp_self_test(int argc, char *argv[]) |
| { |
| int rv; |
| |
| rv = ec_command(EC_CMD_TP_SELF_TEST, 0, NULL, 0, NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| printf("Touchpad self test: %s\n", |
| rv == EC_RES_SUCCESS ? "passed" : "failed"); |
| |
| return rv; |
| } |
| |
| int cmd_tp_frame_get(int argc, char *argv[]) |
| { |
| int i, j; |
| uint32_t remaining = 0, offset = 0; |
| int rv = EC_SUCCESS; |
| uint8_t *data; |
| struct ec_response_tp_frame_info *r; |
| struct ec_params_tp_frame_get p; |
| |
| data = (uint8_t *)(malloc(ec_max_insize)); |
| r = (struct ec_response_tp_frame_info *)(malloc(ec_max_insize)); |
| |
| if (data == NULL || r == NULL) { |
| fprintf(stderr, "Couldn't allocate memory.\n"); |
| free(r); |
| free(data); |
| return EC_ERROR_UNKNOWN; |
| } |
| |
| rv = ec_command(EC_CMD_TP_FRAME_INFO, 0, NULL, 0, r, ec_max_insize); |
| if (rv < 0) { |
| fprintf(stderr, "Failed to get touchpad frame info.\n"); |
| goto err; |
| } |
| |
| rv = ec_command(EC_CMD_TP_FRAME_SNAPSHOT, 0, NULL, 0, NULL, 0); |
| if (rv < 0) { |
| fprintf(stderr, "Failed to snapshot frame.\n"); |
| goto err; |
| } |
| |
| for (i = 0; i < r->n_frames; i++) { |
| p.frame_index = i; |
| offset = 0; |
| remaining = r->frame_sizes[i]; |
| |
| while (remaining > 0) { |
| p.offset = offset; |
| p.size = MIN(remaining, ec_max_insize); |
| |
| rv = ec_command(EC_CMD_TP_FRAME_GET, 0, &p, sizeof(p), |
| data, p.size); |
| if (rv < 0) { |
| fprintf(stderr, |
| "Failed to get frame data " |
| "at offset 0x%x\n", |
| offset); |
| goto err; |
| } |
| |
| for (j = 0; j < p.size; j++) |
| printf("%02x ", data[j]); |
| |
| offset += p.size; |
| remaining -= p.size; |
| } |
| printf("\n"); |
| } |
| |
| err: |
| free(data); |
| free(r); |
| |
| return rv < 0; |
| } |
| |
| int cmd_wait_event(int argc, char *argv[]) |
| { |
| static const char *const mkbp_event_text[] = EC_MKBP_EVENT_TEXT; |
| static const char *const host_event_text[] = HOST_EVENT_TEXT; |
| |
| int rv, i; |
| ec_response_get_next_event_v1 buffer = { 0 }; |
| long timeout = 5000; |
| long event_type; |
| char *e; |
| |
| BUILD_ASSERT(ARRAY_SIZE(mkbp_event_text) == EC_MKBP_EVENT_COUNT); |
| /* |
| * Only 64 host events are supported. The enum |host_event_code| uses |
| * 1-based counting so it can skip 0 (NONE). The last legal host event |
| * number is 64, so ARRAY_SIZE(host_event_text) <= 64+1. |
| */ |
| BUILD_ASSERT(ARRAY_SIZE(host_event_text) <= 65); |
| |
| if (!ec_pollevent) { |
| fprintf(stderr, "Polling for MKBP event not supported\n"); |
| return -EINVAL; |
| } |
| |
| if (argc < 2) { |
| fprintf(stderr, |
| "Usage: %s <type> [timeout [cmd [cmd_args...]]]\n", |
| argv[0]); |
| fprintf(stderr, "\n"); |
| fprintf(stderr, "type: MKBP event number or name.\n"); |
| for (int i = 0; i < ARRAY_SIZE(mkbp_event_text); i++) { |
| const char *name = mkbp_event_text[i]; |
| |
| if (name) { |
| fprintf(stderr, " %s or %d\n", name, i); |
| } |
| } |
| fprintf(stderr, "timeout: " |
| "Timeout in milliseconds (default 5000).\n"); |
| fprintf(stderr, |
| "cmd: " |
| "Optional ectool command to run while waiting for events.\n"); |
| fprintf(stderr, "cmd_args: " |
| "Optional arguments for the ectool command.\n"); |
| fprintf(stderr, "\n"); |
| fprintf(stderr, |
| "The waitevent command is designed to simply wait for an MKBP event and\n" |
| "print out the event details.\n" |
| "Optionally, you can provide another ectool command with arguments that\n" |
| "will be invoked, while waiting for this MKBP event. This has the\n" |
| "effect of running the ectool command and waiting for it's resultant\n" |
| "MKBP event.\n"); |
| |
| return -1; |
| } |
| |
| rv = find_enum_from_text(argv[1], mkbp_event_text, |
| ARRAY_SIZE(mkbp_event_text), &event_type); |
| if (rv < 0 || event_type < 0 || event_type >= EC_MKBP_EVENT_COUNT) { |
| fprintf(stderr, "Bad event type '%s'.\n", argv[1]); |
| return -1; |
| } |
| if (argc >= 3) { |
| timeout = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad timeout value '%s'.\n", argv[2]); |
| return -1; |
| } |
| } |
| |
| auto events = ec::MkbpEvent( |
| comm_get_fd(), static_cast<enum ec_mkbp_event>(event_type)); |
| events.Enable(); |
| |
| if (argc >= 4) { |
| const command *cmd = commands_find(argv[3]); |
| if (cmd == nullptr) { |
| fprintf(stderr, "Unknown command '%s'\n\n", argv[3]); |
| return -2; |
| } |
| rv = cmd->handler(argc - 3, argv + 3); |
| } |
| |
| rv = events.Wait(timeout); |
| if (rv == 0) { |
| fprintf(stderr, "Timeout waiting for MKBP event\n"); |
| return -ETIMEDOUT; |
| } else if (rv < 0) { |
| perror("Error polling for MKBP event\n"); |
| return -EIO; |
| } |
| rv = read(comm_get_fd(), &buffer, sizeof(buffer)); |
| if (rv < 0) |
| return rv; |
| |
| printf("MKBP event %d data: ", buffer.event_type); |
| for (i = 0; i < rv - 1; ++i) |
| printf("%02x ", buffer.data.key_matrix[i]); |
| printf("\n"); |
| |
| switch (event_type) { |
| case EC_MKBP_EVENT_HOST_EVENT: |
| printf("Host events:"); |
| for (int evt = 1; evt < ARRAY_SIZE(host_event_text); evt++) { |
| if (buffer.data.host_event & EC_HOST_EVENT_MASK(evt)) { |
| const char *name = host_event_text[evt]; |
| |
| printf(" %s", name ? name : "UNKNOWN"); |
| } |
| } |
| printf("\n"); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static void cmd_cec_help(void) |
| { |
| fprintf(stderr, " Usage: cec <port> write [write bytes...]\n" |
| " Write message on the CEC bus\n" |
| " Usage: cec <port> read [timeout]\n" |
| " [timeout] in seconds\n" |
| " Usage: cec <port> get <param>\n" |
| " Usage: cec <port> set <param> <val>\n" |
| " <param> is one of:\n" |
| " address: CEC receive address\n" |
| " <val> is the new CEC address\n" |
| " enable: Enable or disable CEC\n" |
| " <val> is 1 to enable, 0 to disable\n"); |
| } |
| |
| static long timespec_diff_ms(const struct timespec *t1, |
| const struct timespec *t2) |
| { |
| return ((t1->tv_sec - t2->tv_sec) * 1000 + |
| (t1->tv_nsec - t2->tv_nsec) / 1000000); |
| } |
| |
| static int cmd_cec_write(int port, int argc, char *argv[]) |
| { |
| char *e; |
| long val; |
| int rv, i, msg_len; |
| struct ec_params_cec_write p; |
| struct ec_params_cec_write_v1 p_v1; |
| struct ec_response_get_next_event_v1 buffer; |
| int version; |
| uint8_t *msg_param; |
| struct timespec start, now; |
| const long timeout_ms = 1000; /* How long to wait for the send result */ |
| long elapsed_ms; |
| uint32_t event_port, events; |
| |
| if (argc < 3 || argc > 18) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_cec_help(); |
| return -1; |
| } |
| |
| msg_len = argc - 2; |
| |
| rv = get_latest_cmd_version(EC_CMD_CEC_WRITE_MSG, &version); |
| if (rv < 0) |
| return rv; |
| |
| if (version == 0) { |
| msg_param = p.msg; |
| } else { |
| p_v1.port = port; |
| p_v1.msg_len = msg_len; |
| msg_param = p_v1.msg; |
| } |
| |
| for (i = 0; i < msg_len; i++) { |
| val = strtol(argv[i + 2], &e, 16); |
| if (e && *e) |
| return -1; |
| if (val < 0 || val > 0xff) |
| return -1; |
| msg_param[i] = (uint8_t)val; |
| } |
| |
| printf("Write to CEC: "); |
| for (i = 0; i < msg_len; i++) |
| printf("0x%02x ", msg_param[i]); |
| printf("\n"); |
| |
| if (version == 0) |
| rv = ec_command(EC_CMD_CEC_WRITE_MSG, 0, &p, msg_len, NULL, 0); |
| else |
| rv = ec_command(EC_CMD_CEC_WRITE_MSG, version, &p_v1, |
| sizeof(p_v1), NULL, 0); |
| if (rv < 0) |
| return rv; |
| |
| /* |
| * Wait for a send OK or send failed event. Retry multiple times since |
| * we might receive other events or events for other ports. |
| */ |
| clock_gettime(CLOCK_REALTIME, &start); |
| while (true) { |
| clock_gettime(CLOCK_REALTIME, &now); |
| elapsed_ms = timespec_diff_ms(&now, &start); |
| if (elapsed_ms >= timeout_ms) |
| break; |
| |
| rv = wait_event(EC_MKBP_EVENT_CEC_EVENT, &buffer, |
| sizeof(buffer), timeout_ms - elapsed_ms); |
| if (rv < 0) |
| return rv; |
| |
| event_port = EC_MKBP_EVENT_CEC_GET_PORT(buffer.data.cec_events); |
| events = EC_MKBP_EVENT_CEC_GET_EVENTS(buffer.data.cec_events); |
| |
| if (event_port != port) |
| continue; |
| |
| if (events & EC_MKBP_CEC_SEND_OK) |
| return 0; |
| |
| if (events & EC_MKBP_CEC_SEND_FAILED) { |
| fprintf(stderr, "Send failed\n"); |
| return -1; |
| } |
| } |
| |
| fprintf(stderr, "No send result received\n"); |
| |
| return -1; |
| } |
| |
| static int cec_read_handle_cec_event(int port, uint32_t cec_events, |
| uint8_t *msg, uint8_t *msg_len) |
| { |
| int rv; |
| uint32_t event_port, events; |
| struct ec_params_cec_read p; |
| struct ec_response_cec_read r; |
| |
| /* Extract the port and events */ |
| event_port = EC_MKBP_EVENT_CEC_GET_PORT(cec_events); |
| events = EC_MKBP_EVENT_CEC_GET_EVENTS(cec_events); |
| |
| /* Check if it's the HAVE_DATA event we're waiting for */ |
| if (event_port != port || !(events & EC_MKBP_CEC_HAVE_DATA)) { |
| *msg_len = 0; |
| return 0; |
| } |
| |
| /* Data is ready, so send the read command */ |
| p.port = port; |
| rv = ec_command(EC_CMD_CEC_READ_MSG, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) { |
| /* |
| * When the kernel driver is enabled it may read the data out |
| * first, so the queue will be empty and the command will |
| * returns EC_RES_UNAVAILABLE. The ectool read command is still |
| * useful for testing if the kernel driver is not enabled. |
| */ |
| printf("Note: `cec read` doesn't work if the cros_ec_cec " |
| "kernel driver is running\n"); |
| return rv; |
| } |
| |
| /* Message received successfully */ |
| memcpy(msg, r.msg, r.msg_len); |
| *msg_len = r.msg_len; |
| return 0; |
| } |
| |
| static int cmd_cec_read(int port, int argc, char *argv[]) |
| { |
| int i, rv; |
| char *e; |
| struct ec_response_get_next_event_v1 buffer; |
| long timeout_ms = 5000; |
| unsigned long event_mask; |
| struct timespec start, now; |
| long elapsed_ms; |
| int event_size; |
| bool received = false; |
| uint8_t msg[MAX_CEC_MSG_LEN]; |
| uint8_t msg_len; |
| |
| if (!ec_pollevent) { |
| fprintf(stderr, "Polling for MKBP event not supported\n"); |
| return -EINVAL; |
| } |
| |
| if (argc >= 3) { |
| timeout_ms = strtol(argv[2], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad timeout value '%s'.\n", argv[2]); |
| return -1; |
| } |
| } |
| |
| /* |
| * There are two ways of receiving CEC messages: |
| * 1. Old EC firmware which only supports one port sends the data in a |
| * cec_message MKBP event. |
| * 2. New EC firmware which supports multiple ports sends |
| * EC_MKBP_CEC_HAVE_DATA to notify that data is ready, then |
| * EC_CMD_CEC_READ_MSG is used to read it. |
| * To make ectool compatible with both, we wait for either |
| * EC_MKBP_EVENT_CEC_MESSAGE or EC_MKBP_CEC_HAVE_DATA. |
| */ |
| event_mask = BIT(EC_MKBP_EVENT_CEC_EVENT) | |
| BIT(EC_MKBP_EVENT_CEC_MESSAGE); |
| clock_gettime(CLOCK_REALTIME, &start); |
| while (true) { |
| clock_gettime(CLOCK_REALTIME, &now); |
| elapsed_ms = timespec_diff_ms(&now, &start); |
| if (elapsed_ms >= timeout_ms) |
| break; |
| |
| rv = wait_event_mask(event_mask, &buffer, sizeof(buffer), |
| timeout_ms - elapsed_ms); |
| if (rv < 0) |
| return rv; |
| event_size = rv; |
| |
| if (buffer.event_type == EC_MKBP_EVENT_CEC_EVENT) { |
| rv = cec_read_handle_cec_event( |
| port, buffer.data.cec_events, msg, &msg_len); |
| if (rv < 0) |
| return rv; |
| |
| /* Message received successfully */ |
| if (msg_len) { |
| received = true; |
| break; |
| } |
| /* No message received, continue waiting */ |
| |
| } else if (buffer.event_type == EC_MKBP_EVENT_CEC_MESSAGE) { |
| /* Message received successfully */ |
| received = true; |
| msg_len = event_size - 1; |
| memcpy(msg, buffer.data.cec_message, msg_len); |
| break; |
| } |
| } |
| |
| if (!received) { |
| fprintf(stderr, "Timed out waiting for message\n"); |
| return -1; |
| } |
| |
| printf("CEC data: "); |
| for (i = 0; i < msg_len; i++) |
| printf("0x%02x ", msg[i]); |
| printf("\n"); |
| |
| return 0; |
| } |
| |
| static int cec_cmd_from_str(const char *str) |
| { |
| if (!strcmp("address", str)) |
| return CEC_CMD_LOGICAL_ADDRESS; |
| if (!strcmp("enable", str)) |
| return CEC_CMD_ENABLE; |
| return -1; |
| } |
| |
| static int cmd_cec_set(int port, int argc, char *argv[]) |
| { |
| char *e; |
| struct ec_params_cec_set p; |
| uint8_t val; |
| int cmd; |
| |
| if (argc != 4) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_cec_help(); |
| return -1; |
| } |
| |
| val = (uint8_t)strtol(argv[3], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Bad parameter '%s'.\n", argv[3]); |
| return -1; |
| } |
| |
| cmd = cec_cmd_from_str(argv[2]); |
| if (cmd < 0) { |
| fprintf(stderr, "Invalid command '%s'.\n", argv[2]); |
| return -1; |
| } |
| p.cmd = cmd; |
| p.port = port; |
| p.val = val; |
| |
| return ec_command(EC_CMD_CEC_SET, 0, &p, sizeof(p), NULL, 0); |
| } |
| |
| static int cmd_cec_get(int port, int argc, char *argv[]) |
| { |
| int rv, cmd; |
| struct ec_params_cec_get p; |
| struct ec_response_cec_get r; |
| |
| if (argc != 3) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_cec_help(); |
| return -1; |
| } |
| |
| cmd = cec_cmd_from_str(argv[2]); |
| if (cmd < 0) { |
| fprintf(stderr, "Invalid command '%s'.\n", argv[2]); |
| return -1; |
| } |
| p.cmd = cmd; |
| p.port = port; |
| |
| rv = ec_command(EC_CMD_CEC_GET, 0, &p, sizeof(p), &r, sizeof(r)); |
| if (rv < 0) |
| return rv; |
| |
| printf("%d\n", r.val); |
| |
| return 0; |
| } |
| |
| int cmd_cec(int argc, char *argv[]) |
| { |
| int port; |
| char *e; |
| |
| if (argc < 3) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_cec_help(); |
| return -1; |
| } |
| |
| port = strtol(argv[1], &e, 0); |
| if (e && *e) { |
| fprintf(stderr, "Invalid port: %s\n", argv[1]); |
| cmd_cec_help(); |
| return -1; |
| } |
| argc--; |
| argv++; |
| |
| if (!strcmp(argv[1], "write")) |
| return cmd_cec_write(port, argc, argv); |
| if (!strcmp(argv[1], "read")) |
| return cmd_cec_read(port, argc, argv); |
| if (!strcmp(argv[1], "get")) |
| return cmd_cec_get(port, argc, argv); |
| if (!strcmp(argv[1], "set")) |
| return cmd_cec_set(port, argc, argv); |
| |
| fprintf(stderr, "Invalid sub command: %s\n", argv[1]); |
| cmd_cec_help(); |
| |
| return -1; |
| } |
| |
| static void cmd_s0ix_counter_help(char *cmd) |
| { |
| fprintf(stderr, |
| " Usage: %s get - to get the value of s0ix counter\n" |
| " %s reset - to reset s0ix counter \n", |
| cmd, cmd); |
| } |
| |
| static int cmd_s0ix_counter(int argc, char *argv[]) |
| { |
| struct ec_params_s0ix_cnt p; |
| struct ec_response_s0ix_cnt r; |
| int rv; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Invalid number of params\n"); |
| cmd_s0ix_counter_help(argv[0]); |
| return -1; |
| } |
| |
| if (!strcasecmp(argv[1], "get")) { |
| p.flags = 0; |
| } else if (!strcasecmp(argv[1], "reset")) { |
| p.flags = EC_S0IX_COUNTER_RESET; |
| } else { |
| fprintf(stderr, "Bad subcommand: %s\n", argv[1]); |
| return -1; |
| } |
| |
| rv = ec_command(EC_CMD_GET_S0IX_COUNTER, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) { |
| return rv; |
| } |
| |
| printf("s0ix_counter: %u\n", r.s0ix_counter); |
| |
| return 0; |
| } |
| |
| int cmd_thread_info(int argc, char *argv[]) |
| { |
| struct ec_response_thread_info_list list; |
| int rv; |
| |
| rv = ec_command(EC_CMD_THREAD_INFO_LIST, 0, NULL, 0, &list, |
| sizeof(list)); |
| if (rv < 0) { |
| return rv; |
| } |
| |
| printf("Found %u threads:\n", list.thread_count); |
| |
| for (uint32_t i = 0; i < list.thread_count; i++) { |
| struct ec_params_thread_info_detail p; |
| struct ec_response_thread_info_detail r; |
| |
| p.thread_id = list.thread_ids[i]; |
| rv = ec_command(EC_CMD_THREAD_INFO_DETAIL, 0, &p, sizeof(p), &r, |
| sizeof(r)); |
| if (rv < 0) { |
| fprintf(stderr, |
| "Failed to get thread detail for ID 0x%08x: %d\n", |
| p.thread_id, rv); |
| continue; |
| } |
| |
| printf("\nThread ID 0x%08x (@ %" PRIu64 " us):\n", p.thread_id, |
| r.timestamp_us); |
| |
| /* Name */ |
| if (r.valid_flags & EC_THREAD_INFO_DETAIL_NAME_VALID) { |
| printf(" %-15s%s\n", "Name:", r.name); |
| } else { |
| printf(" %-15s(unknown)\n", "Name:"); |
| } |
| |
| /* Basic information */ |
| printf(" %-15s0x%08x\n", "Entry Point:", r.entry_point); |
| if (r.timeout_us == 0xffffffff) { |
| printf(" %-15sNone\n", "Timeout:"); |
| } else { |
| printf(" %-15s%u us\n", "Timeout:", r.timeout_us); |
| } |
| |
| printf(" %-15s0x%04x\n", "User Options:", r.user_options); |
| printf(" %-15s%d\n", "Priority:", r.prio); |
| printf(" %-15s0x%02x\n", "Thread State:", r.thread_state); |
| printf(" %-15s%s\n", "Is Idle:", r.is_idle ? "Yes" : "No"); |
| printf(" %-15s%s\n", |
| "Is Current:", r.is_current ? "Yes" : "No"); |
| |
| /* Stack usage */ |
| if (r.valid_flags & EC_THREAD_INFO_DETAIL_STACK_VALID) { |
| printf(" %-15s%u bytes\n", "Stack Cur:", r.stack_cur); |
| printf(" %-15s%u bytes\n", "Stack Max:", r.stack_max); |
| printf(" %-15s%u bytes\n", |
| "Stack Size:", r.stack_size); |
| } |
| |
| /* Timing */ |
| if (r.valid_flags & EC_THREAD_INFO_DETAIL_RUNTIME_USAGE_VALID) { |
| printf(" %-15s%u us\n", |
| "Execution:", r.execution_time_us); |
| } |
| |
| /* Analysis */ |
| if (r.valid_flags & |
| EC_THREAD_INFO_DETAIL_USAGE_ANALYSIS_VALID) { |
| printf(" %-15s%u us\n", |
| "Window Peak:", r.window_peak_us); |
| printf(" %-15s%u us\n", |
| "Window Avg:", r.window_avg_us); |
| } |
| |
| /* Scheduling */ |
| printf(" %-15s0x%08x\n", "Pending On:", r.pending_on); |
| |
| /* CPU registers */ |
| if (r.valid_flags & EC_THREAD_INFO_DETAIL_PC_VALID) { |
| printf(" %-15s0x%08x\n", "PC:", r.pc); |
| } |
| if (r.valid_flags & EC_THREAD_INFO_DETAIL_LR_VALID) { |
| printf(" %-15s0x%08x\n", "LR:", r.lr); |
| } |
| if (r.valid_flags & EC_THREAD_INFO_DETAIL_SP_VALID) { |
| printf(" %-15s0x%08x\n", "SP:", r.sp); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* NULL-terminated list of commands. Please keep sorted. */ |
| const struct command commands[] = { |
| { "adcread", cmd_adc_read, "<channel>\n\tRead an ADC channel." }, |
| { "addentropy", cmd_add_entropy, |
| "[reset]\n\tAdd entropy to device secret." }, |
| { "apreset", cmd_apreset, "\n\tIssue AP reset." }, |
| { "autofanctrl", cmd_thermal_auto_fan_ctrl, |
| "<on>\n\tTurn on automatic fan speed control." }, |
| { "backlight", cmd_lcd_backlight, |
| "<enabled>\n\tEnable/disable LCD backlight." }, |
| { "basestate", cmd_basestate, |
| "[attach | detach | reset]\n" |
| "\tManually force base state to attached, detached or reset." }, |
| { "battery", cmd_battery, "\n\tPrints battery info." }, |
| { "batterycutoff", cmd_battery_cut_off, |
| "[at-shutdown]\n\tCut off battery output power." }, |
| { "batteryparam", cmd_battery_vendor_param, |
| "\n\tRead or write board-specific battery parameter." }, |
| { "bcfg", cmd_battery_config, "\n\tPrint an active battery config." }, |
| { "boardversion", cmd_board_version, "\n\tPrints the board version." }, |
| { "boottime", cmd_boottime, "\n\tGet boot time." }, |
| { "button", cmd_button, |
| "[vup|vdown|rec] <Delay-ms>\n\tSimulates button press." }, |
| { "cbi", cmd_cbi, "\n\tGet/Set/Remove Cros Board Info." }, |
| { "cbibin", cmd_cbi_bin, "\n\tRead/Write Cros Board Info into file." }, |
| { "cec", cmd_cec, "\n\tRead or write CEC messages and settings." }, |
| { "chargecontrol", cmd_charge_control, |
| "\n\tForce the battery to stop charging or discharge." }, |
| { "chargecurrentlimit", cmd_charge_current_limit, |
| "\n\tSet the maximum battery charging current and the minimum battery\n" |
| "\tSoC at which it will apply." }, |
| { "chargeoverride", cmd_charge_port_override, |
| "\n\tOverrides charge port selection logic." }, |
| { "chargesplash", cmd_chargesplash, |
| "\n\tShow and manipulate chargesplash variables." }, |
| { "chargestate", cmd_charge_state, |
| "\n\tHandle commands related to charge state v2 (and later)." }, |
| { "chipinfo", cmd_chipinfo, "\n\tPrints chip info." }, |
| { "cmdversions", cmd_cmdversions, |
| "<cmd>\n\tPrints supported version mask for a command number." }, |
| { "console", cmd_console, |
| "\n\tPrints the last output to the EC debug console." }, |
| { "console_print", cmd_console_print, |
| "<message>\n" |
| "\tPrints a message to the EC console." }, |
| { "echash", cmd_ec_hash, "[CMDS]\n\tVarious EC hash commands." }, |
| { "eventclear", cmd_host_event_clear, |
| "<mask>\n" |
| "\tClears EC host events flags where mask has bits set." }, |
| { "eventclearb", cmd_host_event_clear_b, |
| "<mask>\n" |
| "\tClears EC host events flags copy B where mask has bits set." }, |
| { "eventget", cmd_host_event_get_raw, |
| "\n\tPrints raw EC host event flags." }, |
| { "eventgetb", cmd_host_event_get_b, |
| "\n\tPrints raw EC host event flags copy B." }, |
| { "eventgetscimask", cmd_host_event_get_sci_mask, |
| "\n\tPrints SCI mask for EC host events." }, |
| { "eventgetsmimask", cmd_host_event_get_smi_mask, |
| "\n\tPrints SMI mask for EC host events." }, |
| { "eventgetwakemask", cmd_host_event_get_wake_mask, |
| "\n\tPrints wake mask for EC host events." }, |
| { "eventsetscimask", cmd_host_event_set_sci_mask, |
| "<mask>\n\tSets the SCI mask for EC host events." }, |
| { "eventsetsmimask", cmd_host_event_set_smi_mask, |
| "<mask>\n\tSets the SMI mask for EC host events." }, |
| { "eventsetwakemask", cmd_host_event_set_wake_mask, |
| "<mask>\n\tSets the wake mask for EC host events" }, |
| { "extpwrlimit", cmd_ext_power_limit, |
| "\n\tSet the maximum external power limit." }, |
| { "fanduty", cmd_fanduty, |
| "<percent>\n\tForces the fan PWM to a constant duty cycle." }, |
| { "flasherase", cmd_flash_erase, |
| "<offset> <size>\n\tErases EC flash." }, |
| { "flasheraseasync", cmd_flash_erase, |
| "<offset> <size>\n" |
| "\tErases EC flash asynchronously." }, |
| { "flashinfo", cmd_flash_info, |
| "\n\tPrints information on the EC flash." }, |
| { "flashpd", cmd_flash_pd, |
| "<dev_id> <port> <filename>\n" |
| "\tFlash commands over PD." }, |
| { "flashprotect", cmd_flash_protect, |
| "[now] [enable | disable]\n" |
| "\tPrints or sets EC flash protection state." }, |
| { "flashread", cmd_flash_read, |
| "<offset> <size> <outfile>\n" |
| "\tReads from EC flash to a file." }, |
| { "flashspiinfo", cmd_flash_spi_info, |
| "\n\tPrints information on EC SPI flash, if present." }, |
| { "flashwrite", cmd_flash_write, |
| "<offset> <infile>\n" |
| "\tWrites to EC flash from a file." }, |
| { "forcelidopen", cmd_force_lid_open, |
| "<enable>\n" |
| "\tForces the lid switch to open position." }, |
| { "fpcontext", cmd_fp_context, |
| "\n\tSets the fingerprint sensor context." }, |
| { "fpencstatus", cmd_fp_enc_status, |
| "\n\tPrints status of Fingerprint sensor encryption engine." }, |
| { "fpframe", cmd_fp_frame, |
| "\n\tRetrieve the finger image as a PGM image." }, |
| { "fpinfo", cmd_fp_info, |
| "\n\tPrints information about the Fingerprint sensor." }, |
| { "fpmode", cmd_fp_mode, |
| "[mode... [capture_type]]\n" |
| "\tConfigure/Read the fingerprint sensor current mode.\n" |
| "\tmode: capture|deepsleep|fingerdown|fingerup|enroll|match|\n" |
| "\t\treset|reset_sensor|maintenance\n" |
| "\tcapture_type: vendor|pattern0|pattern1|qual|test_reset" }, |
| { "fpseed", cmd_fp_seed, "\n\tSets the value of the TPM seed." }, |
| { "fpstats", cmd_fp_stats, |
| "\n\tPrints timing statisitcs relating to capture and matching." }, |
| { "fptemplate", cmd_fp_template, |
| "[<infile>|<index 0..2>]\n" |
| "\tAdd a template if <infile> is provided, else dump it." }, |
| { "gpioget", cmd_gpio_get, |
| "<GPIO name>\n" |
| "\tGet the value of GPIO signal." }, |
| { "gpioset", cmd_gpio_set, |
| "<GPIO name>\n" |
| "\tSet the value of GPIO signal." }, |
| { "hangdetect", cmd_hang_detect, |
| "reload|cancel|set_timeout <reboot_sec>|get_status|clear_status\n" |
| "\tConfigure the ap hang detect mechanism." }, |
| { "hello", cmd_hello, "\n\tChecks for basic communication with EC." }, |
| { "hibdelay", cmd_hibdelay, |
| "[sec]\n" |
| "\tSet the delay before going into hibernation." }, |
| { "hostevent", cmd_hostevent, "\n\tGet & set host event masks." }, |
| { "hostsleepstate", cmd_hostsleepstate, |
| "\n\tReport host sleep state to the EC." }, |
| { "i2cprotect", cmd_i2c_protect, |
| "<port> [status]\n" |
| "\tProtect EC's I2C bus." }, |
| { "i2cread", cmd_i2c_read, "\n\tRead I2C bus." }, |
| { "i2cspeed", cmd_i2c_speed, |
| "<port> [speed]\n" |
| "\tGet or set EC's I2C bus speed." }, |
| { "i2cwrite", cmd_i2c_write, "\n\tWrite I2C bus." }, |
| { "i2cxfer", cmd_i2c_xfer, |
| "<port> <peripheral_addr> <read_count> [write bytes...]\n" |
| "\tPerform I2C transfer on EC's I2C bus." }, |
| { "infopddev", cmd_pd_device_info, |
| "<port>\n" |
| "\tGet info about USB type-C accessory attached to port." }, |
| { "inventory", cmd_inventory, |
| "\n\tReturn the list of supported features." }, |
| { "kbfactorytest", cmd_keyboard_factory_test, |
| "\n\tScan out keyboard if any pins are shorted." }, |
| { "kbgetconfig", cmd_keyboard_get_config, |
| "\n\tGet keyboard Vivaldi configuration." }, |
| { "kbinfo", cmd_kbinfo, "\n\tDump keyboard matrix dimensions." }, |
| { "kbpress", cmd_kbpress, "\n\tSimulate key press." }, |
| { "keyconfig", cmd_keyconfig, |
| "get [<param>] | set [<param>> <value>]\n" |
| "\tConfigure keyboard scanning." }, |
| { "keyscan", cmd_keyscan, |
| "<beat_us> <filename>\n" |
| "\tTest low-level key scanning." }, |
| { "led", cmd_led, |
| "<name> <query | auto | off | <color> | <color>=<value>...>\n" |
| "\tSet the color of an LED or query brightness range." }, |
| { "lightbar", cmd_lightbar, |
| "[CMDS]\n" |
| "\tVarious lightbar control commands." }, |
| { "locatechip", cmd_locate_chip, |
| "<type> <index>\n" |
| "\tGet the addresses and ports of i2c connected and embedded chips." }, |
| { "memory_dump", cmd_memory_dump, |
| "[<address> [<size>]]\n" |
| "\tOutputs the memory dump in hexdump canonical format." }, |
| { "mkbpget", cmd_mkbp_get, |
| "<buttons|switches>\n" |
| "\tGet MKBP buttons/switches supported mask and current state." }, |
| { "mkbpwakemask", cmd_mkbp_wake_mask, |
| "<get|set> <event|hostevent> [mask]\n" |
| "\tGet or Set the MKBP event wake mask, or host event wake mask." }, |
| { "motionsense", cmd_motionsense, |
| "[CMDS]\n" |
| "\tVarious motion sense control commands." }, |
| { "nextevent", cmd_next_event, "\n\tGet the next pending MKBP event." }, |
| { "paniclog", cmd_panic_log, "\n\tPrints saved panic log." }, |
| { "panicinfo", cmd_panic_info, "\n\tPrints saved panic info." }, |
| { "pause_in_s5", cmd_s5, |
| "[on|off]\n" |
| "\tWhether or not the AP should pause in S5 on shutdown." }, |
| { "pchg", cmd_pchg, |
| "[<port>]\n" |
| "\tGet peripheral charge port count and status." }, |
| { "pdchipinfo", cmd_pd_chip_info, |
| "<port>\n" |
| "\tGet PD chip information." }, |
| { "pdcontrol", cmd_pd_control, |
| "[suspend|resume|reset|disable|on]\n" |
| "\tControls the PD chip." }, |
| { "pdctrace", cmd_pdc_trace, cmd_pdc_trace_usage }, |
| { "pdgetmode", cmd_pd_get_amode, |
| "<port>\n" |
| "\tGet All USB-PD alternate SVIDs and modes on <port>." }, |
| { "pdlog", cmd_pd_log, "\n\tPrints the PD event log entries." }, |
| { "pdsetmode", cmd_pd_set_amode, |
| "<port> <svid> <opos>\n" |
| "\tSet USB-PD alternate SVID and mode on <port>." }, |
| { "pdwritelog", cmd_pd_write_log, |
| "<type> <port>\n" |
| "\tWrites a PD event log of the given <type>." }, |
| { "port80flood", cmd_port_80_flood, |
| "\n\tRapidly write bytes to port 80." }, |
| { "port80read", cmd_port80_read, |
| "\n\tPrint history of port 80 write." }, |
| { "powerbtn_press", cmd_powerbtn_press, |
| "[<1st_del_ms>] [<1st_dur_ms>] [<2nd_del_ms>] [<2nd_dur_ms>]\n" |
| "\tSimulate up to 2 consecutive delayed power button presses." }, |
| { "powerinfo", cmd_power_info, |
| "\n\tPrints power-related information." }, |
| { "protoinfo", cmd_proto_info, |
| "\n\tPrints EC host protocol information." }, |
| { "pse", cmd_pse, "\n\tGet and set PoE PSE port power status." }, |
| { "pstoreinfo", cmd_pstore_info, |
| "\n\tPrints information on the EC host persistent storage." }, |
| { "pstoreread", cmd_pstore_read, |
| "<offset> <size> <outfile>\n" |
| "\tReads from EC host persistent storage to a file." }, |
| { "pstorewrite", cmd_pstore_write, |
| "<offset> <infile>\n" |
| "\tWrites to EC host persistent storage from a file." }, |
| { "pwmgetduty", cmd_pwm_get_duty, |
| "\n\tPrints the current 16 bit duty cycle for given PWM." }, |
| { "pwmgetfanrpm", cmd_pwm_get_fan_rpm, |
| "[<index> | all]\n" |
| "\tPrints current fan RPM." }, |
| { "pwmgetkblight", cmd_pwm_get_keyboard_backlight, |
| "\n\tPrints current keyboard backlight percent." }, |
| { "pwmgetnumfans", cmd_pwm_get_num_fans, |
| "\n\tPrints the number of fans present." }, |
| { "pwmsetduty", cmd_pwm_set_duty, |
| "\n\tSet 16 bit duty cycle of given PWM." }, |
| { "pwmsetfanrpm", cmd_pwm_set_fan_rpm, |
| "<targetrpm>\n" |
| "\tSet target fan RPM." }, |
| { "pwmsetkblight", cmd_pwm_set_keyboard_backlight, |
| "<percent>\n" |
| "\tSet keyboard backlight in percent." }, |
| { "rand", cmd_rand, |
| "<num_bytes>\n" |
| "\tgenerate <num_bytes> of random numbers." }, |
| { "reboot_ap_on_g3", cmd_reboot_ap_on_g3, |
| " [<delay>]\n" |
| "\tRequests that the EC will automatically reboot the AP after a\n" |
| "\tconfigurable number of seconds the next time we enter the G3\n" |
| "\tpower state." }, |
| { "reboot_ec", cmd_reboot_ec, |
| "<RO|RW|cold|hibernate|hibernate-clear-ap-off|disable-jump|cold-ap-off>\n" |
| "\t[at-shutdown|switch-slot|clear-ap-idle]\n" |
| "\tReboot EC to RO or RW" }, |
| { "rgbkbd", cmd_rgbkbd, |
| "...\n" |
| "\tSet/get RGB keyboard status, config, etc.." }, |
| { "rollbackinfo", cmd_rollback_info, |
| "\n\tPrint rollback block information." }, |
| { "rtcget", cmd_rtc_get, "\n\tPrint real-time clock." }, |
| { "rtcgetalarm", cmd_rtc_get_alarm, |
| "\n\tPrint # of seconds before real-time clock alarm goes off." }, |
| { "rtcset", cmd_rtc_set, |
| "<time>\n" |
| "\tSet real-time clock." }, |
| { "rtcsetalarm", cmd_rtc_set_alarm, |
| "<sec>\n" |
| "\tSet real-time clock alarm to go off in <sec> seconds." }, |
| { "rwhashpd", cmd_rw_hash_pd, |
| "<dev_id> <HASH[0] ... <HASH[4]>\n" |
| "\tSet entry in PD MCU's device rw_hash table." }, |
| { "rwsig", cmd_rwsig, |
| "<info|dump|action|status> ...\n" |
| "\tinfo: Get all info about rwsig.\n" |
| "\tdump: Show individual rwsig field.\n" |
| "\taction: Control the behavior of RWSIG task.\n" |
| "\tstatus: Run RW signature verification and get status." }, |
| { "rwsigaction", cmd_rwsig_action_legacy, |
| "(DEPRECATED; use \"rwsig action\")\n" |
| "\tControl the behavior of RWSIG task." }, |
| { "rwsigstatus", cmd_rwsig_status, |
| "(DEPRECATED; use \"rwsig status\"\n" |
| "\tRun RW signature verification and get status." }, |
| { "s0ix_counter", cmd_s0ix_counter, |
| "get|set\n" |
| "\tGet or reset s0ix counter." }, |
| { "sertest", cmd_serial_test, "\n\tSerial output test for COM2." }, |
| { "smartdischarge", cmd_smart_discharge, |
| "\n\tSet/Get smart discharge parameters." }, |
| { "stress", cmd_stress_test, |
| "[reboot] [help]\n" |
| "\tStress test the ec host command interface." }, |
| { "switches", cmd_switches, "\n\tPrints current EC switch positions" }, |
| { "slps0dbgsetalarm", cmd_set_alarm_slp_s0_dbg, |
| "<sec>\n" |
| "\tSet alarm to wake host in <sec> seconds, " |
| "PS: EC won't wake host if SLP_S0 is not asserted" }, |
| { "sleepsignaltransitions", cmd_sleep_signal_transitions, |
| "\n\tPrints current EC sleep_signal_transitions value" }, |
| { "sysinfo", cmd_sysinfo, |
| "[flags|reset_flags|firmware_copy]\n" |
| "\tDisplay system info." }, |
| { "tabletmode", cmd_tabletmode, |
| "[on | off | reset]\n" |
| "\tManually force tablet mode to on, off or reset." }, |
| { "temps", cmd_temperature, |
| "<sensorid>\n" |
| "\tPrint temperature and temperature ratio between fan_off and\n" |
| "\tfan_max values, which could be a fan speed if it's controlled\n" |
| "\tlinearly." }, |
| { "tempsinfo", cmd_temp_sensor_info, |
| "<sensorid>\n" |
| "\tPrint temperature sensor info." }, |
| { "test", cmd_test, |
| "result length [version]\n" |
| "\tFake a variety of responses, purely for testing purposes." }, |
| { "threadinfo", cmd_thread_info, |
| "\n\tDumps Zephyr thread tracking and info." }, |
| { "thermalget", cmd_thermal_get_threshold, |
| "<platform-specific args>\n" |
| "\tGet the threshold temperature values from the thermal engine." }, |
| { "thermalset", cmd_thermal_set_threshold, |
| "<platform-specific args>\n" |
| "\tSet the threshold temperature values for the thermal engine." }, |
| { "tmp006cal", cmd_tmp006cal, |
| "<tmp006_index> [params...]\n" |
| "\tGet/set TMP006 calibration." }, |
| { "tmp006raw", cmd_tmp006raw, |
| "<tmp006_index>\n" |
| "\tGet raw TMP006 data." }, |
| { "tpframeget", cmd_tp_frame_get, "\n\tGet touchpad frame data." }, |
| { "tpselftest", cmd_tp_self_test, "\n\tRun touchpad self test." }, |
| { "typeccontrol", cmd_typec_control, |
| "<port> <command>\n" |
| "\tControl USB PD policy." }, |
| { "typecdiscovery", cmd_typec_discovery, |
| "<port> <type>\n" |
| "\tGet discovery information for port and type." }, |
| { "typecstatus", cmd_typec_status, |
| "<port>\n" |
| "\tGet status information for port." }, |
| { "typecvdmresponse", cmd_typec_vdm_response, |
| "<port>\n" |
| "\tGet last VDM response for AP-requested VDM." }, |
| { "uptimeinfo", cmd_uptimeinfo, |
| "\n\tGet info about how long the EC has been running and the most\n" |
| "\trecent AP resets." }, |
| { "usbchargemode", cmd_usb_charge_set_mode, |
| "<port> <mode> [<inhibit_charge>]\n" |
| "\tSet USB charging mode." }, |
| { "usbmux", cmd_usb_mux, |
| "<mux>\n" |
| "\tSet USB mux switch state." }, |
| { "usbpd", cmd_usb_pd, |
| "<port> <auto | " |
| "[toggle|toggle-off|sink|source] [none|usb|dp|dock]\n" |
| "\t[dr_swap|pr_swap|vconn_swap]>\n" |
| "\tControl USB PD/type-C [deprecated]." }, |
| { "usbpddps", cmd_usb_pd_dps, |
| "[enable | disable | status]\n" |
| "\tEnable or disable dynamic pdo selection." }, |
| { "usbpdmuxinfo", cmd_usb_pd_mux_info, |
| "[tsv]\n" |
| "\tGet USB-C SS mux info.\n" |
| "\t tsv: Output as tab separated values. Columns are defined " |
| "as:\n" |
| "\t\t Port, USB enabled, DP enabled, Polarity, HPD IRQ, " |
| "HPD LVL." }, |
| { "usbpdportcaps", cmd_usb_pd_port_caps, |
| "[port]\n" |
| "\tGet port capabilities for a specified USB-C port." }, |
| { "usbpdpower", cmd_usb_pd_power, |
| "[port]\n" |
| "\tGet USB PD power information." }, |
| { "version", cmd_version, "\n\tPrints EC version." }, |
| { "waitevent", cmd_wait_event, |
| "<type> [timeout [command [command_args...]]]\n" |
| "\tWait for the MKBP event of type and display it.\n" |
| "\tOptionaly, run the command and wait for the mkbp event.\n" |
| "\tRun with no arguments for more information." }, |
| { "watchdoginfo", cmd_watchdog_info, |
| "[reset_stats]\n" |
| "\tGet watchdog info." }, |
| { "wireless", cmd_wireless, |
| "<flags> [<mask> [<suspend_flags> <suspend_mask>]]\n" |
| "\tEnable/disable WLAN/Bluetooth radio." }, |
| { "poe", cmd_poe, "<enabled>\n\tEnable/disable Power over Ethernet." }, |
| { NULL, NULL } |
| }; |
| |
| /** |
| * Find the command entry by name. |
| * |
| * @param name The name of the command to search for. |
| * @returns The command struct entry or nullptr if not found. |
| */ |
| const command *commands_find(const char *name) |
| { |
| const command *cmd; |
| for (cmd = commands; cmd->name; cmd++) { |
| if (!strcasecmp(name, cmd->name)) { |
| return cmd; |
| } |
| } |
| return nullptr; |
| } |
| |
| void print_help(const char *prog, int print_cmds) |
| { |
| printf("Usage: %s [--dev=n]" |
| " [--interface=dev|i2c|lpc] [--i2c_bus=n] [--device=vid:pid]" |
| " --verbose", |
| prog); |
| printf(" [--name=cros_ec|cros_fp|cros_pd|cros_scp|cros_ish] [--ascii] "); |
| printf("<command> [params]\n\n"); |
| printf(" --i2c_bus=n Specifies the number of an I2C bus to use. For\n" |
| " example, to use /dev/i2c-7, pass --i2c_bus=7.\n" |
| " Implies --interface=i2c.\n\n"); |
| printf(" --interface Specifies the interface.\n\n"); |
| printf(" --device Specifies USB endpoint by vendor ID and product\n" |
| " ID (e.g. 18d1:5022).\n\n"); |
| printf(" --verbose Print more messages.\n\n"); |
| if (print_cmds) { |
| const struct command *cmd; |
| puts(help_str); |
| for (cmd = commands; cmd->name != NULL; cmd++) { |
| printf(" %s ", cmd->name); |
| if (cmd->help != NULL) |
| puts(cmd->help); |
| else |
| puts(""); |
| } |
| |
| } else |
| printf("Use '%s help' to print a list of commands.\n", prog); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| const struct command *cmd; |
| int dev = 0; |
| int interfaces = COMM_ALL; |
| int i2c_bus = -1; |
| char device_name[41] = CROS_EC_DEV_NAME; |
| uint16_t vid = USB_VID_GOOGLE, pid = USB_PID_HAMMER; |
| int rv = -1; |
| int parse_error = 0; |
| char *e; |
| int i; |
| |
| BUILD_ASSERT(ARRAY_SIZE(lb_command_paramcount) == LIGHTBAR_NUM_CMDS); |
| |
| while ((i = getopt_long(argc, argv, "+v?", long_opts, NULL)) != -1) { |
| switch (i) { |
| case '?': |
| /* Unhandled option */ |
| parse_error = 1; |
| break; |
| |
| case OPT_DEV: |
| dev = strtoull(optarg, &e, 0); |
| if (!*optarg || (e && *e)) { |
| fprintf(stderr, "Invalid --dev\n"); |
| parse_error = 1; |
| } |
| break; |
| |
| case OPT_INTERFACE: |
| if (!strcasecmp(optarg, "dev")) { |
| interfaces = COMM_DEV; |
| } else if (!strcasecmp(optarg, "lpc")) { |
| interfaces = COMM_LPC; |
| } else if (!strcasecmp(optarg, "i2c")) { |
| interfaces = COMM_I2C; |
| } else if (!strcasecmp(optarg, "servo")) { |
| interfaces = COMM_SERVO; |
| } else { |
| fprintf(stderr, "Invalid --interface\n"); |
| parse_error = 1; |
| } |
| break; |
| case OPT_DEVICE: |
| if (parse_vidpid(optarg, &vid, &pid)) { |
| interfaces = COMM_USB; |
| } else { |
| fprintf(stderr, "Invalid --device\n"); |
| parse_error = 1; |
| } |
| break; |
| case OPT_NAME: |
| strncpy(device_name, optarg, 40); |
| device_name[40] = '\0'; |
| break; |
| case OPT_I2C_BUS: |
| i2c_bus = strtoull(optarg, &e, 0); |
| if (*optarg == '\0' || (e && *e != '\0') || |
| i2c_bus < 0) { |
| fprintf(stderr, "Invalid --i2c_bus\n"); |
| parse_error = 1; |
| } |
| break; |
| case OPT_ASCII: |
| ascii_mode = 1; |
| break; |
| case OPT_VERBOSE: |
| case 'v': |
| verbose = 1; |
| break; |
| } |
| } |
| |
| if (i2c_bus != -1) { |
| if (!(interfaces & COMM_I2C)) { |
| fprintf(stderr, |
| "--i2c_bus is specified, but --interface is set to something other than I2C\n"); |
| parse_error = 1; |
| } else { |
| interfaces = COMM_I2C; |
| } |
| } |
| |
| /* Must specify a command */ |
| if (!parse_error && optind == argc) |
| parse_error = 1; |
| |
| /* 'ectool help' prints help with commands */ |
| if (!parse_error && !strcasecmp(argv[optind], "help")) { |
| print_help(argv[0], 1); |
| exit(1); |
| } |
| |
| /* Handle sub-devices command offset */ |
| if (dev > 0 && dev < 4) { |
| set_command_offset(EC_CMD_PASSTHRU_OFFSET(dev)); |
| } else if (dev == 8) { |
| /* Special offset for Fingerprint MCU */ |
| strcpy(device_name, "cros_fp"); |
| } else if (dev != 0) { |
| fprintf(stderr, "Bad device number %d\n", dev); |
| parse_error = 1; |
| } |
| |
| if (parse_error) { |
| print_help(argv[0], 0); |
| exit(1); |
| } |
| |
| /* Prefer /dev method, which supports built-in mutex */ |
| if (!(interfaces & COMM_DEV) || comm_init_dev(device_name)) { |
| /* If dev is excluded or isn't supported, find alternative */ |
| |
| /* Lock is not needed for COMM_USB */ |
| if (!(interfaces & COMM_USB) && |
| acquire_gec_lock(GEC_LOCK_TIMEOUT_SECS) < 0) { |
| fprintf(stderr, "Could not acquire GEC lock.\n"); |
| exit(1); |
| } |
| if (interfaces == COMM_USB) { |
| if (comm_init_usb(vid, pid)) { |
| fprintf(stderr, "Couldn't find EC on USB.\n"); |
| goto out; |
| } |
| } else if (comm_init_alt(interfaces, device_name, i2c_bus)) { |
| fprintf(stderr, "Couldn't find EC\n"); |
| goto out; |
| } |
| } |
| |
| if (comm_init_buffer()) { |
| fprintf(stderr, "Couldn't initialize buffers\n"); |
| goto out; |
| } |
| |
| /* Handle commands */ |
| cmd = commands_find(argv[optind]); |
| if (cmd == nullptr) { |
| fprintf(stderr, "Unknown command '%s'\n\n", argv[optind]); |
| print_help(argv[0], 0); |
| goto out; |
| } |
| rv = cmd->handler(argc - optind, argv + optind); |
| |
| out: |
| release_gec_lock(); |
| |
| if (interfaces == COMM_USB) |
| comm_usb_exit(); |
| |
| /* Negative values from the command handler should be treated as errors |
| * return an exit code of 1. Some command handlers return 0 or the |
| * number of bytes received upon success. */ |
| return (rv < 0) ? 1 : 0; |
| } |