mirror of https://github.com/commaai/panda.git
610 lines
16 KiB
C
610 lines
16 KiB
C
#include "config.h"
|
|
#include "obj/gitversion.h"
|
|
|
|
// ********************* includes *********************
|
|
|
|
#include "libc.h"
|
|
#include "safety.h"
|
|
#include "provision.h"
|
|
|
|
#include "drivers/drivers.h"
|
|
|
|
#include "drivers/llgpio.h"
|
|
#include "gpio.h"
|
|
|
|
#include "drivers/uart.h"
|
|
#include "drivers/adc.h"
|
|
#include "drivers/usb.h"
|
|
#include "drivers/can.h"
|
|
#include "drivers/spi.h"
|
|
#include "drivers/timer.h"
|
|
|
|
|
|
// ***************************** fan *****************************
|
|
|
|
void fan_init() {
|
|
// timer for fan PWM
|
|
TIM3->CCMR2 = TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1;
|
|
TIM3->CCER = TIM_CCER_CC3E;
|
|
timer_init(TIM3, 10);
|
|
}
|
|
|
|
void fan_set_speed(int fan_speed) {
|
|
TIM3->CCR3 = fan_speed;
|
|
}
|
|
|
|
// ********************* serial debugging *********************
|
|
|
|
void debug_ring_callback(uart_ring *ring) {
|
|
char rcv;
|
|
while (getc(ring, &rcv)) {
|
|
putc(ring, rcv);
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ***************************** USB port *****************************
|
|
|
|
int get_health_pkt(void *dat) {
|
|
struct __attribute__((packed)) {
|
|
uint32_t voltage;
|
|
uint32_t current;
|
|
uint8_t started;
|
|
uint8_t controls_allowed;
|
|
uint8_t gas_interceptor_detected;
|
|
uint8_t started_signal_detected;
|
|
uint8_t started_alt;
|
|
} *health = dat;
|
|
|
|
health->voltage = adc_get(ADCCHAN_VOLTAGE);
|
|
|
|
#ifdef PANDA
|
|
health->current = adc_get(ADCCHAN_CURRENT);
|
|
health->started = (GPIOA->IDR & (1 << 1)) == 0;
|
|
#else
|
|
health->current = 0;
|
|
health->started = (GPIOC->IDR & (1 << 13)) != 0;
|
|
#endif
|
|
|
|
health->controls_allowed = controls_allowed;
|
|
health->gas_interceptor_detected = gas_interceptor_detected;
|
|
|
|
// DEPRECATED
|
|
health->started_alt = 0;
|
|
health->started_signal_detected = 0;
|
|
|
|
return sizeof(*health);
|
|
}
|
|
|
|
int usb_cb_ep1_in(uint8_t *usbdata, int len, int 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(uint8_t *usbdata, int len, int hardwired) {
|
|
if (len == 0) return;
|
|
uart_ring *ur = get_ring_by_number(usbdata[0]);
|
|
if (!ur) return;
|
|
if ((usbdata[0] < 2) || safety_tx_lin_hook(usbdata[0]-2, usbdata+1, len-1)) {
|
|
for (int i = 1; i < len; i++) while (!putc(ur, usbdata[i]));
|
|
}
|
|
}
|
|
|
|
// send on CAN
|
|
void usb_cb_ep3_out(uint8_t *usbdata, int len, int hardwired) {
|
|
int dpkt = 0;
|
|
for (dpkt = 0; dpkt < len; dpkt += 0x10) {
|
|
uint32_t *tf = (uint32_t*)(&usbdata[dpkt]);
|
|
|
|
// make a copy
|
|
CAN_FIFOMailBox_TypeDef to_push;
|
|
to_push.RDHR = tf[3];
|
|
to_push.RDLR = tf[2];
|
|
to_push.RDTR = tf[1];
|
|
to_push.RIR = tf[0];
|
|
|
|
uint8_t bus_number = (to_push.RDTR >> 4) & CAN_BUS_NUM_MASK;
|
|
can_send(&to_push, bus_number);
|
|
}
|
|
}
|
|
|
|
int 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, int hardwired) {
|
|
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;
|
|
// **** 0xd0: fetch serial number
|
|
case 0xd0:
|
|
#ifdef PANDA
|
|
// addresses are OTP
|
|
if (setup->b.wValue.w == 1) {
|
|
memcpy(resp, (void *)0x1fff79c0, 0x10);
|
|
resp_len = 0x10;
|
|
} else {
|
|
get_provision_chunk(resp);
|
|
resp_len = PROVISION_CHUNK_LEN;
|
|
}
|
|
#endif
|
|
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;
|
|
}
|
|
break;
|
|
// **** 0xd2: get health packet
|
|
case 0xd2:
|
|
resp_len = get_health_pkt(resp);
|
|
break;
|
|
// **** 0xd3: set fan speed
|
|
case 0xd3:
|
|
fan_set_speed(setup->b.wValue.w);
|
|
break;
|
|
// **** 0xd6: get version
|
|
case 0xd6:
|
|
COMPILE_TIME_ASSERT(sizeof(gitversion) <= MAX_RESP_LEN)
|
|
memcpy(resp, gitversion, sizeof(gitversion));
|
|
resp_len = sizeof(gitversion)-1;
|
|
break;
|
|
// **** 0xd8: reset ST
|
|
case 0xd8:
|
|
NVIC_SystemReset();
|
|
break;
|
|
// **** 0xd9: set ESP power
|
|
case 0xd9:
|
|
if (setup->b.wValue.w == 1) {
|
|
set_esp_mode(ESP_ENABLED);
|
|
} 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 == 1) {
|
|
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:
|
|
#ifdef PANDA
|
|
if (setup->b.wValue.w == 1) {
|
|
// GMLAN ON
|
|
if (setup->b.wIndex.w == 1) {
|
|
can_set_gmlan(1);
|
|
} else if (setup->b.wIndex.w == 2) {
|
|
// might be ignored on rev b panda
|
|
can_set_gmlan(2);
|
|
}
|
|
} else {
|
|
can_set_gmlan(-1);
|
|
}
|
|
#endif
|
|
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) {
|
|
safety_set_mode(setup->b.wValue.w);
|
|
switch (setup->b.wValue.w) {
|
|
case SAFETY_NOOUTPUT:
|
|
can_silent = 0xff;
|
|
break;
|
|
case SAFETY_ELM327:
|
|
can_silent = 0xfe;
|
|
break;
|
|
default:
|
|
can_silent = 0;
|
|
break;
|
|
}
|
|
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 == 0xFF){ //Clear Forwarding
|
|
can_set_forwarding(setup->b.wValue.w, -1);
|
|
}
|
|
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;
|
|
// **** 0xe0: uart read
|
|
case 0xe0:
|
|
ur = get_ring_by_number(setup->b.wValue.w);
|
|
if (!ur) break;
|
|
// 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 > 0);
|
|
can_init_all();
|
|
break;
|
|
// **** 0xe6: set USB power
|
|
case 0xe6:
|
|
if (revision == PANDA_REV_C) {
|
|
if (setup->b.wValue.w == 1) {
|
|
puts("user setting CDP mode\n");
|
|
set_usb_power_mode(USB_POWER_CDP);
|
|
} 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 == 1) {
|
|
GPIOC->ODR &= ~(1 << 10);
|
|
GPIOC->MODER &= ~GPIO_MODER_MODER10_1;
|
|
GPIOC->MODER |= GPIO_MODER_MODER10_0;
|
|
} else {
|
|
GPIOC->ODR &= ~(1 << 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 == 1) {
|
|
GPIOC->ODR |= (1 << 10);
|
|
GPIOC->ODR &= ~(1 << 10);
|
|
} else {
|
|
GPIOC->ODR |= (1 << 12);
|
|
GPIOC->ODR &= ~(1 << 12);
|
|
}
|
|
}
|
|
|
|
if (setup->b.wValue.w == 1) {
|
|
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 == 0xFFFF) {
|
|
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]);
|
|
}
|
|
break;
|
|
// **** 0xf2: Clear UART ring buffer.
|
|
case 0xf2:
|
|
{
|
|
uart_ring * rb = get_ring_by_number(setup->b.wValue.w);
|
|
if (rb) {
|
|
puts("Clearing UART queue.\n");
|
|
clear_uart_buff(rb);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
puts("NO HANDLER ");
|
|
puth(setup->b.bRequest);
|
|
puts("\n");
|
|
break;
|
|
}
|
|
return resp_len;
|
|
}
|
|
|
|
#ifdef PANDA
|
|
int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) {
|
|
// data[0] = endpoint
|
|
// data[2] = length
|
|
// data[4:] = data
|
|
|
|
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;
|
|
}
|
|
return resp_len;
|
|
}
|
|
|
|
#else
|
|
|
|
int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) { return 0; };
|
|
|
|
#endif
|
|
|
|
|
|
// ***************************** main code *****************************
|
|
|
|
void __initialize_hardware_early() {
|
|
early();
|
|
}
|
|
|
|
int main() {
|
|
// 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");
|
|
#ifdef PANDA
|
|
puts(revision == PANDA_REV_C ? " panda rev c\n" : " panda rev a or b\n");
|
|
#else
|
|
puts(" legacy\n");
|
|
#endif
|
|
puts(has_external_debug_serial ? " real serial\n" : " USB serial\n");
|
|
puts(is_giant_panda ? " GIANTpanda detected\n" : " not GIANTpanda\n");
|
|
puts(is_entering_bootmode ? " ESP wants bootmode\n" : " no bootmode\n");
|
|
gpio_init();
|
|
|
|
// 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);
|
|
}
|
|
|
|
#ifdef PANDA
|
|
// 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;
|
|
#endif
|
|
|
|
// enable USB
|
|
usb_init();
|
|
|
|
// default to silent mode to prevent issues with Ford
|
|
safety_set_mode(SAFETY_NOOUTPUT);
|
|
can_silent = 1;
|
|
can_init_all();
|
|
|
|
adc_init();
|
|
|
|
#ifdef PANDA
|
|
spi_init();
|
|
#endif
|
|
|
|
// set PWM
|
|
fan_init();
|
|
fan_set_speed(0);
|
|
|
|
puts("**** INTERRUPTS ON ****\n");
|
|
|
|
__enable_irq();
|
|
|
|
// LED should keep on blinking all the time
|
|
uint64_t cnt = 0;
|
|
|
|
#ifdef PANDA
|
|
uint64_t marker = 0;
|
|
#define CURRENT_THRESHOLD 0xF00
|
|
#define CLICKS 8
|
|
#endif
|
|
|
|
for (cnt=0;;cnt++) {
|
|
can_live = pending_can_live;
|
|
|
|
#ifdef PANDA
|
|
int current = adc_get(ADCCHAN_CURRENT);
|
|
|
|
switch (usb_power_mode) {
|
|
case USB_POWER_CLIENT:
|
|
if ((cnt-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 = cnt;
|
|
}
|
|
}
|
|
// keep resetting the timer if it's enumerated
|
|
if (is_enumerated) {
|
|
marker = cnt;
|
|
}
|
|
break;
|
|
case USB_POWER_CDP:
|
|
// been CLICKS clicks since we switched to CDP
|
|
if ((cnt-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 = cnt;
|
|
}
|
|
}
|
|
// keep resetting the timer if there's no current draw in CDP
|
|
if (current >= CURRENT_THRESHOLD) {
|
|
marker = cnt;
|
|
}
|
|
break;
|
|
case USB_POWER_DCP:
|
|
// been at least CLICKS clicks since we switched to DCP
|
|
if ((cnt-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 = cnt;
|
|
}
|
|
}
|
|
// keep resetting the timer if there's current draw in DCP
|
|
if (current < CURRENT_THRESHOLD) {
|
|
marker = cnt;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// ~0x9a = 500 ma
|
|
/*puth(current);
|
|
puts("\n");*/
|
|
#endif
|
|
|
|
// reset this every 16th pass
|
|
if ((cnt&0xF) == 0) 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);
|
|
|
|
// blink the red LED
|
|
for (int fade = 0; fade < 1024; fade += 8) {
|
|
for (int i = 0; i < 128; i++) {
|
|
set_led(LED_RED, 0);
|
|
if (fade < 512) { delay(512-fade); } else { delay(fade-512); }
|
|
set_led(LED_RED, 1);
|
|
if (fade < 512) { delay(fade); } else { delay(1024-fade); }
|
|
}
|
|
}
|
|
|
|
// turn off the blue LED, turned on by CAN
|
|
#ifdef PANDA
|
|
set_led(LED_BLUE, 0);
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|