diff options
Diffstat (limited to 'common/usb_pd_protocol.c')
-rw-r--r-- | common/usb_pd_protocol.c | 127 |
1 files changed, 120 insertions, 7 deletions
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index e496e3e821..545eb36f8f 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -203,6 +203,7 @@ enum pd_states { PD_STATE_SNK_REQUESTED, PD_STATE_SNK_TRANSITION, PD_STATE_SNK_READY, + PD_STATE_VDM_COMM, #endif /* CONFIG_USB_PD_DUAL_ROLE */ PD_STATE_SRC_DISCONNECTED, @@ -432,6 +433,10 @@ static int send_request(int port, uint32_t rdo) return bit_len; } + +/* next Vendor Defined Message to send */ +static int vdo_count[PD_PORT_COUNT]; +static uint32_t vdo_data[PD_PORT_COUNT][7]; #endif /* CONFIG_USB_PD_DUAL_ROLE */ static int send_bist_cmd(int port) @@ -504,8 +509,13 @@ static void handle_vdm_request(int port, int cnt, uint32_t *payload) #ifdef CONFIG_USB_PD_CUSTOM_VDM int rlen; uint32_t *rdata; +#endif if (vid == USB_VID_GOOGLE) { +#ifdef CONFIG_USB_PD_DUAL_ROLE + vdo_count[port] = 0; /* Done */ +#endif +#ifdef CONFIG_USB_PD_CUSTOM_VDM rlen = pd_custom_vdm(port, cnt, payload, &rdata); if (rlen > 0) { uint16_t header = PD_HEADER(PD_DATA_VENDOR_DEF, @@ -513,9 +523,9 @@ static void handle_vdm_request(int port, int cnt, uint32_t *payload) rlen); send_validate_message(port, header, rlen, rdata); } +#endif return; } -#endif CPRINTF("Unhandled VDM VID %04x CMD %04x\n", vid, payload[0] & 0xFFFF); } @@ -836,7 +846,8 @@ static void execute_hard_reset(int port) { pd[port].msg_id = 0; #ifdef CONFIG_USB_PD_DUAL_ROLE - pd[port].task_state = pd[port].role == PD_ROLE_SINK ? + if (pd[port].task_state != PD_STATE_VDM_COMM) + pd[port].task_state = pd[port].role == PD_ROLE_SINK ? PD_STATE_SNK_DISCONNECTED : PD_STATE_SRC_DISCONNECTED; #else pd[port].task_state = PD_STATE_SRC_DISCONNECTED; @@ -1112,6 +1123,23 @@ void pd_task(void) } timeout = 100*MSEC; break; + case PD_STATE_VDM_COMM: + if (vdo_count[port] > 7) { /* TIMEOUT */ + vdo_count[port] = -EC_ERROR_TIMEOUT; + } else if (vdo_count[port] > 0) { + int len; + uint16_t header = PD_HEADER(PD_DATA_VENDOR_DEF, + pd[port].role, pd[port].msg_id, + vdo_count[port]); + len = send_validate_message(port, header, + vdo_count[port], + vdo_data[port]); + vdo_count[port] = 8; /* Transmitting */ + if (len < 0) + vdo_count[port] = -EC_ERROR_BUSY; + } + timeout = 500*MSEC; + break; #endif /* CONFIG_USB_PD_DUAL_ROLE */ case PD_STATE_HARD_RESET: send_hard_reset(port); @@ -1147,10 +1175,12 @@ void pd_task(void) #ifdef CONFIG_USB_PD_DUAL_ROLE if (pd[port].role == PD_ROLE_SINK && !pd_snk_is_vbus_provided(port)) { - /* Sink: detect disconnect by monitoring VBUS */ - pd[port].task_state = PD_STATE_SNK_DISCONNECTED; - /* set timeout small to reconnect fast */ - timeout = 5*MSEC; + if (pd[port].task_state != PD_STATE_VDM_COMM) { + /* Sink: detect disconnect by monitoring VBUS */ + pd[port].task_state = PD_STATE_SNK_DISCONNECTED; + /* set timeout small to reconnect fast */ + timeout = 5*MSEC; + } } #endif /* CONFIG_USB_PD_DUAL_ROLE */ } @@ -1169,6 +1199,87 @@ void pd_set_suspend(int port, int enable) task_wake(PORT_TO_TASK_ID(port)); } +static int hex8tou32(char *str, uint32_t *val) +{ + char *ptr = str; + uint32_t tmp = 0; + + while (*ptr) { + char c = *ptr++; + if (c >= '0' && c <= '9') + tmp = (tmp << 4) + (c - '0'); + else if (c >= 'A' && c <= 'F') + tmp = (tmp << 4) + (c - 'A' + 10); + else if (c >= 'a' && c <= 'f') + tmp = (tmp << 4) + (c - 'a' + 10); + else + return EC_ERROR_INVAL; + } + if (ptr != str + 8) + return EC_ERROR_INVAL; + *val = tmp; + return EC_SUCCESS; +} + +static int remote_flashing(int argc, char **argv) +{ + int port; + char *e; + static int flash_offset[PD_PORT_COUNT]; + + if (argc < 4) + return EC_ERROR_PARAM_COUNT; + + port = strtoi(argv[1], &e, 10); + if (*e || port >= PD_PORT_COUNT) + return EC_ERROR_PARAM2; + + if (!strcasecmp(argv[3], "erase")) { + vdo_data[port][0] = VDO(USB_VID_GOOGLE, VDO_CMD_FLASH_ERASE); + vdo_count[port] = 1; + flash_offset[port] = 0; + ccprintf("ERASE ..."); + } else if (!strcasecmp(argv[3], "reboot")) { + vdo_data[port][0] = VDO(USB_VID_GOOGLE, VDO_CMD_REBOOT); + vdo_count[port] = 1; + ccprintf("REBOOT ..."); + } else if (!strcasecmp(argv[3], "hash")) { + int i; + for (i = 4; i < argc; i++) + if (hex8tou32(argv[i], vdo_data[port] + i - 3)) + return EC_ERROR_INVAL; + vdo_data[port][0] = VDO(USB_VID_GOOGLE, VDO_CMD_FLASH_HASH); + vdo_count[port] = argc - 3; + ccprintf("HASH ..."); + } else if (!strcasecmp(argv[3], "rw_hash")) { + vdo_data[port][0] = VDO(USB_VID_GOOGLE, VDO_CMD_RW_HASH); + vdo_count[port] = 1; + ccprintf("RW HASH..."); + } else if (!strcasecmp(argv[3], "version")) { + vdo_data[port][0] = VDO(USB_VID_GOOGLE, VDO_CMD_VERSION); + vdo_count[port] = 1; + ccprintf("VERSION..."); + } else { + int i; + for (i = 3; i < argc; i++) + if (hex8tou32(argv[i], vdo_data[port] + i - 2)) + return EC_ERROR_INVAL; + vdo_data[port][0] = VDO(USB_VID_GOOGLE, VDO_CMD_FLASH_WRITE); + vdo_count[port] = argc - 2; + ccprintf("WRITE %d @%04x ...", (argc - 3) * 4, + flash_offset[port]); + flash_offset[port] += (argc - 3) * 4; + } + pd[port].task_state = PD_STATE_VDM_COMM; + task_wake(PORT_TO_TASK_ID(port)); + + /* Wait until VDO is done */ + while (vdo_count[port] > 0) + task_wait_event(100*MSEC); + ccprintf("DONE\n"); + return EC_SUCCESS; +} + void pd_request_source_voltage(int port, int mv) { pd_set_max_voltage(mv); @@ -1264,11 +1375,13 @@ static int command_pd(int argc, char **argv) else return EC_ERROR_PARAM3; } + } else if (!strncasecmp(argv[2], "flash", 4)) { + return remote_flashing(argc, argv); } else if (!strncasecmp(argv[2], "state", 5)) { const char * const state_names[] = { "DISABLED", "SUSPENDED", "SNK_DISCONNECTED", "SNK_DISCOVERY", "SNK_REQUESTED", - "SNK_TRANSITION", "SNK_READY", + "SNK_TRANSITION", "SNK_READY", "VDM_COMM", "SRC_DISCONNECTED", "SRC_DISCOVERY", "SRC_NEGOCIATE", "SRC_ACCEPTED", "SRC_TRANSITION", "SRC_READY", "HARD_RESET", "BIST", |