/* Copyright 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 "atomic.h" #include "clock.h" #include "common.h" #include "console.h" #include "dma.h" #include "gpio.h" #include "hwtimer.h" #include "hooks.h" #include "injector.h" #include "link_defs.h" #include "registers.h" #include "task.h" #include "timer.h" #include "usb_descriptor.h" #include "usb_hw.h" #include "util.h" #include "ina2xx.h" /* Size of one USB packet buffer */ #define EP_BUF_SIZE 64 #define EP_PACKET_HEADER_SIZE 4 /* Size of the payload (packet minus the header) */ #define EP_PAYLOAD_SIZE (EP_BUF_SIZE - EP_PACKET_HEADER_SIZE) /* Buffer enough to avoid overflowing due to USB latencies on both sides */ #define RX_COUNT (16 * EP_PAYLOAD_SIZE) /* Task event for the USB transfer interrupt */ #define USB_EVENT TASK_EVENT_CUSTOM_BIT(0) /* 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 */ static volatile uint32_t filled_dma; /* timestamps of the beginning of DMA buffers */ static uint16_t sample_tstamp[4]; /* sequence number of the beginning of DMA buffers */ static uint16_t sample_seq[4]; /* Bulk endpoint double buffer */ static usb_uint ep_buf[2][EP_BUF_SIZE / 2] __usb_ram; /* USB Buffers not used, ready to be filled */ static volatile uint32_t free_usb = 3; static inline void led_set_activity(int ch) { static int accumul[2]; static uint32_t last_ts[2]; uint32_t now = __hw_clock_source_read(); int delta = now - last_ts[ch]; last_ts[ch] = now; accumul[ch] = MAX(0, accumul[ch] + (30000 - delta)); gpio_set_level(ch ? GPIO_LED_R_L : GPIO_LED_G_L, !accumul[ch]); } static inline void led_set_record(void) { gpio_set_level(GPIO_LED_B_L, 0); } static inline void led_reset_record(void) { gpio_set_level(GPIO_LED_B_L, 1); } /* USB descriptors */ const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_VENDOR) = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = USB_IFACE_VENDOR, .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC, .bInterfaceProtocol = 0, .iInterface = USB_STR_SNIFFER, }; const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_VENDOR, USB_EP_SNIFFER) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x80 | USB_EP_SNIFFER, .bmAttributes = 0x02 /* Bulk IN */, .wMaxPacketSize = USB_MAX_PACKET_SIZE, .bInterval = 1 }; /* USB callbacks */ static void ep_tx(void) { static int b; /* current buffer index */ if (btable_ep[USB_EP_SNIFFER].tx_count) { /* we have transmitted the previous buffer, toggle it */ free_usb |= 1 << b; b = b ? 0 : 1; btable_ep[USB_EP_SNIFFER].tx_addr = usb_sram_addr(ep_buf[b]); } /* re-enable data transmission if we have available data */ btable_ep[USB_EP_SNIFFER].tx_count = (free_usb & (1<> 12) & 0x1) void tim_rx1_handler(uint32_t stat) { stm32_dma_regs_t *dma = STM32_DMA1_REGS; int idx = !(stat & STM32_DMA_ISR_HTIF(DMAC_TIM_RX1)); uint32_t mask = idx ? 0xFF00 : 0x00FF; uint32_t next = idx ? 0x0001 : 0x0100; sample_tstamp[idx] = __hw_clock_source_read(); sample_seq[idx] = ((seq++ << 3) & 0x0ff8) | (SNIFFER_CHANNEL_CC1<<12); if (filled_dma & next) { oflow++; sample_seq[idx] |= 0x8000; } else { led_set_record(); } filled_dma |= mask; dma->ifcr = STM32_DMA_ISR_ALL(DMAC_TIM_RX1); led_set_activity(0); } void tim_rx2_handler(uint32_t stat) { stm32_dma_regs_t *dma = STM32_DMA1_REGS; int idx = !(stat & STM32_DMA_ISR_HTIF(DMAC_TIM_RX2)); uint32_t mask = idx ? 0xFF000000 : 0x00FF0000; uint32_t next = idx ? 0x00010000 : 0x01000000; idx += 2; sample_tstamp[idx] = __hw_clock_source_read(); sample_seq[idx] = ((seq++ << 3) & 0x0ff8) | (SNIFFER_CHANNEL_CC2<<12); if (filled_dma & next) { oflow++; sample_seq[idx] |= 0x8000; } else { led_set_record(); } filled_dma |= mask; dma->ifcr = STM32_DMA_ISR_ALL(DMAC_TIM_RX2); led_set_activity(1); } void tim_dma_handler(void) { stm32_dma_regs_t *dma = STM32_DMA1_REGS; uint32_t stat = dma->isr & (STM32_DMA_ISR_HTIF(DMAC_TIM_RX1) | STM32_DMA_ISR_TCIF(DMAC_TIM_RX1) | STM32_DMA_ISR_HTIF(DMAC_TIM_RX2) | STM32_DMA_ISR_TCIF(DMAC_TIM_RX2)); if (stat & STM32_DMA_ISR_ALL(DMAC_TIM_RX2)) tim_rx2_handler(stat); else tim_rx1_handler(stat); /* time to process the samples */ task_set_event(TASK_ID_SNIFFER, USB_EVENT); } DECLARE_IRQ(STM32_IRQ_DMA_CHANNEL_4_7, tim_dma_handler, 1); static void rx_timer_init(int tim_id, timer_ctlr_t *tim, int ch_idx, int up_idx) { int bit_idx = 8 * ((ch_idx - 1) % 2); /* --- set counter for RX timing : 2.4Mhz rate, free-running --- */ __hw_timer_enable_clock(tim_id, 1); /* Timer configuration */ tim->cr1 = 0x0004; tim->cr2 = 0x0000; /* Auto-reload value : 8-bit free running counter */ tim->arr = 0xFF; /* Counter reloading event after 106us */ tim->ccr[1] = 0xFF; /* Timer ICx input configuration */ if (ch_idx <= 2) tim->ccmr1 = 1 << bit_idx; else tim->ccmr2 = 1 << bit_idx; tim->ccer = 0xB << ((ch_idx - 1) * 4); /* TODO: add input filtering */ /* configure DMA request on CCRx update and overflow/update event */ tim->dier = (1 << (8 + ch_idx)) | (1 << (8 + up_idx)); /* set prescaler to /26 (F=2.4Mhz, T=0.4us) */ tim->psc = RX_CLOCK_DIV; /* Reload the pre-scaler and reset the counter, clear CCRx */ tim->egr = 0x001F; /* clear update event from reloading */ tim->sr = 0; } void sniffer_init(void) { /* remap TIM1 CH1/2/3 to DMA channel 6 */ STM32_SYSCFG_CFGR1 |= BIT(28); /* TIM1 CH1 for CC1 RX */ rx_timer_init(TIM_RX1, (void *)STM32_TIM_BASE(TIM_RX1), TIM_RX1_CCR_IDX, 2); /* TIM3 CH4 for CC2 RX */ rx_timer_init(TIM_RX2, (void *)STM32_TIM_BASE(TIM_RX2), TIM_RX2_CCR_IDX, 2); /* turn on COMP/SYSCFG */ STM32_RCC_APB2ENR |= BIT(0); STM32_COMP_CSR = STM32_COMP_CMP1EN | STM32_COMP_CMP1MODE_HSPEED | STM32_COMP_CMP1INSEL_VREF12 | STM32_COMP_CMP1OUTSEL_TIM1_IC1 | STM32_COMP_CMP1HYST_HI | STM32_COMP_CMP2EN | STM32_COMP_CMP2MODE_HSPEED | STM32_COMP_CMP2INSEL_VREF12 | STM32_COMP_CMP2OUTSEL_TIM2_IC4 | STM32_COMP_CMP2HYST_HI; /* start sampling the edges on the CC lines using the RX timers */ dma_start_rx(&dma_tim_cc1, RX_COUNT, samples[0]); dma_start_rx(&dma_tim_cc2, RX_COUNT, samples[1]); task_enable_irq(STM32_IRQ_DMA_CHANNEL_4_7); /* start RX timers on CC1 and CC2 */ STM32_TIM_CR1(TIM_RX1) |= 1; STM32_TIM_CR1(TIM_RX2) |= 1; } 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) { int u = 0; /* current USB buffer index */ int d = 0; /* current DMA buffer index */ int off = 0; /* DMA buffer offset */ while (1) { /* Wait for a new buffer of samples or a new USB free buffer */ task_wait_event(-1); /* send the available samples over USB if we have a buffer*/ while (filled_dma && free_usb) { while (!(filled_dma & BIT(d))) { d = (d + 1) & 31; off += EP_PAYLOAD_SIZE; if (off >= RX_COUNT) off = 0; } ep_buf[u][0] = sample_seq[d >> 3] | (d & 7); ep_buf[u][1] = sample_tstamp[d >> 3]; memcpy_to_usbram( ((void *)usb_sram_addr(ep_buf[u] + (EP_PACKET_HEADER_SIZE>>1))), samples[d >> 4]+off, EP_PAYLOAD_SIZE); atomic_clear_bits((uint32_t *)&free_usb, 1 << u); u = !u; atomic_clear_bits((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); } } } int wait_packet(int pol, uint32_t min_edges, uint32_t timeout_us) { stm32_dma_chan_t *chan = dma_get_channel(pol ? DMAC_TIM_RX2 : DMAC_TIM_RX1); uint32_t t0 = __hw_clock_source_read(); uint32_t c0 = chan->cndtr; uint32_t t_gap = t0; uint32_t c_gap = c0; uint32_t total_edges = 0; while (1) { uint32_t t = __hw_clock_source_read(); uint32_t c = chan->cndtr; if (t - t0 > timeout_us) /* Timeout */ break; if (min_edges) { /* real packet detection */ int nb = (int)c_gap - (int)c; if (nb < 0) nb = RX_COUNT - nb; if (nb > 3) { /* NOT IDLE */ t_gap = t; c_gap = c; total_edges += nb; } else { if ((t - t_gap) > 20 && (total_edges - (t - t0)/256) >= min_edges) /* real gap after the packet */ break; } } } return (__hw_clock_source_read() - t0 > timeout_us); } uint8_t recording_enable(uint8_t new_mask) { 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) { /* Stop DMA before jumping to avoid memory corruption */ recording_enable(0); } DECLARE_HOOK(HOOK_SYSJUMP, sniffer_sysjump, HOOK_PRIO_DEFAULT); static int command_sniffer(int argc, char **argv) { ccprintf("Seq number:%d Overflows: %d\n", seq, oflow); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(sniffer, command_sniffer, "[]", "Buffering status");