summaryrefslogtreecommitdiff
path: root/common/usb_pd_protocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usb_pd_protocol.c')
-rw-r--r--common/usb_pd_protocol.c127
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",