67d5208 fix signedness issue in toyota safety fe15d3f bump pandacan 11c2b08 add fault invalid 2c26e45 add sleep 27c7637 forgot the counter 3a6d7db don't hang bfa7d2e canloader works b259e2a can flasher is close to working 83f2edf isotp can support in softloader 7ae7c79 typo e85cc47 forgot the selfs 190b4f6 start work on canflasher 5c655c9 add recover support ae3457f usbflash is reliable f7a0ab0 pedal usbflash works 585d0f9 add way to call isotp be82899 despite it being bad code, move isotp 000715b start work on pedal canloader 626e312 pedal has a bootstub now 3662d1e redundant check 81e6b0d fix bug 083cd12 should have bounty to refactor that ish b65d30c bad asserts b2e6c3f isotp untested support for subaddr 30fd66a Merge pull request #93 from vntarasov/volt 06f5109 Merge pull request #94 from gregjhogan/can-printer-hex c7d098c Merge pull request #95 from gregjhogan/setup-script 22fe250 Merge pull request #99 from gregjhogan/bit-transition-example ba16ba3 Merge pull request #100 from gregjhogan/j2534-troubleshooting-instructions ad08ea4 Merge pull request #90 from gregjhogan/can-forwarding f3b6f5d added j2534 troubleshooting instructions 858d150 added script to find bits that transition from 0 to 1 c6acac8 added checking pedal interceptor message length f7226ff added brake safety checks d0c2634 added gas safety checks d378e4a removed bosch safety forwarding restriction on 29 bit addresses 5c7ef9e added bosch safety hooks and forwarding 90c64b6 add note 23de8d4 Merge pull request #97 from commaai/pedal_improvements 0261641 added missing python packages b92b235 fix bytearray encode issue 2434f1c Tweak Volt's brake pedal stickiness e2f73d2 enable has a whole byte to itself d5a9e1e correct checksum f8ed9fa better names 986a14c don't alias pointers 9b8472e add watchdog support 8f0add9 handle faults 1d917f8 split gas set into 2 values, and have a fault state 1b77026 j2534 isn't alpha anymore fbcc872 Merge pull request #92 from commaai/pedal 8a6f44b pedal is sending messages 08f464c python 3 bro is bad bro 9390961 kline checksum algo was broken... 3b7c33b add kline debug support aa622bc init values 631ea9f better refactor eb1fd75 add PEDAL adc sets ccaa310 don't build with usb 8d4d763 debug console works bd09883 comma pedal is building 75a29d5 Merge pull request #84 from gregjhogan/j2534-hds eece37d only the panda has gmlan 9f43abe Merge pull request #89 from vntarasov/volt 5364d43 Merge pull request #88 from vntarasov/smaller-firmware 377a1ec bump version for descriptor fix 4fabdf0 Merge pull request #87 from gregjhogan/usb-multi-packet-control 8580773 fix sending WinUSB Extended Properties Feature Descriptor 6908feb Chevy Volt safety 786a004 Enable optimization to reduce firmware size d70f43b hack to fix thinkpad 95ab1ae fixed flow control message padding bbd04d1 updated installer 62216d0 single standalone DLL for J2534 driver 5c9138d fixed 11 bit address issue f3b0ad2 fix LOOPBACK getting set when DATA_RATE is set b750d36 updated README a9a097f lowered CPU utilization 7c26a70 TIS needs unsupported protocols to return an error 42692b4 TIS doesn't like ChannelID being zero cf126bb SET_CONFIG return error for reserved parameters 2e99dbf fix HDS issues 8203cc8 add is_grey e946a54 add insecure_okay flag 4363b3e check webpage 4f59ded add secure mode note 6b11fb5 add autosecuring to tests b27d185 Merge pull request #86 from commaai/better_pairing 4b53b42 elm wifi isn't an automated test 99f85cb Merge pull request #85 from gregjhogan/usb-wcid 0d38060 auto-install WinUSB device driver c6653ca from python import 38cc0ee add wifi_secure_mode, boots in insecure mode git-subtree-dir: panda git-subtree-split: 67d52089a1300b86800d897f2b271e0a24cf6dd6pull/219/head
parent
db67cffb4d
commit
098e304118
64 changed files with 2044 additions and 575 deletions
@ -1,3 +1,3 @@ |
||||
#!/bin/bash |
||||
sudo apt-get install gcc-arm-none-eabi python-pip |
||||
sudo pip2 install libusb1 |
||||
sudo pip2 install libusb1 pycrypto requests |
||||
|
@ -0,0 +1 @@ |
||||
obj/* |
@ -0,0 +1,59 @@ |
||||
# :set noet
|
||||
PROJ_NAME = comma
|
||||
|
||||
CFLAGS = -O2 -Wall -std=gnu11 -DPEDAL
|
||||
CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m3
|
||||
CFLAGS += -msoft-float -DSTM32F2 -DSTM32F205xx
|
||||
CFLAGS += -I ../inc -I ../ -I ../../ -nostdlib
|
||||
CFLAGS += -T../stm32_flash.ld
|
||||
|
||||
STARTUP_FILE = startup_stm32f205xx
|
||||
|
||||
CC = arm-none-eabi-gcc
|
||||
OBJCOPY = arm-none-eabi-objcopy
|
||||
OBJDUMP = arm-none-eabi-objdump
|
||||
DFU_UTIL = "dfu-util"
|
||||
|
||||
# pedal only uses the debug cert
|
||||
CERT = ../../certs/debug
|
||||
CFLAGS += "-DALLOW_DEBUG"
|
||||
|
||||
canflash: obj/$(PROJ_NAME).bin |
||||
../../tests/pedal/enter_canloader.py $<
|
||||
|
||||
usbflash: obj/$(PROJ_NAME).bin |
||||
../../tests/pedal/enter_canloader.py; sleep 0.5
|
||||
PYTHONPATH=../../ python -c "from python import Panda; p = [x for x in [Panda(x) for x in Panda.list()] if x.bootstub]; assert(len(p)==1); p[0].flash('obj/$(PROJ_NAME).bin', reconnect=False)"
|
||||
|
||||
recover: obj/bootstub.bin obj/$(PROJ_NAME).bin |
||||
../../tests/pedal/enter_canloader.py --recover; sleep 0.5
|
||||
$(DFU_UTIL) -d 0483:df11 -a 0 -s 0x08004000 -D obj/$(PROJ_NAME).bin
|
||||
$(DFU_UTIL) -d 0483:df11 -a 0 -s 0x08000000:leave -D obj/bootstub.bin
|
||||
|
||||
obj/main.o: main.c ../*.h |
||||
mkdir -p obj
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
obj/bootstub.o: ../bootstub.c ../*.h |
||||
mkdir -p obj
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
obj/$(STARTUP_FILE).o: ../$(STARTUP_FILE).s |
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
obj/%.o: ../../crypto/%.c |
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
obj/$(PROJ_NAME).bin: obj/$(STARTUP_FILE).o obj/main.o |
||||
# hack
|
||||
$(CC) -Wl,--section-start,.isr_vector=0x8004000 $(CFLAGS) -o obj/$(PROJ_NAME).elf $^
|
||||
$(OBJCOPY) -v -O binary obj/$(PROJ_NAME).elf obj/code.bin
|
||||
SETLEN=1 ../../crypto/sign.py obj/code.bin $@ $(CERT)
|
||||
|
||||
obj/bootstub.bin: obj/$(STARTUP_FILE).o obj/bootstub.o obj/sha.o obj/rsa.o |
||||
$(CC) $(CFLAGS) -o obj/bootstub.$(PROJ_NAME).elf $^
|
||||
$(OBJCOPY) -v -O binary obj/bootstub.$(PROJ_NAME).elf $@
|
||||
|
||||
clean: |
||||
rm -f obj/*
|
||||
|
@ -0,0 +1,28 @@ |
||||
This is the firmware for the comma pedal. It borrows a lot from panda. |
||||
|
||||
The comma pedal is a gas pedal interceptor for Honda/Acura. It allows you to "virtually" press the pedal. |
||||
|
||||
This is the open source software. Note that it is not ready to use yet. |
||||
|
||||
== Test Plan == |
||||
|
||||
* Startup |
||||
** Confirm STATE_FAULT_STARTUP |
||||
* Timeout |
||||
** Send value |
||||
** Confirm value is output |
||||
** Stop sending messages |
||||
** Confirm value is passthru after 100ms |
||||
** Confirm STATE_FAULT_TIMEOUT |
||||
* Random values |
||||
** Send random 6 byte messages |
||||
** Confirm random values cause passthru |
||||
** Confirm STATE_FAULT_BAD_CHECKSUM |
||||
* Same message lockout |
||||
** Send same message repeated |
||||
** Confirm timeout behavior |
||||
* Don't set enable |
||||
** Confirm no output |
||||
* Set enable and values |
||||
** Confirm output |
||||
|
@ -0,0 +1,295 @@ |
||||
//#define DEBUG
|
||||
//#define CAN_LOOPBACK_MODE
|
||||
//#define USE_INTERNAL_OSC
|
||||
|
||||
#include "../config.h" |
||||
|
||||
#include "drivers/drivers.h" |
||||
#include "drivers/llgpio.h" |
||||
#include "gpio.h" |
||||
|
||||
#define CUSTOM_CAN_INTERRUPTS |
||||
|
||||
#include "libc.h" |
||||
#include "safety.h" |
||||
#include "drivers/adc.h" |
||||
#include "drivers/uart.h" |
||||
#include "drivers/dac.h" |
||||
#include "drivers/can.h" |
||||
#include "drivers/timer.h" |
||||
|
||||
#define CAN CAN1 |
||||
|
||||
//#define PEDAL_USB
|
||||
|
||||
#ifdef PEDAL_USB |
||||
#include "drivers/usb.h" |
||||
#endif |
||||
|
||||
#define ENTER_BOOTLOADER_MAGIC 0xdeadbeef |
||||
uint32_t enter_bootloader_mode; |
||||
|
||||
void __initialize_hardware_early() { |
||||
early(); |
||||
} |
||||
|
||||
// ********************* serial debugging *********************
|
||||
|
||||
void debug_ring_callback(uart_ring *ring) { |
||||
char rcv; |
||||
while (getc(ring, &rcv)) { |
||||
putc(ring, rcv); |
||||
} |
||||
} |
||||
|
||||
#ifdef PEDAL_USB |
||||
|
||||
int usb_cb_ep1_in(uint8_t *usbdata, int len, int hardwired) { return 0; } |
||||
void usb_cb_ep2_out(uint8_t *usbdata, int len, int hardwired) {} |
||||
void usb_cb_ep3_out(uint8_t *usbdata, int len, int hardwired) {} |
||||
void usb_cb_enumeration_complete() {} |
||||
|
||||
int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, int hardwired) { |
||||
int resp_len = 0; |
||||
uart_ring *ur = NULL; |
||||
switch (setup->b.bRequest) { |
||||
// **** 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; |
||||
} |
||||
return resp_len; |
||||
} |
||||
|
||||
#endif |
||||
|
||||
// ***************************** honda can checksum *****************************
|
||||
|
||||
int can_cksum(uint8_t *dat, int len, int addr, int idx) { |
||||
int i; |
||||
int s = 0; |
||||
for (i = 0; i < len; i++) { |
||||
s += (dat[i] >> 4);
|
||||
s += dat[i] & 0xF; |
||||
} |
||||
s += (addr>>0)&0xF; |
||||
s += (addr>>4)&0xF; |
||||
s += (addr>>8)&0xF; |
||||
s += idx; |
||||
s = 8-s; |
||||
return s&0xF; |
||||
} |
||||
|
||||
// ***************************** can port *****************************
|
||||
|
||||
// addresses to be used on CAN
|
||||
#define CAN_GAS_INPUT 0x200 |
||||
#define CAN_GAS_OUTPUT 0x201 |
||||
|
||||
void CAN1_TX_IRQHandler() { |
||||
// clear interrupt
|
||||
CAN->TSR |= CAN_TSR_RQCP0; |
||||
} |
||||
|
||||
// two independent values
|
||||
uint16_t gas_set_0 = 0; |
||||
uint16_t gas_set_1 = 0; |
||||
|
||||
#define MAX_TIMEOUT 10 |
||||
uint32_t timeout = 0; |
||||
uint32_t current_index = 0; |
||||
|
||||
#define NO_FAULT 0 |
||||
#define FAULT_BAD_CHECKSUM 1 |
||||
#define FAULT_SEND 2 |
||||
#define FAULT_SCE 3 |
||||
#define FAULT_STARTUP 4 |
||||
#define FAULT_TIMEOUT 5 |
||||
#define FAULT_INVALID 6 |
||||
uint8_t state = FAULT_STARTUP; |
||||
|
||||
void CAN1_RX0_IRQHandler() { |
||||
while (CAN->RF0R & CAN_RF0R_FMP0) { |
||||
#ifdef DEBUG |
||||
puts("CAN RX\n"); |
||||
#endif |
||||
uint32_t address = CAN->sFIFOMailBox[0].RIR>>21; |
||||
if (address == CAN_GAS_INPUT) { |
||||
// softloader entry
|
||||
if (CAN->sFIFOMailBox[0].RDLR == 0xdeadface) { |
||||
if (CAN->sFIFOMailBox[0].RDHR == 0x0ab00b1e) { |
||||
enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; |
||||
NVIC_SystemReset(); |
||||
} else if (CAN->sFIFOMailBox[0].RDHR == 0x02b00b1e) { |
||||
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; |
||||
NVIC_SystemReset(); |
||||
} |
||||
} |
||||
|
||||
// normal packet
|
||||
uint8_t *dat = (uint8_t *)&CAN->sFIFOMailBox[0].RDLR; |
||||
uint8_t *dat2 = (uint8_t *)&CAN->sFIFOMailBox[0].RDHR; |
||||
uint16_t value_0 = (dat[0] << 8) | dat[1]; |
||||
uint16_t value_1 = (dat[2] << 8) | dat[3]; |
||||
uint8_t enable = (dat2[0] >> 7) & 1; |
||||
uint8_t index = (dat2[1] >> 4) & 3; |
||||
if (can_cksum(dat, 5, CAN_GAS_INPUT, index) == (dat2[1] & 0xF)) { |
||||
if (((current_index+1)&3) == index) { |
||||
#ifdef DEBUG |
||||
puts("setting gas "); |
||||
puth(value); |
||||
puts("\n"); |
||||
#endif |
||||
if (enable) { |
||||
gas_set_0 = value_0; |
||||
gas_set_1 = value_1; |
||||
} else { |
||||
// clear the fault state if values are 0
|
||||
if (value_0 == 0 && value_1 == 0) { |
||||
state = NO_FAULT; |
||||
} else { |
||||
state = FAULT_INVALID; |
||||
} |
||||
gas_set_0 = gas_set_1 = 0; |
||||
} |
||||
// clear the timeout
|
||||
timeout = 0; |
||||
} |
||||
current_index = index; |
||||
} else { |
||||
// wrong checksum = fault
|
||||
state = FAULT_BAD_CHECKSUM; |
||||
} |
||||
} |
||||
// next
|
||||
CAN->RF0R |= CAN_RF0R_RFOM0; |
||||
} |
||||
} |
||||
|
||||
void CAN1_SCE_IRQHandler() { |
||||
state = FAULT_SCE; |
||||
can_sce(CAN); |
||||
} |
||||
|
||||
int pdl0 = 0, pdl1 = 0; |
||||
int pkt_idx = 0; |
||||
|
||||
int led_value = 0; |
||||
|
||||
void TIM3_IRQHandler() { |
||||
#ifdef DEBUG |
||||
puth(TIM3->CNT); |
||||
puts(" "); |
||||
puth(pdl0); |
||||
puts(" "); |
||||
puth(pdl1); |
||||
puts("\n"); |
||||
#endif |
||||
|
||||
// check timer for sending the user pedal and clearing the CAN
|
||||
if ((CAN->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) { |
||||
uint8_t dat[8]; |
||||
dat[0] = (pdl0>>8)&0xFF; |
||||
dat[1] = (pdl0>>0)&0xFF; |
||||
dat[2] = (pdl1>>8)&0xFF; |
||||
dat[3] = (pdl1>>0)&0xFF; |
||||
dat[4] = state; |
||||
dat[5] = can_cksum(dat, 5, CAN_GAS_OUTPUT, pkt_idx) | (pkt_idx<<4); |
||||
CAN->sTxMailBox[0].TDLR = dat[0] | (dat[1]<<8) | (dat[2]<<16) | (dat[3]<<24); |
||||
CAN->sTxMailBox[0].TDHR = dat[4] | (dat[5]<<8); |
||||
CAN->sTxMailBox[0].TDTR = 6; // len of packet is 5
|
||||
CAN->sTxMailBox[0].TIR = (CAN_GAS_OUTPUT << 21) | 1; |
||||
++pkt_idx; |
||||
pkt_idx &= 3; |
||||
} else { |
||||
// old can packet hasn't sent!
|
||||
state = FAULT_SEND; |
||||
#ifdef DEBUG |
||||
puts("CAN MISS\n"); |
||||
#endif |
||||
} |
||||
|
||||
// blink the LED
|
||||
set_led(LED_GREEN, led_value); |
||||
led_value = !led_value; |
||||
|
||||
TIM3->SR = 0; |
||||
|
||||
// up timeout for gas set
|
||||
if (timeout == MAX_TIMEOUT) { |
||||
state = FAULT_TIMEOUT; |
||||
} else { |
||||
timeout += 1; |
||||
} |
||||
} |
||||
|
||||
// ***************************** main code *****************************
|
||||
|
||||
void pedal() { |
||||
// read/write
|
||||
pdl0 = adc_get(ADCCHAN_ACCEL0); |
||||
pdl1 = adc_get(ADCCHAN_ACCEL1); |
||||
|
||||
// write the pedal to the DAC
|
||||
if (state == NO_FAULT) { |
||||
dac_set(0, max(gas_set_0, pdl0)); |
||||
dac_set(1, max(gas_set_1, pdl1)); |
||||
} else { |
||||
dac_set(0, pdl0); |
||||
dac_set(1, pdl1); |
||||
} |
||||
|
||||
// feed the watchdog
|
||||
IWDG->KR = 0xAAAA; |
||||
} |
||||
|
||||
int main() { |
||||
__disable_irq(); |
||||
|
||||
// init devices
|
||||
clock_init(); |
||||
periph_init(); |
||||
gpio_init(); |
||||
|
||||
#ifdef PEDAL_USB |
||||
// enable USB
|
||||
usb_init(); |
||||
#endif |
||||
|
||||
// pedal stuff
|
||||
dac_init(); |
||||
adc_init(); |
||||
|
||||
// init can
|
||||
can_silent = ALL_CAN_LIVE; |
||||
can_init(0); |
||||
|
||||
// 48mhz / 65536 ~= 732
|
||||
timer_init(TIM3, 15); |
||||
NVIC_EnableIRQ(TIM3_IRQn); |
||||
|
||||
// setup watchdog
|
||||
IWDG->KR = 0x5555; |
||||
IWDG->PR = 0; // divider /4
|
||||
// 0 = 0.125 ms, let's have a 50ms watchdog
|
||||
IWDG->RLR = 400 - 1; |
||||
IWDG->KR = 0xCCCC; |
||||
|
||||
puts("**** INTERRUPTS ON ****\n"); |
||||
__enable_irq(); |
||||
|
||||
// main pedal loop
|
||||
while (1) { |
||||
pedal(); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
@ -0,0 +1,186 @@ |
||||
// board enforces
|
||||
// in-state
|
||||
// accel set/resume
|
||||
// out-state
|
||||
// cancel button
|
||||
// regen paddle
|
||||
// accel rising edge
|
||||
// brake rising edge
|
||||
// brake > 0mph
|
||||
|
||||
// gm_: poor man's namespacing
|
||||
int gm_brake_prev = 0; |
||||
int gm_gas_prev = 0; |
||||
int gm_speed = 0; |
||||
|
||||
// silence everything if stock ECUs are still online
|
||||
int gm_ascm_detected = 0; |
||||
|
||||
static void gm_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
|
||||
uint32_t addr; |
||||
if (to_push->RIR & 4) { |
||||
// Extended
|
||||
// Not looked at, but have to be separated
|
||||
// to avoid address collision
|
||||
addr = to_push->RIR >> 3; |
||||
} else { |
||||
// Normal
|
||||
addr = to_push->RIR >> 21; |
||||
} |
||||
|
||||
// sample speed, really only care if car is moving or not
|
||||
// rear left wheel speed
|
||||
if (addr == 842) { |
||||
gm_speed = to_push->RDLR & 0xFFFF; |
||||
} |
||||
|
||||
// check if stock ASCM ECU is still online
|
||||
int bus_number = (to_push->RDTR >> 4) & 0xFF; |
||||
if (bus_number == 0 && addr == 715) { |
||||
gm_ascm_detected = 1; |
||||
controls_allowed = 0; |
||||
} |
||||
|
||||
// ACC steering wheel buttons
|
||||
if (addr == 481) { |
||||
int buttons = (to_push->RDHR >> 12) & 0x7; |
||||
// res/set - enable, cancel button - disable
|
||||
if (buttons == 2 || buttons == 3) { |
||||
controls_allowed = 1; |
||||
} else if (buttons == 6) { |
||||
controls_allowed = 0; |
||||
} |
||||
} |
||||
|
||||
// exit controls on rising edge of brake press or on brake press when
|
||||
// speed > 0
|
||||
if (addr == 241) { |
||||
int brake = (to_push->RDLR & 0xFF00) >> 8; |
||||
// Brake pedal's potentiometer returns near-zero reading
|
||||
// even when pedal is not pressed
|
||||
if (brake < 10) { |
||||
brake = 0; |
||||
} |
||||
if (brake && (!gm_brake_prev || gm_speed)) { |
||||
controls_allowed = 0; |
||||
} |
||||
gm_brake_prev = brake; |
||||
} |
||||
|
||||
// exit controls on rising edge of gas press
|
||||
if (addr == 417) { |
||||
int gas = to_push->RDHR & 0xFF0000; |
||||
if (gas && !gm_gas_prev) { |
||||
controls_allowed = 0; |
||||
} |
||||
gm_gas_prev = gas; |
||||
} |
||||
|
||||
// exit controls on regen paddle
|
||||
if (addr == 189) { |
||||
int regen = to_push->RDLR & 0x20; |
||||
if (regen) { |
||||
controls_allowed = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// all commands: gas/regen, friction brake and steering
|
||||
// if controls_allowed and no pedals pressed
|
||||
// allow all commands up to limit
|
||||
// else
|
||||
// block all commands that produce actuation
|
||||
|
||||
static int gm_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
|
||||
// There can be only one! (ASCM)
|
||||
if (gm_ascm_detected) { |
||||
return 0; |
||||
} |
||||
|
||||
// disallow actuator commands if gas or brake (with vehicle moving) are pressed
|
||||
// and the the latching controls_allowed flag is True
|
||||
int pedal_pressed = gm_gas_prev || (gm_brake_prev && gm_speed); |
||||
int current_controls_allowed = controls_allowed && !pedal_pressed; |
||||
|
||||
uint32_t addr; |
||||
if (to_send->RIR & 4) { |
||||
// Extended
|
||||
addr = to_send->RIR >> 3; |
||||
} else { |
||||
// Normal
|
||||
addr = to_send->RIR >> 21; |
||||
} |
||||
|
||||
// BRAKE: safety check
|
||||
if (addr == 789) { |
||||
int rdlr = to_send->RDLR; |
||||
int brake = ((rdlr & 0xF) << 8) + ((rdlr & 0xFF00) >> 8); |
||||
brake = (0x1000 - brake) & 0xFFF; |
||||
if (current_controls_allowed) { |
||||
if (brake > 255) return 0; |
||||
} else { |
||||
if (brake != 0) return 0; |
||||
} |
||||
} |
||||
|
||||
// LKA STEER: safety check
|
||||
if (addr == 384) { |
||||
int rdlr = to_send->RDLR; |
||||
int steer = ((rdlr & 0x7) << 8) + ((rdlr & 0xFF00) >> 8); |
||||
int max_steer = 255; |
||||
if (current_controls_allowed) { |
||||
// Signed arithmetic
|
||||
if (steer & 0x400) { |
||||
if (steer < (0x800 - max_steer)) return 0; |
||||
} else { |
||||
if (steer > max_steer) return 0; |
||||
} |
||||
} else { |
||||
if (steer != 0) return 0; |
||||
} |
||||
} |
||||
|
||||
// PARK ASSIST STEER: unlimited torque, no thanks
|
||||
if (addr == 823) return 0; |
||||
|
||||
// GAS/REGEN: safety check
|
||||
if (addr == 715) { |
||||
int rdlr = to_send->RDLR; |
||||
int gas_regen = ((rdlr & 0x7F0000) >> 11) + ((rdlr & 0xF8000000) >> 27); |
||||
int apply = rdlr & 1; |
||||
if (current_controls_allowed) { |
||||
if (gas_regen > 3072) return 0; |
||||
} else { |
||||
// Disabled message is !engaed with gas
|
||||
// value that corresponds to max regen.
|
||||
if (apply || gas_regen != 1404) return 0; |
||||
} |
||||
} |
||||
|
||||
// 1 allows the message through
|
||||
return true; |
||||
} |
||||
|
||||
static int gm_tx_lin_hook(int lin_num, uint8_t *data, int len) { |
||||
// LIN is not used in Volt
|
||||
return false; |
||||
} |
||||
|
||||
static void gm_init(int16_t param) { |
||||
controls_allowed = 0; |
||||
} |
||||
|
||||
static int gm_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
return -1; |
||||
} |
||||
|
||||
const safety_hooks gm_hooks = { |
||||
.init = gm_init, |
||||
.rx = gm_rx_hook, |
||||
.tx = gm_tx_hook, |
||||
.tx_lin = gm_tx_lin_hook, |
||||
.fwd = gm_fwd_hook, |
||||
}; |
||||
|
@ -1,10 +1,13 @@ |
||||
#include "stdafx.h" |
||||
|
||||
#include <SetupAPI.h> |
||||
#include <Devpkey.h> |
||||
|
||||
#include <unordered_map> |
||||
#include <string> |
||||
|
||||
#include <winusb.h> |
||||
|
||||
#include "device.h" |
||||
|
||||
using namespace panda; |
@ -0,0 +1,25 @@ |
||||
# How to use can_bit_transition.py to reverse engineer a single bit field |
||||
|
||||
Let's say our goal is to find a brake pedal signal (caused by your foot pressing the brake pedal vs adaptive cruise control braking). |
||||
|
||||
The following process will allow you to quickly find bits that are always 0 during a period of time (when you know you were not pressing the brake with your foot) and always 1 in a different period of time (when you know you were pressing the brake with your foot). |
||||
|
||||
Open up a drive in cabana where you can find a place you used the brake pedal and another place where you did not use the brake pedal (and you can identify when you were on the brake pedal and when you were not). You may want to go out for a drive and put something in front of the camera after you put your foot on the brake and take it away before you take your foot off the brake so you can easily identify exactly when you had your foot on the brake based on the video in cabana. This is critical because this script needs the brake signal to always be high the entire time for one of the time frames. A 10 second time frame worked well for me. |
||||
|
||||
I found a drive where I knew I was not pressing the brake between timestamp 50.0 thru 65.0 and I was pressing the brake between timestamp 69.0 thru 79.0. Determine what the timestamps are in cabana by plotting any message and putting your mouse over the plot at the location you want to discover the timestamp. The tool tip on mouse hover has the format: timestamp: value |
||||
|
||||
Now download the log from cabana (Save Log button) and run the script passing in the timestamps |
||||
(replace csv file name with cabana log you downloaded and time ranges with your own) |
||||
``` |
||||
./can_bit_transition.py ./honda_crv_ex_2017_can-1520354796875.csv 50.0-65.0 69.0-79.0 |
||||
``` |
||||
|
||||
The script will output bits that were always low in the first time range and always high in the second time range (and vice versa) |
||||
``` |
||||
id 17c 0 -> 1 at byte 4 bitmask 1 |
||||
id 17c 0 -> 1 at byte 6 bitmask 32 |
||||
id 221 1 -> 0 at byte 0 bitmask 4 |
||||
id 1be 0 -> 1 at byte 0 bitmask 16 |
||||
``` |
||||
|
||||
Now I go back to cabana and graph the above bits by searching for the message by id, double clicking on the appropriate bit, and then selecting "show plot". I already knew that message id 0x17c is both user brake and adaptive cruise control braking combined, so I plotted one of those signals along side 0x221 and 0x1be. By replaying a drive I could see that 0x221 was not a brake signal (when high at random times that did not correspond to braking). Next I looked at 0x1be and I found it was the brake pedal signal I was looking for (went high whenever I pressed the brake pedal and did not go high when adaptive cruise control was braking). |
@ -0,0 +1,87 @@ |
||||
#!/usr/bin/env python |
||||
|
||||
import binascii |
||||
import csv |
||||
import sys |
||||
|
||||
class Message(): |
||||
"""Details about a specific message ID.""" |
||||
def __init__(self, message_id): |
||||
self.message_id = message_id |
||||
self.ones = [0] * 8 # bit set if 1 is always seen |
||||
self.zeros = [0] * 8 # bit set if 0 is always seen |
||||
|
||||
def printBitDiff(self, other): |
||||
"""Prints bits that transition from always zero to always 1 and vice versa.""" |
||||
for i in xrange(len(self.ones)): |
||||
zero_to_one = other.zeros[i] & self.ones[i] |
||||
if zero_to_one: |
||||
print 'id %s 0 -> 1 at byte %d bitmask %d' % (self.message_id, i, zero_to_one) |
||||
one_to_zero = other.ones[i] & self.zeros[i] |
||||
if one_to_zero: |
||||
print 'id %s 1 -> 0 at byte %d bitmask %d' % (self.message_id, i, one_to_zero) |
||||
|
||||
|
||||
class Info(): |
||||
"""A collection of Messages.""" |
||||
|
||||
def __init__(self): |
||||
self.messages = {} # keyed by MessageID |
||||
|
||||
def load(self, filename, start, end): |
||||
"""Given a CSV file, adds information about message IDs and their values.""" |
||||
with open(filename, 'rb') as input: |
||||
reader = csv.reader(input) |
||||
next(reader, None) # skip the CSV header |
||||
for row in reader: |
||||
if not len(row): continue |
||||
time = float(row[0]) |
||||
bus = int(row[2]) |
||||
if time < start or bus > 127: |
||||
continue |
||||
elif time > end: |
||||
break |
||||
if row[1].startswith('0x'): |
||||
message_id = row[1][2:] # remove leading '0x' |
||||
else: |
||||
message_id = hex(int(row[1]))[2:] # old message IDs are in decimal |
||||
|
||||
if row[3].startswith('0x'): |
||||
data = row[3][2:] # remove leading '0x' |
||||
else: |
||||
data = row[3] |
||||
new_message = False |
||||
if message_id not in self.messages: |
||||
self.messages[message_id] = Message(message_id) |
||||
new_message = True |
||||
message = self.messages[message_id] |
||||
bytes = bytearray.fromhex(data) |
||||
for i in xrange(len(bytes)): |
||||
ones = int(bytes[i]) |
||||
message.ones[i] = ones if new_message else message.ones[i] & ones |
||||
# Inverts the data and masks it to a byte to get the zeros as ones. |
||||
zeros = (~int(bytes[i])) & 0xff |
||||
message.zeros[i] = zeros if new_message else message.zeros[i] & zeros |
||||
|
||||
def PrintUnique(log_file, low_range, high_range): |
||||
# find messages with bits that are always low |
||||
start, end = map(float, low_range.split('-')) |
||||
low = Info() |
||||
low.load(log_file, start, end) |
||||
# find messages with bits that are always high |
||||
start, end = map(float, high_range.split('-')) |
||||
high = Info() |
||||
high.load(log_file, start, end) |
||||
# print messages that go from low to high |
||||
found = False |
||||
for message_id in high.messages: |
||||
if message_id in low.messages: |
||||
high.messages[message_id].printBitDiff(low.messages[message_id]) |
||||
found = True |
||||
if not found: print 'No messages that transition from always low to always high found!' |
||||
|
||||
if __name__ == "__main__": |
||||
if len(sys.argv) < 4: |
||||
print 'Usage:\n%s log.csv <low-start>-<low-end> <high-start>-<high-end>' % sys.argv[0] |
||||
sys.exit(0) |
||||
PrintUnique(sys.argv[1], sys.argv[2], sys.argv[3]) |
@ -1,85 +0,0 @@ |
||||
DEBUG = False |
||||
|
||||
def msg(x): |
||||
if DEBUG: |
||||
print "S:",x.encode("hex") |
||||
if len(x) <= 7: |
||||
ret = chr(len(x)) + x |
||||
else: |
||||
assert False |
||||
return ret.ljust(8, "\x00") |
||||
|
||||
kmsgs = [] |
||||
def recv(panda, cnt, addr, nbus): |
||||
global kmsgs |
||||
ret = [] |
||||
|
||||
while len(ret) < cnt: |
||||
kmsgs += panda.can_recv() |
||||
nmsgs = [] |
||||
for ids, ts, dat, bus in kmsgs: |
||||
if ids == addr and bus == nbus and len(ret) < cnt: |
||||
ret.append(dat) |
||||
else: |
||||
# leave around |
||||
nmsgs.append((ids, ts, dat, bus)) |
||||
kmsgs = nmsgs[-256:] |
||||
return map(str, ret) |
||||
|
||||
def isotp_send(panda, x, addr, bus=0, recvaddr=None): |
||||
if recvaddr is None: |
||||
recvaddr = addr+8 |
||||
|
||||
if len(x) <= 7: |
||||
panda.can_send(addr, msg(x), bus) |
||||
else: |
||||
ss = chr(0x10 + (len(x)>>8)) + chr(len(x)&0xFF) + x[0:6] |
||||
x = x[6:] |
||||
idx = 1 |
||||
sends = [] |
||||
while len(x) > 0: |
||||
sends.append(((chr(0x20 + (idx&0xF)) + x[0:7]).ljust(8, "\x00"))) |
||||
x = x[7:] |
||||
idx += 1 |
||||
|
||||
# actually send |
||||
panda.can_send(addr, ss, bus) |
||||
rr = recv(panda, 1, recvaddr, bus)[0] |
||||
panda.can_send_many([(addr, None, s, 0) for s in sends]) |
||||
|
||||
def isotp_recv(panda, addr, bus=0, sendaddr=None): |
||||
msg = recv(panda, 1, addr, bus)[0] |
||||
|
||||
if sendaddr is None: |
||||
sendaddr = addr-8 |
||||
|
||||
|
||||
if ord(msg[0])&0xf0 == 0x10: |
||||
# first |
||||
tlen = ((ord(msg[0]) & 0xf) << 8) | ord(msg[1]) |
||||
dat = msg[2:] |
||||
|
||||
# 0 block size? |
||||
CONTINUE = "\x30" + "\x00"*7 |
||||
|
||||
panda.can_send(sendaddr, CONTINUE, bus) |
||||
|
||||
idx = 1 |
||||
for mm in recv(panda, (tlen-len(dat) + 7)/8, addr, bus): |
||||
assert ord(mm[0]) == (0x20 | idx) |
||||
dat += mm[1:] |
||||
idx += 1 |
||||
elif ord(msg[0])&0xf0 == 0x00: |
||||
# single |
||||
tlen = ord(msg[0]) & 0xf |
||||
dat = msg[1:] |
||||
else: |
||||
assert False |
||||
|
||||
dat = dat[0:tlen] |
||||
|
||||
if DEBUG: |
||||
print "R:",dat.encode("hex") |
||||
|
||||
return dat |
||||
|
@ -0,0 +1,135 @@ |
||||
DEBUG = False |
||||
|
||||
def msg(x): |
||||
if DEBUG: |
||||
print "S:",x.encode("hex") |
||||
if len(x) <= 7: |
||||
ret = chr(len(x)) + x |
||||
else: |
||||
assert False |
||||
return ret.ljust(8, "\x00") |
||||
|
||||
kmsgs = [] |
||||
def recv(panda, cnt, addr, nbus): |
||||
global kmsgs |
||||
ret = [] |
||||
|
||||
while len(ret) < cnt: |
||||
kmsgs += panda.can_recv() |
||||
nmsgs = [] |
||||
for ids, ts, dat, bus in kmsgs: |
||||
if ids == addr and bus == nbus and len(ret) < cnt: |
||||
ret.append(dat) |
||||
else: |
||||
# leave around |
||||
nmsgs.append((ids, ts, dat, bus)) |
||||
kmsgs = nmsgs[-256:] |
||||
return map(str, ret) |
||||
|
||||
def isotp_recv_subaddr(panda, addr, bus, sendaddr, subaddr): |
||||
msg = recv(panda, 1, addr, bus)[0] |
||||
|
||||
# TODO: handle other subaddr also communicating |
||||
assert ord(msg[0]) == subaddr |
||||
|
||||
if ord(msg[1])&0xf0 == 0x10: |
||||
# first |
||||
tlen = ((ord(msg[1]) & 0xf) << 8) | ord(msg[2]) |
||||
dat = msg[3:] |
||||
|
||||
# 0 block size? |
||||
CONTINUE = chr(subaddr) + "\x30" + "\x00"*6 |
||||
panda.can_send(sendaddr, CONTINUE, bus) |
||||
|
||||
idx = 1 |
||||
for mm in recv(panda, (tlen-len(dat) + 5)/6, addr, bus): |
||||
assert ord(mm[0]) == subaddr |
||||
assert ord(mm[1]) == (0x20 | idx) |
||||
dat += mm[2:] |
||||
idx += 1 |
||||
elif ord(msg[1])&0xf0 == 0x00: |
||||
# single |
||||
tlen = ord(msg[1]) & 0xf |
||||
dat = msg[2:] |
||||
else: |
||||
print msg.encode("hex") |
||||
assert False |
||||
|
||||
return dat[0:tlen] |
||||
|
||||
# **** import below this line **** |
||||
|
||||
def isotp_send(panda, x, addr, bus=0, recvaddr=None, subaddr=None): |
||||
if recvaddr is None: |
||||
recvaddr = addr+8 |
||||
|
||||
if len(x) <= 7 and subaddr is None: |
||||
panda.can_send(addr, msg(x), bus) |
||||
elif len(x) <= 6 and subaddr is not None: |
||||
panda.can_send(addr, chr(subaddr)+msg(x)[0:7], bus) |
||||
else: |
||||
if subaddr: |
||||
ss = chr(subaddr) + chr(0x10 + (len(x)>>8)) + chr(len(x)&0xFF) + x[0:5] |
||||
x = x[5:] |
||||
else: |
||||
ss = chr(0x10 + (len(x)>>8)) + chr(len(x)&0xFF) + x[0:6] |
||||
x = x[6:] |
||||
idx = 1 |
||||
sends = [] |
||||
while len(x) > 0: |
||||
if subaddr: |
||||
sends.append(((chr(subaddr) + chr(0x20 + (idx&0xF)) + x[0:6]).ljust(8, "\x00"))) |
||||
x = x[6:] |
||||
else: |
||||
sends.append(((chr(0x20 + (idx&0xF)) + x[0:7]).ljust(8, "\x00"))) |
||||
x = x[7:] |
||||
idx += 1 |
||||
|
||||
# actually send |
||||
panda.can_send(addr, ss, bus) |
||||
rr = recv(panda, 1, recvaddr, bus)[0] |
||||
if rr.find("\x30\x01") != -1: |
||||
for s in sends[:-1]: |
||||
panda.can_send(addr, s, 0) |
||||
rr = recv(panda, 1, recvaddr, bus)[0] |
||||
panda.can_send(addr, sends[-1], 0) |
||||
else: |
||||
panda.can_send_many([(addr, None, s, 0) for s in sends]) |
||||
|
||||
def isotp_recv(panda, addr, bus=0, sendaddr=None, subaddr=None): |
||||
if sendaddr is None: |
||||
sendaddr = addr-8 |
||||
|
||||
if subaddr is not None: |
||||
dat = isotp_recv_subaddr(panda, addr, bus, sendaddr, subaddr) |
||||
else: |
||||
msg = recv(panda, 1, addr, bus)[0] |
||||
|
||||
if ord(msg[0])&0xf0 == 0x10: |
||||
# first |
||||
tlen = ((ord(msg[0]) & 0xf) << 8) | ord(msg[1]) |
||||
dat = msg[2:] |
||||
|
||||
# 0 block size? |
||||
CONTINUE = "\x30" + "\x00"*7 |
||||
|
||||
panda.can_send(sendaddr, CONTINUE, bus) |
||||
|
||||
idx = 1 |
||||
for mm in recv(panda, (tlen-len(dat) + 6)/7, addr, bus): |
||||
assert ord(mm[0]) == (0x20 | idx) |
||||
dat += mm[1:] |
||||
idx += 1 |
||||
elif ord(msg[0])&0xf0 == 0x00: |
||||
# single |
||||
tlen = ord(msg[0]) & 0xf |
||||
dat = msg[1:] |
||||
else: |
||||
assert False |
||||
dat = dat[0:tlen] |
||||
|
||||
if DEBUG: |
||||
print "R:",dat.encode("hex") |
||||
|
||||
return dat |
||||
|
@ -0,0 +1,76 @@ |
||||
#!/usr/bin/env python |
||||
import sys |
||||
import time |
||||
import struct |
||||
import argparse |
||||
import signal |
||||
from panda import Panda |
||||
|
||||
class CanHandle(object): |
||||
def __init__(self, p): |
||||
self.p = p |
||||
|
||||
def transact(self, dat): |
||||
#print "W:",dat.encode("hex") |
||||
self.p.isotp_send(1, dat, 0, recvaddr=2) |
||||
|
||||
def _handle_timeout(signum, frame): |
||||
# will happen on reset |
||||
raise Exception("timeout") |
||||
|
||||
signal.signal(signal.SIGALRM, _handle_timeout) |
||||
signal.alarm(1) |
||||
try: |
||||
ret = self.p.isotp_recv(2, 0, sendaddr=1) |
||||
finally: |
||||
signal.alarm(0) |
||||
|
||||
#print "R:",ret.encode("hex") |
||||
return ret |
||||
|
||||
def controlWrite(self, request_type, request, value, index, data, timeout=0): |
||||
# ignore data in reply, panda doesn't use it |
||||
return self.controlRead(request_type, request, value, index, 0, timeout) |
||||
|
||||
def controlRead(self, request_type, request, value, index, length, timeout=0): |
||||
dat = struct.pack("HHBBHHH", 0, 0, request_type, request, value, index, length) |
||||
return self.transact(dat) |
||||
|
||||
def bulkWrite(self, endpoint, data, timeout=0): |
||||
if len(data) > 0x10: |
||||
raise ValueError("Data must not be longer than 0x10") |
||||
dat = struct.pack("HH", endpoint, len(data))+data |
||||
return self.transact(dat) |
||||
|
||||
def bulkRead(self, endpoint, length, timeout=0): |
||||
dat = struct.pack("HH", endpoint, 0) |
||||
return self.transact(dat) |
||||
|
||||
if __name__ == "__main__": |
||||
parser = argparse.ArgumentParser(description='Flash pedal over can') |
||||
parser.add_argument('--recover', action='store_true') |
||||
parser.add_argument("fn", type=str, nargs='?', help="flash file") |
||||
args = parser.parse_args() |
||||
|
||||
p = Panda() |
||||
p.set_safety_mode(0x1337) |
||||
|
||||
while 1: |
||||
if len(p.can_recv()) == 0: |
||||
break |
||||
|
||||
if args.recover: |
||||
p.can_send(0x200, "\xce\xfa\xad\xde\x1e\x0b\xb0\x02", 0) |
||||
exit(0) |
||||
else: |
||||
p.can_send(0x200, "\xce\xfa\xad\xde\x1e\x0b\xb0\x0a", 0) |
||||
|
||||
if args.fn: |
||||
time.sleep(0.1) |
||||
print "flashing", args.fn |
||||
code = open(args.fn).read() |
||||
Panda.flash_static(CanHandle(p), code) |
||||
|
||||
print "can flash done" |
||||
|
||||
|
@ -0,0 +1,29 @@ |
||||
#!/usr/bin/env python |
||||
from panda import Panda |
||||
from hexdump import hexdump |
||||
|
||||
DEBUG = False |
||||
|
||||
if __name__ == "__main__": |
||||
p = Panda() |
||||
|
||||
len = p._handle.controlRead(Panda.REQUEST_IN, 0x06, 3 << 8 | 238, 0, 1) |
||||
print 'Microsoft OS String Descriptor' |
||||
dat = p._handle.controlRead(Panda.REQUEST_IN, 0x06, 3 << 8 | 238, 0, len[0]) |
||||
if DEBUG: print 'LEN: {}'.format(hex(len[0])) |
||||
hexdump("".join(map(chr, dat))) |
||||
|
||||
ms_vendor_code = dat[16] |
||||
if DEBUG: print 'MS_VENDOR_CODE: {}'.format(hex(len[0])) |
||||
|
||||
print '\nMicrosoft Compatible ID Feature Descriptor' |
||||
len = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 4, 1) |
||||
if DEBUG: print 'LEN: {}'.format(hex(len[0])) |
||||
dat = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 4, len[0]) |
||||
hexdump("".join(map(chr, dat))) |
||||
|
||||
print '\nMicrosoft Extended Properties Feature Descriptor' |
||||
len = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 5, 1) |
||||
if DEBUG: print 'LEN: {}'.format(hex(len[0])) |
||||
dat = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 5, len[0]) |
||||
hexdump("".join(map(chr, dat))) |
@ -0,0 +1,125 @@ |
||||
#!/usr/bin/env python |
||||
from __future__ import print_function |
||||
import os |
||||
import sys |
||||
import time |
||||
import random |
||||
import argparse |
||||
|
||||
from hexdump import hexdump |
||||
from itertools import permutations |
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) |
||||
from panda import Panda |
||||
|
||||
def get_test_string(): |
||||
return b"test"+os.urandom(10) |
||||
|
||||
def run_test(sleep_duration): |
||||
pandas = Panda.list() |
||||
print(pandas) |
||||
|
||||
if len(pandas) == 0: |
||||
print("NO PANDAS") |
||||
assert False |
||||
|
||||
if len(pandas) == 1: |
||||
# if we only have one on USB, assume the other is on wifi |
||||
pandas.append("WIFI") |
||||
run_test_w_pandas(pandas, sleep_duration) |
||||
|
||||
def run_test_w_pandas(pandas, sleep_duration): |
||||
h = list(map(lambda x: Panda(x), pandas)) |
||||
print("H", h) |
||||
|
||||
for hh in h: |
||||
hh.set_controls_allowed(True) |
||||
|
||||
# test both directions |
||||
for ho in permutations(range(len(h)), r=2): |
||||
print("***************** TESTING", ho) |
||||
|
||||
panda0, panda1 = h[ho[0]], h[ho[1]] |
||||
|
||||
if(panda0._serial == "WIFI"): |
||||
print(" *** Can not send can data over wifi panda. Skipping! ***") |
||||
continue |
||||
|
||||
# **** test health packet **** |
||||
print("health", ho[0], h[ho[0]].health()) |
||||
|
||||
# **** test K/L line loopback **** |
||||
for bus in [2,3]: |
||||
# flush the output |
||||
h[ho[1]].kline_drain(bus=bus) |
||||
|
||||
# send the characters |
||||
st = get_test_string() |
||||
st = b"\xaa"+chr(len(st)+3).encode()+st |
||||
h[ho[0]].kline_send(st, bus=bus, checksum=False) |
||||
|
||||
# check for receive |
||||
ret = h[ho[1]].kline_drain(bus=bus) |
||||
|
||||
print("ST Data:") |
||||
hexdump(st) |
||||
print("RET Data:") |
||||
hexdump(ret) |
||||
assert st == ret |
||||
print("K/L pass", bus, ho, "\n") |
||||
time.sleep(sleep_duration) |
||||
|
||||
# **** test can line loopback **** |
||||
# for bus, gmlan in [(0, None), (1, False), (2, False), (1, True), (2, True)]: |
||||
for bus, gmlan in [(0, None), (1, None)]: |
||||
print("\ntest can", bus) |
||||
# flush |
||||
cans_echo = panda0.can_recv() |
||||
cans_loop = panda1.can_recv() |
||||
|
||||
if gmlan is not None: |
||||
panda0.set_gmlan(gmlan, bus) |
||||
panda1.set_gmlan(gmlan, bus) |
||||
|
||||
# send the characters |
||||
# pick addresses high enough to not conflict with honda code |
||||
at = random.randint(1024, 2000) |
||||
st = get_test_string()[0:8] |
||||
panda0.can_send(at, st, bus) |
||||
time.sleep(0.1) |
||||
|
||||
# check for receive |
||||
cans_echo = panda0.can_recv() |
||||
cans_loop = panda1.can_recv() |
||||
|
||||
print("Bus", bus, "echo", cans_echo, "loop", cans_loop) |
||||
|
||||
assert len(cans_echo) == 1 |
||||
assert len(cans_loop) == 1 |
||||
|
||||
assert cans_echo[0][0] == at |
||||
assert cans_loop[0][0] == at |
||||
|
||||
assert cans_echo[0][2] == st |
||||
assert cans_loop[0][2] == st |
||||
|
||||
assert cans_echo[0][3] == 0x80 | bus |
||||
if cans_loop[0][3] != bus: |
||||
print("EXPECTED %d GOT %d" % (bus, cans_loop[0][3])) |
||||
assert cans_loop[0][3] == bus |
||||
|
||||
print("CAN pass", bus, ho) |
||||
time.sleep(sleep_duration) |
||||
|
||||
if __name__ == "__main__": |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument("-n", type=int, help="Number of test iterations to run") |
||||
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0) |
||||
args = parser.parse_args() |
||||
|
||||
if args.n is None: |
||||
while True: |
||||
run_test(sleep_duration=args.sleep) |
||||
else: |
||||
for i in range(args.n): |
||||
run_test(sleep_duration=args.sleep) |
Loading…
Reference in new issue