//#define EON #include "config.h" #include "obj/gitversion.h" // ********************* includes ********************* #include "libc.h" #include "provision.h" #include "drivers/llcan.h" #include "drivers/llgpio.h" #include "gpio.h" #include "drivers/uart.h" #include "drivers/adc.h" #include "drivers/usb.h" #include "drivers/gmlan_alt.h" #include "drivers/timer.h" #include "drivers/clock.h" #ifndef EON #include "drivers/spi.h" #endif #include "power_saving.h" #include "safety.h" #include "drivers/can.h" // ********************* serial debugging ********************* void debug_ring_callback(uart_ring *ring) { char rcv; while (getc(ring, &rcv)) { (void)putc(ring, rcv); // misra-c2012-17.7: cast to void is ok: debug function // jump to DFU flash if (rcv == 'z') { enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; NVIC_SystemReset(); } // normal reset if (rcv == 'x') { NVIC_SystemReset(); } // enable CDP mode if (rcv == 'C') { puts("switching USB to CDP mode\n"); set_usb_power_mode(USB_POWER_CDP); } if (rcv == 'c') { puts("switching USB to client mode\n"); set_usb_power_mode(USB_POWER_CLIENT); } if (rcv == 'D') { puts("switching USB to DCP mode\n"); set_usb_power_mode(USB_POWER_DCP); } } } // ***************************** started logic ***************************** bool is_gpio_started(void) { // ignition is on PA1 return (GPIOA->IDR & (1U << 1)) == 0; } // cppcheck-suppress unusedFunction ; used in headers not included in cppcheck void EXTI1_IRQHandler(void) { volatile unsigned int pr = EXTI->PR & (1U << 1); if ((pr & (1U << 1)) != 0U) { #ifdef DEBUG puts("got started interrupt\n"); #endif // jenky debounce delay(100000); // set power savings mode here int power_save_state = is_gpio_started() ? POWER_SAVE_STATUS_DISABLED : POWER_SAVE_STATUS_ENABLED; set_power_save_state(power_save_state); EXTI->PR = (1U << 1); } } void started_interrupt_init(void) { SYSCFG->EXTICR[1] = SYSCFG_EXTICR1_EXTI1_PA; EXTI->IMR |= (1U << 1); EXTI->RTSR |= (1U << 1); EXTI->FTSR |= (1U << 1); NVIC_EnableIRQ(EXTI1_IRQn); } // ***************************** USB port ***************************** int get_health_pkt(void *dat) { struct __attribute__((packed)) { uint32_t voltage_pkt; uint32_t current_pkt; uint8_t started_pkt; uint8_t controls_allowed_pkt; uint8_t gas_interceptor_detected_pkt; uint32_t can_send_errs_pkt; uint32_t can_fwd_errs_pkt; uint32_t gmlan_send_errs_pkt; } *health = dat; //Voltage will be measured in mv. 5000 = 5V uint32_t voltage = adc_get(ADCCHAN_VOLTAGE); // REVC has a 10, 1 (1/11) voltage divider // Here is the calculation for the scale (s) // ADCV = VIN_S * (1/11) * (4095/3.3) // RETVAL = ADCV * s = VIN_S*1000 // s = 1000/((4095/3.3)*(1/11)) = 8.8623046875 // Avoid needing floating point math health->voltage_pkt = (voltage * 8862U) / 1000U; health->current_pkt = adc_get(ADCCHAN_CURRENT); int safety_ignition = safety_ignition_hook(); if (safety_ignition < 0) { //Use the GPIO pin to determine ignition health->started_pkt = is_gpio_started(); } else { //Current safety hooks want to determine ignition (ex: GM) health->started_pkt = safety_ignition; } health->controls_allowed_pkt = controls_allowed; health->gas_interceptor_detected_pkt = gas_interceptor_detected; health->can_send_errs_pkt = can_send_errs; health->can_fwd_errs_pkt = can_fwd_errs; health->gmlan_send_errs_pkt = gmlan_send_errs; return sizeof(*health); } int usb_cb_ep1_in(void *usbdata, int len, bool hardwired) { UNUSED(hardwired); CAN_FIFOMailBox_TypeDef *reply = (CAN_FIFOMailBox_TypeDef *)usbdata; int ilen = 0; while (ilen < MIN(len/0x10, 4) && can_pop(&can_rx_q, &reply[ilen])) { ilen++; } return ilen*0x10; } // send on serial, first byte to select the ring void usb_cb_ep2_out(void *usbdata, int len, bool hardwired) { UNUSED(hardwired); uint8_t *usbdata8 = (uint8_t *)usbdata; uart_ring *ur = get_ring_by_number(usbdata8[0]); if ((len != 0) && (ur != NULL)) { if ((usbdata8[0] < 2U) || safety_tx_lin_hook(usbdata8[0] - 2U, usbdata8 + 1, len - 1)) { for (int i = 1; i < len; i++) { while (!putc(ur, usbdata8[i])) { // wait } } } } } // send on CAN void usb_cb_ep3_out(void *usbdata, int len, bool hardwired) { UNUSED(hardwired); int dpkt = 0; uint32_t *d32 = (uint32_t *)usbdata; for (dpkt = 0; dpkt < (len / 4); dpkt += 4) { CAN_FIFOMailBox_TypeDef to_push; to_push.RDHR = d32[dpkt + 3]; to_push.RDLR = d32[dpkt + 2]; to_push.RDTR = d32[dpkt + 1]; to_push.RIR = d32[dpkt]; uint8_t bus_number = (to_push.RDTR >> 4) & CAN_BUS_NUM_MASK; can_send(&to_push, bus_number); } } bool is_enumerated = 0; void usb_cb_enumeration_complete() { puts("USB enumeration complete\n"); is_enumerated = 1; } int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) { unsigned int resp_len = 0; uart_ring *ur = NULL; int i; switch (setup->b.bRequest) { // **** 0xc0: get CAN debug info case 0xc0: puts("can tx: "); puth(can_tx_cnt); puts(" txd: "); puth(can_txd_cnt); puts(" rx: "); puth(can_rx_cnt); puts(" err: "); puth(can_err_cnt); puts("\n"); break; // **** 0xc1: is grey panda case 0xc1: resp[0] = is_grey_panda; resp_len = 1; break; // **** 0xd0: fetch serial number case 0xd0: // addresses are OTP if (setup->b.wValue.w == 1U) { (void)memcpy(resp, (uint8_t *)0x1fff79c0, 0x10); resp_len = 0x10; } else { get_provision_chunk(resp); resp_len = PROVISION_CHUNK_LEN; } break; // **** 0xd1: enter bootloader mode case 0xd1: // this allows reflashing of the bootstub // so it's blocked over wifi switch (setup->b.wValue.w) { case 0: if (hardwired) { puts("-> entering bootloader\n"); enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; NVIC_SystemReset(); } break; case 1: puts("-> entering softloader\n"); enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; NVIC_SystemReset(); break; default: puts("Bootloader mode invalid\n"); break; } break; // **** 0xd2: get health packet case 0xd2: resp_len = get_health_pkt(resp); break; // **** 0xd6: get version case 0xd6: COMPILE_TIME_ASSERT(sizeof(gitversion) <= MAX_RESP_LEN); (void)memcpy(resp, gitversion, sizeof(gitversion)); resp_len = sizeof(gitversion) - 1U; break; // **** 0xd8: reset ST case 0xd8: NVIC_SystemReset(); break; // **** 0xd9: set ESP power case 0xd9: if (setup->b.wValue.w == 1U) { set_esp_mode(ESP_ENABLED); } else if (setup->b.wValue.w == 2U) { set_esp_mode(ESP_BOOTMODE); } else { set_esp_mode(ESP_DISABLED); } break; // **** 0xda: reset ESP, with optional boot mode case 0xda: set_esp_mode(ESP_DISABLED); delay(1000000); if (setup->b.wValue.w == 1U) { set_esp_mode(ESP_BOOTMODE); } else { set_esp_mode(ESP_ENABLED); } delay(1000000); set_esp_mode(ESP_ENABLED); break; // **** 0xdb: set GMLAN multiplexing mode case 0xdb: if (setup->b.wValue.w == 1U) { // GMLAN ON if (setup->b.wIndex.w == 1U) { can_set_gmlan(1); } else if (setup->b.wIndex.w == 2U) { can_set_gmlan(2); } else { puts("Invalid bus num for GMLAN CAN set\n"); } } else { can_set_gmlan(-1); } break; // **** 0xdc: set safety mode case 0xdc: // this is the only way to leave silent mode // and it's blocked over WiFi // Allow ELM security mode to be set over wifi. if (hardwired || (setup->b.wValue.w == SAFETY_NOOUTPUT) || (setup->b.wValue.w == SAFETY_ELM327)) { int err = safety_set_mode(setup->b.wValue.w, (int16_t)setup->b.wIndex.w); if (err == -1) { puts("Error: safety set mode failed\n"); } else { #ifndef EON // always LIVE on EON switch (setup->b.wValue.w) { case SAFETY_NOOUTPUT: can_silent = ALL_CAN_SILENT; break; case SAFETY_ELM327: can_silent = ALL_CAN_BUT_MAIN_SILENT; break; default: can_silent = ALL_CAN_LIVE; break; } #endif if (safety_ignition_hook() != -1) { // if the ignition hook depends on something other than the started GPIO // we have to disable power savings (fix for GM and Tesla) set_power_save_state(POWER_SAVE_STATUS_DISABLED); } else { // power mode is already POWER_SAVE_STATUS_DISABLED and CAN TXs are active } can_init_all(); } } break; // **** 0xdd: enable can forwarding case 0xdd: // wValue = Can Bus Num to forward from // wIndex = Can Bus Num to forward to if ((setup->b.wValue.w < BUS_MAX) && (setup->b.wIndex.w < BUS_MAX) && (setup->b.wValue.w != setup->b.wIndex.w)) { // set forwarding can_set_forwarding(setup->b.wValue.w, setup->b.wIndex.w & CAN_BUS_NUM_MASK); } else if((setup->b.wValue.w < BUS_MAX) && (setup->b.wIndex.w == 0xFFU)){ //Clear Forwarding can_set_forwarding(setup->b.wValue.w, -1); } else { puts("Invalid CAN bus forwarding\n"); } break; // **** 0xde: set can bitrate case 0xde: if (setup->b.wValue.w < BUS_MAX) { can_speed[setup->b.wValue.w] = setup->b.wIndex.w; can_init(CAN_NUM_FROM_BUS_NUM(setup->b.wValue.w)); } break; // **** 0xdf: set long controls allowed case 0xdf: if (hardwired) { long_controls_allowed = setup->b.wValue.w & 1U; } break; // **** 0xe0: uart read case 0xe0: ur = get_ring_by_number(setup->b.wValue.w); if (!ur) { break; } if (ur == &esp_ring) { uart_dma_drain(); } // read while ((resp_len < MIN(setup->b.wLength.w, MAX_RESP_LEN)) && getc(ur, (char*)&resp[resp_len])) { ++resp_len; } break; // **** 0xe1: uart set baud rate case 0xe1: ur = get_ring_by_number(setup->b.wValue.w); if (!ur) { break; } uart_set_baud(ur->uart, setup->b.wIndex.w); break; // **** 0xe2: uart set parity case 0xe2: ur = get_ring_by_number(setup->b.wValue.w); if (!ur) { break; } switch (setup->b.wIndex.w) { case 0: // disable parity, 8-bit ur->uart->CR1 &= ~(USART_CR1_PCE | USART_CR1_M); break; case 1: // even parity, 9-bit ur->uart->CR1 &= ~USART_CR1_PS; ur->uart->CR1 |= USART_CR1_PCE | USART_CR1_M; break; case 2: // odd parity, 9-bit ur->uart->CR1 |= USART_CR1_PS; ur->uart->CR1 |= USART_CR1_PCE | USART_CR1_M; break; default: break; } break; // **** 0xe4: uart set baud rate extended case 0xe4: ur = get_ring_by_number(setup->b.wValue.w); if (!ur) { break; } uart_set_baud(ur->uart, (int)setup->b.wIndex.w*300); break; // **** 0xe5: set CAN loopback (for testing) case 0xe5: can_loopback = (setup->b.wValue.w > 0U); can_init_all(); break; // **** 0xe6: set USB power case 0xe6: if (setup->b.wValue.w == 1U) { puts("user setting CDP mode\n"); set_usb_power_mode(USB_POWER_CDP); } else if (setup->b.wValue.w == 2U) { puts("user setting DCP mode\n"); set_usb_power_mode(USB_POWER_DCP); } else { puts("user setting CLIENT mode\n"); set_usb_power_mode(USB_POWER_CLIENT); } break; // **** 0xf0: do k-line wValue pulse on uart2 for Acura case 0xf0: if (setup->b.wValue.w == 1U) { GPIOC->ODR &= ~(1U << 10); GPIOC->MODER &= ~GPIO_MODER_MODER10_1; GPIOC->MODER |= GPIO_MODER_MODER10_0; } else { GPIOC->ODR &= ~(1U << 12); GPIOC->MODER &= ~GPIO_MODER_MODER12_1; GPIOC->MODER |= GPIO_MODER_MODER12_0; } for (i = 0; i < 80; i++) { delay(8000); if (setup->b.wValue.w == 1U) { GPIOC->ODR |= (1U << 10); GPIOC->ODR &= ~(1U << 10); } else { GPIOC->ODR |= (1U << 12); GPIOC->ODR &= ~(1U << 12); } } if (setup->b.wValue.w == 1U) { GPIOC->MODER &= ~GPIO_MODER_MODER10_0; GPIOC->MODER |= GPIO_MODER_MODER10_1; } else { GPIOC->MODER &= ~GPIO_MODER_MODER12_0; GPIOC->MODER |= GPIO_MODER_MODER12_1; } delay(140 * 9000); break; // **** 0xf1: Clear CAN ring buffer. case 0xf1: if (setup->b.wValue.w == 0xFFFFU) { puts("Clearing CAN Rx queue\n"); can_clear(&can_rx_q); } else if (setup->b.wValue.w < BUS_MAX) { puts("Clearing CAN Tx queue\n"); can_clear(can_queues[setup->b.wValue.w]); } else { puts("Clearing CAN CAN ring buffer failed: wrong bus number\n"); } break; // **** 0xf2: Clear UART ring buffer. case 0xf2: { uart_ring * rb = get_ring_by_number(setup->b.wValue.w); if (rb != NULL) { puts("Clearing UART queue.\n"); clear_uart_buff(rb); } break; } default: puts("NO HANDLER "); puth(setup->b.bRequest); puts("\n"); break; } return resp_len; } #ifndef EON int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) { // data[0] = endpoint // data[2] = length // data[4:] = data UNUSED(len); int resp_len = 0; switch (data[0]) { case 0: // control transfer resp_len = usb_cb_control_msg((USB_Setup_TypeDef *)(data+4), data_out, 0); break; case 1: // ep 1, read resp_len = usb_cb_ep1_in(data_out, 0x40, 0); break; case 2: // ep 2, send serial usb_cb_ep2_out(data+4, data[2], 0); break; case 3: // ep 3, send CAN usb_cb_ep3_out(data+4, data[2], 0); break; default: puts("SPI data invalid"); break; } return resp_len; } #endif // ***************************** main code ***************************** // cppcheck-suppress unusedFunction ; used in headers not included in cppcheck void __initialize_hardware_early(void) { early(); } void __attribute__ ((noinline)) enable_fpu(void) { // enable the FPU SCB->CPACR |= ((3UL << (10U * 2U)) | (3UL << (11U * 2U))); } uint64_t tcnt = 0; uint64_t marker = 0; // called once per second // cppcheck-suppress unusedFunction ; used in headers not included in cppcheck void TIM3_IRQHandler(void) { #define CURRENT_THRESHOLD 0xF00U #define CLICKS 5U // 5 seconds to switch modes if (TIM3->SR != 0) { can_live = pending_can_live; //puth(usart1_dma); puts(" "); puth(DMA2_Stream5->M0AR); puts(" "); puth(DMA2_Stream5->NDTR); puts("\n"); uint32_t current = adc_get(ADCCHAN_CURRENT); switch (usb_power_mode) { case USB_POWER_CLIENT: if ((tcnt - marker) >= CLICKS) { if (!is_enumerated) { puts("USBP: didn't enumerate, switching to CDP mode\n"); // switch to CDP set_usb_power_mode(USB_POWER_CDP); marker = tcnt; } } // keep resetting the timer if it's enumerated if (is_enumerated) { marker = tcnt; } break; case USB_POWER_CDP: // On the EON, if we get into CDP mode we stay here. No need to go to DCP. #ifndef EON // been CLICKS clicks since we switched to CDP if ((tcnt-marker) >= CLICKS) { // measure current draw, if positive and no enumeration, switch to DCP if (!is_enumerated && (current < CURRENT_THRESHOLD)) { puts("USBP: no enumeration with current draw, switching to DCP mode\n"); set_usb_power_mode(USB_POWER_DCP); marker = tcnt; } } // keep resetting the timer if there's no current draw in CDP if (current >= CURRENT_THRESHOLD) { marker = tcnt; } #endif break; case USB_POWER_DCP: // been at least CLICKS clicks since we switched to DCP if ((tcnt-marker) >= CLICKS) { // if no current draw, switch back to CDP if (current >= CURRENT_THRESHOLD) { puts("USBP: no current draw, switching back to CDP mode\n"); set_usb_power_mode(USB_POWER_CDP); marker = tcnt; } } // keep resetting the timer if there's current draw in DCP if (current < CURRENT_THRESHOLD) { marker = tcnt; } break; default: puts("USB power mode invalid\n"); // set_usb_power_mode prevents assigning invalid values break; } // ~0x9a = 500 ma /*puth(current); puts("\n");*/ // reset this every 16th pass if ((tcnt & 0xFU) == 0U) { pending_can_live = 0; } #ifdef DEBUG puts("** blink "); puth(can_rx_q.r_ptr); puts(" "); puth(can_rx_q.w_ptr); puts(" "); puth(can_tx1_q.r_ptr); puts(" "); puth(can_tx1_q.w_ptr); puts(" "); puth(can_tx2_q.r_ptr); puts(" "); puth(can_tx2_q.w_ptr); puts("\n"); #endif // set green LED to be controls allowed set_led(LED_GREEN, controls_allowed); // turn off the blue LED, turned on by CAN // unless we are in power saving mode set_led(LED_BLUE, (tcnt & 1U) && (power_save_status == POWER_SAVE_STATUS_ENABLED)); // on to the next one tcnt += 1U; } TIM3->SR = 0; } int main(void) { // shouldn't have interrupts here, but just in case __disable_irq(); // init early devices clock_init(); periph_init(); detect(); // print hello puts("\n\n\n************************ MAIN START ************************\n"); // detect the revision and init the GPIOs puts("config:\n"); puts((revision == PANDA_REV_C) ? " panda rev c\n" : " panda rev a or b\n"); puts(has_external_debug_serial ? " real serial\n" : " USB serial\n"); puts(is_giant_panda ? " GIANTpanda detected\n" : " not GIANTpanda\n"); puts(is_grey_panda ? " gray panda detected!\n" : " white panda\n"); puts(is_entering_bootmode ? " ESP wants bootmode\n" : " no bootmode\n"); // non rev c panda are no longer supported while (revision != PANDA_REV_C) { // hang } gpio_init(); // panda has an FPU, let's use it! enable_fpu(); // enable main uart if it's connected if (has_external_debug_serial) { // WEIRDNESS: without this gate around the UART, it would "crash", but only if the ESP is enabled // assuming it's because the lines were left floating and spurious noise was on them uart_init(USART2, 115200); } if (is_grey_panda) { uart_init(USART1, 9600); } else { // enable ESP uart uart_init(USART1, 115200); } // enable LIN uart_init(UART5, 10400); UART5->CR2 |= USART_CR2_LINEN; uart_init(USART3, 10400); USART3->CR2 |= USART_CR2_LINEN; // init microsecond system timer // increments 1000000 times per second // generate an update to set the prescaler TIM2->PSC = 48-1; TIM2->CR1 = TIM_CR1_CEN; TIM2->EGR = TIM_EGR_UG; // use TIM2->CNT to read // enable USB usb_init(); // default to silent mode to prevent issues with Ford // hardcode a specific safety mode if you want to force the panda to be in a specific mode int err = safety_set_mode(SAFETY_NOOUTPUT, 0); if (err == -1) { puts("Failed to set safety mode\n"); while (true) { // if SAFETY_NOOUTPUT isn't succesfully set, we can't continue } } #ifdef EON // if we're on an EON, it's fine for CAN to be live for fingerprinting can_silent = ALL_CAN_LIVE; #else can_silent = ALL_CAN_SILENT; #endif can_init_all(); adc_init(); #ifndef EON spi_init(); #endif #ifdef EON // have to save power if (!is_grey_panda) { set_esp_mode(ESP_DISABLED); } // only enter power save after the first cycle /*if (is_gpio_started()) { set_power_save_state(POWER_SAVE_STATUS_ENABLED); }*/ // interrupt on started line started_interrupt_init(); #endif // 48mhz / 65536 ~= 732 / 732 = 1 timer_init(TIM3, 732); NVIC_EnableIRQ(TIM3_IRQn); #ifdef DEBUG puts("DEBUG ENABLED\n"); #endif puts("**** INTERRUPTS ON ****\n"); enable_interrupts(); // LED should keep on blinking all the time uint64_t cnt = 0; for (cnt=0;;cnt++) { if (power_save_status == POWER_SAVE_STATUS_DISABLED) { int div_mode = ((usb_power_mode == USB_POWER_DCP) ? 4 : 1); // useful for debugging, fade breaks = panda is overloaded for (int div_mode_loop = 0; div_mode_loop < div_mode; div_mode_loop++) { for (int fade = 0; fade < 1024; fade += 8) { for (int i = 0; i < (128/div_mode); i++) { set_led(LED_RED, 1); if (fade < 512) { delay(fade); } else { delay(1024-fade); } set_led(LED_RED, 0); if (fade < 512) { delay(512-fade); } else { delay(fade-512); } } } } } else { __WFI(); } } return 0; }