a648ccae Add os import
042562dd Extracted wifi connect from test helpers
ac0fd5dd query fw versions example - use extended diagnostic session
4e9d788a Remove not-needed cadillac-init
f0a5d154 typo
c093286b Add bootkick after re-enabling phone power (#401)
eadb0dbb security upgrades (#397)
7c13bec0 Command to get signature (#399)
dad439a4 static assert on size of health packet (#398)
da9da466 Fix VERSION
df4159c8 Revert "Revert "Register readback on most modules. Still need to convert the other ones (#396)""
56ec2150 Revert "Register readback on most modules. Still need to convert the other ones (#396)"
893e4861 Register readback on most modules. Still need to convert the other ones (#396)
6bbae7be VW safety: allow cancel spam on both buses to be compatible with camera and gateway integration
d5f7a287 bump panda
1bcc351f ignition_can: set it to False after 2s of not seeing CAN msgs
96137f1a VW can based ignition not needed. it has ignition line at camera as well.
1b004a18 Same flake8 version as the one in openpilot
e82ba5f2 Same pylint version as the one in openpilot
656f99b0 Interrupt refactor (NVIC_SM_1: #334) and Fault handling (#377) (PR #373)
000282e5 Fix can_logger.py to run correctly on python3 (#392)
7f9b4a59 Fix USB device enumeration on Windows 8.1 and Windows 10 (#393)
dec565c7 Update Misra test coverage, which now includes rule 2.7
fb6bc3ba Fix Misra
878dd00a solve race condition is relay_malfunction right after changing the relay status by adding a counter
2d4cb05c add a safety mode counter
a6797a21 Implement USB power mode on uno
670f90cc Merge branch 'master' of github.com:commaai/panda
ca39a5d8 Added faults integer to health packet
e1c34a1a Panda Jungle testing (#394)
2a093a39 Added heartbeat to echo test
22464356 Fixed health struct size. We should really get an automated test for this
f458d67a Add uptime counter to the health packet (#391)
16624811 enable CAN transcievers outside the set_safety_mode function, which is not related
a7c98744 bump panda ver
1192d934 Power saving refactor (#389)
d58d08fb Fix Misra 17.8: can't mod function params
bc685ac9 Minor indent
a54b86c4 Failure of set_safety_mode falls back to SILENT. Failure to set silent results in hanging
597436d3 NOOUTPUT safety mode is now SILENT. NOOUTPUT still exists but keeps C… (#388)
d229f8dc ESP forced off in EON build. this prevents ESP to be turned on when e… (#387)
8a044b34 forgot Hyundai: now also using make_msg
4f9c8796 remove abunch of lines from safety regression tests by using common make_msg function
fb814143 mispelled word
57f5ef8c Fix misra: addr can't be more than 29 bits anyway
68ff5012 typo
d5c772b0 Fixe Toyota message white-list
48197a92 Better masking for ELM mode
b8fe78c3 VW is also tested for safety replay
212d336b Safety Chrysler: Added cancel spam button check
d44b5621 fix print in example
02d579a5 functional addr handling
6249a183 tx_hook shall have a white-list of messages (#381)
8138fc14 uds: handle function addrs and fw version query example
6626a542 Fixed python health api
b9b79e8b uds zero second timeout
e0de1a4f define ALLOW_DEBUG in safety tests
86dec4b8 Safety modes that violate ISO26262 requirements are not compiled in RELEASE build
e74ed936 safety tests a bit more simplified
2027765b relay malfunction test centralized
8af1a01a clean up safety tests
e8f7a3b2 upd panda
cfcce8f0 WIP: Relay malfunction (#384)
69d9d610 No tabs in mazda safety
a86418c1 insignificant changes
f239b996 single addr was better
d063a188 Hyundai safety: re-enable button spam safety check
4d1edc06 skip tx_hook if a message is forwarded (#379)
df2ff045 bump version
168461d5 added fault state to health packet
b3e1a133 uds: better debug prints
68c39fb3 uds: no need for threads if you always drain rx
91b7c5bb bump Panda Ver
26cb4dc4 Fixed pylint error
32725cc3 Fixed misra compliance
e33b4bea Added echo script
312ba62d minor comment cleanupo
e90897a8 Fix board detection on white
0e72c183 always stop executing if safety mode fails to be set (suggested by jyoung8607)
e8d7ed1d Rename function name to not confuse safety_set_mode and set_safety_mode
ff86db65 improve uds message processing
512ab3f2 except Exception
37ce507a py3 all
bac4d854 dos and python3
501db8d1 uds drain before send and use has_obd()
f2cbec16 Added has_obd() to python library
48e5b182 Add SDK downloading to the build step (#314)
e0762c2e Add Python & USB API for controlling phone power (#313)
ba9fb69f New health packet struct also in the python libs
git-subtree-dir: panda
git-subtree-split: a648ccae4b3661ca6de7a4ac199cc44a41442b74
old-commit-hash: bc7b9b38ae
commatwo_master
parent
0ba412d52e
commit
6d13ef279e
105 changed files with 2448 additions and 1677 deletions
@ -0,0 +1,23 @@ |
||||
// ********************* Critical section helpers *********************
|
||||
volatile bool interrupts_enabled = false; |
||||
|
||||
void enable_interrupts(void) { |
||||
interrupts_enabled = true; |
||||
__enable_irq(); |
||||
} |
||||
|
||||
void disable_interrupts(void) { |
||||
interrupts_enabled = false; |
||||
__disable_irq(); |
||||
} |
||||
|
||||
uint8_t global_critical_depth = 0U; |
||||
#define ENTER_CRITICAL() \ |
||||
__disable_irq(); \
|
||||
global_critical_depth += 1U; |
||||
|
||||
#define EXIT_CRITICAL() \ |
||||
global_critical_depth -= 1U; \
|
||||
if ((global_critical_depth == 0U) && interrupts_enabled) { \
|
||||
__enable_irq(); \
|
||||
} |
@ -0,0 +1,164 @@ |
||||
typedef struct interrupt { |
||||
IRQn_Type irq_type; |
||||
void (*handler)(void); |
||||
uint32_t call_counter; |
||||
uint32_t max_call_rate; // Call rate is defined as the amount of calls each second
|
||||
uint32_t call_rate_fault; |
||||
} interrupt; |
||||
|
||||
void unused_interrupt_handler(void) { |
||||
// Something is wrong if this handler is called!
|
||||
puts("Unused interrupt handler called!\n"); |
||||
fault_occurred(FAULT_UNUSED_INTERRUPT_HANDLED); |
||||
} |
||||
|
||||
#define NUM_INTERRUPTS 102U // There are 102 external interrupt sources (see stm32f413.h)
|
||||
interrupt interrupts[NUM_INTERRUPTS]; |
||||
|
||||
#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate, rate_fault) \ |
||||
interrupts[irq_num].irq_type = irq_num; \
|
||||
interrupts[irq_num].handler = func_ptr; \
|
||||
interrupts[irq_num].call_counter = 0U; \
|
||||
interrupts[irq_num].max_call_rate = call_rate; \
|
||||
interrupts[irq_num].call_rate_fault = rate_fault; |
||||
|
||||
bool check_interrupt_rate = false; |
||||
|
||||
void handle_interrupt(IRQn_Type irq_type){ |
||||
interrupts[irq_type].call_counter++; |
||||
interrupts[irq_type].handler(); |
||||
|
||||
// Check that the interrupts don't fire too often
|
||||
if(check_interrupt_rate && (interrupts[irq_type].call_counter > interrupts[irq_type].max_call_rate)){ |
||||
puts("Interrupt 0x"); puth(irq_type); puts(" fired too often (0x"); puth(interrupts[irq_type].call_counter); puts("/s)!\n"); |
||||
fault_occurred(interrupts[irq_type].call_rate_fault); |
||||
} |
||||
} |
||||
|
||||
// Reset interrupt counter every second
|
||||
void TIM6_DAC_IRQ_Handler(void) { |
||||
if (TIM6->SR != 0) { |
||||
for(uint16_t i=0U; i<NUM_INTERRUPTS; i++){ |
||||
interrupts[i].call_counter = 0U; |
||||
} |
||||
} |
||||
TIM6->SR = 0; |
||||
} |
||||
|
||||
void init_interrupts(bool check_rate_limit){ |
||||
check_interrupt_rate = check_rate_limit; |
||||
|
||||
for(uint16_t i=0U; i<NUM_INTERRUPTS; i++){ |
||||
interrupts[i].handler = unused_interrupt_handler; |
||||
} |
||||
|
||||
// Init timer 10 for a 1s interval
|
||||
register_set_bits(&(RCC->APB1ENR), RCC_APB1ENR_TIM6EN); // Enable interrupt timer peripheral
|
||||
REGISTER_INTERRUPT(TIM6_DAC_IRQn, TIM6_DAC_IRQ_Handler, 1, FAULT_INTERRUPT_RATE_INTERRUPTS) |
||||
register_set(&(TIM6->PSC), (732-1), 0xFFFFU); |
||||
register_set(&(TIM6->DIER), TIM_DIER_UIE, 0x5F5FU); |
||||
register_set(&(TIM6->CR1), TIM_CR1_CEN, 0x3FU); |
||||
TIM6->SR = 0; |
||||
NVIC_EnableIRQ(TIM6_DAC_IRQn); |
||||
} |
||||
|
||||
// ********************* Bare interrupt handlers *********************
|
||||
// Only implemented the STM32F413 interrupts for now, the STM32F203 specific ones do not fall into the scope of SIL2
|
||||
|
||||
void WWDG_IRQHandler(void) {handle_interrupt(WWDG_IRQn);} |
||||
void PVD_IRQHandler(void) {handle_interrupt(PVD_IRQn);} |
||||
void TAMP_STAMP_IRQHandler(void) {handle_interrupt(TAMP_STAMP_IRQn);} |
||||
void RTC_WKUP_IRQHandler(void) {handle_interrupt(RTC_WKUP_IRQn);} |
||||
void FLASH_IRQHandler(void) {handle_interrupt(FLASH_IRQn);} |
||||
void RCC_IRQHandler(void) {handle_interrupt(RCC_IRQn);} |
||||
void EXTI0_IRQHandler(void) {handle_interrupt(EXTI0_IRQn);} |
||||
void EXTI1_IRQHandler(void) {handle_interrupt(EXTI1_IRQn);} |
||||
void EXTI2_IRQHandler(void) {handle_interrupt(EXTI2_IRQn);} |
||||
void EXTI3_IRQHandler(void) {handle_interrupt(EXTI3_IRQn);} |
||||
void EXTI4_IRQHandler(void) {handle_interrupt(EXTI4_IRQn);} |
||||
void DMA1_Stream0_IRQHandler(void) {handle_interrupt(DMA1_Stream0_IRQn);} |
||||
void DMA1_Stream1_IRQHandler(void) {handle_interrupt(DMA1_Stream1_IRQn);} |
||||
void DMA1_Stream2_IRQHandler(void) {handle_interrupt(DMA1_Stream2_IRQn);} |
||||
void DMA1_Stream3_IRQHandler(void) {handle_interrupt(DMA1_Stream3_IRQn);} |
||||
void DMA1_Stream4_IRQHandler(void) {handle_interrupt(DMA1_Stream4_IRQn);} |
||||
void DMA1_Stream5_IRQHandler(void) {handle_interrupt(DMA1_Stream5_IRQn);} |
||||
void DMA1_Stream6_IRQHandler(void) {handle_interrupt(DMA1_Stream6_IRQn);} |
||||
void ADC_IRQHandler(void) {handle_interrupt(ADC_IRQn);} |
||||
void CAN1_TX_IRQHandler(void) {handle_interrupt(CAN1_TX_IRQn);} |
||||
void CAN1_RX0_IRQHandler(void) {handle_interrupt(CAN1_RX0_IRQn);} |
||||
void CAN1_RX1_IRQHandler(void) {handle_interrupt(CAN1_RX1_IRQn);} |
||||
void CAN1_SCE_IRQHandler(void) {handle_interrupt(CAN1_SCE_IRQn);} |
||||
void EXTI9_5_IRQHandler(void) {handle_interrupt(EXTI9_5_IRQn);} |
||||
void TIM1_BRK_TIM9_IRQHandler(void) {handle_interrupt(TIM1_BRK_TIM9_IRQn);} |
||||
void TIM1_UP_TIM10_IRQHandler(void) {handle_interrupt(TIM1_UP_TIM10_IRQn);} |
||||
void TIM1_TRG_COM_TIM11_IRQHandler(void) {handle_interrupt(TIM1_TRG_COM_TIM11_IRQn);} |
||||
void TIM1_CC_IRQHandler(void) {handle_interrupt(TIM1_CC_IRQn);} |
||||
void TIM2_IRQHandler(void) {handle_interrupt(TIM2_IRQn);} |
||||
void TIM3_IRQHandler(void) {handle_interrupt(TIM3_IRQn);} |
||||
void TIM4_IRQHandler(void) {handle_interrupt(TIM4_IRQn);} |
||||
void I2C1_EV_IRQHandler(void) {handle_interrupt(I2C1_EV_IRQn);} |
||||
void I2C1_ER_IRQHandler(void) {handle_interrupt(I2C1_ER_IRQn);} |
||||
void I2C2_EV_IRQHandler(void) {handle_interrupt(I2C2_EV_IRQn);} |
||||
void I2C2_ER_IRQHandler(void) {handle_interrupt(I2C2_ER_IRQn);} |
||||
void SPI1_IRQHandler(void) {handle_interrupt(SPI1_IRQn);} |
||||
void SPI2_IRQHandler(void) {handle_interrupt(SPI2_IRQn);} |
||||
void USART1_IRQHandler(void) {handle_interrupt(USART1_IRQn);} |
||||
void USART2_IRQHandler(void) {handle_interrupt(USART2_IRQn);} |
||||
void USART3_IRQHandler(void) {handle_interrupt(USART3_IRQn);} |
||||
void EXTI15_10_IRQHandler(void) {handle_interrupt(EXTI15_10_IRQn);} |
||||
void RTC_Alarm_IRQHandler(void) {handle_interrupt(RTC_Alarm_IRQn);} |
||||
void OTG_FS_WKUP_IRQHandler(void) {handle_interrupt(OTG_FS_WKUP_IRQn);} |
||||
void TIM8_BRK_TIM12_IRQHandler(void) {handle_interrupt(TIM8_BRK_TIM12_IRQn);} |
||||
void TIM8_UP_TIM13_IRQHandler(void) {handle_interrupt(TIM8_UP_TIM13_IRQn);} |
||||
void TIM8_TRG_COM_TIM14_IRQHandler(void) {handle_interrupt(TIM8_TRG_COM_TIM14_IRQn);} |
||||
void TIM8_CC_IRQHandler(void) {handle_interrupt(TIM8_CC_IRQn);} |
||||
void DMA1_Stream7_IRQHandler(void) {handle_interrupt(DMA1_Stream7_IRQn);} |
||||
void FSMC_IRQHandler(void) {handle_interrupt(FSMC_IRQn);} |
||||
void SDIO_IRQHandler(void) {handle_interrupt(SDIO_IRQn);} |
||||
void TIM5_IRQHandler(void) {handle_interrupt(TIM5_IRQn);} |
||||
void SPI3_IRQHandler(void) {handle_interrupt(SPI3_IRQn);} |
||||
void UART4_IRQHandler(void) {handle_interrupt(UART4_IRQn);} |
||||
void UART5_IRQHandler(void) {handle_interrupt(UART5_IRQn);} |
||||
void TIM6_DAC_IRQHandler(void) {handle_interrupt(TIM6_DAC_IRQn);} |
||||
void TIM7_IRQHandler(void) {handle_interrupt(TIM7_IRQn);} |
||||
void DMA2_Stream0_IRQHandler(void) {handle_interrupt(DMA2_Stream0_IRQn);} |
||||
void DMA2_Stream1_IRQHandler(void) {handle_interrupt(DMA2_Stream1_IRQn);} |
||||
void DMA2_Stream2_IRQHandler(void) {handle_interrupt(DMA2_Stream2_IRQn);} |
||||
void DMA2_Stream3_IRQHandler(void) {handle_interrupt(DMA2_Stream3_IRQn);} |
||||
void DMA2_Stream4_IRQHandler(void) {handle_interrupt(DMA2_Stream4_IRQn);} |
||||
void CAN2_TX_IRQHandler(void) {handle_interrupt(CAN2_TX_IRQn);} |
||||
void CAN2_RX0_IRQHandler(void) {handle_interrupt(CAN2_RX0_IRQn);} |
||||
void CAN2_RX1_IRQHandler(void) {handle_interrupt(CAN2_RX1_IRQn);} |
||||
void CAN2_SCE_IRQHandler(void) {handle_interrupt(CAN2_SCE_IRQn);} |
||||
void OTG_FS_IRQHandler(void) {handle_interrupt(OTG_FS_IRQn);} |
||||
void DMA2_Stream5_IRQHandler(void) {handle_interrupt(DMA2_Stream5_IRQn);} |
||||
void DMA2_Stream6_IRQHandler(void) {handle_interrupt(DMA2_Stream6_IRQn);} |
||||
void DMA2_Stream7_IRQHandler(void) {handle_interrupt(DMA2_Stream7_IRQn);} |
||||
void USART6_IRQHandler(void) {handle_interrupt(USART6_IRQn);} |
||||
void I2C3_EV_IRQHandler(void) {handle_interrupt(I2C3_EV_IRQn);} |
||||
void I2C3_ER_IRQHandler(void) {handle_interrupt(I2C3_ER_IRQn);} |
||||
#ifdef STM32F4 |
||||
void DFSDM1_FLT0_IRQHandler(void) {handle_interrupt(DFSDM1_FLT0_IRQn);} |
||||
void DFSDM1_FLT1_IRQHandler(void) {handle_interrupt(DFSDM1_FLT1_IRQn);} |
||||
void CAN3_TX_IRQHandler(void) {handle_interrupt(CAN3_TX_IRQn);} |
||||
void CAN3_RX0_IRQHandler(void) {handle_interrupt(CAN3_RX0_IRQn);} |
||||
void CAN3_RX1_IRQHandler(void) {handle_interrupt(CAN3_RX1_IRQn);} |
||||
void CAN3_SCE_IRQHandler(void) {handle_interrupt(CAN3_SCE_IRQn);} |
||||
void RNG_IRQHandler(void) {handle_interrupt(RNG_IRQn);} |
||||
void FPU_IRQHandler(void) {handle_interrupt(FPU_IRQn);} |
||||
void UART7_IRQHandler(void) {handle_interrupt(UART7_IRQn);} |
||||
void UART8_IRQHandler(void) {handle_interrupt(UART8_IRQn);} |
||||
void SPI4_IRQHandler(void) {handle_interrupt(SPI4_IRQn);} |
||||
void SPI5_IRQHandler(void) {handle_interrupt(SPI5_IRQn);} |
||||
void SAI1_IRQHandler(void) {handle_interrupt(SAI1_IRQn);} |
||||
void UART9_IRQHandler(void) {handle_interrupt(UART9_IRQn);} |
||||
void UART10_IRQHandler(void) {handle_interrupt(UART10_IRQn);} |
||||
void QUADSPI_IRQHandler(void) {handle_interrupt(QUADSPI_IRQn);} |
||||
void FMPI2C1_EV_IRQHandler(void) {handle_interrupt(FMPI2C1_EV_IRQn);} |
||||
void FMPI2C1_ER_IRQHandler(void) {handle_interrupt(FMPI2C1_ER_IRQn);} |
||||
void LPTIM1_IRQHandler(void) {handle_interrupt(LPTIM1_IRQn);} |
||||
void DFSDM2_FLT0_IRQHandler(void) {handle_interrupt(DFSDM2_FLT0_IRQn);} |
||||
void DFSDM2_FLT1_IRQHandler(void) {handle_interrupt(DFSDM2_FLT1_IRQn);} |
||||
void DFSDM2_FLT2_IRQHandler(void) {handle_interrupt(DFSDM2_FLT2_IRQn);} |
||||
void DFSDM2_FLT3_IRQHandler(void) {handle_interrupt(DFSDM2_FLT3_IRQn);} |
||||
#endif |
@ -0,0 +1,81 @@ |
||||
|
||||
typedef struct reg { |
||||
volatile uint32_t *address; |
||||
uint32_t value; |
||||
uint32_t check_mask; |
||||
} reg; |
||||
|
||||
// 10 bit hash with 23 as a prime
|
||||
#define REGISTER_MAP_SIZE 0x3FFU |
||||
#define HASHING_PRIME 23U |
||||
#define CHECK_COLLISION(hash, addr) (((uint32_t) register_map[hash].address != 0U) && (register_map[hash].address != addr)) |
||||
|
||||
reg register_map[REGISTER_MAP_SIZE]; |
||||
|
||||
// Hash spread in first and second iterations seems to be reasonable.
|
||||
// See: tests/development/register_hashmap_spread.py
|
||||
// Also, check the collision warnings in the debug output, and minimize those.
|
||||
uint16_t hash_addr(uint32_t input){ |
||||
return (((input >> 16U) ^ ((((input + 1U) & 0xFFFFU) * HASHING_PRIME) & 0xFFFFU)) & REGISTER_MAP_SIZE); |
||||
} |
||||
|
||||
// Do not put bits in the check mask that get changed by the hardware
|
||||
void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask){ |
||||
ENTER_CRITICAL() |
||||
// Set bits in register that are also in the mask
|
||||
(*addr) = ((*addr) & (~mask)) | (val & mask); |
||||
|
||||
// Add these values to the map
|
||||
uint16_t hash = hash_addr((uint32_t) addr); |
||||
uint16_t tries = REGISTER_MAP_SIZE; |
||||
while(CHECK_COLLISION(hash, addr) && (tries > 0U)) { hash = hash_addr((uint32_t) hash); tries--;} |
||||
if (tries != 0U){ |
||||
register_map[hash].address = addr; |
||||
register_map[hash].value = (register_map[hash].value & (~mask)) | (val & mask); |
||||
register_map[hash].check_mask |= mask; |
||||
} else { |
||||
#ifdef DEBUG_FAULTS |
||||
puts("Hash collision: address 0x"); puth((uint32_t) addr); puts("!\n"); |
||||
#endif |
||||
} |
||||
EXIT_CRITICAL() |
||||
} |
||||
|
||||
// Set individual bits. Also add them to the check_mask.
|
||||
// Do not use this to change bits that get reset by the hardware
|
||||
void register_set_bits(volatile uint32_t *addr, uint32_t val) { |
||||
return register_set(addr, val, val); |
||||
} |
||||
|
||||
// Clear individual bits. Also add them to the check_mask.
|
||||
// Do not use this to clear bits that get set by the hardware
|
||||
void register_clear_bits(volatile uint32_t *addr, uint32_t val) { |
||||
return register_set(addr, (~val), val); |
||||
} |
||||
|
||||
// To be called periodically
|
||||
void check_registers(void){ |
||||
for(uint16_t i=0U; i<REGISTER_MAP_SIZE; i++){ |
||||
if((uint32_t) register_map[i].address != 0U){ |
||||
ENTER_CRITICAL() |
||||
if((*(register_map[i].address) & register_map[i].check_mask) != (register_map[i].value & register_map[i].check_mask)){ |
||||
#ifdef DEBUG_FAULTS |
||||
puts("Register at address 0x"); puth((uint32_t) register_map[i].address); puts(" is divergent!"); |
||||
puts(" Map: 0x"); puth(register_map[i].value); |
||||
puts(" Register: 0x"); puth(*(register_map[i].address)); |
||||
puts(" Mask: 0x"); puth(register_map[i].check_mask); |
||||
puts("\n"); |
||||
#endif |
||||
fault_occurred(FAULT_REGISTER_DIVERGENT); |
||||
} |
||||
EXIT_CRITICAL() |
||||
} |
||||
} |
||||
} |
||||
|
||||
void init_registers(void) { |
||||
for(uint16_t i=0U; i<REGISTER_MAP_SIZE; i++){ |
||||
register_map[i].address = (volatile uint32_t *) 0U; |
||||
register_map[i].check_mask = 0U; |
||||
} |
||||
} |
@ -1,7 +1,7 @@ |
||||
void timer_init(TIM_TypeDef *TIM, int psc) { |
||||
TIM->PSC = psc-1; |
||||
TIM->DIER = TIM_DIER_UIE; |
||||
TIM->CR1 = TIM_CR1_CEN; |
||||
register_set(&(TIM->PSC), (psc-1), 0xFFFFU); |
||||
register_set(&(TIM->DIER), TIM_DIER_UIE, 0x5F5FU); |
||||
register_set(&(TIM->CR1), TIM_CR1_CEN, 0x3FU); |
||||
TIM->SR = 0; |
||||
} |
||||
|
||||
|
@ -0,0 +1,49 @@ |
||||
#define FAULT_STATUS_NONE 0U |
||||
#define FAULT_STATUS_TEMPORARY 1U |
||||
#define FAULT_STATUS_PERMANENT 2U |
||||
|
||||
// Fault types
|
||||
#define FAULT_RELAY_MALFUNCTION (1U << 0) |
||||
#define FAULT_UNUSED_INTERRUPT_HANDLED (1U << 1) |
||||
#define FAULT_INTERRUPT_RATE_CAN_1 (1U << 2) |
||||
#define FAULT_INTERRUPT_RATE_CAN_2 (1U << 3) |
||||
#define FAULT_INTERRUPT_RATE_CAN_3 (1U << 4) |
||||
#define FAULT_INTERRUPT_RATE_TACH (1U << 5) |
||||
#define FAULT_INTERRUPT_RATE_GMLAN (1U << 6) |
||||
#define FAULT_INTERRUPT_RATE_INTERRUPTS (1U << 7) |
||||
#define FAULT_INTERRUPT_RATE_SPI_DMA (1U << 8) |
||||
#define FAULT_INTERRUPT_RATE_SPI_CS (1U << 9) |
||||
#define FAULT_INTERRUPT_RATE_UART_1 (1U << 10) |
||||
#define FAULT_INTERRUPT_RATE_UART_2 (1U << 11) |
||||
#define FAULT_INTERRUPT_RATE_UART_3 (1U << 12) |
||||
#define FAULT_INTERRUPT_RATE_UART_5 (1U << 13) |
||||
#define FAULT_INTERRUPT_RATE_UART_DMA (1U << 14) |
||||
#define FAULT_INTERRUPT_RATE_USB (1U << 15) |
||||
#define FAULT_INTERRUPT_RATE_TIM1 (1U << 16) |
||||
#define FAULT_INTERRUPT_RATE_TIM3 (1U << 17) |
||||
#define FAULT_REGISTER_DIVERGENT (1U << 18) |
||||
|
||||
// Permanent faults
|
||||
#define PERMANENT_FAULTS 0U |
||||
|
||||
uint8_t fault_status = FAULT_STATUS_NONE; |
||||
uint32_t faults = 0U; |
||||
|
||||
void fault_occurred(uint32_t fault) { |
||||
faults |= fault; |
||||
if((PERMANENT_FAULTS & fault) != 0U){ |
||||
puts("Permanent fault occurred: 0x"); puth(fault); puts("\n"); |
||||
fault_status = FAULT_STATUS_PERMANENT; |
||||
} else { |
||||
puts("Temporary fault occurred: 0x"); puth(fault); puts("\n"); |
||||
fault_status = FAULT_STATUS_TEMPORARY; |
||||
} |
||||
} |
||||
|
||||
void fault_recovered(uint32_t fault) { |
||||
if((PERMANENT_FAULTS & fault) == 0U){ |
||||
faults &= ~fault; |
||||
} else { |
||||
puts("Cannot recover from a permanent fault!\n"); |
||||
} |
||||
} |
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d0d192a64b7d859a76a631cf9bfdb865825ca83172a455eff661217c56392638 |
||||
size 1145211 |
||||
oid sha256:7c2cb1deecee360e4e8069c4215f05f652e14c70bd88f9bd072dc5502a5fd530 |
||||
size 1145325 |
||||
|
@ -1,59 +0,0 @@ |
||||
#!/usr/bin/env python3 |
||||
from panda import Panda |
||||
from panda.python.uds import UdsClient, NegativeResponseError, DATA_IDENTIFIER_TYPE |
||||
|
||||
if __name__ == "__main__": |
||||
address = 0x18da30f1 # Honda EPS |
||||
panda = Panda() |
||||
uds_client = UdsClient(panda, address, debug=False) |
||||
|
||||
print("tester present ...") |
||||
uds_client.tester_present() |
||||
|
||||
try: |
||||
print("") |
||||
print("read data by id: boot software id ...") |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_IDENTIFICATION) |
||||
print(data.decode('utf-8')) |
||||
except NegativeResponseError as e: |
||||
print(e) |
||||
|
||||
try: |
||||
print("") |
||||
print("read data by id: application software id ...") |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) |
||||
print(data.decode('utf-8')) |
||||
except NegativeResponseError as e: |
||||
print(e) |
||||
|
||||
try: |
||||
print("") |
||||
print("read data by id: application data id ...") |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION) |
||||
print(data.decode('utf-8')) |
||||
except NegativeResponseError as e: |
||||
print(e) |
||||
|
||||
try: |
||||
print("") |
||||
print("read data by id: boot software fingerprint ...") |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_FINGERPRINT) |
||||
print(data.decode('utf-8')) |
||||
except NegativeResponseError as e: |
||||
print(e) |
||||
|
||||
try: |
||||
print("") |
||||
print("read data by id: application software fingerprint ...") |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_FINGERPRINT) |
||||
print(data.decode('utf-8')) |
||||
except NegativeResponseError as e: |
||||
print(e) |
||||
|
||||
try: |
||||
print("") |
||||
print("read data by id: application data fingerprint ...") |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_FINGERPRINT) |
||||
print(data.decode('utf-8')) |
||||
except NegativeResponseError as e: |
||||
print(e) |
@ -0,0 +1,77 @@ |
||||
#!/usr/bin/env python3 |
||||
from tqdm import tqdm |
||||
from panda import Panda |
||||
from panda.python.uds import UdsClient, MessageTimeoutError, NegativeResponseError, SESSION_TYPE, DATA_IDENTIFIER_TYPE |
||||
|
||||
if __name__ == "__main__": |
||||
addrs = [0x700 + i for i in range(256)] |
||||
addrs += [0x18da0000 + (i<<8) + 0xf1 for i in range(256)] |
||||
results = {} |
||||
|
||||
panda = Panda() |
||||
panda.set_safety_mode(Panda.SAFETY_ELM327) |
||||
print("querying addresses ...") |
||||
with tqdm(addrs) as t: |
||||
for addr in t: |
||||
# skip functional broadcast addrs |
||||
if addr == 0x7df or addr == 0x18db33f1: |
||||
continue |
||||
t.set_description(hex(addr)) |
||||
|
||||
uds_client = UdsClient(panda, addr, bus=1 if panda.has_obd() else 0, timeout=0.1, debug=False) |
||||
try: |
||||
uds_client.tester_present() |
||||
uds_client.diagnostic_session_control(SESSION_TYPE.EXTENDED_DIAGNOSTIC) |
||||
except NegativeResponseError: |
||||
pass |
||||
except MessageTimeoutError: |
||||
continue |
||||
|
||||
resp = {} |
||||
|
||||
try: |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_IDENTIFICATION) |
||||
if data: resp[DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_IDENTIFICATION] = data |
||||
except NegativeResponseError: |
||||
pass |
||||
|
||||
try: |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) |
||||
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION] = data |
||||
except NegativeResponseError: |
||||
pass |
||||
|
||||
try: |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION) |
||||
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION] = data |
||||
except NegativeResponseError: |
||||
pass |
||||
|
||||
try: |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_FINGERPRINT) |
||||
if data: resp[DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_FINGERPRINT] = data |
||||
except NegativeResponseError: |
||||
pass |
||||
|
||||
try: |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_FINGERPRINT) |
||||
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_FINGERPRINT] = data |
||||
except NegativeResponseError: |
||||
pass |
||||
|
||||
try: |
||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_FINGERPRINT) |
||||
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_DATA_FINGERPRINT] = data |
||||
except NegativeResponseError: |
||||
pass |
||||
|
||||
if resp.keys(): |
||||
results[addr] = resp |
||||
|
||||
print("results:") |
||||
if len(results.items()): |
||||
for addr, resp in results.items(): |
||||
for rid, dat in resp.items(): |
||||
print(hex(addr), hex(rid), dat.decode()) |
||||
else: |
||||
print("no fw versions found!") |
@ -1,11 +1,34 @@ |
||||
from .helpers import test_all_pandas, panda_connect_and_init |
||||
import os |
||||
|
||||
from nose.tools import assert_equal |
||||
|
||||
from panda import Panda, BASEDIR |
||||
from .helpers import reset_pandas, test_all_pandas, panda_connect_and_init |
||||
|
||||
|
||||
# Reset the pandas before flashing them |
||||
def aaaa_reset_before_tests(): |
||||
reset_pandas() |
||||
|
||||
|
||||
@test_all_pandas |
||||
@panda_connect_and_init |
||||
def test_recover(p): |
||||
assert p.recover(timeout=30) |
||||
|
||||
|
||||
@test_all_pandas |
||||
@panda_connect_and_init |
||||
def test_flash(p): |
||||
p.flash() |
||||
|
||||
|
||||
@test_all_pandas |
||||
@panda_connect_and_init |
||||
def test_get_signature(p): |
||||
fn = os.path.join(BASEDIR, "board/obj/panda.bin") |
||||
|
||||
firmware_sig = Panda.get_signature_from_firmware(fn) |
||||
panda_sig = p.get_signature() |
||||
|
||||
assert_equal(panda_sig, firmware_sig) |
||||
|
@ -0,0 +1,44 @@ |
||||
import time |
||||
from panda_jungle import PandaJungle # pylint: disable=import-error |
||||
from .helpers import panda_jungle, reset_pandas, test_all_pandas, test_all_gen2_pandas, panda_connect_and_init |
||||
|
||||
# Reset the pandas before running tests |
||||
def aaaa_reset_before_tests(): |
||||
reset_pandas() |
||||
|
||||
@test_all_pandas |
||||
@panda_connect_and_init |
||||
def test_ignition(p): |
||||
try: |
||||
# Set harness orientation to #2, since the ignition line is on the wrong SBU bus :/ |
||||
panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_2) |
||||
reset_pandas() |
||||
p.reconnect() |
||||
panda_jungle.set_ignition(False) |
||||
time.sleep(2) |
||||
assert p.health()['ignition_line'] == False |
||||
panda_jungle.set_ignition(True) |
||||
time.sleep(2) |
||||
assert p.health()['ignition_line'] == True |
||||
finally: |
||||
panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_1) |
||||
|
||||
@test_all_gen2_pandas |
||||
@panda_connect_and_init |
||||
def test_orientation_detection(p): |
||||
seen_orientations = [] |
||||
for i in range(3): |
||||
panda_jungle.set_harness_orientation(i) |
||||
reset_pandas() |
||||
p.reconnect() |
||||
detected_harness_orientation = p.health()['car_harness_status'] |
||||
if (i == 0 and detected_harness_orientation != 0) or detected_harness_orientation in seen_orientations: |
||||
assert False |
||||
seen_orientations.append(detected_harness_orientation) |
||||
|
||||
|
||||
@test_all_pandas |
||||
@panda_connect_and_init |
||||
def test_voltage(p): |
||||
voltage = p.health()['voltage'] |
||||
assert ((voltage > 10000) and (voltage < 14000)) |
@ -1,8 +1,12 @@ |
||||
import time |
||||
from panda import Panda |
||||
from .helpers import connect_wifi, test_white, test_all_pandas, panda_type_to_serial, panda_connect_and_init |
||||
from .helpers import reset_pandas, connect_wifi, test_white, test_all_pandas, panda_type_to_serial, panda_connect_and_init |
||||
import requests |
||||
|
||||
# Reset the pandas before running tests |
||||
def aaaa_reset_before_tests(): |
||||
reset_pandas() |
||||
|
||||
@test_all_pandas |
||||
@panda_connect_and_init |
||||
def test_get_serial(p): |
@ -1,195 +0,0 @@ |
||||
|
||||
import os |
||||
import time |
||||
import random |
||||
from panda import Panda |
||||
from nose.tools import assert_equal, assert_less, assert_greater |
||||
from .helpers import time_many_sends, test_two_panda, test_two_black_panda, panda_type_to_serial, clear_can_buffers, panda_connect_and_init |
||||
|
||||
@test_two_panda |
||||
@panda_type_to_serial |
||||
@panda_connect_and_init |
||||
def test_send_recv(p_send, p_recv): |
||||
p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
p_recv.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
p_send.set_can_loopback(False) |
||||
p_recv.set_can_loopback(False) |
||||
|
||||
assert not p_send.legacy |
||||
assert not p_recv.legacy |
||||
|
||||
p_send.can_send_many([(0x1ba, 0, b"message", 0)]*2) |
||||
time.sleep(0.05) |
||||
p_recv.can_recv() |
||||
p_send.can_recv() |
||||
|
||||
busses = [0,1,2] |
||||
|
||||
for bus in busses: |
||||
for speed in [100, 250, 500, 750, 1000]: |
||||
p_send.set_can_speed_kbps(bus, speed) |
||||
p_recv.set_can_speed_kbps(bus, speed) |
||||
time.sleep(0.05) |
||||
|
||||
comp_kbps = time_many_sends(p_send, bus, p_recv, two_pandas=True) |
||||
|
||||
saturation_pct = (comp_kbps/speed) * 100.0 |
||||
assert_greater(saturation_pct, 80) |
||||
assert_less(saturation_pct, 100) |
||||
|
||||
print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct)) |
||||
|
||||
@test_two_panda |
||||
@panda_type_to_serial |
||||
@panda_connect_and_init |
||||
def test_latency(p_send, p_recv): |
||||
p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
p_recv.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
p_send.set_can_loopback(False) |
||||
p_recv.set_can_loopback(False) |
||||
|
||||
assert not p_send.legacy |
||||
assert not p_recv.legacy |
||||
|
||||
p_send.set_can_speed_kbps(0, 100) |
||||
p_recv.set_can_speed_kbps(0, 100) |
||||
time.sleep(0.05) |
||||
|
||||
p_send.can_send_many([(0x1ba, 0, b"testmsg", 0)]*10) |
||||
time.sleep(0.05) |
||||
p_recv.can_recv() |
||||
p_send.can_recv() |
||||
|
||||
busses = [0,1,2] |
||||
|
||||
for bus in busses: |
||||
for speed in [100, 250, 500, 750, 1000]: |
||||
p_send.set_can_speed_kbps(bus, speed) |
||||
p_recv.set_can_speed_kbps(bus, speed) |
||||
time.sleep(0.1) |
||||
|
||||
#clear can buffers |
||||
clear_can_buffers(p_send) |
||||
clear_can_buffers(p_recv) |
||||
|
||||
latencies = [] |
||||
comp_kbps_list = [] |
||||
saturation_pcts = [] |
||||
|
||||
num_messages = 100 |
||||
|
||||
for i in range(num_messages): |
||||
st = time.time() |
||||
p_send.can_send(0x1ab, b"message", bus) |
||||
r = [] |
||||
while len(r) < 1 and (time.time() - st) < 5: |
||||
r = p_recv.can_recv() |
||||
et = time.time() |
||||
r_echo = [] |
||||
while len(r_echo) < 1 and (time.time() - st) < 10: |
||||
r_echo = p_send.can_recv() |
||||
|
||||
if len(r) == 0 or len(r_echo) == 0: |
||||
print("r: {}, r_echo: {}".format(r, r_echo)) |
||||
|
||||
assert_equal(len(r),1) |
||||
assert_equal(len(r_echo),1) |
||||
|
||||
et = (et - st)*1000.0 |
||||
comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7) / et |
||||
latency = et - ((1+11+1+1+1+4+8*8+15+1+1+1+7) / speed) |
||||
|
||||
assert_less(latency, 5.0) |
||||
|
||||
saturation_pct = (comp_kbps/speed) * 100.0 |
||||
latencies.append(latency) |
||||
comp_kbps_list.append(comp_kbps) |
||||
saturation_pcts.append(saturation_pct) |
||||
|
||||
average_latency = sum(latencies)/num_messages |
||||
assert_less(average_latency, 1.0) |
||||
average_comp_kbps = sum(comp_kbps_list)/num_messages |
||||
average_saturation_pct = sum(saturation_pcts)/num_messages |
||||
|
||||
print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\ |
||||
.format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct)) |
||||
|
||||
@test_two_black_panda |
||||
@panda_type_to_serial |
||||
@panda_connect_and_init |
||||
def test_black_loopback(panda0, panda1): |
||||
# disable safety modes |
||||
panda0.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
panda1.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
|
||||
# disable loopback |
||||
panda0.set_can_loopback(False) |
||||
panda1.set_can_loopback(False) |
||||
|
||||
# clear stuff |
||||
panda0.can_send_many([(0x1ba, 0, b"testmsg", 0)]*10) |
||||
time.sleep(0.05) |
||||
panda0.can_recv() |
||||
panda1.can_recv() |
||||
|
||||
# test array (send bus, sender obd, reciever obd, expected busses) |
||||
test_array = [ |
||||
(0, False, False, [0]), |
||||
(1, False, False, [1]), |
||||
(2, False, False, [2]), |
||||
(0, False, True, [0, 1]), |
||||
(1, False, True, []), |
||||
(2, False, True, [2]), |
||||
(0, True, False, [0]), |
||||
(1, True, False, [0]), |
||||
(2, True, False, [2]), |
||||
(0, True, True, [0, 1]), |
||||
(1, True, True, [0, 1]), |
||||
(2, True, True, [2]) |
||||
] |
||||
|
||||
# test functions |
||||
def get_test_string(): |
||||
return b"test"+os.urandom(10) |
||||
|
||||
def _test_buses(send_panda, recv_panda, _test_array): |
||||
for send_bus, send_obd, recv_obd, recv_buses in _test_array: |
||||
print("\nSend bus:", send_bus, " Send OBD:", send_obd, " Recv OBD:", recv_obd) |
||||
|
||||
# set OBD on pandas |
||||
send_panda.set_gmlan(True if send_obd else None) |
||||
recv_panda.set_gmlan(True if recv_obd else None) |
||||
|
||||
# clear buffers |
||||
clear_can_buffers(send_panda) |
||||
clear_can_buffers(recv_panda) |
||||
|
||||
# send the characters |
||||
at = random.randint(1, 2000) |
||||
st = get_test_string()[0:8] |
||||
send_panda.can_send(at, st, send_bus) |
||||
time.sleep(0.1) |
||||
|
||||
# check for receive |
||||
_ = send_panda.can_recv() # cans echo |
||||
cans_loop = recv_panda.can_recv() |
||||
|
||||
loop_buses = [] |
||||
for loop in cans_loop: |
||||
print(" Loop on bus", str(loop[3])) |
||||
loop_buses.append(loop[3]) |
||||
if len(cans_loop) == 0: |
||||
print(" No loop") |
||||
|
||||
# test loop buses |
||||
recv_buses.sort() |
||||
loop_buses.sort() |
||||
assert recv_buses == loop_buses |
||||
print(" TEST PASSED") |
||||
print("\n") |
||||
|
||||
# test both orientations |
||||
print("***************** TESTING (0 --> 1) *****************") |
||||
_test_buses(panda0, panda1, test_array) |
||||
print("***************** TESTING (1 --> 0) *****************") |
||||
_test_buses(panda1, panda0, test_array) |
@ -1,16 +1,23 @@ |
||||
|
||||
import sys |
||||
import time |
||||
from .helpers import time_many_sends, connect_wifi, test_white, panda_type_to_serial |
||||
from .helpers import start_heartbeat_thread, reset_pandas, time_many_sends, connect_wifi, test_white, panda_type_to_serial |
||||
from panda import Panda, PandaWifiStreaming |
||||
from nose.tools import assert_less, assert_greater |
||||
|
||||
# Reset the pandas before running tests |
||||
def aaaa_reset_before_tests(): |
||||
reset_pandas() |
||||
|
||||
@test_white |
||||
@panda_type_to_serial |
||||
def test_udp_doesnt_drop(serials=None): |
||||
connect_wifi(serials[0]) |
||||
|
||||
p = Panda(serials[0]) |
||||
|
||||
# Start heartbeat |
||||
start_heartbeat_thread(p) |
||||
|
||||
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
p.set_can_loopback(True) |
||||
|
@ -0,0 +1,202 @@ |
||||
import os |
||||
import time |
||||
import random |
||||
from panda import Panda |
||||
from nose.tools import assert_equal, assert_less, assert_greater |
||||
from .helpers import panda_jungle, start_heartbeat_thread, reset_pandas, time_many_sends, test_all_pandas, test_all_gen2_pandas, clear_can_buffers, panda_connect_and_init |
||||
|
||||
# Reset the pandas before running tests |
||||
def aaaa_reset_before_tests(): |
||||
reset_pandas() |
||||
|
||||
@test_all_pandas |
||||
@panda_connect_and_init |
||||
def test_send_recv(p): |
||||
def test(p_send, p_recv): |
||||
p_send.set_can_loopback(False) |
||||
p_recv.set_can_loopback(False) |
||||
|
||||
p_send.can_send_many([(0x1ba, 0, b"message", 0)]*2) |
||||
time.sleep(0.05) |
||||
p_recv.can_recv() |
||||
p_send.can_recv() |
||||
|
||||
busses = [0,1,2] |
||||
|
||||
for bus in busses: |
||||
for speed in [100, 250, 500, 750, 1000]: |
||||
p_send.set_can_speed_kbps(bus, speed) |
||||
p_recv.set_can_speed_kbps(bus, speed) |
||||
time.sleep(0.05) |
||||
|
||||
clear_can_buffers(p_send) |
||||
clear_can_buffers(p_recv) |
||||
|
||||
comp_kbps = time_many_sends(p_send, bus, p_recv, two_pandas=True) |
||||
|
||||
saturation_pct = (comp_kbps/speed) * 100.0 |
||||
assert_greater(saturation_pct, 80) |
||||
assert_less(saturation_pct, 100) |
||||
|
||||
print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct)) |
||||
|
||||
# Start heartbeat |
||||
start_heartbeat_thread(p) |
||||
|
||||
# Set safety mode and power saving |
||||
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
p.set_power_save(False) |
||||
|
||||
try: |
||||
# Run tests in both directions |
||||
test(p, panda_jungle) |
||||
test(panda_jungle, p) |
||||
except Exception as e: |
||||
# Raise errors again, we don't want them to get lost |
||||
raise e |
||||
finally: |
||||
# Set back to silent mode |
||||
p.set_safety_mode(Panda.SAFETY_SILENT) |
||||
|
||||
@test_all_pandas |
||||
@panda_connect_and_init |
||||
def test_latency(p): |
||||
def test(p_send, p_recv): |
||||
p_send.set_can_loopback(False) |
||||
p_recv.set_can_loopback(False) |
||||
|
||||
p_send.set_can_speed_kbps(0, 100) |
||||
p_recv.set_can_speed_kbps(0, 100) |
||||
time.sleep(0.05) |
||||
|
||||
p_send.can_send_many([(0x1ba, 0, b"testmsg", 0)]*10) |
||||
time.sleep(0.05) |
||||
p_recv.can_recv() |
||||
p_send.can_recv() |
||||
|
||||
busses = [0,1,2] |
||||
|
||||
for bus in busses: |
||||
for speed in [100, 250, 500, 750, 1000]: |
||||
p_send.set_can_speed_kbps(bus, speed) |
||||
p_recv.set_can_speed_kbps(bus, speed) |
||||
time.sleep(0.1) |
||||
|
||||
# clear can buffers |
||||
clear_can_buffers(p_send) |
||||
clear_can_buffers(p_recv) |
||||
|
||||
latencies = [] |
||||
comp_kbps_list = [] |
||||
saturation_pcts = [] |
||||
|
||||
num_messages = 100 |
||||
|
||||
for i in range(num_messages): |
||||
st = time.time() |
||||
p_send.can_send(0x1ab, b"message", bus) |
||||
r = [] |
||||
while len(r) < 1 and (time.time() - st) < 5: |
||||
r = p_recv.can_recv() |
||||
et = time.time() |
||||
r_echo = [] |
||||
while len(r_echo) < 1 and (time.time() - st) < 10: |
||||
r_echo = p_send.can_recv() |
||||
|
||||
if len(r) == 0 or len(r_echo) == 0: |
||||
print("r: {}, r_echo: {}".format(r, r_echo)) |
||||
|
||||
assert_equal(len(r),1) |
||||
assert_equal(len(r_echo),1) |
||||
|
||||
et = (et - st)*1000.0 |
||||
comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7) / et |
||||
latency = et - ((1+11+1+1+1+4+8*8+15+1+1+1+7) / speed) |
||||
|
||||
assert_less(latency, 5.0) |
||||
|
||||
saturation_pct = (comp_kbps/speed) * 100.0 |
||||
latencies.append(latency) |
||||
comp_kbps_list.append(comp_kbps) |
||||
saturation_pcts.append(saturation_pct) |
||||
|
||||
average_latency = sum(latencies)/num_messages |
||||
assert_less(average_latency, 1.0) |
||||
average_comp_kbps = sum(comp_kbps_list)/num_messages |
||||
average_saturation_pct = sum(saturation_pcts)/num_messages |
||||
|
||||
print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\ |
||||
.format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct)) |
||||
|
||||
# Start heartbeat |
||||
start_heartbeat_thread(p) |
||||
|
||||
# Set safety mode and power saving |
||||
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
p.set_power_save(False) |
||||
|
||||
try: |
||||
# Run tests in both directions |
||||
test(p, panda_jungle) |
||||
test(panda_jungle, p) |
||||
except Exception as e: |
||||
# Raise errors again, we don't want them to get lost |
||||
raise e |
||||
finally: |
||||
# Set back to silent mode |
||||
p.set_safety_mode(Panda.SAFETY_SILENT) |
||||
|
||||
|
||||
@test_all_gen2_pandas |
||||
@panda_connect_and_init |
||||
def test_gen2_loopback(p): |
||||
def test(p_send, p_recv): |
||||
for bus in range(4): |
||||
obd = False |
||||
if bus == 3: |
||||
obd = True |
||||
bus = 1 |
||||
|
||||
# Clear buses |
||||
clear_can_buffers(p_send) |
||||
clear_can_buffers(p_recv) |
||||
|
||||
# Send a random string |
||||
addr = random.randint(1, 2000) |
||||
string = b"test"+os.urandom(4) |
||||
p_send.set_obd(obd) |
||||
p_recv.set_obd(obd) |
||||
time.sleep(0.2) |
||||
p_send.can_send(addr, string, bus) |
||||
time.sleep(0.2) |
||||
|
||||
content = p_recv.can_recv() |
||||
|
||||
# Check amount of messages |
||||
assert len(content) == 1 |
||||
|
||||
# Check content |
||||
assert content[0][0] == addr and content[0][2] == string |
||||
|
||||
# Check bus |
||||
assert content[0][3] == bus |
||||
|
||||
print("Bus:", bus, "OBD:", obd, "OK") |
||||
|
||||
# Start heartbeat |
||||
start_heartbeat_thread(p) |
||||
|
||||
# Set safety mode and power saving |
||||
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
p.set_power_save(False) |
||||
|
||||
try: |
||||
# Run tests in both directions |
||||
test(p, panda_jungle) |
||||
test(panda_jungle, p) |
||||
except Exception as e: |
||||
# Raise errors again, we don't want them to get lost |
||||
raise e |
||||
finally: |
||||
# Set back to silent mode |
||||
p.set_safety_mode(Panda.SAFETY_SILENT) |
@ -0,0 +1,25 @@ |
||||
import time |
||||
from multiprocessing import Process |
||||
|
||||
# Note: this does not return any return values of the function, just the exit status |
||||
INTERVAL = 0.1 |
||||
def run_with_timeout(timeout, fn, *kwargs): |
||||
def runner(fn, kwargs): |
||||
try: |
||||
fn(*kwargs) |
||||
except Exception as e: |
||||
print(e) |
||||
raise e |
||||
|
||||
process = Process(target=runner, args=(fn, kwargs)) |
||||
process.start() |
||||
|
||||
counter = 0 |
||||
while process.is_alive(): |
||||
time.sleep(INTERVAL) |
||||
counter+=1 |
||||
if (counter * INTERVAL) > timeout: |
||||
process.terminate() |
||||
raise TimeoutError("Function timed out!") |
||||
if process.exitcode != 0: |
||||
raise RuntimeError("Test failed with exit code: ", str(process.exitcode)) |
@ -0,0 +1,85 @@ |
||||
import os |
||||
FNULL = open(os.devnull, 'w') |
||||
def _connect_wifi(dongle_id, pw, insecure_okay=False): |
||||
ssid = "panda-" + dongle_id |
||||
|
||||
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) |
||||
if not r: |
||||
# Can already ping, try connecting on wifi |
||||
try: |
||||
p = Panda("WIFI") |
||||
p.get_serial() |
||||
print("Already connected") |
||||
return |
||||
except: |
||||
pass |
||||
|
||||
print("WIFI: connecting to %s" % ssid) |
||||
|
||||
while 1: |
||||
if sys.platform == "darwin": |
||||
os.system("networksetup -setairportnetwork en0 %s %s" % (ssid, pw)) |
||||
else: |
||||
wlan_interface = subprocess.check_output(["sh", "-c", "iw dev | awk '/Interface/ {print $2}'"]).strip().decode('utf8') |
||||
cnt = 0 |
||||
MAX_TRIES = 10 |
||||
while cnt < MAX_TRIES: |
||||
print("WIFI: scanning %d" % cnt) |
||||
os.system("iwlist %s scanning > /dev/null" % wlan_interface) |
||||
os.system("nmcli device wifi rescan") |
||||
wifi_networks = [x.decode("utf8") for x in subprocess.check_output(["nmcli","dev", "wifi", "list"]).split(b"\n")] |
||||
wifi_scan = [x for x in wifi_networks if ssid in x] |
||||
if len(wifi_scan) != 0: |
||||
break |
||||
time.sleep(0.1) |
||||
# MAX_TRIES tries, ~10 seconds max |
||||
cnt += 1 |
||||
assert cnt < MAX_TRIES |
||||
if "-pair" in wifi_scan[0]: |
||||
os.system("nmcli d wifi connect %s-pair" % (ssid)) |
||||
connect_cnt = 0 |
||||
MAX_TRIES = 100 |
||||
while connect_cnt < MAX_TRIES: |
||||
connect_cnt += 1 |
||||
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) |
||||
if r: |
||||
print("Waiting for panda to ping...") |
||||
time.sleep(0.5) |
||||
else: |
||||
break |
||||
if insecure_okay: |
||||
break |
||||
# fetch webpage |
||||
print("connecting to insecure network to secure") |
||||
try: |
||||
r = requests.get("http://192.168.0.10/") |
||||
except requests.ConnectionError: |
||||
r = requests.get("http://192.168.0.10/") |
||||
assert r.status_code==200 |
||||
|
||||
print("securing") |
||||
try: |
||||
r = requests.get("http://192.168.0.10/secure", timeout=0.01) |
||||
except requests.exceptions.Timeout: |
||||
print("timeout http request to secure") |
||||
pass |
||||
else: |
||||
ret = os.system("nmcli d wifi connect %s password %s" % (ssid, pw)) |
||||
if os.WEXITSTATUS(ret) == 0: |
||||
#check ping too |
||||
ping_ok = False |
||||
connect_cnt = 0 |
||||
MAX_TRIES = 10 |
||||
while connect_cnt < MAX_TRIES: |
||||
connect_cnt += 1 |
||||
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) |
||||
if r: |
||||
print("Waiting for panda to ping...") |
||||
time.sleep(0.1) |
||||
else: |
||||
ping_ok = True |
||||
break |
||||
if ping_ok: |
||||
break |
||||
|
||||
# TODO: confirm that it's connected to the right panda |
@ -0,0 +1,50 @@ |
||||
#!/usr/bin/env python3 |
||||
import matplotlib.pyplot as plt # pylint: disable=import-error |
||||
|
||||
HASHING_PRIME = 23 |
||||
REGISTER_MAP_SIZE = 0x3FF |
||||
BYTES_PER_REG = 4 |
||||
|
||||
# From ST32F413 datasheet |
||||
REGISTER_ADDRESS_REGIONS = [ |
||||
(0x40000000, 0x40007FFF), |
||||
(0x40010000, 0x400107FF), |
||||
(0x40011000, 0x400123FF), |
||||
(0x40012C00, 0x40014BFF), |
||||
(0x40015000, 0x400153FF), |
||||
(0x40015800, 0x40015BFF), |
||||
(0x40016000, 0x400167FF), |
||||
(0x40020000, 0x40021FFF), |
||||
(0x40023000, 0x400233FF), |
||||
(0x40023800, 0x40023FFF), |
||||
(0x40026000, 0x400267FF), |
||||
(0x50000000, 0x5003FFFF), |
||||
(0x50060000, 0x500603FF), |
||||
(0x50060800, 0x50060BFF), |
||||
(0x50060800, 0x50060BFF), |
||||
(0xE0000000, 0xE00FFFFF) |
||||
] |
||||
|
||||
def hash(reg_addr): |
||||
return (((reg_addr >> 16) ^ ((((reg_addr + 1) & 0xFFFF) * HASHING_PRIME) & 0xFFFF)) & REGISTER_MAP_SIZE) |
||||
|
||||
# Calculate hash for each address |
||||
hashes = [] |
||||
double_hashes = [] |
||||
for (start_addr, stop_addr) in REGISTER_ADDRESS_REGIONS: |
||||
for addr in range(start_addr, stop_addr+1, BYTES_PER_REG): |
||||
h = hash(addr) |
||||
hashes.append(h) |
||||
double_hashes.append(hash(h)) |
||||
|
||||
# Make histograms |
||||
plt.subplot(2, 1, 1) |
||||
plt.hist(hashes, bins=REGISTER_MAP_SIZE) |
||||
plt.title("Number of collisions per hash") |
||||
plt.xlabel("Address") |
||||
|
||||
plt.subplot(2, 1, 2) |
||||
plt.hist(double_hashes, bins=REGISTER_MAP_SIZE) |
||||
plt.title("Number of collisions per double hash") |
||||
plt.xlabel("Address") |
||||
plt.show() |
@ -0,0 +1,35 @@ |
||||
#!/usr/bin/env python3 |
||||
import os |
||||
import sys |
||||
import time |
||||
import _thread |
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) |
||||
from panda import Panda |
||||
|
||||
# This script is intended to be used in conjunction with the echo_loopback_test.py test script from panda jungle. |
||||
# It sends a reversed response back for every message received containing b"test". |
||||
|
||||
def heartbeat_thread(p): |
||||
while True: |
||||
try: |
||||
p.send_heartbeat() |
||||
time.sleep(1) |
||||
except: |
||||
break |
||||
|
||||
# Resend every CAN message that has been received on the same bus, but with the data reversed |
||||
if __name__ == "__main__": |
||||
p = Panda() |
||||
_thread.start_new_thread(heartbeat_thread, (p,)) |
||||
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||
p.set_power_save(False) |
||||
|
||||
while True: |
||||
incoming = p.can_recv() |
||||
for message in incoming: |
||||
address, notused, data, bus = message |
||||
if b'test' in data: |
||||
p.can_send(address, data[::-1], bus) |
||||
|
||||
|
@ -0,0 +1 @@ |
||||
cppcheck/ |
@ -0,0 +1,36 @@ |
||||
from panda.tests.safety import libpandasafety_py |
||||
|
||||
def make_msg(bus, addr, length=8): |
||||
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') |
||||
if addr >= 0x800: |
||||
to_send[0].RIR = (addr << 3) | 5 |
||||
else: |
||||
to_send[0].RIR = (addr << 21) | 1 |
||||
to_send[0].RDTR = length |
||||
to_send[0].RDTR |= bus << 4 |
||||
|
||||
return to_send |
||||
|
||||
def test_relay_malfunction(test, addr): |
||||
# input is a test class and the address that, if seen on bus 0, triggers |
||||
# the relay_malfunction protection logic: both tx_hook and fwd_hook are |
||||
# expected to return failure |
||||
test.assertFalse(test.safety.get_relay_malfunction()) |
||||
test.safety.safety_rx_hook(make_msg(0, addr, 8)) |
||||
test.assertTrue(test.safety.get_relay_malfunction()) |
||||
for a in range(1, 0x800): |
||||
for b in range(0, 3): |
||||
test.assertFalse(test.safety.safety_tx_hook(make_msg(b, a, 8))) |
||||
test.assertEqual(-1, test.safety.safety_fwd_hook(b, make_msg(b, a, 8))) |
||||
|
||||
def test_manually_enable_controls_allowed(test): |
||||
test.safety.set_controls_allowed(1) |
||||
test.assertTrue(test.safety.get_controls_allowed()) |
||||
test.safety.set_controls_allowed(0) |
||||
test.assertFalse(test.safety.get_controls_allowed()) |
||||
|
||||
def test_spam_can_buses(test, TX_MSGS): |
||||
for addr in range(1, 0x800): |
||||
for bus in range(0, 4): |
||||
if all(addr != m[0] or bus != m[1] for m in TX_MSGS): |
||||
test.assertFalse(test.safety.safety_tx_hook(make_msg(bus, addr, 8))) |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue