From 15391709fd964e23d91deaf3db832a9d83da1ea5 Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Sat, 20 Dec 2014 18:03:47 -0800 Subject: twinkie: add simple text tracing mechanism When using the Twinkie dongle without a protocol decoder on the host, add a simple text tracing mechanism, so the user can get the timestamped traces of the packets on the wire (in a best effort fashion). Signed-off-by: Vincent Palatin BRANCH=none BUG=none TEST=On Twinkie command-line, type "tw trace on" then plug a DingDong to Samus through Twinkie and see the PD message traces on the console. Change-Id: I4fa35d6783cc6279c95209c86f37e6d717de7301 Reviewed-on: https://chromium-review.googlesource.com/237222 Reviewed-by: Alec Berg Reviewed-by: Todd Broch Trybot-Ready: Vincent Palatin Tested-by: Vincent Palatin Commit-Queue: Vincent Palatin --- board/twinkie/board.h | 12 +- board/twinkie/build.mk | 2 +- board/twinkie/injector.c | 34 ++++++ board/twinkie/injector.h | 9 ++ board/twinkie/simpletrace.c | 276 ++++++++++++++++++++++++++++++++++++++++++ board/twinkie/sniffer.c | 37 +++++- board/twinkie/usb_pd_config.h | 7 +- 7 files changed, 366 insertions(+), 11 deletions(-) create mode 100644 board/twinkie/simpletrace.c (limited to 'board') diff --git a/board/twinkie/board.h b/board/twinkie/board.h index b748104ad2..992eeb83f1 100644 --- a/board/twinkie/board.h +++ b/board/twinkie/board.h @@ -27,6 +27,8 @@ #ifndef HAS_TASK_PD /* PD sniffer mode */ #undef CONFIG_DMA_DEFAULT_HANDLERS #define CONFIG_USB_PD_TX_PHY_ONLY +/* override the comparator interrupt handler */ +#undef CONFIG_USB_PD_RX_COMP_IRQ #endif #define CONFIG_ADC @@ -53,9 +55,17 @@ #ifndef __ASSEMBLER__ +void sniffer_init(void); + int wait_packet(int pol, uint32_t min_edges, uint32_t timeout_us); -void recording_enable(uint8_t mask); +int expect_packet(int pol, uint8_t cmd, uint32_t timeout_us); + +uint8_t recording_enable(uint8_t mask); + +void trace_packets(void); + +void set_trace_mode(int mode); /* Timer selection */ #define TIM_CLOCK_MSB 3 diff --git a/board/twinkie/build.mk b/board/twinkie/build.mk index ca503a7095..8179cbaaf3 100644 --- a/board/twinkie/build.mk +++ b/board/twinkie/build.mk @@ -11,4 +11,4 @@ CHIP_FAMILY:=stm32f0 CHIP_VARIANT:=stm32f07x board-y=board.o usb_pd_policy.o -board-$(HAS_TASK_SNIFFER)+=sniffer.o injector.o +board-$(HAS_TASK_SNIFFER)+=sniffer.o injector.o simpletrace.o diff --git a/board/twinkie/injector.c b/board/twinkie/injector.c index 916bc1c65e..6c5116c9e6 100644 --- a/board/twinkie/injector.c +++ b/board/twinkie/injector.c @@ -189,6 +189,13 @@ static void fsm_wait(uint32_t w) wait_packet(inj_polarity, min_edges, timeout_ms * 1000); } +static void fsm_expect(uint32_t w) +{ + uint32_t timeout_ms = INJ_ARG0(w); + uint8_t cmd = INJ_ARG2(w); + + expect_packet(inj_polarity, cmd, timeout_ms * 1000); +} static void fsm_get(uint32_t w) { int store_idx = INJ_ARG0(w); @@ -243,6 +250,9 @@ static void fsm_set(uint32_t w) case INJ_SET_POLARITY: inj_polarity = guess_polarity(val); break; + case INJ_SET_TRACE: + set_trace_mode(val); + break; default: /* Do nothing */ break; @@ -278,6 +288,9 @@ static int fsm_run(int index) case INJ_CMD_JUMP: index = INJ_ARG0(w); continue; /* do not increment index */ + case INJ_CMD_EXPCT: + fsm_expect(w); + break; case INJ_CMD_NOP: default: /* Do nothing */ @@ -502,6 +515,25 @@ static int cmd_sink(int argc, char **argv) return EC_SUCCESS; } +static int cmd_trace(int argc, char **argv) +{ + if (argc < 1) + return EC_ERROR_PARAM_COUNT; + + if (!strcasecmp(argv[0], "on") || + !strcasecmp(argv[0], "1")) + set_trace_mode(TRACE_MODE_ON); + else if (!strcasecmp(argv[0], "raw")) + set_trace_mode(TRACE_MODE_RAW); + else if (!strcasecmp(argv[0], "off") || + !strcasecmp(argv[0], "0")) + set_trace_mode(TRACE_MODE_OFF); + else + return EC_ERROR_PARAM2; + + return EC_SUCCESS; +} + static int command_tw(int argc, char **argv) { if (!strcasecmp(argv[1], "send")) @@ -518,6 +550,8 @@ static int command_tw(int argc, char **argv) return cmd_resistor(argc - 2, argv + 2); else if (!strcasecmp(argv[1], "sink")) return cmd_sink(argc - 2, argv + 2); + else if (!strcasecmp(argv[1], "trace")) + return cmd_trace(argc - 2, argv + 2); else if (!strcasecmp(argv[1], "txclock")) return cmd_tx_clock(argc - 2, argv + 2); else if (!strncasecmp(argv[1], "rxthresh", 8)) diff --git a/board/twinkie/injector.h b/board/twinkie/injector.h index 0e360a2fdc..11c3c095f2 100644 --- a/board/twinkie/injector.h +++ b/board/twinkie/injector.h @@ -39,6 +39,8 @@ enum inj_cmd { INJ_CMD_GET = 0x5, /* Get parameter arg1 (INJ_GET_x) at index arg0 */ INJ_CMD_SET = 0x6, /* Set parameter arg1 (INJ_SET_x) with arg0 */ INJ_CMD_JUMP = 0x8, /* Jump to index (as arg0) */ + INJ_CMD_EXPCT = 0xC, /* Expect a packet with command arg2 */ + /* and timeout after arg0 ms */ INJ_CMD_NOP = 0xF, /* No-Operation */ }; @@ -49,6 +51,7 @@ enum inj_set { INJ_SET_TX_SPEED = 3, /* TX frequency is arg0 kHz */ INJ_SET_RX_THRESH = 4, /* RX voltage threshold is arg0 mV */ INJ_SET_POLARITY = 5, /* Polarity for other operations (INJ_POL_CC) */ + INJ_SET_TRACE = 6, /* Text packet trace on/raw/off */ }; enum inj_get { @@ -73,6 +76,12 @@ enum inj_pol { INJ_POL_AUTO = 0xffff, }; +enum trace_mode { + TRACE_MODE_OFF = 0, + TRACE_MODE_RAW = 1, + TRACE_MODE_ON = 2, +}; + /* Number of words in the FSM command/data buffer */ #define INJ_CMD_COUNT 128 diff --git a/board/twinkie/simpletrace.c b/board/twinkie/simpletrace.c new file mode 100644 index 0000000000..f3ff5d3487 --- /dev/null +++ b/board/twinkie/simpletrace.c @@ -0,0 +1,276 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "adc.h" +#include "common.h" +#include "console.h" +#include "dma.h" +#include "gpio.h" +#include "hooks.h" +#include "hwtimer.h" +#include "injector.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "timer.h" +#include "usb_pd.h" +#include "usb_pd_config.h" +#include "util.h" + +/* PD packet text tracing state : TRACE_MODE_OFF/RAW/ON */ +int trace_mode; + +/* The FSM is waiting for the following command (0 == None) */ +uint8_t expected_cmd; + +static const char * const ctrl_msg_name[] = { + [0] = "RSVD-C0", + [PD_CTRL_GOOD_CRC] = "GOODCRC", + [PD_CTRL_GOTO_MIN] = "GOTOMIN", + [PD_CTRL_ACCEPT] = "ACCEPT", + [PD_CTRL_REJECT] = "REJECT", + [PD_CTRL_PING] = "PING", + [PD_CTRL_PS_RDY] = "PSRDY", + [PD_CTRL_GET_SOURCE_CAP] = "GSRCCAP", + [PD_CTRL_GET_SINK_CAP] = "GSNKCAP", + [PD_CTRL_DR_SWAP] = "DRSWAP", + [PD_CTRL_PR_SWAP] = "PRSWAP", + [PD_CTRL_VCONN_SWAP] = "VCONNSW", + [PD_CTRL_WAIT] = "WAIT", + [PD_CTRL_SOFT_RESET] = "SFT-RST", + [14] = "RSVD-C14", + [15] = "RSVD-C15", +}; + +static const char * const data_msg_name[] = { + [0] = "RSVD-D0", + [PD_DATA_SOURCE_CAP] = "SRCCAP", + [PD_DATA_REQUEST] = "REQUEST", + [PD_DATA_BIST] = "BIST", + [PD_DATA_SINK_CAP] = "SNKCAP", + /* 5-14 Reserved */ + [PD_DATA_VENDOR_DEF] = "VDM", +}; + +static const char * const svdm_cmd_name[] = { + [CMD_DISCOVER_IDENT] = "DISCID", + [CMD_DISCOVER_SVID] = "DISCSVID", + [CMD_DISCOVER_MODES] = "DISCMODE", + [CMD_ENTER_MODE] = "ENTER", + [CMD_EXIT_MODE] = "EXIT", + [CMD_ATTENTION] = "ATTN", + [CMD_DP_STATUS] = "DPSTAT", + [CMD_DP_CONFIG] = "DPCFG", +}; + +static const char * const svdm_cmdt_name[] = { + [CMDT_INIT] = "INI", + [CMDT_RSP_ACK] = "ACK", + [CMDT_RSP_NAK] = "NAK", + [CMDT_RSP_BUSY] = "BSY", +}; + +static void print_pdo(uint32_t word) +{ + if ((word & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) + ccprintf(" %dmV/%dmW", ((word>>10)&0x3ff)*50, + (word&0x3ff)*250); + else + ccprintf(" %dmV/%dmA", ((word>>10)&0x3ff)*50, + (word&0x3ff)*10); +} + +static void print_rdo(uint32_t word) +{ + ccprintf("{%d} %08x", RDO_POS(word), word); +} + +static void print_vdo(int idx, uint32_t word) +{ + if (idx == 0 && (word & VDO_SVDM_TYPE)) { + const char *cmd = svdm_cmd_name[PD_VDO_CMD(word)]; + const char *cmdt = svdm_cmdt_name[PD_VDO_CMDT(word)]; + uint16_t vid = PD_VDO_VID(word); + if (!cmd) + cmd = "????"; + ccprintf(" V%04x:%s,%s:%08x", vid, cmd, cmdt, word); + } else { + ccprintf(" %08x", word); + } +} + +static void print_packet(int head, uint32_t *payload) +{ + int i; + int cnt = PD_HEADER_CNT(head); + int typ = PD_HEADER_TYPE(head); + int id = PD_HEADER_ID(head); + const char *name; + const char *prole; + + if (trace_mode == TRACE_MODE_RAW) { + ccprintf("%T[%04x]", head); + for (i = 0; i < cnt; i++) + ccprintf(" %08x", payload[i]); + ccputs("\n"); + return; + } + name = cnt ? data_msg_name[typ] : ctrl_msg_name[typ]; + prole = head & (PD_ROLE_SOURCE << 8) ? "SRC" : "SNK"; + ccprintf("%T %s/%d [%04x]%s", prole, id, head, name); + if (!cnt) { /* Control message : we are done */ + ccputs("\n"); + return; + } + /* Print payload for data message */ + for (i = 0; i < cnt; i++) + switch (typ) { + case PD_DATA_SOURCE_CAP: + case PD_DATA_SINK_CAP: + print_pdo(payload[i]); + break; + case PD_DATA_REQUEST: + print_rdo(payload[i]); + break; + case PD_DATA_BIST: + ccprintf("mode %d cnt %04x", payload[i] >> 28, + payload[i] & 0xffff); + break; + case PD_DATA_VENDOR_DEF: + print_vdo(i, payload[i]); + break; + default: + ccprintf(" %08x", payload[i]); + } + ccputs("\n"); +} + +static void print_error(enum pd_errors err) +{ + if (err == PD_ERR_INVAL) + ccprintf("%T TMOUT\n"); + else if (err == PD_ERR_HARD_RESET) + ccprintf("%T HARD-RST\n"); + else if (err == -5) + ccprintf("%T SOP*\n"); + else + ccprintf("ERR %d\n", err); +} + +/* keep track of RX edge timing in order to trigger receive */ +static timestamp_t rx_edge_ts[2][PD_RX_TRANSITION_COUNT]; +static int rx_edge_ts_idx[2]; + +void rx_event(void) +{ + int pending, i; + int next_idx; + pending = STM32_EXTI_PR; + + /* Iterate over the 2 CC lines */ + for (i = 0; i < 2; i++) { + if (pending & (1 << (21 + i))) { + rx_edge_ts[i][rx_edge_ts_idx[i]].val = get_time().val; + next_idx = (rx_edge_ts_idx[i] == + PD_RX_TRANSITION_COUNT - 1) ? + 0 : rx_edge_ts_idx[i] + 1; + + /* + * If we have seen enough edges in a certain amount of + * time, then trigger RX start. + */ + if ((rx_edge_ts[i][rx_edge_ts_idx[i]].val - + rx_edge_ts[i][next_idx].val) + < PD_RX_TRANSITION_WINDOW) { + /* acquire the message only on the active CC */ + STM32_COMP_CSR &= ~(i ? STM32_COMP_CMP1EN + : STM32_COMP_CMP2EN); + /* start sampling */ + pd_rx_start(0); + /* + * ignore the comparator IRQ until we are done + * with current message + */ + pd_rx_disable_monitoring(0); + /* trigger the analysis in the task */ + task_set_event(TASK_ID_SNIFFER, 1 << i, 0); + /* start reception only one CC line */ + break; + } else { + /* do not trigger RX start, just clear int */ + STM32_EXTI_PR = EXTI_COMP_MASK(0); + } + rx_edge_ts_idx[i] = next_idx; + } + } +} +DECLARE_IRQ(STM32_IRQ_COMP, rx_event, 1); + +void trace_packets(void) +{ + int head; + uint32_t payload[7]; + + /* Disable sniffer DMA configuration */ + dma_disable(STM32_DMAC_CH6); + dma_disable(STM32_DMAC_CH7); + task_disable_irq(STM32_IRQ_DMA_CHANNEL_4_7); + /* remove TIM1 CH1/2/3 DMA remapping */ + STM32_SYSCFG_CFGR1 &= ~(1 << 28); + + /* "classical" PD RX configuration */ + pd_hw_init_rx(0); + pd_select_polarity(0, 0); + /* detect messages on both CCx lines */ + STM32_COMP_CSR |= STM32_COMP_CMP2EN | STM32_COMP_CMP1EN; + /* Enable the RX interrupts */ + pd_rx_enable_monitoring(0); + + while (1) { + task_wait_event(-1); + if (trace_mode == TRACE_MODE_OFF) + break; + /* incoming packet processing */ + head = pd_analyze_rx(0, payload); + pd_rx_complete(0); + /* re-enabled detection on both CCx lines */ + STM32_COMP_CSR |= STM32_COMP_CMP2EN | STM32_COMP_CMP1EN; + pd_rx_enable_monitoring(0); + /* print the last packet content */ + if (head > 0) + print_packet(head, payload); + else + print_error(head); + if (head > 0 && expected_cmd == PD_HEADER_TYPE(head)) + task_wake(TASK_ID_CONSOLE); + } + + task_disable_irq(STM32_IRQ_COMP); + /* Disable tracer DMA configuration */ + dma_disable(STM32_DMAC_CH2); + /* Put back : sniffer RX hardware configuration */ + sniffer_init(); +} + +int expect_packet(int pol, uint8_t cmd, uint32_t timeout_us) +{ + uint32_t evt; + + expected_cmd = cmd; + evt = task_wait_event(timeout_us); + + return !(evt == TASK_EVENT_TIMER); +} + +void set_trace_mode(int mode) +{ + /* No change */ + if (mode == trace_mode) + return; + + trace_mode = mode; + /* kick the task to take into account the new value */ + task_wake(TASK_ID_SNIFFER); +} diff --git a/board/twinkie/sniffer.c b/board/twinkie/sniffer.c index f08bd8b68b..dfbf6d131d 100644 --- a/board/twinkie/sniffer.c +++ b/board/twinkie/sniffer.c @@ -11,6 +11,7 @@ #include "gpio.h" #include "hwtimer.h" #include "hooks.h" +#include "injector.h" #include "link_defs.h" #include "registers.h" #include "task.h" @@ -29,6 +30,9 @@ /* Task event for the USB transfer interrupt */ #define USB_EVENTS TASK_EVENT_CUSTOM(3) +/* Bitmap of enabled capture channels : CC1+CC2 by default */ +static uint8_t channel_mask = 0x3; + /* edge timing samples */ static uint8_t samples[2][RX_COUNT]; /* bitmap of the samples sub-buffer filled with DMA data */ @@ -233,7 +237,7 @@ static void rx_timer_init(int tim_id, timer_ctlr_t *tim, int ch_idx, int up_idx) tim->sr = 0; } -static void sniffer_init(void) +void sniffer_init(void) { /* remap TIM1 CH1/2/3 to DMA channel 6 */ STM32_SYSCFG_CFGR1 |= 1 << 28; @@ -255,8 +259,6 @@ static void sniffer_init(void) STM32_COMP_CMP2INSEL_VREF12 | STM32_COMP_CMP2OUTSEL_TIM2_IC4 | STM32_COMP_CMP2HYST_HI; - ccprintf("Sniffer initialized\n"); - /* start sampling the edges on the CC lines using the RX timers */ dma_start_rx(&dma_tim_cc1, RX_COUNT, samples[0]); @@ -268,6 +270,9 @@ static void sniffer_init(void) } DECLARE_HOOK(HOOK_INIT, sniffer_init, HOOK_PRIO_DEFAULT); +/* state of the simple text tracer */ +extern int trace_mode; + /* Task to post-process the samples and copy them the USB endpoint buffer */ void sniffer_task(void) { @@ -296,6 +301,12 @@ void sniffer_task(void) atomic_clear((uint32_t *)&filled_dma, 1 << d); } led_reset_record(); + + if (trace_mode != TRACE_MODE_OFF) { + uint8_t curr = recording_enable(0); + trace_packets(); + recording_enable(curr); + } } } @@ -333,9 +344,25 @@ int wait_packet(int pol, uint32_t min_edges, uint32_t timeout_us) return (__hw_clock_source_read() - t0 > timeout_us); } -void recording_enable(uint8_t mask) +uint8_t recording_enable(uint8_t new_mask) { - /* TODO implement */ + uint8_t old_mask = channel_mask; + uint8_t diff = channel_mask ^ new_mask; + /* start/stop RX timers according to the channel mask */ + if (diff & 1) { + if (new_mask & 1) + STM32_TIM_CR1(TIM_RX1) |= 1; + else + STM32_TIM_CR1(TIM_RX1) &= ~1; + } + if (diff & 2) { + if (new_mask & 2) + STM32_TIM_CR1(TIM_RX2) |= 1; + else + STM32_TIM_CR1(TIM_RX2) &= ~1; + } + channel_mask = new_mask; + return old_mask; } static void sniffer_sysjump(void) diff --git a/board/twinkie/usb_pd_config.h b/board/twinkie/usb_pd_config.h index 466cc40905..6a5e18b128 100644 --- a/board/twinkie/usb_pd_config.h +++ b/board/twinkie/usb_pd_config.h @@ -167,11 +167,10 @@ static inline void pd_set_host_mode(int port, int enable) static inline void pd_config_init(int port, uint8_t power_role) { - /* - * Set CC pull resistors, and charge_en and vbus_en GPIOs to match - * the initial role. - */ +#ifndef CONFIG_USB_PD_TX_PHY_ONLY + /* Set CC pull resistors */ pd_set_host_mode(port, power_role); +#endif /* CONFIG_USB_PD_TX_PHY_ONLY */ /* Initialize TX pins and put them in Hi-Z */ pd_tx_init(); -- cgit v1.2.1