git-subtree-dir: panda
git-subtree-split: ae816c104a99a8cd4d508ccd6abdc7b93053529c
old-commit-hash: dba8e01e54
commatwo_master
commit
de9b3512b3
323 changed files with 31148 additions and 0 deletions
@ -0,0 +1,110 @@ |
||||
version: 2 |
||||
jobs: |
||||
safety: |
||||
machine: |
||||
docker_layer_caching: true |
||||
steps: |
||||
- checkout |
||||
- run: |
||||
name: Build image |
||||
command: "docker build -t panda_safety -f tests/safety/Dockerfile ." |
||||
- run: |
||||
name: Run safety test |
||||
command: | |
||||
docker run panda_safety /bin/bash -c "cd /panda/tests/safety; ./test.sh" |
||||
|
||||
misra-c2012: |
||||
machine: |
||||
docker_layer_caching: true |
||||
steps: |
||||
- checkout |
||||
- run: |
||||
name: Build image |
||||
command: "docker build -t panda_misra -f tests/misra/Dockerfile ." |
||||
- run: |
||||
name: Run Misra C 2012 test |
||||
command: | |
||||
mkdir /tmp/misra |
||||
docker run -v /tmp/misra:/tmp/misra panda_misra /bin/bash -c "cd /panda/tests/misra; ./test_misra.sh" |
||||
- store_artifacts: |
||||
name: Store cppcheck test output |
||||
path: /tmp/misra/cppcheck_output.txt |
||||
- store_artifacts: |
||||
name: Store misra test output |
||||
path: /tmp/misra/misra_output.txt |
||||
- store_artifacts: |
||||
name: Store cppcheck safety test output |
||||
path: /tmp/misra/cppcheck_safety_output.txt |
||||
- store_artifacts: |
||||
name: Store misra safety test output |
||||
path: /tmp/misra/misra_safety_output.txt |
||||
|
||||
|
||||
strict-compiler: |
||||
machine: |
||||
docker_layer_caching: true |
||||
steps: |
||||
- checkout |
||||
- run: |
||||
name: Build image |
||||
command: "docker build -t panda_strict_compiler -f tests/build_strict/Dockerfile ." |
||||
- run: |
||||
name: Build Panda with strict compiler rules |
||||
command: | |
||||
docker run panda_strict_compiler /bin/bash -c "cd /panda/tests/build_strict; ./test_build_strict.sh" |
||||
|
||||
build: |
||||
machine: |
||||
docker_layer_caching: true |
||||
steps: |
||||
- checkout |
||||
- run: |
||||
name: Build image |
||||
command: "docker build -t panda_build -f tests/build/Dockerfile ." |
||||
- run: |
||||
name: Test python package installer |
||||
command: | |
||||
docker run panda_build /bin/bash -c "cd /panda; python setup.py install" |
||||
- run: |
||||
name: Build Panda STM image |
||||
command: | |
||||
docker run panda_build /bin/bash -c "cd /panda/board; make bin" |
||||
- run: |
||||
name: Build Panda STM bootstub image |
||||
command: | |
||||
docker run panda_build /bin/bash -c "cd /panda/board; make obj/bootstub.panda.bin" |
||||
- run: |
||||
name: Build Pedal STM image |
||||
command: | |
||||
docker run panda_build /bin/bash -c "cd /panda/board/pedal; make obj/comma.bin" |
||||
- run: |
||||
name: Build Pedal STM bootstub image |
||||
command: | |
||||
docker run panda_build /bin/bash -c "cd /panda/board/pedal; make obj/bootstub.bin" |
||||
- run: |
||||
name: Build ESP image |
||||
command: | |
||||
docker run panda_build /bin/bash -c "cd /panda/boardesp; make user1.bin" |
||||
|
||||
safety_replay: |
||||
machine: |
||||
docker_layer_caching: true |
||||
steps: |
||||
- checkout |
||||
- run: |
||||
name: Build image |
||||
command: "docker build -t panda_safety_replay -f tests/safety_replay/Dockerfile ." |
||||
- run: |
||||
name: Replay drives |
||||
command: | |
||||
docker run panda_safety_replay /bin/bash -c "cd /openpilot/panda/tests/safety_replay; PYTHONPATH=/openpilot ./test_safety_replay.py" |
||||
|
||||
workflows: |
||||
version: 2 |
||||
main: |
||||
jobs: |
||||
- safety |
||||
- misra-c2012 |
||||
- strict-compiler |
||||
- build |
||||
- safety_replay |
@ -0,0 +1,3 @@ |
||||
.git |
||||
.DS_Store |
||||
boardesp/esp-open-sdk |
@ -0,0 +1,87 @@ |
||||
*.ico filter=lfs diff=lfs merge=lfs -text |
||||
*.dlc filter=lfs diff=lfs merge=lfs -text |
||||
*.onnx filter=lfs diff=lfs merge=lfs -text |
||||
*.svg filter=lfs diff=lfs merge=lfs -text |
||||
*.png filter=lfs diff=lfs merge=lfs -text |
||||
*.gif filter=lfs diff=lfs merge=lfs -text |
||||
*.ttf filter=lfs diff=lfs merge=lfs -text |
||||
*.wav filter=lfs diff=lfs merge=lfs -text |
||||
system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/ui/qt/spinner_larch64 filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/ui/qt/text_larch64 filter=lfs diff=lfs merge=lfs -text |
||||
third_party/**/*.a filter=lfs diff=lfs merge=lfs -text |
||||
third_party/**/*.so filter=lfs diff=lfs merge=lfs -text |
||||
third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text |
||||
third_party/**/*.dylib filter=lfs diff=lfs merge=lfs -text |
||||
third_party/acados/*/t_renderer filter=lfs diff=lfs merge=lfs -text |
||||
third_party/qt5/larch64/bin/lrelease filter=lfs diff=lfs merge=lfs -text |
||||
third_party/qt5/larch64/bin/lupdate filter=lfs diff=lfs merge=lfs -text |
||||
third_party/catch2/include/catch2/catch.hpp filter=lfs diff=lfs merge=lfs -text |
||||
*.apk filter=lfs diff=lfs merge=lfs -text |
||||
*.apkpatch filter=lfs diff=lfs merge=lfs -text |
||||
*.jar filter=lfs diff=lfs merge=lfs -text |
||||
*.pdf filter=lfs diff=lfs merge=lfs -text |
||||
*.jpg filter=lfs diff=lfs merge=lfs -text |
||||
*.mp3 filter=lfs diff=lfs merge=lfs -text |
||||
*.thneed filter=lfs diff=lfs merge=lfs -text |
||||
*.tar.gz filter=lfs diff=lfs merge=lfs -text |
||||
*.npy filter=lfs diff=lfs merge=lfs -text |
||||
*.csv filter=lfs diff=lfs merge=lfs -text |
||||
*.a filter=lfs diff=lfs merge=lfs -text |
||||
*.so* filter=lfs diff=lfs merge=lfs -text |
||||
*.dylib filter=lfs diff=lfs merge=lfs -text |
||||
*.o filter=lfs diff=lfs merge=lfs -text |
||||
*.b64 filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/boardd/tests/test_boardd filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/ui/qt/spinner_aarch64 filter=lfs diff=lfs merge=lfs -text |
||||
installer/updater/updater filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/debug/profiling/simpleperf/**/* filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/hardware/eon/updater filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/ui/qt/text_aarch64 filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/debug/profiling/pyflame/**/* filter=lfs diff=lfs merge=lfs -text |
||||
installer/installers/installer_openpilot filter=lfs diff=lfs merge=lfs -text |
||||
installer/installers/installer_dashcam filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/ui/text/text filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/ui/android/text/text filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/ui/spinner/spinner filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/visiond/visiond filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/loggerd/loggerd filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/sensord/sensord filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/sensord/gpsd filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/ui/android/spinner/spinner filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/ui/qt/spinner filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/ui/qt/text filter=lfs diff=lfs merge=lfs -text |
||||
_stringdefs.py filter=lfs diff=lfs merge=lfs -text |
||||
dfu-util-aarch64-linux filter=lfs diff=lfs merge=lfs -text |
||||
dfu-util-aarch64 filter=lfs diff=lfs merge=lfs -text |
||||
dfu-util-x86_64-linux filter=lfs diff=lfs merge=lfs -text |
||||
dfu-util-x86_64 filter=lfs diff=lfs merge=lfs -text |
||||
stb_image.h filter=lfs diff=lfs merge=lfs -text |
||||
clpeak3 filter=lfs diff=lfs merge=lfs -text |
||||
clwaste filter=lfs diff=lfs merge=lfs -text |
||||
apk/**/* filter=lfs diff=lfs merge=lfs -text |
||||
external/**/* filter=lfs diff=lfs merge=lfs -text |
||||
phonelibs/**/* filter=lfs diff=lfs merge=lfs -text |
||||
third_party/boringssl/**/* filter=lfs diff=lfs merge=lfs -text |
||||
pyextra/**/* filter=lfs diff=lfs merge=lfs -text |
||||
panda/board/**/inc/*.h filter=lfs diff=lfs merge=lfs -text |
||||
panda/board/obj/*.elf filter=lfs diff=lfs merge=lfs -text |
||||
board/inc/*.h filter=lfs diff=lfs merge=lfs -text |
||||
third_party/nanovg/**/* filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/controls/lib/lateral_mpc/lib_mpc_export/**/* filter=lfs diff=lfs merge=lfs -text |
||||
third_party/android_hardware_libhardware/**/* filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/controls/lib/lead_mpc_lib/lib_mpc_export/**/* filter=lfs diff=lfs merge=lfs -text |
||||
*.pro filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/controls/lib/longitudinal_mpc/lib_mpc_export/**/* filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/controls/lib/lateral_mpc/mpc_export/**/* filter=lfs diff=lfs merge=lfs -text |
||||
third_party/curl/**/* filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/modeld/thneed/debug/**/* filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/modeld/thneed/include/**/* filter=lfs diff=lfs merge=lfs -text |
||||
third_party/openmax/**/* filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/controls/lib/longitudinal_mpc/mpc_export/**/* filter=lfs diff=lfs merge=lfs -text |
||||
selfdrive/controls/lib/longitudinal_mpc_model/lib_mpc_export/**/* filter=lfs diff=lfs merge=lfs -text |
||||
Pipfile filter=lfs diff=lfs merge=lfs -text |
||||
Pipfile.lock filter=lfs diff=lfs merge=lfs -text |
||||
poetry.lock filter=lfs diff=lfs merge=lfs -text |
||||
*.qm filter=lfs diff=lfs merge=lfs -text |
@ -0,0 +1,15 @@ |
||||
*.pyc |
||||
.*.swp |
||||
.*.swo |
||||
*.o |
||||
*.so |
||||
*.d |
||||
a.out |
||||
*~ |
||||
.#* |
||||
dist/ |
||||
pandacan.egg-info/ |
||||
board/obj/ |
||||
examples/output.csv |
||||
.DS_Store |
||||
nosetests.xml |
@ -0,0 +1,62 @@ |
||||
FROM ubuntu:16.04 |
||||
ENV PYTHONUNBUFFERED 1 |
||||
|
||||
RUN apt-get update && apt-get install -y \ |
||||
autoconf \ |
||||
automake \ |
||||
bash \ |
||||
bison \ |
||||
bzip2 \ |
||||
curl \ |
||||
dfu-util \ |
||||
flex \ |
||||
g++ \ |
||||
gawk \ |
||||
gcc \ |
||||
git \ |
||||
gperf \ |
||||
help2man \ |
||||
iputils-ping \ |
||||
libexpat-dev \ |
||||
libstdc++-arm-none-eabi-newlib \ |
||||
libtool \ |
||||
libtool-bin \ |
||||
libusb-1.0-0 \ |
||||
make \ |
||||
ncurses-dev \ |
||||
network-manager \ |
||||
python-dev \ |
||||
python-serial \ |
||||
sed \ |
||||
texinfo \ |
||||
unrar-free \ |
||||
unzip \ |
||||
wget \ |
||||
build-essential \ |
||||
python-dev \ |
||||
python-pip \ |
||||
screen \ |
||||
vim \ |
||||
wget \ |
||||
wireless-tools |
||||
|
||||
RUN pip install --upgrade pip==18.0 |
||||
|
||||
COPY requirements.txt /tmp/ |
||||
RUN pip install -r /tmp/requirements.txt |
||||
|
||||
RUN mkdir -p /home/batman |
||||
ENV HOME /home/batman |
||||
|
||||
ENV PYTHONPATH /tmp:$PYTHONPATH |
||||
|
||||
COPY ./boardesp/get_sdk_ci.sh /tmp/panda/boardesp/ |
||||
|
||||
RUN useradd --system -s /sbin/nologin pandauser |
||||
RUN mkdir -p /tmp/panda/boardesp/esp-open-sdk |
||||
RUN chown pandauser /tmp/panda/boardesp/esp-open-sdk |
||||
USER pandauser |
||||
RUN cd /tmp/panda/boardesp && ./get_sdk_ci.sh |
||||
USER root |
||||
|
||||
ADD ./panda.tar.gz /tmp/panda |
@ -0,0 +1,53 @@ |
||||
pipeline { |
||||
agent any |
||||
environment { |
||||
AUTHOR = """${sh( |
||||
returnStdout: true, |
||||
script: "git --no-pager show -s --format='%an' ${GIT_COMMIT}" |
||||
).trim()}""" |
||||
|
||||
DOCKER_IMAGE_TAG = "panda:build-${env.GIT_COMMIT}" |
||||
DOCKER_NAME = "panda-test-${env.GIT_COMMIT}" |
||||
} |
||||
stages { |
||||
stage('Build Docker Image') { |
||||
steps { |
||||
timeout(time: 60, unit: 'MINUTES') { |
||||
script { |
||||
sh 'git archive -v -o panda.tar.gz --format=tar.gz HEAD' |
||||
dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
stage('Test Dev Build') { |
||||
steps { |
||||
lock(resource: "Pandas", inversePrecedence: true, quantity:1){ |
||||
timeout(time: 60, unit: 'MINUTES') { |
||||
sh "docker run --name ${env.DOCKER_NAME} --privileged --volume /dev/bus/usb:/dev/bus/usb --volume /var/run/dbus:/var/run/dbus --net host ${env.DOCKER_IMAGE_TAG} bash -c 'cd /tmp/panda; ./run_automated_tests.sh '" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
stage('Test EON Build') { |
||||
steps { |
||||
lock(resource: "Pandas", inversePrecedence: true, quantity:1){ |
||||
timeout(time: 60, unit: 'MINUTES') { |
||||
sh "docker cp ${env.DOCKER_NAME}:/tmp/panda/nosetests.xml test_results_dev.xml" |
||||
sh "touch EON && docker cp EON ${env.DOCKER_NAME}:/EON" |
||||
sh "docker start -a ${env.DOCKER_NAME}" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
post { |
||||
always { |
||||
script { |
||||
sh "docker cp ${env.DOCKER_NAME}:/tmp/panda/nosetests.xml test_results_EON.xml" |
||||
sh "docker rm ${env.DOCKER_NAME}" |
||||
} |
||||
junit "test_results*.xml" |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,7 @@ |
||||
Copyright (c) 2016, Comma.ai, Inc. |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,31 @@ |
||||
** Projects ** |
||||
|
||||
== ELM327 Emulator == |
||||
|
||||
Write an elm327 emulator in boardesp/elm327.c and make it work with Torque |
||||
|
||||
You'll find a start at this in the "elm327" branch. |
||||
|
||||
== socketcan Kernel Driver == |
||||
|
||||
Write a kernel driver version of lib/panda.py that exposes the Panda on socketcan and makes it work with those tools. |
||||
|
||||
You may want to switch to interrupt endpoint first. Should LIN be exposed as a serial interface? |
||||
|
||||
== Windows J2534 DLL == |
||||
|
||||
Write a Windows DLL that exposes the J2534 API. |
||||
|
||||
Will make the Panda work with car diagnostic software. |
||||
|
||||
|
||||
** Refactors ** |
||||
|
||||
== USB Interrupt Endpoint == |
||||
|
||||
Switch USB to use an interrupt endpoint instead of a bulk endpoint for can recv |
||||
|
||||
== WebSocket Support == |
||||
|
||||
Add CAN streaming over WebSocket to the ELM code in addition to the UDP pipe. |
||||
|
@ -0,0 +1,9 @@ |
||||
# Updating your panda |
||||
|
||||
Panda should update automatically via the [Chffr](http://chffr.comma.ai/) app ([apple](https://itunes.apple.com/us/app/chffr-dash-cam-that-remembers/id1146683979) and [android](https://play.google.com/store/apps/details?id=ai.comma.chffr)) |
||||
|
||||
If it doesn't however, you can use the following commands on linux or Mac OSX |
||||
`sudo pip install --upgrade pandacan` |
||||
` PYTHONPATH="" sudo python -c "import panda; panda.flash_release()"` |
||||
|
||||
(You'll need to have `pip` and `sudo` installed.) |
@ -0,0 +1 @@ |
||||
from .python import Panda, PandaWifiStreaming, PandaDFU, ESPROM, CesantaFlasher, flash_release, BASEDIR, ensure_st_up_to_date, build_st, PandaSerial |
@ -0,0 +1,8 @@ |
||||
PROJ_NAME = panda
|
||||
CFLAGS = -g -Wall
|
||||
|
||||
CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4
|
||||
CFLAGS += -mhard-float -DSTM32F4 -DSTM32F413xx -mfpu=fpv4-sp-d16 -fsingle-precision-constant
|
||||
STARTUP_FILE = startup_stm32f413xx
|
||||
|
||||
include build.mk |
@ -0,0 +1,8 @@ |
||||
PROJ_NAME = panda
|
||||
CFLAGS = -g -Wall -Wextra -Wstrict-prototypes
|
||||
|
||||
CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4
|
||||
CFLAGS += -mhard-float -DSTM32F4 -DSTM32F413xx -mfpu=fpv4-sp-d16 -fsingle-precision-constant
|
||||
STARTUP_FILE = startup_stm32f413xx
|
||||
|
||||
include build.mk |
@ -0,0 +1,35 @@ |
||||
Dependencies |
||||
-------- |
||||
|
||||
**Mac** |
||||
|
||||
``` |
||||
./get_sdk_mac.sh |
||||
``` |
||||
|
||||
**Debian / Ubuntu** |
||||
|
||||
``` |
||||
./get_sdk.sh |
||||
``` |
||||
|
||||
|
||||
Programming |
||||
---- |
||||
|
||||
**Panda** |
||||
|
||||
``` |
||||
make |
||||
``` |
||||
|
||||
Troubleshooting |
||||
---- |
||||
|
||||
If your panda will not flash and is quickly blinking a single Green LED, use: |
||||
``` |
||||
make recover |
||||
``` |
||||
|
||||
|
||||
[dfu-util](http://github.com/dsigma/dfu-util.git) for flashing |
@ -0,0 +1,94 @@ |
||||
#define BOOTSTUB |
||||
|
||||
#include "config.h" |
||||
#include "obj/gitversion.h" |
||||
|
||||
#ifdef STM32F4 |
||||
#define PANDA |
||||
#include "stm32f4xx.h" |
||||
#include "stm32f4xx_hal_gpio_ex.h" |
||||
#else |
||||
#include "stm32f2xx.h" |
||||
#include "stm32f2xx_hal_gpio_ex.h" |
||||
#endif |
||||
|
||||
// default since there's no serial
|
||||
void puts(const char *a) {} |
||||
void puth(unsigned int i) {} |
||||
|
||||
#include "libc.h" |
||||
#include "provision.h" |
||||
|
||||
#include "drivers/clock.h" |
||||
#include "drivers/llgpio.h" |
||||
#include "gpio.h" |
||||
|
||||
#include "drivers/spi.h" |
||||
#include "drivers/usb.h" |
||||
//#include "drivers/uart.h"
|
||||
|
||||
#include "crypto/rsa.h" |
||||
#include "crypto/sha.h" |
||||
|
||||
#include "obj/cert.h" |
||||
|
||||
#include "spi_flasher.h" |
||||
|
||||
void __initialize_hardware_early() { |
||||
early(); |
||||
} |
||||
|
||||
void fail() { |
||||
soft_flasher_start(); |
||||
} |
||||
|
||||
// know where to sig check
|
||||
extern void *_app_start[]; |
||||
|
||||
// FIXME: sometimes your panda will fail flashing and will quickly blink a single Green LED
|
||||
// BOUNTY: $200 coupon on shop.comma.ai or $100 check.
|
||||
|
||||
int main() { |
||||
__disable_irq(); |
||||
clock_init(); |
||||
detect(); |
||||
|
||||
if (revision == PANDA_REV_C) { |
||||
set_usb_power_mode(USB_POWER_CLIENT); |
||||
} |
||||
|
||||
if (enter_bootloader_mode == ENTER_SOFTLOADER_MAGIC) { |
||||
enter_bootloader_mode = 0; |
||||
soft_flasher_start(); |
||||
} |
||||
|
||||
// validate length
|
||||
int len = (int)_app_start[0]; |
||||
if ((len < 8) || (len > (0x1000000 - 0x4000 - 4 - RSANUMBYTES))) goto fail; |
||||
|
||||
// compute SHA hash
|
||||
uint8_t digest[SHA_DIGEST_SIZE]; |
||||
SHA_hash(&_app_start[1], len-4, digest); |
||||
|
||||
// verify RSA signature
|
||||
if (RSA_verify(&release_rsa_key, ((void*)&_app_start[0]) + len, RSANUMBYTES, digest, SHA_DIGEST_SIZE)) { |
||||
goto good; |
||||
} |
||||
|
||||
// allow debug if built from source
|
||||
#ifdef ALLOW_DEBUG |
||||
if (RSA_verify(&debug_rsa_key, ((void*)&_app_start[0]) + len, RSANUMBYTES, digest, SHA_DIGEST_SIZE)) { |
||||
goto good; |
||||
} |
||||
#endif |
||||
|
||||
// here is a failure
|
||||
fail: |
||||
fail(); |
||||
return 0; |
||||
good: |
||||
// jump to flash
|
||||
((void(*)()) _app_start[1])(); |
||||
return 0; |
||||
} |
||||
|
@ -0,0 +1,84 @@ |
||||
CFLAGS += -I inc -I ../ -nostdlib -fno-builtin -std=gnu11 -Os
|
||||
|
||||
CFLAGS += -Tstm32_flash.ld
|
||||
|
||||
DFU_UTIL = "dfu-util"
|
||||
|
||||
# Compile fast charge (DCP) only not on EON
|
||||
ifeq (,$(wildcard /EON)) |
||||
BUILDER = DEV
|
||||
else |
||||
CFLAGS += "-DEON"
|
||||
BUILDER = EON
|
||||
DFU_UTIL = "tools/dfu-util-aarch64"
|
||||
endif |
||||
|
||||
CC = arm-none-eabi-gcc
|
||||
OBJCOPY = arm-none-eabi-objcopy
|
||||
OBJDUMP = arm-none-eabi-objdump
|
||||
|
||||
ifeq ($(RELEASE),1) |
||||
CERT = ../../pandaextra/certs/release
|
||||
else |
||||
# enable the debug cert
|
||||
CERT = ../certs/debug
|
||||
CFLAGS += "-DALLOW_DEBUG"
|
||||
endif |
||||
|
||||
|
||||
DEPDIR = generated_dependencies
|
||||
$(shell mkdir -p -m 777 $(DEPDIR) >/dev/null) |
||||
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td
|
||||
POSTCOMPILE = @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d && touch $@
|
||||
|
||||
# this no longer pushes the bootstub
|
||||
flash: obj/$(PROJ_NAME).bin |
||||
PYTHONPATH=../ python -c "from python import Panda; Panda().flash('obj/$(PROJ_NAME).bin')"
|
||||
|
||||
ota: obj/$(PROJ_NAME).bin |
||||
curl http://192.168.0.10/stupdate --upload-file $<
|
||||
|
||||
bin: obj/$(PROJ_NAME).bin |
||||
|
||||
# this flashes everything
|
||||
recover: obj/bootstub.$(PROJ_NAME).bin obj/$(PROJ_NAME).bin |
||||
-PYTHONPATH=../ python -c "from python import Panda; Panda().reset(enter_bootloader=True)"
|
||||
sleep 1.0
|
||||
$(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.$(PROJ_NAME).bin
|
||||
|
||||
include ../common/version.mk |
||||
|
||||
obj/cert.h: ../crypto/getcertheader.py |
||||
../crypto/getcertheader.py ../certs/debug.pub ../certs/release.pub > $@
|
||||
|
||||
obj/%.$(PROJ_NAME).o: %.c obj/gitversion.h obj/cert.h $(DEPDIR)/%.d |
||||
$(CC) $(DEPFLAGS) $(CFLAGS) -o $@ -c $<
|
||||
$(POSTCOMPILE)
|
||||
|
||||
obj/%.$(PROJ_NAME).o: ../crypto/%.c |
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
obj/$(STARTUP_FILE).o: $(STARTUP_FILE).s |
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
obj/$(PROJ_NAME).bin: obj/$(STARTUP_FILE).o obj/main.$(PROJ_NAME).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)
|
||||
@BINSIZE=$$(du -b "obj/$(PROJ_NAME).bin" | cut -f 1) ; \
|
||||
if [ $$BINSIZE -ge 32768 ]; then echo "ERROR obj/$(PROJ_NAME).bin is too big!"; exit 1; fi;
|
||||
|
||||
obj/bootstub.$(PROJ_NAME).bin: obj/$(STARTUP_FILE).o obj/bootstub.$(PROJ_NAME).o obj/sha.$(PROJ_NAME).o obj/rsa.$(PROJ_NAME).o |
||||
$(CC) $(CFLAGS) -o obj/bootstub.$(PROJ_NAME).elf $^
|
||||
$(OBJCOPY) -v -O binary obj/bootstub.$(PROJ_NAME).elf $@
|
||||
|
||||
$(DEPDIR)/%.d: ; |
||||
.PRECIOUS: $(DEPDIR)/%.d |
||||
|
||||
include $(wildcard $(patsubst %,$(DEPDIR)/%.d,$(basename $(wildcard *.c)))) |
||||
|
||||
clean: |
||||
@$(RM) obj/*
|
||||
@rm -rf $(DEPDIR)
|
@ -0,0 +1,40 @@ |
||||
#ifndef PANDA_CONFIG_H |
||||
#define PANDA_CONFIG_H |
||||
|
||||
//#define DEBUG
|
||||
//#define DEBUG_USB
|
||||
//#define DEBUG_SPI
|
||||
|
||||
#ifdef STM32F4 |
||||
#define PANDA |
||||
#include "stm32f4xx.h" |
||||
#else |
||||
#include "stm32f2xx.h" |
||||
#endif |
||||
|
||||
#define USB_VID 0xbbaa |
||||
|
||||
#ifdef BOOTSTUB |
||||
#define USB_PID 0xddee |
||||
#else |
||||
#define USB_PID 0xddcc |
||||
#endif |
||||
|
||||
#include <stdbool.h> |
||||
#define NULL ((void*)0) |
||||
#define COMPILE_TIME_ASSERT(pred) ((void)sizeof(char[1 - (2 * (!(pred)))])) |
||||
|
||||
#define MIN(a,b) \ |
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
(_a < _b) ? _a : _b; }) |
||||
|
||||
#define MAX(a,b) \ |
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
(_a > _b) ? _a : _b; }) |
||||
|
||||
#define MAX_RESP_LEN 0x40 |
||||
|
||||
#endif |
||||
|
@ -0,0 +1,38 @@ |
||||
// ACCEL1 = ADC10
|
||||
// ACCEL2 = ADC11
|
||||
// VOLT_S = ADC12
|
||||
// CURR_S = ADC13
|
||||
|
||||
#define ADCCHAN_ACCEL0 10 |
||||
#define ADCCHAN_ACCEL1 11 |
||||
#define ADCCHAN_VOLTAGE 12 |
||||
#define ADCCHAN_CURRENT 13 |
||||
|
||||
void adc_init(void) { |
||||
// global setup
|
||||
ADC->CCR = ADC_CCR_TSVREFE | ADC_CCR_VBATE; |
||||
//ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_EOCS | ADC_CR2_DDS;
|
||||
ADC1->CR2 = ADC_CR2_ADON; |
||||
|
||||
// long
|
||||
//ADC1->SMPR1 = ADC_SMPR1_SMP10 | ADC_SMPR1_SMP11 | ADC_SMPR1_SMP12 | ADC_SMPR1_SMP13;
|
||||
ADC1->SMPR1 = ADC_SMPR1_SMP12 | ADC_SMPR1_SMP13; |
||||
} |
||||
|
||||
uint32_t adc_get(int channel) { |
||||
// includes length
|
||||
//ADC1->SQR1 = 0;
|
||||
|
||||
// select channel
|
||||
ADC1->JSQR = channel << 15; |
||||
|
||||
//ADC1->CR1 = ADC_CR1_DISCNUM_0;
|
||||
//ADC1->CR1 = ADC_CR1_EOCIE;
|
||||
|
||||
ADC1->SR &= ~(ADC_SR_JEOC); |
||||
ADC1->CR2 |= ADC_CR2_JSWSTART; |
||||
while (!(ADC1->SR & ADC_SR_JEOC)); |
||||
|
||||
return ADC1->JDR1; |
||||
} |
||||
|
@ -0,0 +1,363 @@ |
||||
// IRQs: CAN1_TX, CAN1_RX0, CAN1_SCE
|
||||
// CAN2_TX, CAN2_RX0, CAN2_SCE
|
||||
// CAN3_TX, CAN3_RX0, CAN3_SCE
|
||||
|
||||
typedef struct { |
||||
volatile uint32_t w_ptr; |
||||
volatile uint32_t r_ptr; |
||||
uint32_t fifo_size; |
||||
CAN_FIFOMailBox_TypeDef *elems; |
||||
} can_ring; |
||||
|
||||
#define CAN_BUS_RET_FLAG 0x80 |
||||
#define CAN_BUS_NUM_MASK 0x7F |
||||
|
||||
#define BUS_MAX 4 |
||||
|
||||
extern int can_live, pending_can_live; |
||||
|
||||
// must reinit after changing these
|
||||
extern int can_loopback, can_silent; |
||||
extern uint32_t can_speed[4]; |
||||
|
||||
void can_set_forwarding(int from, int to); |
||||
|
||||
void can_init(uint8_t can_number); |
||||
void can_init_all(void); |
||||
void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number); |
||||
bool can_pop(can_ring *q, CAN_FIFOMailBox_TypeDef *elem); |
||||
|
||||
// end API
|
||||
|
||||
#define ALL_CAN_SILENT 0xFF |
||||
#define ALL_CAN_BUT_MAIN_SILENT 0xFE |
||||
#define ALL_CAN_LIVE 0 |
||||
|
||||
int can_live = 0, pending_can_live = 0, can_loopback = 0, can_silent = ALL_CAN_SILENT; |
||||
|
||||
// ********************* instantiate queues *********************
|
||||
|
||||
#define can_buffer(x, size) \ |
||||
CAN_FIFOMailBox_TypeDef elems_##x[size]; \
|
||||
can_ring can_##x = { .w_ptr = 0, .r_ptr = 0, .fifo_size = size, .elems = (CAN_FIFOMailBox_TypeDef *)&elems_##x }; |
||||
|
||||
can_buffer(rx_q, 0x1000) |
||||
can_buffer(tx1_q, 0x100) |
||||
can_buffer(tx2_q, 0x100) |
||||
can_buffer(tx3_q, 0x100) |
||||
can_buffer(txgmlan_q, 0x100) |
||||
can_ring *can_queues[] = {&can_tx1_q, &can_tx2_q, &can_tx3_q, &can_txgmlan_q}; |
||||
|
||||
// global CAN stats
|
||||
int can_rx_cnt = 0; |
||||
int can_tx_cnt = 0; |
||||
int can_txd_cnt = 0; |
||||
int can_err_cnt = 0; |
||||
int can_overflow_cnt = 0; |
||||
|
||||
// ********************* interrupt safe queue *********************
|
||||
|
||||
bool can_pop(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) { |
||||
bool ret = 0; |
||||
|
||||
enter_critical_section(); |
||||
if (q->w_ptr != q->r_ptr) { |
||||
*elem = q->elems[q->r_ptr]; |
||||
if ((q->r_ptr + 1) == q->fifo_size) q->r_ptr = 0; |
||||
else q->r_ptr += 1; |
||||
ret = 1; |
||||
} |
||||
exit_critical_section(); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int can_push(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) { |
||||
int ret = 0; |
||||
uint32_t next_w_ptr; |
||||
|
||||
enter_critical_section(); |
||||
if ((q->w_ptr + 1) == q->fifo_size) next_w_ptr = 0; |
||||
else next_w_ptr = q->w_ptr + 1; |
||||
if (next_w_ptr != q->r_ptr) { |
||||
q->elems[q->w_ptr] = *elem; |
||||
q->w_ptr = next_w_ptr; |
||||
ret = 1; |
||||
} |
||||
exit_critical_section(); |
||||
if (ret == 0) { |
||||
can_overflow_cnt++; |
||||
#ifdef DEBUG |
||||
puts("can_push failed!\n"); |
||||
#endif |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
void can_clear(can_ring *q) { |
||||
enter_critical_section(); |
||||
q->w_ptr = 0; |
||||
q->r_ptr = 0; |
||||
exit_critical_section(); |
||||
} |
||||
|
||||
// assign CAN numbering
|
||||
// bus num: Can bus number on ODB connector. Sent to/from USB
|
||||
// Min: 0; Max: 127; Bit 7 marks message as receipt (bus 129 is receipt for but 1)
|
||||
// cans: Look up MCU can interface from bus number
|
||||
// can number: numeric lookup for MCU CAN interfaces (0 = CAN1, 1 = CAN2, etc);
|
||||
// bus_lookup: Translates from 'can number' to 'bus number'.
|
||||
// can_num_lookup: Translates from 'bus number' to 'can number'.
|
||||
// can_forwarding: Given a bus num, lookup bus num to forward to. -1 means no forward.
|
||||
|
||||
// Panda: Bus 0=CAN1 Bus 1=CAN2 Bus 2=CAN3
|
||||
CAN_TypeDef *cans[] = {CAN1, CAN2, CAN3}; |
||||
uint8_t bus_lookup[] = {0,1,2}; |
||||
uint8_t can_num_lookup[] = {0,1,2,-1}; |
||||
int8_t can_forwarding[] = {-1,-1,-1,-1}; |
||||
uint32_t can_speed[] = {5000, 5000, 5000, 333}; |
||||
#define CAN_MAX 3 |
||||
|
||||
#define CANIF_FROM_CAN_NUM(num) (cans[num]) |
||||
#define CAN_NUM_FROM_CANIF(CAN) ((CAN)==CAN1 ? 0 : ((CAN) == CAN2 ? 1 : 2)) |
||||
#define CAN_NAME_FROM_CANIF(CAN) ((CAN)==CAN1 ? "CAN1" : ((CAN) == CAN2 ? "CAN2" : "CAN3")) |
||||
#define BUS_NUM_FROM_CAN_NUM(num) (bus_lookup[num]) |
||||
#define CAN_NUM_FROM_BUS_NUM(num) (can_num_lookup[num]) |
||||
|
||||
void process_can(uint8_t can_number); |
||||
|
||||
void can_set_speed(uint8_t can_number) { |
||||
CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); |
||||
uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); |
||||
|
||||
if (!llcan_set_speed(CAN, can_speed[bus_number], can_loopback, can_silent & (1 << can_number))) { |
||||
puts("CAN init FAILED!!!!!\n"); |
||||
puth(can_number); puts(" "); |
||||
puth(BUS_NUM_FROM_CAN_NUM(can_number)); puts("\n"); |
||||
} |
||||
} |
||||
|
||||
void can_init(uint8_t can_number) { |
||||
if (can_number != 0xff) { |
||||
CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); |
||||
set_can_enable(CAN, 1); |
||||
can_set_speed(can_number); |
||||
|
||||
llcan_init(CAN); |
||||
|
||||
// in case there are queued up messages
|
||||
process_can(can_number); |
||||
} |
||||
} |
||||
|
||||
void can_init_all(void) { |
||||
for (int i=0; i < CAN_MAX; i++) { |
||||
can_init(i); |
||||
} |
||||
} |
||||
|
||||
void can_set_gmlan(int bus) { |
||||
if ((bus == -1) || (bus != can_num_lookup[3])) { |
||||
// GMLAN OFF
|
||||
switch (can_num_lookup[3]) { |
||||
case 1: |
||||
puts("disable GMLAN on CAN2\n"); |
||||
set_can_mode(1, 0); |
||||
bus_lookup[1] = 1; |
||||
can_num_lookup[1] = 1; |
||||
can_num_lookup[3] = -1; |
||||
can_init(1); |
||||
break; |
||||
case 2: |
||||
puts("disable GMLAN on CAN3\n"); |
||||
set_can_mode(2, 0); |
||||
bus_lookup[2] = 2; |
||||
can_num_lookup[2] = 2; |
||||
can_num_lookup[3] = -1; |
||||
can_init(2); |
||||
break; |
||||
default: |
||||
puts("GMLAN bus value invalid\n"); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (bus == 1) { |
||||
puts("GMLAN on CAN2\n"); |
||||
// GMLAN on CAN2
|
||||
set_can_mode(1, 1); |
||||
bus_lookup[1] = 3; |
||||
can_num_lookup[1] = -1; |
||||
can_num_lookup[3] = 1; |
||||
can_init(1); |
||||
} else if (bus == 2) { |
||||
puts("GMLAN on CAN3\n"); |
||||
// GMLAN on CAN3
|
||||
set_can_mode(2, 1); |
||||
bus_lookup[2] = 3; |
||||
can_num_lookup[2] = -1; |
||||
can_num_lookup[3] = 2; |
||||
can_init(2); |
||||
} |
||||
} |
||||
|
||||
// CAN error
|
||||
void can_sce(CAN_TypeDef *CAN) { |
||||
enter_critical_section(); |
||||
|
||||
#ifdef DEBUG |
||||
if (CAN==CAN1) puts("CAN1: "); |
||||
if (CAN==CAN2) puts("CAN2: "); |
||||
#ifdef CAN3 |
||||
if (CAN==CAN3) puts("CAN3: "); |
||||
#endif |
||||
puts("MSR:"); |
||||
puth(CAN->MSR); |
||||
puts(" TSR:"); |
||||
puth(CAN->TSR); |
||||
puts(" RF0R:"); |
||||
puth(CAN->RF0R); |
||||
puts(" RF1R:"); |
||||
puth(CAN->RF1R); |
||||
puts(" ESR:"); |
||||
puth(CAN->ESR); |
||||
puts("\n"); |
||||
#endif |
||||
|
||||
can_err_cnt += 1; |
||||
llcan_clear_send(CAN); |
||||
exit_critical_section(); |
||||
} |
||||
|
||||
// ***************************** CAN *****************************
|
||||
|
||||
void process_can(uint8_t can_number) { |
||||
if (can_number != 0xff) { |
||||
|
||||
enter_critical_section(); |
||||
|
||||
CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); |
||||
uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); |
||||
|
||||
// check for empty mailbox
|
||||
CAN_FIFOMailBox_TypeDef to_send; |
||||
if ((CAN->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) { |
||||
// add successfully transmitted message to my fifo
|
||||
if ((CAN->TSR & CAN_TSR_RQCP0) == CAN_TSR_RQCP0) { |
||||
can_txd_cnt += 1; |
||||
|
||||
if ((CAN->TSR & CAN_TSR_TXOK0) == CAN_TSR_TXOK0) { |
||||
CAN_FIFOMailBox_TypeDef to_push; |
||||
to_push.RIR = CAN->sTxMailBox[0].TIR; |
||||
to_push.RDTR = (CAN->sTxMailBox[0].TDTR & 0xFFFF000F) | ((CAN_BUS_RET_FLAG | bus_number) << 4); |
||||
to_push.RDLR = CAN->sTxMailBox[0].TDLR; |
||||
to_push.RDHR = CAN->sTxMailBox[0].TDHR; |
||||
can_push(&can_rx_q, &to_push); |
||||
} |
||||
|
||||
if ((CAN->TSR & CAN_TSR_TERR0) == CAN_TSR_TERR0) { |
||||
#ifdef DEBUG |
||||
puts("CAN TX ERROR!\n"); |
||||
#endif |
||||
} |
||||
|
||||
if ((CAN->TSR & CAN_TSR_ALST0) == CAN_TSR_ALST0) { |
||||
#ifdef DEBUG |
||||
puts("CAN TX ARBITRATION LOST!\n"); |
||||
#endif |
||||
} |
||||
|
||||
// clear interrupt
|
||||
// careful, this can also be cleared by requesting a transmission
|
||||
CAN->TSR |= CAN_TSR_RQCP0; |
||||
} |
||||
|
||||
if (can_pop(can_queues[bus_number], &to_send)) { |
||||
can_tx_cnt += 1; |
||||
// only send if we have received a packet
|
||||
CAN->sTxMailBox[0].TDLR = to_send.RDLR; |
||||
CAN->sTxMailBox[0].TDHR = to_send.RDHR; |
||||
CAN->sTxMailBox[0].TDTR = to_send.RDTR; |
||||
CAN->sTxMailBox[0].TIR = to_send.RIR; |
||||
} |
||||
} |
||||
|
||||
exit_critical_section(); |
||||
} |
||||
} |
||||
|
||||
// CAN receive handlers
|
||||
// blink blue when we are receiving CAN messages
|
||||
void can_rx(uint8_t can_number) { |
||||
CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); |
||||
uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); |
||||
while ((CAN->RF0R & CAN_RF0R_FMP0) != 0) { |
||||
can_rx_cnt += 1; |
||||
|
||||
// can is live
|
||||
pending_can_live = 1; |
||||
|
||||
// add to my fifo
|
||||
CAN_FIFOMailBox_TypeDef to_push; |
||||
to_push.RIR = CAN->sFIFOMailBox[0].RIR; |
||||
to_push.RDTR = CAN->sFIFOMailBox[0].RDTR; |
||||
to_push.RDLR = CAN->sFIFOMailBox[0].RDLR; |
||||
to_push.RDHR = CAN->sFIFOMailBox[0].RDHR; |
||||
|
||||
// modify RDTR for our API
|
||||
to_push.RDTR = (to_push.RDTR & 0xFFFF000F) | (bus_number << 4); |
||||
|
||||
// forwarding (panda only)
|
||||
int bus_fwd_num = (can_forwarding[bus_number] != -1) ? can_forwarding[bus_number] : safety_fwd_hook(bus_number, &to_push); |
||||
if (bus_fwd_num != -1) { |
||||
CAN_FIFOMailBox_TypeDef to_send; |
||||
to_send.RIR = to_push.RIR | 1; // TXRQ
|
||||
to_send.RDTR = to_push.RDTR; |
||||
to_send.RDLR = to_push.RDLR; |
||||
to_send.RDHR = to_push.RDHR; |
||||
can_send(&to_send, bus_fwd_num); |
||||
} |
||||
|
||||
safety_rx_hook(&to_push); |
||||
|
||||
set_led(LED_BLUE, 1); |
||||
can_push(&can_rx_q, &to_push); |
||||
|
||||
// next
|
||||
CAN->RF0R |= CAN_RF0R_RFOM0; |
||||
} |
||||
} |
||||
|
||||
void CAN1_TX_IRQHandler(void) { process_can(0); } |
||||
void CAN1_RX0_IRQHandler(void) { can_rx(0); } |
||||
void CAN1_SCE_IRQHandler(void) { can_sce(CAN1); } |
||||
|
||||
void CAN2_TX_IRQHandler(void) { process_can(1); } |
||||
void CAN2_RX0_IRQHandler(void) { can_rx(1); } |
||||
void CAN2_SCE_IRQHandler(void) { can_sce(CAN2); } |
||||
|
||||
void CAN3_TX_IRQHandler(void) { process_can(2); } |
||||
void CAN3_RX0_IRQHandler(void) { can_rx(2); } |
||||
void CAN3_SCE_IRQHandler(void) { can_sce(CAN3); } |
||||
|
||||
void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number) { |
||||
if (safety_tx_hook(to_push) != 0) { |
||||
if (bus_number < BUS_MAX) { |
||||
// add CAN packet to send queue
|
||||
// bus number isn't passed through
|
||||
to_push->RDTR &= 0xF; |
||||
if ((bus_number == 3) && (can_num_lookup[3] == 0xFF)) { |
||||
// TODO: why uint8 bro? only int8?
|
||||
bitbang_gmlan(to_push); |
||||
} else { |
||||
can_push(can_queues[bus_number], to_push); |
||||
process_can(CAN_NUM_FROM_BUS_NUM(bus_number)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void can_set_forwarding(int from, int to) { |
||||
can_forwarding[from] = to; |
||||
} |
||||
|
@ -0,0 +1,40 @@ |
||||
void clock_init(void) { |
||||
// enable external oscillator
|
||||
RCC->CR |= RCC_CR_HSEON; |
||||
while ((RCC->CR & RCC_CR_HSERDY) == 0); |
||||
|
||||
// divide shit
|
||||
RCC->CFGR = RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE2_DIV2 | RCC_CFGR_PPRE1_DIV4; |
||||
|
||||
// 16mhz crystal
|
||||
RCC->PLLCFGR = RCC_PLLCFGR_PLLQ_2 | RCC_PLLCFGR_PLLM_3 | |
||||
RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLSRC_HSE; |
||||
|
||||
// start PLL
|
||||
RCC->CR |= RCC_CR_PLLON; |
||||
while ((RCC->CR & RCC_CR_PLLRDY) == 0); |
||||
|
||||
// Configure Flash prefetch, Instruction cache, Data cache and wait state
|
||||
// *** without this, it breaks ***
|
||||
FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS; |
||||
|
||||
// switch to PLL
|
||||
RCC->CFGR |= RCC_CFGR_SW_PLL; |
||||
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); |
||||
|
||||
// *** running on PLL ***
|
||||
} |
||||
|
||||
void watchdog_init(void) { |
||||
// 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; |
||||
} |
||||
|
||||
void watchdog_feed(void) { |
||||
IWDG->KR = 0xAAAA; |
||||
} |
||||
|
@ -0,0 +1,16 @@ |
||||
void dac_init() { |
||||
// no buffers required since we have an opamp
|
||||
//DAC->CR = DAC_CR_EN1 | DAC_CR_BOFF1 | DAC_CR_EN2 | DAC_CR_BOFF2;
|
||||
DAC->DHR12R1 = 0; |
||||
DAC->DHR12R2 = 0; |
||||
DAC->CR = DAC_CR_EN1 | DAC_CR_EN2; |
||||
} |
||||
|
||||
void dac_set(int channel, uint32_t value) { |
||||
if (channel == 0) { |
||||
DAC->DHR12R1 = value; |
||||
} else if (channel == 1) { |
||||
DAC->DHR12R2 = value; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,279 @@ |
||||
#define GMLAN_TICKS_PER_SECOND 33300 //1sec @ 33.3kbps
|
||||
#define GMLAN_TICKS_PER_TIMEOUT_TICKLE 500 //15ms @ 33.3kbps
|
||||
#define GMLAN_HIGH 0 //0 is high on bus (dominant)
|
||||
#define GMLAN_LOW 1 //1 is low on bus
|
||||
|
||||
#define DISABLED -1 |
||||
#define BITBANG 0 |
||||
#define GPIO_SWITCH 1 |
||||
|
||||
#define MAX_BITS_CAN_PACKET (200) |
||||
|
||||
int gmlan_alt_mode = DISABLED; |
||||
|
||||
// returns out_len
|
||||
int do_bitstuff(char *out, char *in, int in_len) { |
||||
int last_bit = -1; |
||||
int bit_cnt = 0; |
||||
int j = 0; |
||||
for (int i = 0; i < in_len; i++) { |
||||
char bit = in[i]; |
||||
out[j] = bit; |
||||
j++; |
||||
|
||||
// do the stuffing
|
||||
if (bit == last_bit) { |
||||
bit_cnt++; |
||||
if (bit_cnt == 5) { |
||||
// 5 in a row the same, do stuff
|
||||
last_bit = !bit; |
||||
out[j] = last_bit; |
||||
j++; |
||||
bit_cnt = 1; |
||||
} |
||||
} else { |
||||
// this is a new bit
|
||||
last_bit = bit; |
||||
bit_cnt = 1; |
||||
} |
||||
} |
||||
return j; |
||||
} |
||||
|
||||
int append_crc(char *in, int in_len) { |
||||
int crc = 0; |
||||
for (int i = 0; i < in_len; i++) { |
||||
crc <<= 1; |
||||
if ((in[i] ^ ((crc >> 15) & 1)) != 0) { |
||||
crc = crc ^ 0x4599; |
||||
} |
||||
crc &= 0x7fff; |
||||
} |
||||
for (int i = 14; i >= 0; i--) { |
||||
in[in_len] = (crc>>i)&1; |
||||
in_len++; |
||||
} |
||||
return in_len; |
||||
} |
||||
|
||||
int append_bits(char *in, int in_len, char *app, int app_len) { |
||||
for (int i = 0; i < app_len; i++) { |
||||
in[in_len] = app[i]; |
||||
in_len++; |
||||
} |
||||
return in_len; |
||||
} |
||||
|
||||
int append_int(char *in, int in_len, int val, int val_len) { |
||||
for (int i = val_len-1; i >= 0; i--) { |
||||
in[in_len] = (val&(1<<i)) != 0; |
||||
in_len++; |
||||
} |
||||
return in_len; |
||||
} |
||||
|
||||
int get_bit_message(char *out, CAN_FIFOMailBox_TypeDef *to_bang) { |
||||
char pkt[MAX_BITS_CAN_PACKET]; |
||||
char footer[] = { |
||||
1, // CRC delimiter
|
||||
1, // ACK
|
||||
1, // ACK delimiter
|
||||
1,1,1,1,1,1,1, // EOF
|
||||
1,1,1, // IFS
|
||||
}; |
||||
|
||||
int len = 0; |
||||
|
||||
// test packet
|
||||
int dlc_len = to_bang->RDTR & 0xF; |
||||
len = append_int(pkt, len, 0, 1); // Start-of-frame
|
||||
|
||||
if ((to_bang->RIR & 4) != 0) { |
||||
// extended identifier
|
||||
len = append_int(pkt, len, to_bang->RIR >> 21, 11); // Identifier
|
||||
len = append_int(pkt, len, 3, 2); // SRR+IDE
|
||||
len = append_int(pkt, len, (to_bang->RIR >> 3) & ((1<<18)-1), 18); // Identifier
|
||||
len = append_int(pkt, len, 0, 3); // RTR+r1+r0
|
||||
} else { |
||||
// standard identifier
|
||||
len = append_int(pkt, len, to_bang->RIR >> 21, 11); // Identifier
|
||||
len = append_int(pkt, len, 0, 3); // RTR+IDE+reserved
|
||||
} |
||||
|
||||
len = append_int(pkt, len, dlc_len, 4); // Data length code
|
||||
|
||||
// append data
|
||||
for (int i = 0; i < dlc_len; i++) { |
||||
unsigned char dat = ((unsigned char *)(&(to_bang->RDLR)))[i]; |
||||
len = append_int(pkt, len, dat, 8); |
||||
} |
||||
|
||||
// append crc
|
||||
len = append_crc(pkt, len); |
||||
|
||||
// do bitstuffing
|
||||
len = do_bitstuff(out, pkt, len); |
||||
|
||||
// append footer
|
||||
len = append_bits(out, len, footer, sizeof(footer)); |
||||
return len; |
||||
} |
||||
|
||||
void setup_timer4(void) { |
||||
// setup
|
||||
TIM4->PSC = 48-1; // tick on 1 us
|
||||
TIM4->CR1 = TIM_CR1_CEN; // enable
|
||||
TIM4->ARR = 30-1; // 33.3 kbps
|
||||
|
||||
// in case it's disabled
|
||||
NVIC_EnableIRQ(TIM4_IRQn); |
||||
|
||||
// run the interrupt
|
||||
TIM4->DIER = TIM_DIER_UIE; // update interrupt
|
||||
TIM4->SR = 0; |
||||
} |
||||
|
||||
int gmlan_timeout_counter = GMLAN_TICKS_PER_TIMEOUT_TICKLE; //GMLAN transceiver times out every 17ms held high; tickle every 15ms
|
||||
int can_timeout_counter = GMLAN_TICKS_PER_SECOND; //1 second
|
||||
|
||||
int inverted_bit_to_send = GMLAN_HIGH; |
||||
int gmlan_switch_below_timeout = -1; |
||||
int gmlan_switch_timeout_enable = 0; |
||||
|
||||
void gmlan_switch_init(int timeout_enable) { |
||||
gmlan_switch_timeout_enable = timeout_enable; |
||||
gmlan_alt_mode = GPIO_SWITCH; |
||||
gmlan_switch_below_timeout = 1; |
||||
set_gpio_mode(GPIOB, 13, MODE_OUTPUT); |
||||
|
||||
setup_timer4(); |
||||
|
||||
inverted_bit_to_send = GMLAN_LOW; //We got initialized, set the output low
|
||||
} |
||||
|
||||
void set_gmlan_digital_output(int to_set) { |
||||
inverted_bit_to_send = to_set; |
||||
/*
|
||||
puts("Writing "); |
||||
puth(inverted_bit_to_send); |
||||
puts("\n"); |
||||
*/ |
||||
} |
||||
|
||||
void reset_gmlan_switch_timeout(void) { |
||||
can_timeout_counter = GMLAN_TICKS_PER_SECOND; |
||||
gmlan_switch_below_timeout = 1; |
||||
gmlan_alt_mode = GPIO_SWITCH; |
||||
} |
||||
|
||||
void set_bitbanged_gmlan(int val) { |
||||
if (val != 0) { |
||||
GPIOB->ODR |= (1 << 13); |
||||
} else { |
||||
GPIOB->ODR &= ~(1 << 13); |
||||
} |
||||
} |
||||
|
||||
char pkt_stuffed[MAX_BITS_CAN_PACKET]; |
||||
int gmlan_sending = -1; |
||||
int gmlan_sendmax = -1; |
||||
|
||||
int gmlan_silent_count = 0; |
||||
int gmlan_fail_count = 0; |
||||
#define REQUIRED_SILENT_TIME 10 |
||||
#define MAX_FAIL_COUNT 10 |
||||
|
||||
void TIM4_IRQHandler(void) { |
||||
if (gmlan_alt_mode == BITBANG) { |
||||
if ((TIM4->SR & TIM_SR_UIF) && (gmlan_sendmax != -1)) { |
||||
int read = get_gpio_input(GPIOB, 12); |
||||
if (gmlan_silent_count < REQUIRED_SILENT_TIME) { |
||||
if (read == 0) { |
||||
gmlan_silent_count = 0; |
||||
} else { |
||||
gmlan_silent_count++; |
||||
} |
||||
} else if (gmlan_silent_count == REQUIRED_SILENT_TIME) { |
||||
bool retry = 0; |
||||
// in send loop
|
||||
if ((gmlan_sending > 0) && // not first bit
|
||||
((read == 0) && (pkt_stuffed[gmlan_sending-1] == 1)) && // bus wrongly dominant
|
||||
(gmlan_sending != (gmlan_sendmax - 11))) { //not ack bit
|
||||
puts("GMLAN ERR: bus driven at "); |
||||
puth(gmlan_sending); |
||||
puts("\n"); |
||||
retry = 1; |
||||
} else if ((read == 1) && (gmlan_sending == (gmlan_sendmax - 11))) { // recessive during ACK
|
||||
puts("GMLAN ERR: didn't recv ACK\n"); |
||||
retry = 1; |
||||
} |
||||
if (retry) { |
||||
// reset sender (retry after 7 silent)
|
||||
set_bitbanged_gmlan(1); // recessive
|
||||
gmlan_silent_count = 0; |
||||
gmlan_sending = 0; |
||||
gmlan_fail_count++; |
||||
if (gmlan_fail_count == MAX_FAIL_COUNT) { |
||||
puts("GMLAN ERR: giving up send\n"); |
||||
} |
||||
} else { |
||||
set_bitbanged_gmlan(pkt_stuffed[gmlan_sending]); |
||||
gmlan_sending++; |
||||
} |
||||
} |
||||
if ((gmlan_sending == gmlan_sendmax) || (gmlan_fail_count == MAX_FAIL_COUNT)) { |
||||
set_bitbanged_gmlan(1); // recessive
|
||||
set_gpio_mode(GPIOB, 13, MODE_INPUT); |
||||
TIM4->DIER = 0; // no update interrupt
|
||||
TIM4->CR1 = 0; // disable timer
|
||||
gmlan_sendmax = -1; // exit
|
||||
} |
||||
} |
||||
TIM4->SR = 0; |
||||
} //bit bang mode
|
||||
|
||||
else if (gmlan_alt_mode == GPIO_SWITCH) { |
||||
if ((TIM4->SR & TIM_SR_UIF) && (gmlan_switch_below_timeout != -1)) { |
||||
if ((can_timeout_counter == 0) && gmlan_switch_timeout_enable) { |
||||
//it has been more than 1 second since timeout was reset; disable timer and restore the GMLAN output
|
||||
set_gpio_output(GPIOB, 13, GMLAN_LOW); |
||||
gmlan_switch_below_timeout = -1; |
||||
gmlan_timeout_counter = GMLAN_TICKS_PER_TIMEOUT_TICKLE; |
||||
gmlan_alt_mode = DISABLED; |
||||
} |
||||
else { |
||||
can_timeout_counter--; |
||||
if (gmlan_timeout_counter == 0) { |
||||
//Send a 1 (bus low) every 15ms to reset the GMLAN transceivers timeout
|
||||
gmlan_timeout_counter = GMLAN_TICKS_PER_TIMEOUT_TICKLE; |
||||
set_gpio_output(GPIOB, 13, GMLAN_LOW); |
||||
} |
||||
else { |
||||
set_gpio_output(GPIOB, 13, inverted_bit_to_send); |
||||
gmlan_timeout_counter--; |
||||
} |
||||
} |
||||
} |
||||
TIM4->SR = 0; |
||||
} //gmlan switch mode
|
||||
} |
||||
|
||||
void bitbang_gmlan(CAN_FIFOMailBox_TypeDef *to_bang) { |
||||
gmlan_alt_mode = BITBANG; |
||||
// TODO: make failure less silent
|
||||
if (gmlan_sendmax == -1) { |
||||
|
||||
int len = get_bit_message(pkt_stuffed, to_bang); |
||||
gmlan_fail_count = 0; |
||||
gmlan_silent_count = 0; |
||||
gmlan_sending = 0; |
||||
gmlan_sendmax = len; |
||||
|
||||
// setup for bitbang loop
|
||||
set_bitbanged_gmlan(1); // recessive
|
||||
set_gpio_mode(GPIOB, 13, MODE_OUTPUT); |
||||
|
||||
setup_timer4(); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,81 @@ |
||||
// this is needed for 1 mbps support
|
||||
#define CAN_QUANTA 8 |
||||
#define CAN_SEQ1 6 // roundf(quanta * 0.875f) - 1;
|
||||
#define CAN_SEQ2 1 // roundf(quanta * 0.125f);
|
||||
|
||||
#define CAN_PCLK 24000 |
||||
// 333 = 33.3 kbps
|
||||
// 5000 = 500 kbps
|
||||
#define can_speed_to_prescaler(x) (CAN_PCLK / CAN_QUANTA * 10 / (x)) |
||||
|
||||
bool llcan_set_speed(CAN_TypeDef *CAN, uint32_t speed, bool loopback, bool silent) { |
||||
// initialization mode
|
||||
CAN->MCR = CAN_MCR_TTCM | CAN_MCR_INRQ; |
||||
while((CAN->MSR & CAN_MSR_INAK) != CAN_MSR_INAK); |
||||
|
||||
// set time quanta from defines
|
||||
CAN->BTR = (CAN_BTR_TS1_0 * (CAN_SEQ1-1)) | |
||||
(CAN_BTR_TS2_0 * (CAN_SEQ2-1)) | |
||||
(can_speed_to_prescaler(speed) - 1); |
||||
|
||||
// silent loopback mode for debugging
|
||||
if (loopback) { |
||||
CAN->BTR |= CAN_BTR_SILM | CAN_BTR_LBKM; |
||||
} |
||||
if (silent) { |
||||
CAN->BTR |= CAN_BTR_SILM; |
||||
} |
||||
|
||||
// reset
|
||||
CAN->MCR = CAN_MCR_TTCM | CAN_MCR_ABOM; |
||||
|
||||
#define CAN_TIMEOUT 1000000 |
||||
int tmp = 0; |
||||
bool ret = false; |
||||
while(((CAN->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) && (tmp < CAN_TIMEOUT)) tmp++; |
||||
if (tmp < CAN_TIMEOUT) { |
||||
ret = true; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
void llcan_init(CAN_TypeDef *CAN) { |
||||
// accept all filter
|
||||
CAN->FMR |= CAN_FMR_FINIT; |
||||
|
||||
// no mask
|
||||
CAN->sFilterRegister[0].FR1 = 0; |
||||
CAN->sFilterRegister[0].FR2 = 0; |
||||
CAN->sFilterRegister[14].FR1 = 0; |
||||
CAN->sFilterRegister[14].FR2 = 0; |
||||
CAN->FA1R |= 1 | (1U << 14); |
||||
|
||||
CAN->FMR &= ~(CAN_FMR_FINIT); |
||||
|
||||
// enable certain CAN interrupts
|
||||
CAN->IER |= CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_WKUIE; |
||||
|
||||
if (CAN == CAN1) { |
||||
NVIC_EnableIRQ(CAN1_TX_IRQn); |
||||
NVIC_EnableIRQ(CAN1_RX0_IRQn); |
||||
NVIC_EnableIRQ(CAN1_SCE_IRQn); |
||||
} else if (CAN == CAN2) { |
||||
NVIC_EnableIRQ(CAN2_TX_IRQn); |
||||
NVIC_EnableIRQ(CAN2_RX0_IRQn); |
||||
NVIC_EnableIRQ(CAN2_SCE_IRQn); |
||||
#ifdef CAN3 |
||||
} else if (CAN == CAN3) { |
||||
NVIC_EnableIRQ(CAN3_TX_IRQn); |
||||
NVIC_EnableIRQ(CAN3_RX0_IRQn); |
||||
NVIC_EnableIRQ(CAN3_SCE_IRQn); |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
void llcan_clear_send(CAN_TypeDef *CAN) { |
||||
CAN->TSR |= CAN_TSR_ABRQ0; |
||||
CAN->MSR &= ~(CAN_MSR_ERRI); |
||||
CAN->MSR = CAN->MSR; |
||||
} |
||||
|
@ -0,0 +1,44 @@ |
||||
#define MODE_INPUT 0 |
||||
#define MODE_OUTPUT 1 |
||||
#define MODE_ALTERNATE 2 |
||||
#define MODE_ANALOG 3 |
||||
|
||||
#define PULL_NONE 0 |
||||
#define PULL_UP 1 |
||||
#define PULL_DOWN 2 |
||||
|
||||
void set_gpio_mode(GPIO_TypeDef *GPIO, int pin, int mode) { |
||||
uint32_t tmp = GPIO->MODER; |
||||
tmp &= ~(3 << (pin*2)); |
||||
tmp |= (mode << (pin*2)); |
||||
GPIO->MODER = tmp; |
||||
} |
||||
|
||||
void set_gpio_output(GPIO_TypeDef *GPIO, int pin, bool enabled) { |
||||
if (enabled) { |
||||
GPIO->ODR |= (1 << pin); |
||||
} else { |
||||
GPIO->ODR &= ~(1 << pin); |
||||
} |
||||
set_gpio_mode(GPIO, pin, MODE_OUTPUT); |
||||
} |
||||
|
||||
void set_gpio_alternate(GPIO_TypeDef *GPIO, int pin, int mode) { |
||||
uint32_t tmp = GPIO->AFR[pin>>3]; |
||||
tmp &= ~(0xF << ((pin&7)*4)); |
||||
tmp |= mode << ((pin&7)*4); |
||||
GPIO->AFR[pin>>3] = tmp; |
||||
set_gpio_mode(GPIO, pin, MODE_ALTERNATE); |
||||
} |
||||
|
||||
void set_gpio_pullup(GPIO_TypeDef *GPIO, int pin, int mode) { |
||||
uint32_t tmp = GPIO->PUPDR; |
||||
tmp &= ~(3 << (pin*2)); |
||||
tmp |= (mode << (pin*2)); |
||||
GPIO->PUPDR = tmp; |
||||
} |
||||
|
||||
int get_gpio_input(GPIO_TypeDef *GPIO, int pin) { |
||||
return (GPIO->IDR & (1U << pin)) == (1U << pin); |
||||
} |
||||
|
@ -0,0 +1,127 @@ |
||||
// IRQs: DMA2_Stream2, DMA2_Stream3, EXTI4
|
||||
|
||||
void spi_init(void); |
||||
int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out); |
||||
|
||||
// end API
|
||||
|
||||
#define SPI_BUF_SIZE 256 |
||||
uint8_t spi_buf[SPI_BUF_SIZE]; |
||||
int spi_buf_count = 0; |
||||
int spi_total_count = 0; |
||||
|
||||
void spi_init(void) { |
||||
//puts("SPI init\n");
|
||||
SPI1->CR1 = SPI_CR1_SPE; |
||||
|
||||
// enable SPI interrupts
|
||||
//SPI1->CR2 = SPI_CR2_RXNEIE | SPI_CR2_ERRIE | SPI_CR2_TXEIE;
|
||||
SPI1->CR2 = SPI_CR2_RXNEIE; |
||||
|
||||
NVIC_EnableIRQ(DMA2_Stream2_IRQn); |
||||
NVIC_EnableIRQ(DMA2_Stream3_IRQn); |
||||
//NVIC_EnableIRQ(SPI1_IRQn);
|
||||
|
||||
// reset handshake back to pull up
|
||||
set_gpio_mode(GPIOB, 0, MODE_INPUT); |
||||
set_gpio_pullup(GPIOB, 0, PULL_UP); |
||||
|
||||
// setup interrupt on falling edge of SPI enable (on PA4)
|
||||
SYSCFG->EXTICR[2] = SYSCFG_EXTICR2_EXTI4_PA; |
||||
EXTI->IMR |= (1 << 4); |
||||
EXTI->FTSR |= (1 << 4); |
||||
NVIC_EnableIRQ(EXTI4_IRQn); |
||||
} |
||||
|
||||
void spi_tx_dma(void *addr, int len) { |
||||
// disable DMA
|
||||
SPI1->CR2 &= ~SPI_CR2_TXDMAEN; |
||||
DMA2_Stream3->CR &= ~DMA_SxCR_EN; |
||||
|
||||
// DMA2, stream 3, channel 3
|
||||
DMA2_Stream3->M0AR = (uint32_t)addr; |
||||
DMA2_Stream3->NDTR = len; |
||||
DMA2_Stream3->PAR = (uint32_t)&(SPI1->DR); |
||||
|
||||
// channel3, increment memory, memory -> periph, enable
|
||||
DMA2_Stream3->CR = DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_EN; |
||||
delay(0); |
||||
DMA2_Stream3->CR |= DMA_SxCR_TCIE; |
||||
|
||||
SPI1->CR2 |= SPI_CR2_TXDMAEN; |
||||
|
||||
// signal data is ready by driving low
|
||||
// esp must be configured as input by this point
|
||||
set_gpio_output(GPIOB, 0, 0); |
||||
} |
||||
|
||||
void spi_rx_dma(void *addr, int len) { |
||||
// disable DMA
|
||||
SPI1->CR2 &= ~SPI_CR2_RXDMAEN; |
||||
DMA2_Stream2->CR &= ~DMA_SxCR_EN; |
||||
|
||||
// drain the bus
|
||||
volatile uint8_t dat = SPI1->DR; |
||||
(void)dat; |
||||
|
||||
// DMA2, stream 2, channel 3
|
||||
DMA2_Stream2->M0AR = (uint32_t)addr; |
||||
DMA2_Stream2->NDTR = len; |
||||
DMA2_Stream2->PAR = (uint32_t)&(SPI1->DR); |
||||
|
||||
// channel3, increment memory, periph -> memory, enable
|
||||
DMA2_Stream2->CR = DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_EN; |
||||
delay(0); |
||||
DMA2_Stream2->CR |= DMA_SxCR_TCIE; |
||||
|
||||
SPI1->CR2 |= SPI_CR2_RXDMAEN; |
||||
} |
||||
|
||||
// ***************************** SPI IRQs *****************************
|
||||
|
||||
// can't go on the stack cause it's DMAed
|
||||
uint8_t spi_tx_buf[0x44]; |
||||
|
||||
// SPI RX
|
||||
void DMA2_Stream2_IRQHandler(void) { |
||||
int *resp_len = (int*)spi_tx_buf; |
||||
memset(spi_tx_buf, 0xaa, 0x44); |
||||
*resp_len = spi_cb_rx(spi_buf, 0x14, spi_tx_buf+4); |
||||
#ifdef DEBUG_SPI |
||||
puts("SPI write: "); |
||||
puth(*resp_len); |
||||
puts("\n"); |
||||
#endif |
||||
spi_tx_dma(spi_tx_buf, *resp_len + 4); |
||||
|
||||
// ack
|
||||
DMA2->LIFCR = DMA_LIFCR_CTCIF2; |
||||
} |
||||
|
||||
// SPI TX
|
||||
void DMA2_Stream3_IRQHandler(void) { |
||||
#ifdef DEBUG_SPI |
||||
puts("SPI handshake\n"); |
||||
#endif |
||||
|
||||
// reset handshake back to pull up
|
||||
set_gpio_mode(GPIOB, 0, MODE_INPUT); |
||||
set_gpio_pullup(GPIOB, 0, PULL_UP); |
||||
|
||||
// ack
|
||||
DMA2->LIFCR = DMA_LIFCR_CTCIF3; |
||||
} |
||||
|
||||
void EXTI4_IRQHandler(void) { |
||||
volatile int pr = EXTI->PR & (1 << 4); |
||||
#ifdef DEBUG_SPI |
||||
puts("exti4\n"); |
||||
#endif |
||||
// SPI CS falling
|
||||
if ((pr & (1 << 4)) != 0) { |
||||
spi_total_count = 0; |
||||
spi_rx_dma(spi_buf, 0x14); |
||||
} |
||||
EXTI->PR = pr; |
||||
} |
||||
|
@ -0,0 +1,7 @@ |
||||
void timer_init(TIM_TypeDef *TIM, int psc) { |
||||
TIM->PSC = psc-1; |
||||
TIM->DIER = TIM_DIER_UIE; |
||||
TIM->CR1 = TIM_CR1_CEN; |
||||
TIM->SR = 0; |
||||
} |
||||
|
@ -0,0 +1,353 @@ |
||||
// IRQs: USART1, USART2, USART3, UART5
|
||||
|
||||
#define FIFO_SIZE 0x400 |
||||
typedef struct uart_ring { |
||||
volatile uint16_t w_ptr_tx; |
||||
volatile uint16_t r_ptr_tx; |
||||
uint8_t elems_tx[FIFO_SIZE]; |
||||
volatile uint16_t w_ptr_rx; |
||||
volatile uint16_t r_ptr_rx; |
||||
uint8_t elems_rx[FIFO_SIZE]; |
||||
USART_TypeDef *uart; |
||||
void (*callback)(struct uart_ring*); |
||||
} uart_ring; |
||||
|
||||
void uart_init(USART_TypeDef *u, int baud); |
||||
|
||||
bool getc(uart_ring *q, char *elem); |
||||
bool putc(uart_ring *q, char elem); |
||||
|
||||
void puts(const char *a); |
||||
void puth(unsigned int i); |
||||
void hexdump(const void *a, int l); |
||||
|
||||
|
||||
// ***************************** serial port queues *****************************
|
||||
|
||||
// esp = USART1
|
||||
uart_ring esp_ring = { .w_ptr_tx = 0, .r_ptr_tx = 0, |
||||
.w_ptr_rx = 0, .r_ptr_rx = 0, |
||||
.uart = USART1, |
||||
.callback = NULL}; |
||||
|
||||
// lin1, K-LINE = UART5
|
||||
// lin2, L-LINE = USART3
|
||||
uart_ring lin1_ring = { .w_ptr_tx = 0, .r_ptr_tx = 0, |
||||
.w_ptr_rx = 0, .r_ptr_rx = 0, |
||||
.uart = UART5, |
||||
.callback = NULL}; |
||||
uart_ring lin2_ring = { .w_ptr_tx = 0, .r_ptr_tx = 0, |
||||
.w_ptr_rx = 0, .r_ptr_rx = 0, |
||||
.uart = USART3, |
||||
.callback = NULL}; |
||||
|
||||
// debug = USART2
|
||||
void debug_ring_callback(uart_ring *ring); |
||||
uart_ring debug_ring = { .w_ptr_tx = 0, .r_ptr_tx = 0, |
||||
.w_ptr_rx = 0, .r_ptr_rx = 0, |
||||
.uart = USART2, |
||||
.callback = debug_ring_callback}; |
||||
|
||||
|
||||
uart_ring *get_ring_by_number(int a) { |
||||
uart_ring *ring = NULL; |
||||
switch(a) { |
||||
case 0: |
||||
ring = &debug_ring; |
||||
break; |
||||
case 1: |
||||
ring = &esp_ring; |
||||
break; |
||||
case 2: |
||||
ring = &lin1_ring; |
||||
break; |
||||
case 3: |
||||
ring = &lin2_ring; |
||||
break; |
||||
default: |
||||
ring = NULL; |
||||
break; |
||||
} |
||||
return ring; |
||||
} |
||||
|
||||
// ***************************** serial port *****************************
|
||||
|
||||
void uart_ring_process(uart_ring *q) { |
||||
enter_critical_section(); |
||||
// TODO: check if external serial is connected
|
||||
int sr = q->uart->SR; |
||||
|
||||
if (q->w_ptr_tx != q->r_ptr_tx) { |
||||
if ((sr & USART_SR_TXE) != 0) { |
||||
q->uart->DR = q->elems_tx[q->r_ptr_tx]; |
||||
q->r_ptr_tx = (q->r_ptr_tx + 1) % FIFO_SIZE; |
||||
} |
||||
// there could be more to send
|
||||
q->uart->CR1 |= USART_CR1_TXEIE; |
||||
} else { |
||||
// nothing to send
|
||||
q->uart->CR1 &= ~USART_CR1_TXEIE; |
||||
} |
||||
|
||||
if ((sr & USART_SR_RXNE) || (sr & USART_SR_ORE)) { |
||||
uint8_t c = q->uart->DR; // TODO: can drop packets
|
||||
if (q != &esp_ring) { |
||||
uint16_t next_w_ptr = (q->w_ptr_rx + 1) % FIFO_SIZE; |
||||
if (next_w_ptr != q->r_ptr_rx) { |
||||
q->elems_rx[q->w_ptr_rx] = c; |
||||
q->w_ptr_rx = next_w_ptr; |
||||
if (q->callback != NULL) { |
||||
q->callback(q); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
if ((sr & USART_SR_ORE) != 0) { |
||||
// set dropped packet flag?
|
||||
} |
||||
|
||||
exit_critical_section(); |
||||
} |
||||
|
||||
// interrupt boilerplate
|
||||
|
||||
void USART1_IRQHandler(void) { uart_ring_process(&esp_ring); } |
||||
void USART2_IRQHandler(void) { uart_ring_process(&debug_ring); } |
||||
void USART3_IRQHandler(void) { uart_ring_process(&lin2_ring); } |
||||
void UART5_IRQHandler(void) { uart_ring_process(&lin1_ring); } |
||||
|
||||
bool getc(uart_ring *q, char *elem) { |
||||
bool ret = false; |
||||
|
||||
enter_critical_section(); |
||||
if (q->w_ptr_rx != q->r_ptr_rx) { |
||||
if (elem != NULL) *elem = q->elems_rx[q->r_ptr_rx]; |
||||
q->r_ptr_rx = (q->r_ptr_rx + 1) % FIFO_SIZE; |
||||
ret = true; |
||||
} |
||||
exit_critical_section(); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
bool injectc(uart_ring *q, char elem) { |
||||
int ret = false; |
||||
uint16_t next_w_ptr; |
||||
|
||||
enter_critical_section(); |
||||
next_w_ptr = (q->w_ptr_rx + 1) % FIFO_SIZE; |
||||
if (next_w_ptr != q->r_ptr_rx) { |
||||
q->elems_rx[q->w_ptr_rx] = elem; |
||||
q->w_ptr_rx = next_w_ptr; |
||||
ret = true; |
||||
} |
||||
exit_critical_section(); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
bool putc(uart_ring *q, char elem) { |
||||
bool ret = false; |
||||
uint16_t next_w_ptr; |
||||
|
||||
enter_critical_section(); |
||||
next_w_ptr = (q->w_ptr_tx + 1) % FIFO_SIZE; |
||||
if (next_w_ptr != q->r_ptr_tx) { |
||||
q->elems_tx[q->w_ptr_tx] = elem; |
||||
q->w_ptr_tx = next_w_ptr; |
||||
ret = true; |
||||
} |
||||
exit_critical_section(); |
||||
|
||||
uart_ring_process(q); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
void uart_flush(uart_ring *q) { |
||||
while (q->w_ptr_tx != q->r_ptr_tx) { |
||||
__WFI(); |
||||
} |
||||
} |
||||
|
||||
void uart_flush_sync(uart_ring *q) { |
||||
// empty the TX buffer
|
||||
while (q->w_ptr_tx != q->r_ptr_tx) { |
||||
uart_ring_process(q); |
||||
} |
||||
} |
||||
|
||||
void uart_send_break(uart_ring *u) { |
||||
while ((u->uart->CR1 & USART_CR1_SBK) != 0); |
||||
u->uart->CR1 |= USART_CR1_SBK; |
||||
} |
||||
|
||||
void clear_uart_buff(uart_ring *q) { |
||||
enter_critical_section(); |
||||
q->w_ptr_tx = 0; |
||||
q->r_ptr_tx = 0; |
||||
q->w_ptr_rx = 0; |
||||
q->r_ptr_rx = 0; |
||||
exit_critical_section(); |
||||
} |
||||
|
||||
// ***************************** start UART code *****************************
|
||||
|
||||
#define __DIV(_PCLK_, _BAUD_) (((_PCLK_) * 25) / (4 * (_BAUD_))) |
||||
#define __DIVMANT(_PCLK_, _BAUD_) (__DIV((_PCLK_), (_BAUD_)) / 100) |
||||
#define __DIVFRAQ(_PCLK_, _BAUD_) ((((__DIV((_PCLK_), (_BAUD_)) - (__DIVMANT((_PCLK_), (_BAUD_)) * 100)) * 16) + 50) / 100) |
||||
#define __USART_BRR(_PCLK_, _BAUD_) ((__DIVMANT((_PCLK_), (_BAUD_)) << 4) | (__DIVFRAQ((_PCLK_), (_BAUD_)) & 0x0F)) |
||||
|
||||
void uart_set_baud(USART_TypeDef *u, int baud) { |
||||
if (u == USART1) { |
||||
// USART1 is on APB2
|
||||
u->BRR = __USART_BRR(48000000, baud); |
||||
} else { |
||||
u->BRR = __USART_BRR(24000000, baud); |
||||
} |
||||
} |
||||
|
||||
#define USART1_DMA_LEN 0x20 |
||||
char usart1_dma[USART1_DMA_LEN]; |
||||
|
||||
void uart_dma_drain(void) { |
||||
uart_ring *q = &esp_ring; |
||||
|
||||
enter_critical_section(); |
||||
|
||||
if ((DMA2->HISR & DMA_HISR_TCIF5) || (DMA2->HISR & DMA_HISR_HTIF5) || (DMA2_Stream5->NDTR != USART1_DMA_LEN)) { |
||||
// disable DMA
|
||||
q->uart->CR3 &= ~USART_CR3_DMAR; |
||||
DMA2_Stream5->CR &= ~DMA_SxCR_EN; |
||||
while ((DMA2_Stream5->CR & DMA_SxCR_EN) != 0); |
||||
|
||||
unsigned int i; |
||||
for (i = 0; i < (USART1_DMA_LEN - DMA2_Stream5->NDTR); i++) { |
||||
char c = usart1_dma[i]; |
||||
uint16_t next_w_ptr = (q->w_ptr_rx + 1) % FIFO_SIZE; |
||||
if (next_w_ptr != q->r_ptr_rx) { |
||||
q->elems_rx[q->w_ptr_rx] = c; |
||||
q->w_ptr_rx = next_w_ptr; |
||||
} |
||||
} |
||||
|
||||
// reset DMA len
|
||||
DMA2_Stream5->NDTR = USART1_DMA_LEN; |
||||
|
||||
// clear interrupts
|
||||
DMA2->HIFCR = DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5; |
||||
//DMA2->HIFCR = DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5;
|
||||
|
||||
// enable DMA
|
||||
DMA2_Stream5->CR |= DMA_SxCR_EN; |
||||
q->uart->CR3 |= USART_CR3_DMAR; |
||||
} |
||||
|
||||
exit_critical_section(); |
||||
} |
||||
|
||||
void DMA2_Stream5_IRQHandler(void) { |
||||
//set_led(LED_BLUE, 1);
|
||||
uart_dma_drain(); |
||||
//set_led(LED_BLUE, 0);
|
||||
} |
||||
|
||||
void uart_init(USART_TypeDef *u, int baud) { |
||||
// enable uart and tx+rx mode
|
||||
u->CR1 = USART_CR1_UE; |
||||
uart_set_baud(u, baud); |
||||
|
||||
u->CR1 |= USART_CR1_TE | USART_CR1_RE; |
||||
//u->CR2 = USART_CR2_STOP_0 | USART_CR2_STOP_1;
|
||||
//u->CR2 = USART_CR2_STOP_0;
|
||||
// ** UART is ready to work **
|
||||
|
||||
// enable interrupts
|
||||
if (u != USART1) { |
||||
u->CR1 |= USART_CR1_RXNEIE; |
||||
} |
||||
|
||||
if (u == USART1) { |
||||
// DMA2, stream 2, channel 3
|
||||
DMA2_Stream5->M0AR = (uint32_t)usart1_dma; |
||||
DMA2_Stream5->NDTR = USART1_DMA_LEN; |
||||
DMA2_Stream5->PAR = (uint32_t)&(USART1->DR); |
||||
|
||||
// channel4, increment memory, periph -> memory, enable
|
||||
DMA2_Stream5->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_HTIE | DMA_SxCR_TCIE | DMA_SxCR_EN; |
||||
|
||||
// this one uses DMA receiver
|
||||
u->CR3 = USART_CR3_DMAR; |
||||
|
||||
NVIC_EnableIRQ(DMA2_Stream5_IRQn); |
||||
NVIC_EnableIRQ(USART1_IRQn); |
||||
} else if (u == USART2) { |
||||
NVIC_EnableIRQ(USART2_IRQn); |
||||
} else if (u == USART3) { |
||||
NVIC_EnableIRQ(USART3_IRQn); |
||||
} else if (u == UART5) { |
||||
NVIC_EnableIRQ(UART5_IRQn); |
||||
} |
||||
} |
||||
|
||||
void putch(const char a) { |
||||
if (has_external_debug_serial) { |
||||
/*while ((debug_ring.uart->SR & USART_SR_TXE) == 0);
|
||||
debug_ring.uart->DR = a;*/ |
||||
|
||||
// assuming debugging is important if there's external serial connected
|
||||
while (!putc(&debug_ring, a)); |
||||
|
||||
//putc(&debug_ring, a);
|
||||
} else { |
||||
injectc(&debug_ring, a); |
||||
} |
||||
} |
||||
|
||||
void puts(const char *a) { |
||||
for (;*a;a++) { |
||||
if (*a == '\n') putch('\r'); |
||||
putch(*a); |
||||
} |
||||
} |
||||
|
||||
void putui(uint32_t i) { |
||||
char str[11]; |
||||
uint8_t idx = 10; |
||||
str[idx] = '\0'; |
||||
idx--; |
||||
do { |
||||
str[idx] = (i % 10) + 0x30; |
||||
idx--; |
||||
i /= 10; |
||||
} while (i != 0); |
||||
puts(str + idx + 1); |
||||
} |
||||
|
||||
void puth(unsigned int i) { |
||||
int pos; |
||||
char c[] = "0123456789abcdef"; |
||||
for (pos = 28; pos != -4; pos -= 4) { |
||||
putch(c[(i >> pos) & 0xF]); |
||||
} |
||||
} |
||||
|
||||
void puth2(unsigned int i) { |
||||
int pos; |
||||
char c[] = "0123456789abcdef"; |
||||
for (pos = 4; pos != -4; pos -= 4) { |
||||
putch(c[(i >> pos) & 0xF]); |
||||
} |
||||
} |
||||
|
||||
void hexdump(const void *a, int l) { |
||||
int i; |
||||
for (i=0;i<l;i++) { |
||||
if ((i != 0) && ((i & 0xf) == 0)) puts("\n"); |
||||
puth2(((const unsigned char*)a)[i]); |
||||
puts(" "); |
||||
} |
||||
puts("\n"); |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@ |
||||
#!/bin/bash |
||||
sudo apt-get install gcc-arm-none-eabi python-pip |
||||
sudo pip2 install libusb1 pycrypto requests |
@ -0,0 +1,5 @@ |
||||
#!/bin/bash |
||||
# Need formula for gcc |
||||
brew tap ArmMbed/homebrew-formulae |
||||
brew install python dfu-util arm-none-eabi-gcc |
||||
pip2 install libusb1 pycrypto requests |
@ -0,0 +1,447 @@ |
||||
// this is last place with ifdef PANDA
|
||||
|
||||
#ifdef STM32F4 |
||||
#include "stm32f4xx_hal_gpio_ex.h" |
||||
#else |
||||
#include "stm32f2xx_hal_gpio_ex.h" |
||||
#endif |
||||
|
||||
// ********************* dynamic configuration detection *********************
|
||||
|
||||
#define PANDA_REV_AB 0 |
||||
#define PANDA_REV_C 1 |
||||
|
||||
#define PULL_EFFECTIVE_DELAY 10 |
||||
|
||||
void puts(const char *a); |
||||
|
||||
bool has_external_debug_serial = 0; |
||||
bool is_giant_panda = 0; |
||||
bool is_entering_bootmode = 0; |
||||
int revision = PANDA_REV_AB; |
||||
bool is_grey_panda = 0; |
||||
|
||||
bool detect_with_pull(GPIO_TypeDef *GPIO, int pin, int mode) { |
||||
set_gpio_mode(GPIO, pin, MODE_INPUT); |
||||
set_gpio_pullup(GPIO, pin, mode); |
||||
for (volatile int i=0; i<PULL_EFFECTIVE_DELAY; i++); |
||||
bool ret = get_gpio_input(GPIO, pin); |
||||
set_gpio_pullup(GPIO, pin, PULL_NONE); |
||||
return ret; |
||||
} |
||||
|
||||
// must call again from main because BSS is zeroed
|
||||
void detect(void) { |
||||
// detect has_external_debug_serial
|
||||
has_external_debug_serial = detect_with_pull(GPIOA, 3, PULL_DOWN); |
||||
|
||||
#ifdef PANDA |
||||
// detect is_giant_panda
|
||||
is_giant_panda = detect_with_pull(GPIOB, 1, PULL_DOWN); |
||||
|
||||
// detect panda REV C.
|
||||
// A13 floats in REV AB. In REV C, A13 is pulled up to 5V with a 10K
|
||||
// resistor and attached to the USB power control chip CTRL
|
||||
// line. Pulling A13 down with an internal 50k resistor in REV C
|
||||
// will produce a voltage divider that results in a high logic
|
||||
// level. Checking if this pin reads high with a pull down should
|
||||
// differentiate REV AB from C.
|
||||
revision = detect_with_pull(GPIOA, 13, PULL_DOWN) ? PANDA_REV_C : PANDA_REV_AB; |
||||
|
||||
// check if the ESP is trying to put me in boot mode
|
||||
is_entering_bootmode = !detect_with_pull(GPIOB, 0, PULL_UP); |
||||
|
||||
// check if it's a grey panda by seeing if the SPI lines are floating
|
||||
// TODO: is this reliable?
|
||||
is_grey_panda = !(detect_with_pull(GPIOA, 4, PULL_DOWN) | detect_with_pull(GPIOA, 5, PULL_DOWN) | detect_with_pull(GPIOA, 6, PULL_DOWN) | detect_with_pull(GPIOA, 7, PULL_DOWN)); |
||||
#else |
||||
// need to do this for early detect
|
||||
is_giant_panda = 0; |
||||
is_grey_panda = 0; |
||||
revision = PANDA_REV_AB; |
||||
is_entering_bootmode = 0; |
||||
#endif |
||||
} |
||||
|
||||
// ********************* bringup *********************
|
||||
|
||||
void periph_init(void) { |
||||
// enable GPIOB, UART2, CAN, USB clock
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; |
||||
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; |
||||
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; |
||||
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; |
||||
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; |
||||
RCC->APB1ENR |= RCC_APB1ENR_USART2EN; |
||||
RCC->APB1ENR |= RCC_APB1ENR_USART3EN; |
||||
#ifdef PANDA |
||||
RCC->APB1ENR |= RCC_APB1ENR_UART5EN; |
||||
#endif |
||||
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; |
||||
RCC->APB1ENR |= RCC_APB1ENR_CAN2EN; |
||||
#ifdef CAN3 |
||||
RCC->APB1ENR |= RCC_APB1ENR_CAN3EN; |
||||
#endif |
||||
RCC->APB1ENR |= RCC_APB1ENR_DACEN; |
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // main counter
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // slow loop and pedal
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // gmlan_alt
|
||||
//RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;
|
||||
//RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
|
||||
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; |
||||
RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; |
||||
//RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
|
||||
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; |
||||
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; |
||||
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; |
||||
} |
||||
|
||||
// ********************* setters *********************
|
||||
|
||||
void set_can_enable(CAN_TypeDef *CAN, bool enabled) { |
||||
// enable CAN busses
|
||||
if (CAN == CAN1) { |
||||
#ifdef PANDA |
||||
// CAN1_EN
|
||||
set_gpio_output(GPIOC, 1, !enabled); |
||||
#else |
||||
#ifdef PEDAL |
||||
// CAN1_EN (not flipped)
|
||||
set_gpio_output(GPIOB, 3, !enabled); |
||||
#else |
||||
// CAN1_EN
|
||||
set_gpio_output(GPIOB, 3, enabled); |
||||
#endif |
||||
#endif |
||||
} else if (CAN == CAN2) { |
||||
#ifdef PANDA |
||||
// CAN2_EN
|
||||
set_gpio_output(GPIOC, 13, !enabled); |
||||
#else |
||||
// CAN2_EN
|
||||
set_gpio_output(GPIOB, 4, enabled); |
||||
#endif |
||||
#ifdef CAN3 |
||||
} else if (CAN == CAN3) { |
||||
// CAN3_EN
|
||||
set_gpio_output(GPIOA, 0, !enabled); |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
#ifdef PANDA |
||||
#define LED_RED 9 |
||||
#define LED_GREEN 7 |
||||
#define LED_BLUE 6 |
||||
#else |
||||
#define LED_RED 10 |
||||
#define LED_GREEN 11 |
||||
#define LED_BLUE -1 |
||||
#endif |
||||
|
||||
void set_led(int led_num, int on) { |
||||
if (led_num != -1) { |
||||
#ifdef PANDA |
||||
set_gpio_output(GPIOC, led_num, !on); |
||||
#else |
||||
set_gpio_output(GPIOB, led_num, !on); |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
void set_can_mode(int can, bool use_gmlan) { |
||||
// connects to CAN2 xcvr or GMLAN xcvr
|
||||
if (use_gmlan) { |
||||
if (can == 1) { |
||||
// B5,B6: disable normal mode
|
||||
set_gpio_mode(GPIOB, 5, MODE_INPUT); |
||||
set_gpio_mode(GPIOB, 6, MODE_INPUT); |
||||
|
||||
// B12,B13: gmlan mode
|
||||
set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2); |
||||
set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2); |
||||
#ifdef CAN3 |
||||
} else if (can == 2) { |
||||
// A8,A15: disable normal mode
|
||||
set_gpio_mode(GPIOA, 8, MODE_INPUT); |
||||
set_gpio_mode(GPIOA, 15, MODE_INPUT); |
||||
|
||||
// B3,B4: enable gmlan mode
|
||||
set_gpio_alternate(GPIOB, 3, GPIO_AF11_CAN3); |
||||
set_gpio_alternate(GPIOB, 4, GPIO_AF11_CAN3); |
||||
#endif |
||||
} |
||||
} else { |
||||
if (can == 1) { |
||||
// B12,B13: disable gmlan mode
|
||||
set_gpio_mode(GPIOB, 12, MODE_INPUT); |
||||
set_gpio_mode(GPIOB, 13, MODE_INPUT); |
||||
|
||||
// B5,B6: normal mode
|
||||
set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); |
||||
set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); |
||||
#ifdef CAN3 |
||||
} else if (can == 2) { |
||||
// B3,B4: disable gmlan mode
|
||||
set_gpio_mode(GPIOB, 3, MODE_INPUT); |
||||
set_gpio_mode(GPIOB, 4, MODE_INPUT); |
||||
// A8,A15: normal mode
|
||||
set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3); |
||||
set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3); |
||||
#endif |
||||
} |
||||
} |
||||
} |
||||
|
||||
#define USB_POWER_NONE 0 |
||||
#define USB_POWER_CLIENT 1 |
||||
#define USB_POWER_CDP 2 |
||||
#define USB_POWER_DCP 3 |
||||
|
||||
int usb_power_mode = USB_POWER_NONE; |
||||
|
||||
void set_usb_power_mode(int mode) { |
||||
bool valid_mode = true; |
||||
switch (mode) { |
||||
case USB_POWER_CLIENT: |
||||
// B2,A13: set client mode
|
||||
set_gpio_output(GPIOB, 2, 0); |
||||
set_gpio_output(GPIOA, 13, 1); |
||||
break; |
||||
case USB_POWER_CDP: |
||||
// B2,A13: set CDP mode
|
||||
set_gpio_output(GPIOB, 2, 1); |
||||
set_gpio_output(GPIOA, 13, 1); |
||||
break; |
||||
case USB_POWER_DCP: |
||||
// B2,A13: set DCP mode on the charger (breaks USB!)
|
||||
set_gpio_output(GPIOB, 2, 0); |
||||
set_gpio_output(GPIOA, 13, 0); |
||||
break; |
||||
default: |
||||
valid_mode = false; |
||||
puts("Invalid usb power mode\n"); |
||||
break; |
||||
} |
||||
|
||||
if (valid_mode) { |
||||
usb_power_mode = mode; |
||||
} |
||||
} |
||||
|
||||
#define ESP_DISABLED 0 |
||||
#define ESP_ENABLED 1 |
||||
#define ESP_BOOTMODE 2 |
||||
|
||||
void set_esp_mode(int mode) { |
||||
switch (mode) { |
||||
case ESP_DISABLED: |
||||
// ESP OFF
|
||||
set_gpio_output(GPIOC, 14, 0); |
||||
set_gpio_output(GPIOC, 5, 0); |
||||
break; |
||||
case ESP_ENABLED: |
||||
// ESP ON
|
||||
set_gpio_output(GPIOC, 14, 1); |
||||
set_gpio_output(GPIOC, 5, 1); |
||||
break; |
||||
case ESP_BOOTMODE: |
||||
set_gpio_output(GPIOC, 14, 1); |
||||
set_gpio_output(GPIOC, 5, 0); |
||||
break; |
||||
default: |
||||
puts("Invalid esp mode\n"); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// ********************* big init function *********************
|
||||
|
||||
// board specific
|
||||
void gpio_init(void) { |
||||
// pull low to hold ESP in reset??
|
||||
// enable OTG out tied to ground
|
||||
GPIOA->ODR = 0; |
||||
GPIOB->ODR = 0; |
||||
GPIOA->PUPDR = 0; |
||||
//GPIOC->ODR = 0;
|
||||
GPIOB->AFR[0] = 0; |
||||
GPIOB->AFR[1] = 0; |
||||
|
||||
// C2,C3: analog mode, voltage and current sense
|
||||
set_gpio_mode(GPIOC, 2, MODE_ANALOG); |
||||
set_gpio_mode(GPIOC, 3, MODE_ANALOG); |
||||
|
||||
#ifdef PEDAL |
||||
// comma pedal has inputs on C0 and C1
|
||||
set_gpio_mode(GPIOC, 0, MODE_ANALOG); |
||||
set_gpio_mode(GPIOC, 1, MODE_ANALOG); |
||||
// DAC outputs on A4 and A5
|
||||
// apparently they don't need GPIO setup
|
||||
#endif |
||||
|
||||
// C8: FAN aka TIM3_CH4
|
||||
set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); |
||||
|
||||
// turn off LEDs and set mode
|
||||
set_led(LED_RED, 0); |
||||
set_led(LED_GREEN, 0); |
||||
set_led(LED_BLUE, 0); |
||||
|
||||
// A11,A12: USB
|
||||
set_gpio_alternate(GPIOA, 11, GPIO_AF10_OTG_FS); |
||||
set_gpio_alternate(GPIOA, 12, GPIO_AF10_OTG_FS); |
||||
GPIOA->OSPEEDR = GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12; |
||||
|
||||
#ifdef PANDA |
||||
// enable started_alt on the panda
|
||||
set_gpio_pullup(GPIOA, 1, PULL_UP); |
||||
|
||||
// A2,A3: USART 2 for debugging
|
||||
set_gpio_alternate(GPIOA, 2, GPIO_AF7_USART2); |
||||
set_gpio_alternate(GPIOA, 3, GPIO_AF7_USART2); |
||||
|
||||
// A9,A10: USART 1 for talking to the ESP
|
||||
set_gpio_alternate(GPIOA, 9, GPIO_AF7_USART1); |
||||
set_gpio_alternate(GPIOA, 10, GPIO_AF7_USART1); |
||||
|
||||
// B12: GMLAN, ignition sense, pull up
|
||||
set_gpio_pullup(GPIOB, 12, PULL_UP); |
||||
|
||||
// A4,A5,A6,A7: setup SPI
|
||||
set_gpio_alternate(GPIOA, 4, GPIO_AF5_SPI1); |
||||
set_gpio_alternate(GPIOA, 5, GPIO_AF5_SPI1); |
||||
set_gpio_alternate(GPIOA, 6, GPIO_AF5_SPI1); |
||||
set_gpio_alternate(GPIOA, 7, GPIO_AF5_SPI1); |
||||
#endif |
||||
|
||||
// B8,B9: CAN 1
|
||||
#ifdef STM32F4 |
||||
set_gpio_alternate(GPIOB, 8, GPIO_AF8_CAN1); |
||||
set_gpio_alternate(GPIOB, 9, GPIO_AF8_CAN1); |
||||
#else |
||||
set_gpio_alternate(GPIOB, 8, GPIO_AF9_CAN1); |
||||
set_gpio_alternate(GPIOB, 9, GPIO_AF9_CAN1); |
||||
#endif |
||||
set_can_enable(CAN1, 1); |
||||
|
||||
// B5,B6: CAN 2
|
||||
set_can_mode(1, 0); |
||||
set_can_enable(CAN2, 1); |
||||
|
||||
// A8,A15: CAN 3
|
||||
#ifdef CAN3 |
||||
set_can_mode(2, 0); |
||||
set_can_enable(CAN3, 1); |
||||
#endif |
||||
|
||||
/* GMLAN mode pins:
|
||||
M0(B15) M1(B14) mode |
||||
======================= |
||||
0 0 sleep |
||||
1 0 100kbit |
||||
0 1 high voltage wakeup |
||||
1 1 33kbit (normal) |
||||
*/ |
||||
|
||||
// put gmlan transceiver in normal mode
|
||||
set_gpio_output(GPIOB, 14, 1); |
||||
set_gpio_output(GPIOB, 15, 1); |
||||
|
||||
#ifdef PANDA |
||||
// K-line enable moved from B4->B7 to make room for GMLAN on CAN3
|
||||
set_gpio_output(GPIOB, 7, 1); // REV C
|
||||
|
||||
// C12,D2: K-Line setup on UART 5
|
||||
set_gpio_alternate(GPIOC, 12, GPIO_AF8_UART5); |
||||
set_gpio_alternate(GPIOD, 2, GPIO_AF8_UART5); |
||||
set_gpio_pullup(GPIOD, 2, PULL_UP); |
||||
|
||||
// L-line enable
|
||||
set_gpio_output(GPIOA, 14, 1); |
||||
|
||||
// C10,C11: L-Line setup on USART 3
|
||||
set_gpio_alternate(GPIOC, 10, GPIO_AF7_USART3); |
||||
set_gpio_alternate(GPIOC, 11, GPIO_AF7_USART3); |
||||
set_gpio_pullup(GPIOC, 11, PULL_UP); |
||||
#endif |
||||
|
||||
set_usb_power_mode(USB_POWER_CLIENT); |
||||
} |
||||
|
||||
// ********************* early bringup *********************
|
||||
|
||||
#define ENTER_BOOTLOADER_MAGIC 0xdeadbeef |
||||
#define ENTER_SOFTLOADER_MAGIC 0xdeadc0de |
||||
#define BOOT_NORMAL 0xdeadb111 |
||||
|
||||
extern void *g_pfnVectors; |
||||
extern uint32_t enter_bootloader_mode; |
||||
|
||||
void jump_to_bootloader(void) { |
||||
// do enter bootloader
|
||||
enter_bootloader_mode = 0; |
||||
void (*bootloader)(void) = (void (*)(void)) (*((uint32_t *)0x1fff0004)); |
||||
|
||||
// jump to bootloader
|
||||
bootloader(); |
||||
|
||||
// reset on exit
|
||||
enter_bootloader_mode = BOOT_NORMAL; |
||||
NVIC_SystemReset(); |
||||
} |
||||
|
||||
void early(void) { |
||||
// after it's been in the bootloader, things are initted differently, so we reset
|
||||
if ((enter_bootloader_mode != BOOT_NORMAL) && |
||||
(enter_bootloader_mode != ENTER_BOOTLOADER_MAGIC) && |
||||
(enter_bootloader_mode != ENTER_SOFTLOADER_MAGIC)) { |
||||
enter_bootloader_mode = BOOT_NORMAL; |
||||
NVIC_SystemReset(); |
||||
} |
||||
|
||||
// if wrong chip, reboot
|
||||
volatile unsigned int id = DBGMCU->IDCODE; |
||||
#ifdef STM32F4 |
||||
if ((id&0xFFF) != 0x463) enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; |
||||
#else |
||||
if ((id&0xFFF) != 0x411) enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; |
||||
#endif |
||||
|
||||
// setup interrupt table
|
||||
SCB->VTOR = (uint32_t)&g_pfnVectors; |
||||
|
||||
// early GPIOs float everything
|
||||
RCC->AHB1ENR = RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN; |
||||
|
||||
GPIOA->MODER = 0; GPIOB->MODER = 0; GPIOC->MODER = 0; |
||||
GPIOA->ODR = 0; GPIOB->ODR = 0; GPIOC->ODR = 0; |
||||
GPIOA->PUPDR = 0; GPIOB->PUPDR = 0; GPIOC->PUPDR = 0; |
||||
|
||||
detect(); |
||||
|
||||
#ifdef PANDA |
||||
// enable the ESP, disable ESP boot mode
|
||||
// unless we are on a giant panda, then there's no ESP
|
||||
// dont disable on grey panda
|
||||
if (is_giant_panda) { |
||||
set_esp_mode(ESP_DISABLED); |
||||
} else { |
||||
set_esp_mode(ESP_ENABLED); |
||||
} |
||||
#endif |
||||
|
||||
|
||||
if (enter_bootloader_mode == ENTER_BOOTLOADER_MAGIC) { |
||||
#ifdef PANDA |
||||
set_esp_mode(ESP_DISABLED); |
||||
#endif |
||||
set_led(LED_GREEN, 1); |
||||
jump_to_bootloader(); |
||||
} |
||||
|
||||
if (is_entering_bootmode) { |
||||
enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; |
||||
} |
||||
} |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6c7e75d09887cb3035f18bc6f4fdbd7392a50379cd556315deb3bcb762d99078 |
||||
size 40062 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:03a80f17ff18c5d0e3b234009f98698cd402b8ea972df687a6cfafd8b8a78654 |
||||
size 102146 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:83c5d601dd5640a863fe4dc43cd228b46239bbf9f07b51102bf0c1c8f235cdfa |
||||
size 112561 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1fdd226d82bde2a90231ba47a59377158b8907e4cada2a6215f32dcf13ab35cd |
||||
size 3533 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c9b8e36d05097da5d493f197aa7f81ce517f4532536925b568ccbd6e2c01eb8a |
||||
size 3549 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5d338d36730ccbd138126078e17f69c308a015f0a88d85bd639af072e527d53d |
||||
size 3566 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:65ac7a82a58b702c8c8b99cf7ee965c71cd59d5dfa663d3729a602bdea8ec4cd |
||||
size 601889 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9591dc7b364d8e963152a4a15f0435e8631a9d4eacc95fca022574269047dada |
||||
size 6738 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a312bdc79b5ac3c780f700869f73237256047e1f134310087cd1c4d3ef8255fd |
||||
size 7352 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e320fb1b6f44dadb3b819146ffce5cf4d1d5b0482d2ba7cddc4845899e47290c |
||||
size 12933 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:21e5ea5bc19e8d45cf4f32d451ea7f79a62c50e5a6c0e07cc7752ed0c9d295c3 |
||||
size 1288438 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7ccd163515bc57fa7a1ce31361ec1bc70a1b3e2c82873e940dfaaaf1072b4d3d |
||||
size 10525 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8f2c6a79cf25619734b65bd7e1babaeb9702fa86c89a221c3f7ea2c7ab8c2146 |
||||
size 8325 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2600c6ad35c8accf5aa0878a151aa69a3e21859fb7675b3ce71c0fb44ef7f521 |
||||
size 81942 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:75bc5bac6341c0e088408c2b1548eafa24b80287bd37f6807cf52cf652984fa3 |
||||
size 3556 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a631a141f5e85b8fcef77b5f756392d1b9546c336fae6a565f037d84dcbe3df7 |
||||
size 3724 |
@ -0,0 +1,61 @@ |
||||
// **** libc ****
|
||||
|
||||
void delay(int a) { |
||||
volatile int i; |
||||
for (i = 0; i < a; i++); |
||||
} |
||||
|
||||
void *memset(void *str, int c, unsigned int n) { |
||||
unsigned int i; |
||||
for (i = 0; i < n; i++) { |
||||
*((uint8_t*)str) = c; |
||||
++str; |
||||
} |
||||
return str; |
||||
} |
||||
|
||||
void *memcpy(void *dest, const void *src, unsigned int n) { |
||||
unsigned int i; |
||||
// TODO: make not slow
|
||||
for (i = 0; i < n; i++) { |
||||
((uint8_t*)dest)[i] = *(uint8_t*)src; |
||||
++src; |
||||
} |
||||
return dest; |
||||
} |
||||
|
||||
int memcmp(const void * ptr1, const void * ptr2, unsigned int num) { |
||||
unsigned int i; |
||||
int ret = 0; |
||||
for (i = 0; i < num; i++) { |
||||
if ( ((uint8_t*)ptr1)[i] != ((uint8_t*)ptr2)[i] ) { |
||||
ret = -1; |
||||
break; |
||||
} |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
// ********************* IRQ helpers *********************
|
||||
|
||||
int interrupts_enabled = 0; |
||||
void enable_interrupts(void) { |
||||
interrupts_enabled = 1; |
||||
__enable_irq(); |
||||
} |
||||
|
||||
int critical_depth = 0; |
||||
void enter_critical_section(void) { |
||||
__disable_irq(); |
||||
// this is safe because interrupts are disabled
|
||||
critical_depth += 1; |
||||
} |
||||
|
||||
void exit_critical_section(void) { |
||||
// this is safe because interrupts are disabled
|
||||
critical_depth -= 1; |
||||
if ((critical_depth == 0) && interrupts_enabled) { |
||||
__enable_irq(); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,747 @@ |
||||
//#define EON
|
||||
|
||||
#include "config.h" |
||||
#include "obj/gitversion.h" |
||||
|
||||
// ********************* includes *********************
|
||||
|
||||
|
||||
#include "libc.h" |
||||
#include "provision.h" |
||||
|
||||
#include "drivers/llcan.h" |
||||
#include "drivers/llgpio.h" |
||||
#include "gpio.h" |
||||
|
||||
#include "drivers/uart.h" |
||||
#include "drivers/adc.h" |
||||
#include "drivers/usb.h" |
||||
#include "drivers/gmlan_alt.h" |
||||
#include "drivers/timer.h" |
||||
#include "drivers/clock.h" |
||||
|
||||
#ifndef EON |
||||
#include "drivers/spi.h" |
||||
#endif |
||||
|
||||
#include "power_saving.h" |
||||
#include "safety.h" |
||||
#include "drivers/can.h" |
||||
|
||||
// ********************* serial debugging *********************
|
||||
|
||||
void debug_ring_callback(uart_ring *ring) { |
||||
char rcv; |
||||
while (getc(ring, &rcv)) { |
||||
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); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// ***************************** started logic *****************************
|
||||
|
||||
bool is_gpio_started(void) { |
||||
// ignition is on PA1
|
||||
return (GPIOA->IDR & (1U << 1)) == 0; |
||||
} |
||||
|
||||
void EXTI1_IRQHandler(void) { |
||||
volatile int pr = EXTI->PR & (1U << 1); |
||||
if ((pr & (1U << 1)) != 0) { |
||||
#ifdef DEBUG |
||||
puts("got started interrupt\n"); |
||||
#endif |
||||
|
||||
// jenky debounce
|
||||
delay(100000); |
||||
|
||||
// set power savings mode here
|
||||
int power_save_state = is_gpio_started() ? POWER_SAVE_STATUS_DISABLED : POWER_SAVE_STATUS_ENABLED; |
||||
set_power_save_state(power_save_state); |
||||
EXTI->PR = (1U << 1); |
||||
} |
||||
} |
||||
|
||||
void started_interrupt_init(void) { |
||||
SYSCFG->EXTICR[1] = SYSCFG_EXTICR1_EXTI1_PA; |
||||
EXTI->IMR |= (1U << 1); |
||||
EXTI->RTSR |= (1U << 1); |
||||
EXTI->FTSR |= (1U << 1); |
||||
NVIC_EnableIRQ(EXTI1_IRQn); |
||||
} |
||||
|
||||
// ***************************** USB port *****************************
|
||||
|
||||
int get_health_pkt(void *dat) { |
||||
struct __attribute__((packed)) { |
||||
uint32_t voltage; |
||||
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; |
||||
|
||||
//Voltage will be measured in mv. 5000 = 5V
|
||||
uint32_t voltage = adc_get(ADCCHAN_VOLTAGE); |
||||
|
||||
// REVC has a 10, 1 (1/11) voltage divider
|
||||
// Here is the calculation for the scale (s)
|
||||
// ADCV = VIN_S * (1/11) * (4095/3.3)
|
||||
// RETVAL = ADCV * s = VIN_S*1000
|
||||
// s = 1000/((4095/3.3)*(1/11)) = 8.8623046875
|
||||
|
||||
// Avoid needing floating point math
|
||||
health->voltage = (voltage * 8862) / 1000; |
||||
|
||||
health->current = adc_get(ADCCHAN_CURRENT); |
||||
int safety_ignition = safety_ignition_hook(); |
||||
if (safety_ignition < 0) { |
||||
//Use the GPIO pin to determine ignition
|
||||
health->started = is_gpio_started(); |
||||
} else { |
||||
//Current safety hooks want to determine ignition (ex: GM)
|
||||
health->started = safety_ignition; |
||||
} |
||||
|
||||
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, bool hardwired) { |
||||
UNUSED(hardwired); |
||||
CAN_FIFOMailBox_TypeDef *reply = (CAN_FIFOMailBox_TypeDef *)usbdata; |
||||
int ilen = 0; |
||||
while (ilen < MIN(len/0x10, 4) && can_pop(&can_rx_q, &reply[ilen])) { |
||||
ilen++; |
||||
} |
||||
return ilen*0x10; |
||||
} |
||||
|
||||
// send on serial, first byte to select the ring
|
||||
void usb_cb_ep2_out(uint8_t *usbdata, int len, bool hardwired) { |
||||
UNUSED(hardwired); |
||||
uart_ring *ur = get_ring_by_number(usbdata[0]); |
||||
if ((len != 0) && (ur != NULL)) { |
||||
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])) { |
||||
// wait
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// send on CAN
|
||||
void usb_cb_ep3_out(uint8_t *usbdata, int len, bool hardwired) { |
||||
UNUSED(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); |
||||
} |
||||
} |
||||
|
||||
bool is_enumerated = 0; |
||||
|
||||
void usb_cb_enumeration_complete() { |
||||
puts("USB enumeration complete\n"); |
||||
is_enumerated = 1; |
||||
} |
||||
|
||||
int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) { |
||||
int resp_len = 0; |
||||
uart_ring *ur = NULL; |
||||
int i; |
||||
switch (setup->b.bRequest) { |
||||
// **** 0xc0: get CAN debug info
|
||||
case 0xc0: |
||||
puts("can tx: "); puth(can_tx_cnt); |
||||
puts(" txd: "); puth(can_txd_cnt); |
||||
puts(" rx: "); puth(can_rx_cnt); |
||||
puts(" err: "); puth(can_err_cnt); |
||||
puts("\n"); |
||||
break; |
||||
// **** 0xc1: is grey panda
|
||||
case 0xc1: |
||||
resp[0] = is_grey_panda; |
||||
resp_len = 1; |
||||
break; |
||||
// **** 0xd0: fetch serial number
|
||||
case 0xd0: |
||||
// addresses are OTP
|
||||
if (setup->b.wValue.w == 1) { |
||||
memcpy(resp, (void *)0x1fff79c0, 0x10); |
||||
resp_len = 0x10; |
||||
} else { |
||||
get_provision_chunk(resp); |
||||
resp_len = PROVISION_CHUNK_LEN; |
||||
} |
||||
break; |
||||
// **** 0xd1: enter bootloader mode
|
||||
case 0xd1: |
||||
// this allows reflashing of the bootstub
|
||||
// so it's blocked over wifi
|
||||
switch (setup->b.wValue.w) { |
||||
case 0: |
||||
if (hardwired) { |
||||
puts("-> entering bootloader\n"); |
||||
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; |
||||
NVIC_SystemReset(); |
||||
} |
||||
break; |
||||
case 1: |
||||
puts("-> entering softloader\n"); |
||||
enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; |
||||
NVIC_SystemReset(); |
||||
break; |
||||
default: |
||||
puts("Bootloader mode invalid\n"); |
||||
break; |
||||
} |
||||
break; |
||||
// **** 0xd2: get health packet
|
||||
case 0xd2: |
||||
resp_len = get_health_pkt(resp); |
||||
break; |
||||
// **** 0xd6: get version
|
||||
case 0xd6: |
||||
COMPILE_TIME_ASSERT(sizeof(gitversion) <= MAX_RESP_LEN); |
||||
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 if (setup->b.wValue.w == 2) { |
||||
set_esp_mode(ESP_BOOTMODE); |
||||
} else { |
||||
set_esp_mode(ESP_DISABLED); |
||||
} |
||||
break; |
||||
// **** 0xda: reset ESP, with optional boot mode
|
||||
case 0xda: |
||||
set_esp_mode(ESP_DISABLED); |
||||
delay(1000000); |
||||
if (setup->b.wValue.w == 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: |
||||
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) { |
||||
can_set_gmlan(2); |
||||
} |
||||
} else { |
||||
can_set_gmlan(-1); |
||||
} |
||||
break; |
||||
// **** 0xdc: set safety mode
|
||||
case 0xdc: |
||||
// this is the only way to leave silent mode
|
||||
// and it's blocked over WiFi
|
||||
// Allow ELM security mode to be set over wifi.
|
||||
if (hardwired || (setup->b.wValue.w == SAFETY_NOOUTPUT) || (setup->b.wValue.w == SAFETY_ELM327)) { |
||||
safety_set_mode(setup->b.wValue.w, (int16_t)setup->b.wIndex.w); |
||||
if (safety_ignition_hook() != -1) { |
||||
// if the ignition hook depends on something other than the started GPIO
|
||||
// we have to disable power savings (fix for GM and Tesla)
|
||||
set_power_save_state(POWER_SAVE_STATUS_DISABLED); |
||||
} |
||||
#ifndef EON |
||||
// always LIVE on EON
|
||||
switch (setup->b.wValue.w) { |
||||
case SAFETY_NOOUTPUT: |
||||
can_silent = ALL_CAN_SILENT; |
||||
break; |
||||
case SAFETY_ELM327: |
||||
can_silent = ALL_CAN_BUT_MAIN_SILENT; |
||||
break; |
||||
default: |
||||
can_silent = ALL_CAN_LIVE; |
||||
break; |
||||
} |
||||
#endif |
||||
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; |
||||
// **** 0xdf: set long controls allowed
|
||||
case 0xdf: |
||||
if (hardwired) { |
||||
long_controls_allowed = setup->b.wValue.w & 1; |
||||
} |
||||
break; |
||||
// **** 0xe0: uart read
|
||||
case 0xe0: |
||||
ur = get_ring_by_number(setup->b.wValue.w); |
||||
if (!ur) { |
||||
break; |
||||
} |
||||
if (ur == &esp_ring) { |
||||
uart_dma_drain(); |
||||
} |
||||
// read
|
||||
while ((resp_len < MIN(setup->b.wLength.w, MAX_RESP_LEN)) && |
||||
getc(ur, (char*)&resp[resp_len])) { |
||||
++resp_len; |
||||
} |
||||
break; |
||||
// **** 0xe1: uart set baud rate
|
||||
case 0xe1: |
||||
ur = get_ring_by_number(setup->b.wValue.w); |
||||
if (!ur) { |
||||
break; |
||||
} |
||||
uart_set_baud(ur->uart, setup->b.wIndex.w); |
||||
break; |
||||
// **** 0xe2: uart set parity
|
||||
case 0xe2: |
||||
ur = get_ring_by_number(setup->b.wValue.w); |
||||
if (!ur) { |
||||
break; |
||||
} |
||||
switch (setup->b.wIndex.w) { |
||||
case 0: |
||||
// disable parity, 8-bit
|
||||
ur->uart->CR1 &= ~(USART_CR1_PCE | USART_CR1_M); |
||||
break; |
||||
case 1: |
||||
// even parity, 9-bit
|
||||
ur->uart->CR1 &= ~USART_CR1_PS; |
||||
ur->uart->CR1 |= USART_CR1_PCE | USART_CR1_M; |
||||
break; |
||||
case 2: |
||||
// odd parity, 9-bit
|
||||
ur->uart->CR1 |= USART_CR1_PS; |
||||
ur->uart->CR1 |= USART_CR1_PCE | USART_CR1_M; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
break; |
||||
// **** 0xe4: uart set baud rate extended
|
||||
case 0xe4: |
||||
ur = get_ring_by_number(setup->b.wValue.w); |
||||
if (!ur) { |
||||
break; |
||||
} |
||||
uart_set_baud(ur->uart, (int)setup->b.wIndex.w*300); |
||||
break; |
||||
// **** 0xe5: set CAN loopback (for testing)
|
||||
case 0xe5: |
||||
can_loopback = (setup->b.wValue.w > 0); |
||||
can_init_all(); |
||||
break; |
||||
// **** 0xe6: set USB power
|
||||
case 0xe6: |
||||
if (setup->b.wValue.w == 1) { |
||||
puts("user setting CDP mode\n"); |
||||
set_usb_power_mode(USB_POWER_CDP); |
||||
} else if (setup->b.wValue.w == 2) { |
||||
puts("user setting DCP mode\n"); |
||||
set_usb_power_mode(USB_POWER_DCP); |
||||
} else { |
||||
puts("user setting CLIENT mode\n"); |
||||
set_usb_power_mode(USB_POWER_CLIENT); |
||||
} |
||||
break; |
||||
// **** 0xf0: do k-line wValue pulse on uart2 for Acura
|
||||
case 0xf0: |
||||
if (setup->b.wValue.w == 1) { |
||||
GPIOC->ODR &= ~(1U << 10); |
||||
GPIOC->MODER &= ~GPIO_MODER_MODER10_1; |
||||
GPIOC->MODER |= GPIO_MODER_MODER10_0; |
||||
} else { |
||||
GPIOC->ODR &= ~(1U << 12); |
||||
GPIOC->MODER &= ~GPIO_MODER_MODER12_1; |
||||
GPIOC->MODER |= GPIO_MODER_MODER12_0; |
||||
} |
||||
|
||||
for (i = 0; i < 80; i++) { |
||||
delay(8000); |
||||
if (setup->b.wValue.w == 1) { |
||||
GPIOC->ODR |= (1U << 10); |
||||
GPIOC->ODR &= ~(1U << 10); |
||||
} else { |
||||
GPIOC->ODR |= (1U << 12); |
||||
GPIOC->ODR &= ~(1U << 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 != NULL) { |
||||
puts("Clearing UART queue.\n"); |
||||
clear_uart_buff(rb); |
||||
} |
||||
break; |
||||
} |
||||
default: |
||||
puts("NO HANDLER "); |
||||
puth(setup->b.bRequest); |
||||
puts("\n"); |
||||
break; |
||||
} |
||||
return resp_len; |
||||
} |
||||
|
||||
int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) { |
||||
// data[0] = endpoint
|
||||
// data[2] = length
|
||||
// data[4:] = data
|
||||
UNUSED(len); |
||||
int resp_len = 0; |
||||
switch (data[0]) { |
||||
case 0: |
||||
// control transfer
|
||||
resp_len = usb_cb_control_msg((USB_Setup_TypeDef *)(data+4), data_out, 0); |
||||
break; |
||||
case 1: |
||||
// ep 1, read
|
||||
resp_len = usb_cb_ep1_in(data_out, 0x40, 0); |
||||
break; |
||||
case 2: |
||||
// ep 2, send serial
|
||||
usb_cb_ep2_out(data+4, data[2], 0); |
||||
break; |
||||
case 3: |
||||
// ep 3, send CAN
|
||||
usb_cb_ep3_out(data+4, data[2], 0); |
||||
break; |
||||
default: |
||||
puts("SPI data invalid"); |
||||
break; |
||||
} |
||||
return resp_len; |
||||
} |
||||
|
||||
|
||||
// ***************************** main code *****************************
|
||||
|
||||
void __initialize_hardware_early(void) { |
||||
early(); |
||||
} |
||||
|
||||
void __attribute__ ((noinline)) enable_fpu(void) { |
||||
// enable the FPU
|
||||
SCB->CPACR |= ((3UL << (10U * 2)) | (3UL << (11U * 2))); |
||||
} |
||||
|
||||
uint64_t tcnt = 0; |
||||
uint64_t marker = 0; |
||||
|
||||
// called once per second
|
||||
void TIM3_IRQHandler(void) { |
||||
#define CURRENT_THRESHOLD 0xF00 |
||||
#define CLICKS 5 // 5 seconds to switch modes
|
||||
|
||||
if (TIM3->SR != 0) { |
||||
can_live = pending_can_live; |
||||
|
||||
//puth(usart1_dma); puts(" "); puth(DMA2_Stream5->M0AR); puts(" "); puth(DMA2_Stream5->NDTR); puts("\n");
|
||||
|
||||
uint32_t current = adc_get(ADCCHAN_CURRENT); |
||||
|
||||
switch (usb_power_mode) { |
||||
case USB_POWER_CLIENT: |
||||
if ((tcnt-marker) >= CLICKS) { |
||||
if (!is_enumerated) { |
||||
puts("USBP: didn't enumerate, switching to CDP mode\n"); |
||||
// switch to CDP
|
||||
set_usb_power_mode(USB_POWER_CDP); |
||||
marker = tcnt; |
||||
} |
||||
} |
||||
// keep resetting the timer if it's enumerated
|
||||
if (is_enumerated) { |
||||
marker = tcnt; |
||||
} |
||||
break; |
||||
case USB_POWER_CDP: |
||||
// On the EON, if we get into CDP mode we stay here. No need to go to DCP.
|
||||
#ifndef EON |
||||
// been CLICKS clicks since we switched to CDP
|
||||
if ((tcnt-marker) >= CLICKS) { |
||||
// measure current draw, if positive and no enumeration, switch to DCP
|
||||
if (!is_enumerated && (current < CURRENT_THRESHOLD)) { |
||||
puts("USBP: no enumeration with current draw, switching to DCP mode\n"); |
||||
set_usb_power_mode(USB_POWER_DCP); |
||||
marker = tcnt; |
||||
} |
||||
} |
||||
// keep resetting the timer if there's no current draw in CDP
|
||||
if (current >= CURRENT_THRESHOLD) { |
||||
marker = tcnt; |
||||
} |
||||
#endif |
||||
break; |
||||
case USB_POWER_DCP: |
||||
// been at least CLICKS clicks since we switched to DCP
|
||||
if ((tcnt-marker) >= CLICKS) { |
||||
// if no current draw, switch back to CDP
|
||||
if (current >= CURRENT_THRESHOLD) { |
||||
puts("USBP: no current draw, switching back to CDP mode\n"); |
||||
set_usb_power_mode(USB_POWER_CDP); |
||||
marker = tcnt; |
||||
} |
||||
} |
||||
// keep resetting the timer if there's current draw in DCP
|
||||
if (current < CURRENT_THRESHOLD) { |
||||
marker = tcnt; |
||||
} |
||||
break; |
||||
default: |
||||
puts("USB power mode invalid\n"); // set_usb_power_mode prevents assigning invalid values
|
||||
break; |
||||
} |
||||
|
||||
// ~0x9a = 500 ma
|
||||
/*puth(current);
|
||||
puts("\n");*/ |
||||
|
||||
// reset this every 16th pass
|
||||
if ((tcnt&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); |
||||
|
||||
// turn off the blue LED, turned on by CAN
|
||||
// unless we are in power saving mode
|
||||
set_led(LED_BLUE, (tcnt & 1) && (power_save_status == POWER_SAVE_STATUS_ENABLED)); |
||||
|
||||
// on to the next one
|
||||
tcnt += 1; |
||||
} |
||||
TIM3->SR = 0; |
||||
} |
||||
|
||||
int main(void) { |
||||
// shouldn't have interrupts here, but just in case
|
||||
__disable_irq(); |
||||
|
||||
// init early devices
|
||||
clock_init(); |
||||
periph_init(); |
||||
detect(); |
||||
|
||||
// print hello
|
||||
puts("\n\n\n************************ MAIN START ************************\n"); |
||||
|
||||
// detect the revision and init the GPIOs
|
||||
puts("config:\n"); |
||||
puts((revision == PANDA_REV_C) ? " panda rev c\n" : " panda rev a or b\n"); |
||||
puts(has_external_debug_serial ? " real serial\n" : " USB serial\n"); |
||||
puts(is_giant_panda ? " GIANTpanda detected\n" : " not GIANTpanda\n"); |
||||
puts(is_grey_panda ? " gray panda detected!\n" : " white panda\n"); |
||||
puts(is_entering_bootmode ? " ESP wants bootmode\n" : " no bootmode\n"); |
||||
|
||||
// non rev c panda are no longer supported
|
||||
while (revision != PANDA_REV_C) { |
||||
// hang
|
||||
} |
||||
|
||||
gpio_init(); |
||||
|
||||
// panda has an FPU, let's use it!
|
||||
enable_fpu(); |
||||
|
||||
// enable main uart if it's connected
|
||||
if (has_external_debug_serial) { |
||||
// WEIRDNESS: without this gate around the UART, it would "crash", but only if the ESP is enabled
|
||||
// assuming it's because the lines were left floating and spurious noise was on them
|
||||
uart_init(USART2, 115200); |
||||
} |
||||
|
||||
if (is_grey_panda) { |
||||
uart_init(USART1, 9600); |
||||
} else { |
||||
// enable ESP uart
|
||||
uart_init(USART1, 115200); |
||||
} |
||||
|
||||
// enable LIN
|
||||
uart_init(UART5, 10400); |
||||
UART5->CR2 |= USART_CR2_LINEN; |
||||
uart_init(USART3, 10400); |
||||
USART3->CR2 |= USART_CR2_LINEN; |
||||
|
||||
// init microsecond system timer
|
||||
// increments 1000000 times per second
|
||||
// generate an update to set the prescaler
|
||||
TIM2->PSC = 48-1; |
||||
TIM2->CR1 = TIM_CR1_CEN; |
||||
TIM2->EGR = TIM_EGR_UG; |
||||
// use TIM2->CNT to read
|
||||
|
||||
// enable USB
|
||||
usb_init(); |
||||
|
||||
// default to silent mode to prevent issues with Ford
|
||||
// hardcode a specific safety mode if you want to force the panda to be in a specific mode
|
||||
safety_set_mode(SAFETY_NOOUTPUT, 0); |
||||
#ifdef EON |
||||
// if we're on an EON, it's fine for CAN to be live for fingerprinting
|
||||
can_silent = ALL_CAN_LIVE; |
||||
#else |
||||
can_silent = ALL_CAN_SILENT; |
||||
#endif |
||||
can_init_all(); |
||||
|
||||
adc_init(); |
||||
|
||||
#ifndef EON |
||||
spi_init(); |
||||
#endif |
||||
|
||||
#ifdef EON |
||||
// have to save power
|
||||
if (!is_grey_panda) { |
||||
set_esp_mode(ESP_DISABLED); |
||||
} |
||||
// only enter power save after the first cycle
|
||||
/*if (is_gpio_started()) {
|
||||
set_power_save_state(POWER_SAVE_STATUS_ENABLED); |
||||
}*/ |
||||
// interrupt on started line
|
||||
started_interrupt_init(); |
||||
#endif |
||||
|
||||
// 48mhz / 65536 ~= 732 / 732 = 1
|
||||
timer_init(TIM3, 732); |
||||
NVIC_EnableIRQ(TIM3_IRQn); |
||||
|
||||
#ifdef DEBUG |
||||
puts("DEBUG ENABLED\n"); |
||||
#endif |
||||
|
||||
puts("**** INTERRUPTS ON ****\n"); |
||||
enable_interrupts(); |
||||
|
||||
// LED should keep on blinking all the time
|
||||
uint64_t cnt = 0; |
||||
|
||||
for (cnt=0;;cnt++) { |
||||
if (power_save_status == POWER_SAVE_STATUS_DISABLED) { |
||||
int div_mode = ((usb_power_mode == USB_POWER_DCP) ? 4 : 1); |
||||
|
||||
// useful for debugging, fade breaks = panda is overloaded
|
||||
for (int div_mode_loop = 0; div_mode_loop < div_mode; div_mode_loop++) { |
||||
for (int fade = 0; fade < 1024; fade += 8) { |
||||
for (int i = 0; i < (128/div_mode); i++) { |
||||
set_led(LED_RED, 1); |
||||
if (fade < 512) { delay(fade); } else { delay(1024-fade); } |
||||
set_led(LED_RED, 0); |
||||
if (fade < 512) { delay(512-fade); } else { delay(fade-512); } |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
__WFI(); |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
@ -0,0 +1 @@ |
||||
obj/* |
@ -0,0 +1,67 @@ |
||||
# :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
|
||||
|
||||
include ../../common/version.mk |
||||
|
||||
obj/cert.h: ../../crypto/getcertheader.py |
||||
../../crypto/getcertheader.py ../../certs/debug.pub ../../certs/release.pub > $@
|
||||
|
||||
obj/main.o: main.c ../*.h |
||||
mkdir -p obj
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
obj/bootstub.o: ../bootstub.c ../*.h obj/gitversion.h obj/cert.h |
||||
mkdir -p obj
|
||||
mkdir -p ../obj
|
||||
cp obj/gitversion.h ../obj/gitversion.h
|
||||
cp obj/cert.h ../obj/cert.h
|
||||
$(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 @@ |
||||
#include "../config.h" |
||||
|
||||
#include "drivers/llcan.h" |
||||
#include "drivers/llgpio.h" |
||||
#include "drivers/clock.h" |
||||
#include "drivers/adc.h" |
||||
#include "drivers/dac.h" |
||||
#include "drivers/timer.h" |
||||
|
||||
#include "gpio.h" |
||||
#include "libc.h" |
||||
|
||||
#define CAN CAN1 |
||||
|
||||
//#define PEDAL_USB
|
||||
|
||||
#ifdef PEDAL_USB |
||||
#include "drivers/uart.h" |
||||
#include "drivers/usb.h" |
||||
#else |
||||
// no serial either
|
||||
void puts(const char *a) {} |
||||
void puth(unsigned int i) {} |
||||
#endif |
||||
|
||||
#define ENTER_BOOTLOADER_MAGIC 0xdeadbeef |
||||
uint32_t enter_bootloader_mode; |
||||
|
||||
void __initialize_hardware_early() { |
||||
early(); |
||||
} |
||||
|
||||
// ********************* serial debugging *********************
|
||||
|
||||
#ifdef PEDAL_USB |
||||
|
||||
void debug_ring_callback(uart_ring *ring) { |
||||
char rcv; |
||||
while (getc(ring, &rcv)) { |
||||
putc(ring, rcv); |
||||
} |
||||
} |
||||
|
||||
int usb_cb_ep1_in(uint8_t *usbdata, int len, bool hardwired) { return 0; } |
||||
void usb_cb_ep2_out(uint8_t *usbdata, int len, bool hardwired) {} |
||||
void usb_cb_ep3_out(uint8_t *usbdata, int len, bool hardwired) {} |
||||
void usb_cb_enumeration_complete() {} |
||||
|
||||
int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool 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 |
||||
|
||||
// ***************************** pedal can checksum *****************************
|
||||
|
||||
uint8_t pedal_checksum(uint8_t *dat, int len) { |
||||
uint8_t crc = 0xFF; |
||||
uint8_t poly = 0xD5; // standard crc8
|
||||
int i, j; |
||||
for (i = len - 1; i >= 0; i--) { |
||||
crc ^= dat[i]; |
||||
for (j = 0; j < 8; j++) { |
||||
if ((crc & 0x80) != 0) { |
||||
crc = (uint8_t)((crc << 1) ^ poly); |
||||
} |
||||
else { |
||||
crc <<= 1; |
||||
} |
||||
} |
||||
} |
||||
return crc; |
||||
} |
||||
|
||||
// ***************************** can port *****************************
|
||||
|
||||
// addresses to be used on CAN
|
||||
#define CAN_GAS_INPUT 0x200 |
||||
#define CAN_GAS_OUTPUT 0x201 |
||||
#define CAN_GAS_SIZE 6 |
||||
#define COUNTER_CYCLE 0xF |
||||
|
||||
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[8]; |
||||
uint8_t *rdlr = (uint8_t *)&CAN->sFIFOMailBox[0].RDLR; |
||||
uint8_t *rdhr = (uint8_t *)&CAN->sFIFOMailBox[0].RDHR; |
||||
for (int i=0; i<4; i++) { |
||||
dat[i] = rdlr[i]; |
||||
dat[i+4] = rdhr[i]; |
||||
} |
||||
uint16_t value_0 = (dat[0] << 8) | dat[1]; |
||||
uint16_t value_1 = (dat[2] << 8) | dat[3]; |
||||
uint8_t enable = (dat[4] >> 7) & 1; |
||||
uint8_t index = dat[4] & COUNTER_CYCLE; |
||||
if (pedal_checksum(dat, CAN_GAS_SIZE - 1) == dat[5]) { |
||||
if (((current_index + 1) & COUNTER_CYCLE) == 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; |
||||
llcan_clear_send(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 & 0xF) << 4 | pkt_idx; |
||||
dat[5] = pedal_checksum(dat, CAN_GAS_SIZE - 1); |
||||
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 &= COUNTER_CYCLE; |
||||
} 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); |
||||
} |
||||
|
||||
watchdog_feed(); |
||||
} |
||||
|
||||
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
|
||||
llcan_set_speed(CAN1, 5000, false, false); |
||||
llcan_init(CAN1); |
||||
|
||||
// 48mhz / 65536 ~= 732
|
||||
timer_init(TIM3, 15); |
||||
NVIC_EnableIRQ(TIM3_IRQn); |
||||
|
||||
watchdog_init(); |
||||
|
||||
puts("**** INTERRUPTS ON ****\n"); |
||||
__enable_irq(); |
||||
|
||||
// main pedal loop
|
||||
while (1) { |
||||
pedal(); |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,43 @@ |
||||
#define POWER_SAVE_STATUS_DISABLED 0 |
||||
#define POWER_SAVE_STATUS_ENABLED 1 |
||||
|
||||
int power_save_status = POWER_SAVE_STATUS_DISABLED; |
||||
|
||||
void set_power_save_state(int state) { |
||||
|
||||
bool is_valid_state = (state == POWER_SAVE_STATUS_ENABLED) || (state == POWER_SAVE_STATUS_DISABLED); |
||||
if (is_valid_state && (state != power_save_status)) { |
||||
bool enable = false; |
||||
if (state == POWER_SAVE_STATUS_ENABLED) { |
||||
puts("enable power savings\n"); |
||||
if (is_grey_panda) { |
||||
char UBLOX_SLEEP_MSG[] = "\xb5\x62\x06\x04\x04\x00\x01\x00\x08\x00\x17\x78"; |
||||
uart_ring *ur = get_ring_by_number(1); |
||||
for (unsigned int i = 0; i < sizeof(UBLOX_SLEEP_MSG) - 1; i++) while (!putc(ur, UBLOX_SLEEP_MSG[i])); |
||||
} |
||||
} else { |
||||
puts("disable power savings\n"); |
||||
if (is_grey_panda) { |
||||
char UBLOX_WAKE_MSG[] = "\xb5\x62\x06\x04\x04\x00\x01\x00\x09\x00\x18\x7a"; |
||||
uart_ring *ur = get_ring_by_number(1); |
||||
for (unsigned int i = 0; i < sizeof(UBLOX_WAKE_MSG) - 1; i++) while (!putc(ur, UBLOX_WAKE_MSG[i])); |
||||
} |
||||
enable = true; |
||||
} |
||||
|
||||
// turn on can
|
||||
set_can_enable(CAN1, enable); |
||||
set_can_enable(CAN2, enable); |
||||
set_can_enable(CAN3, enable); |
||||
|
||||
// turn on GMLAN
|
||||
set_gpio_output(GPIOB, 14, enable); |
||||
set_gpio_output(GPIOB, 15, enable); |
||||
|
||||
// turn on LIN
|
||||
set_gpio_output(GPIOB, 7, enable); |
||||
set_gpio_output(GPIOA, 14, enable); |
||||
|
||||
power_save_status = state; |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
#define PROVISION_CHUNK_LEN 0x20 |
||||
|
||||
// WiFi SSID = 0x0 - 0x10
|
||||
// WiFi password = 0x10 - 0x1C
|
||||
// SHA1 checksum = 0x1C - 0x20
|
||||
|
||||
void get_provision_chunk(uint8_t *resp) { |
||||
memcpy(resp, (void *)0x1fff79e0, PROVISION_CHUNK_LEN); |
||||
if (memcmp(resp, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 0x20) == 0) { |
||||
memcpy(resp, "unprovisioned\x00\x00\x00testing123\x00\x00\xa3\xa6\x99\xec", 0x20); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,215 @@ |
||||
// include first, needed by safety policies
|
||||
#include "safety_declarations.h" |
||||
// Include the actual safety policies.
|
||||
#include "safety/safety_defaults.h" |
||||
#include "safety/safety_honda.h" |
||||
#include "safety/safety_toyota.h" |
||||
#include "safety/safety_toyota_ipas.h" |
||||
#include "safety/safety_tesla.h" |
||||
#include "safety/safety_gm_ascm.h" |
||||
#include "safety/safety_gm.h" |
||||
#include "safety/safety_ford.h" |
||||
#include "safety/safety_cadillac.h" |
||||
#include "safety/safety_hyundai.h" |
||||
#include "safety/safety_chrysler.h" |
||||
#include "safety/safety_subaru.h" |
||||
#include "safety/safety_elm327.h" |
||||
|
||||
const safety_hooks *current_hooks = &nooutput_hooks; |
||||
|
||||
void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_push){ |
||||
current_hooks->rx(to_push); |
||||
} |
||||
|
||||
int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
return current_hooks->tx(to_send); |
||||
} |
||||
|
||||
int safety_tx_lin_hook(int lin_num, uint8_t *data, int len){ |
||||
return current_hooks->tx_lin(lin_num, data, len); |
||||
} |
||||
|
||||
// -1 = Disabled (Use GPIO to determine ignition)
|
||||
// 0 = Off (not started)
|
||||
// 1 = On (started)
|
||||
int safety_ignition_hook() { |
||||
return current_hooks->ignition(); |
||||
} |
||||
int safety_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
return current_hooks->fwd(bus_num, to_fwd); |
||||
} |
||||
|
||||
typedef struct { |
||||
uint16_t id; |
||||
const safety_hooks *hooks; |
||||
} safety_hook_config; |
||||
|
||||
#define SAFETY_NOOUTPUT 0 |
||||
#define SAFETY_HONDA 1 |
||||
#define SAFETY_TOYOTA 2 |
||||
#define SAFETY_GM 3 |
||||
#define SAFETY_HONDA_BOSCH 4 |
||||
#define SAFETY_FORD 5 |
||||
#define SAFETY_CADILLAC 6 |
||||
#define SAFETY_HYUNDAI 7 |
||||
#define SAFETY_TESLA 8 |
||||
#define SAFETY_CHRYSLER 9 |
||||
#define SAFETY_SUBARU 10 |
||||
#define SAFETY_GM_ASCM 0x1334 |
||||
#define SAFETY_TOYOTA_IPAS 0x1335 |
||||
#define SAFETY_ALLOUTPUT 0x1337 |
||||
#define SAFETY_ELM327 0xE327 |
||||
|
||||
const safety_hook_config safety_hook_registry[] = { |
||||
{SAFETY_NOOUTPUT, &nooutput_hooks}, |
||||
{SAFETY_HONDA, &honda_hooks}, |
||||
{SAFETY_HONDA_BOSCH, &honda_bosch_hooks}, |
||||
{SAFETY_TOYOTA, &toyota_hooks}, |
||||
{SAFETY_GM, &gm_hooks}, |
||||
{SAFETY_FORD, &ford_hooks}, |
||||
{SAFETY_CADILLAC, &cadillac_hooks}, |
||||
{SAFETY_HYUNDAI, &hyundai_hooks}, |
||||
{SAFETY_CHRYSLER, &chrysler_hooks}, |
||||
{SAFETY_SUBARU, &subaru_hooks}, |
||||
{SAFETY_TOYOTA_IPAS, &toyota_ipas_hooks}, |
||||
{SAFETY_GM_ASCM, &gm_ascm_hooks}, |
||||
{SAFETY_TESLA, &tesla_hooks}, |
||||
{SAFETY_ALLOUTPUT, &alloutput_hooks}, |
||||
{SAFETY_ELM327, &elm327_hooks}, |
||||
}; |
||||
|
||||
int safety_set_mode(uint16_t mode, int16_t param) { |
||||
int set_status = -1; // not set
|
||||
int hook_config_count = sizeof(safety_hook_registry) / sizeof(safety_hook_config); |
||||
for (int i = 0; i < hook_config_count; i++) { |
||||
if (safety_hook_registry[i].id == mode) { |
||||
current_hooks = safety_hook_registry[i].hooks; |
||||
set_status = 0; // set
|
||||
break; |
||||
} |
||||
} |
||||
if ((set_status == 0) && (current_hooks->init != NULL)) { |
||||
current_hooks->init(param); |
||||
} |
||||
return set_status; |
||||
} |
||||
|
||||
// compute the time elapsed (in microseconds) from 2 counter samples
|
||||
// case where ts < ts_last is ok: overflow is properly re-casted into uint32_t
|
||||
uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last) { |
||||
return ts - ts_last; |
||||
} |
||||
|
||||
// convert a trimmed integer to signed 32 bit int
|
||||
int to_signed(int d, int bits) { |
||||
int d_signed = d; |
||||
if (d >= (1 << MAX((bits - 1), 0))) { |
||||
d_signed = d - (1 << MAX(bits, 0)); |
||||
} |
||||
return d_signed; |
||||
} |
||||
|
||||
// given a new sample, update the smaple_t struct
|
||||
void update_sample(struct sample_t *sample, int sample_new) { |
||||
int sample_size = sizeof(sample->values) / sizeof(sample->values[0]); |
||||
for (int i = sample_size - 1; i > 0; i--) { |
||||
sample->values[i] = sample->values[i-1]; |
||||
} |
||||
sample->values[0] = sample_new; |
||||
|
||||
// get the minimum and maximum measured samples
|
||||
sample->min = sample->values[0]; |
||||
sample->max = sample->values[0]; |
||||
for (int i = 1; i < sample_size; i++) { |
||||
if (sample->values[i] < sample->min) { |
||||
sample->min = sample->values[i]; |
||||
} |
||||
if (sample->values[i] > sample->max) { |
||||
sample->max = sample->values[i]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
bool max_limit_check(int val, const int MAX_VAL, const int MIN_VAL) { |
||||
return (val > MAX_VAL) || (val < MIN_VAL); |
||||
} |
||||
|
||||
// check that commanded value isn't too far from measured
|
||||
bool dist_to_meas_check(int val, int val_last, struct sample_t *val_meas, |
||||
const int MAX_RATE_UP, const int MAX_RATE_DOWN, const int MAX_ERROR) { |
||||
|
||||
// *** val rate limit check ***
|
||||
int highest_allowed_rl = MAX(val_last, 0) + MAX_RATE_UP; |
||||
int lowest_allowed_rl = MIN(val_last, 0) - MAX_RATE_UP; |
||||
|
||||
// if we've exceeded the meas val, we must start moving toward 0
|
||||
int highest_allowed = MIN(highest_allowed_rl, MAX(val_last - MAX_RATE_DOWN, MAX(val_meas->max, 0) + MAX_ERROR)); |
||||
int lowest_allowed = MAX(lowest_allowed_rl, MIN(val_last + MAX_RATE_DOWN, MIN(val_meas->min, 0) - MAX_ERROR)); |
||||
|
||||
// check for violation
|
||||
return (val < lowest_allowed) || (val > highest_allowed); |
||||
} |
||||
|
||||
// check that commanded value isn't fighting against driver
|
||||
bool driver_limit_check(int val, int val_last, struct sample_t *val_driver, |
||||
const int MAX_VAL, const int MAX_RATE_UP, const int MAX_RATE_DOWN, |
||||
const int MAX_ALLOWANCE, const int DRIVER_FACTOR) { |
||||
|
||||
int highest_allowed_rl = MAX(val_last, 0) + MAX_RATE_UP; |
||||
int lowest_allowed_rl = MIN(val_last, 0) - MAX_RATE_UP; |
||||
|
||||
int driver_max_limit = MAX_VAL + (MAX_ALLOWANCE + val_driver->max) * DRIVER_FACTOR; |
||||
int driver_min_limit = -MAX_VAL + (-MAX_ALLOWANCE + val_driver->min) * DRIVER_FACTOR; |
||||
|
||||
// if we've exceeded the applied torque, we must start moving toward 0
|
||||
int highest_allowed = MIN(highest_allowed_rl, MAX(val_last - MAX_RATE_DOWN, |
||||
MAX(driver_max_limit, 0))); |
||||
int lowest_allowed = MAX(lowest_allowed_rl, MIN(val_last + MAX_RATE_DOWN, |
||||
MIN(driver_min_limit, 0))); |
||||
|
||||
// check for violation
|
||||
return (val < lowest_allowed) || (val > highest_allowed); |
||||
} |
||||
|
||||
|
||||
// real time check, mainly used for steer torque rate limiter
|
||||
bool rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA) { |
||||
|
||||
// *** torque real time rate limit check ***
|
||||
int highest_val = MAX(val_last, 0) + MAX_RT_DELTA; |
||||
int lowest_val = MIN(val_last, 0) - MAX_RT_DELTA; |
||||
|
||||
// check for violation
|
||||
return (val < lowest_val) || (val > highest_val); |
||||
} |
||||
|
||||
|
||||
// interp function that holds extreme values
|
||||
float interpolate(struct lookup_t xy, float x) { |
||||
|
||||
int size = sizeof(xy.x) / sizeof(xy.x[0]); |
||||
float ret = xy.y[size - 1]; // default output is last point
|
||||
|
||||
// x is lower than the first point in the x array. Return the first point
|
||||
if (x <= xy.x[0]) { |
||||
ret = xy.y[0]; |
||||
|
||||
} else { |
||||
// find the index such that (xy.x[i] <= x < xy.x[i+1]) and linearly interp
|
||||
for (int i=0; i < (size - 1); i++) { |
||||
if (x < xy.x[i+1]) { |
||||
float x0 = xy.x[i]; |
||||
float y0 = xy.y[i]; |
||||
float dx = xy.x[i+1] - x0; |
||||
float dy = xy.y[i+1] - y0; |
||||
// dx should not be zero as xy.x is supposed ot be monotonic
|
||||
if (dx <= 0.) { |
||||
dx = 0.0001; |
||||
} |
||||
ret = (dy * (x - x0) / dx) + y0; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
return ret; |
||||
} |
@ -0,0 +1,134 @@ |
||||
#define CADILLAC_TORQUE_MSG_N 4 // 4 torque messages: 0x151, 0x152, 0x153, 0x154
|
||||
|
||||
const int CADILLAC_MAX_STEER = 150; // 1s
|
||||
// real time torque limit to prevent controls spamming
|
||||
// the real time limit is 1500/sec
|
||||
const int CADILLAC_MAX_RT_DELTA = 75; // max delta torque allowed for real time checks
|
||||
const uint32_t CADILLAC_RT_INTERVAL = 250000; // 250ms between real time checks
|
||||
const int CADILLAC_MAX_RATE_UP = 2; |
||||
const int CADILLAC_MAX_RATE_DOWN = 5; |
||||
const int CADILLAC_DRIVER_TORQUE_ALLOWANCE = 50; |
||||
const int CADILLAC_DRIVER_TORQUE_FACTOR = 4; |
||||
|
||||
int cadillac_ign = 0; |
||||
int cadillac_cruise_engaged_last = 0; |
||||
int cadillac_rt_torque_last = 0; |
||||
const int cadillac_torque_msgs_n = 4; |
||||
int cadillac_desired_torque_last[CADILLAC_TORQUE_MSG_N] = {0}; |
||||
uint32_t cadillac_ts_last = 0; |
||||
int cadillac_supercruise_on = 0; |
||||
struct sample_t cadillac_torque_driver; // last few driver torques measured
|
||||
|
||||
int cadillac_get_torque_idx(int addr, int array_size) { |
||||
return MIN(MAX(addr - 0x151, 0), array_size); // 0x151 is id 0, 0x152 is id 1 and so on...
|
||||
} |
||||
|
||||
static void cadillac_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
int bus = GET_BUS(to_push); |
||||
int addr = GET_ADDR(to_push); |
||||
|
||||
if (addr == 356) { |
||||
int torque_driver_new = ((to_push->RDLR & 0x7) << 8) | ((to_push->RDLR >> 8) & 0xFF); |
||||
torque_driver_new = to_signed(torque_driver_new, 11); |
||||
// update array of samples
|
||||
update_sample(&cadillac_torque_driver, torque_driver_new); |
||||
} |
||||
|
||||
// this message isn't all zeros when ignition is on
|
||||
if ((addr == 0x160) && (bus == 0)) { |
||||
cadillac_ign = to_push->RDLR > 0; |
||||
} |
||||
|
||||
// enter controls on rising edge of ACC, exit controls on ACC off
|
||||
if ((addr == 0x370) && (bus == 0)) { |
||||
int cruise_engaged = to_push->RDLR & 0x800000; // bit 23
|
||||
if (cruise_engaged && !cadillac_cruise_engaged_last) { |
||||
controls_allowed = 1; |
||||
} |
||||
if (!cruise_engaged) { |
||||
controls_allowed = 0; |
||||
} |
||||
cadillac_cruise_engaged_last = cruise_engaged; |
||||
} |
||||
|
||||
// know supercruise mode and block openpilot msgs if on
|
||||
if ((addr == 0x152) || (addr == 0x154)) { |
||||
cadillac_supercruise_on = (to_push->RDHR>>4) & 0x1; |
||||
} |
||||
} |
||||
|
||||
static int cadillac_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
int tx = 1; |
||||
int addr = GET_ADDR(to_send); |
||||
|
||||
// steer cmd checks
|
||||
if ((addr == 0x151) || (addr == 0x152) || (addr == 0x153) || (addr == 0x154)) { |
||||
int desired_torque = ((to_send->RDLR & 0x3f) << 8) + ((to_send->RDLR & 0xff00) >> 8); |
||||
int violation = 0; |
||||
uint32_t ts = TIM2->CNT; |
||||
int idx = cadillac_get_torque_idx(addr, CADILLAC_TORQUE_MSG_N); |
||||
desired_torque = to_signed(desired_torque, 14); |
||||
|
||||
if (controls_allowed) { |
||||
|
||||
// *** global torque limit check ***
|
||||
violation |= max_limit_check(desired_torque, CADILLAC_MAX_STEER, -CADILLAC_MAX_STEER); |
||||
|
||||
// *** torque rate limit check ***
|
||||
int desired_torque_last = cadillac_desired_torque_last[idx]; |
||||
violation |= driver_limit_check(desired_torque, desired_torque_last, &cadillac_torque_driver, |
||||
CADILLAC_MAX_STEER, CADILLAC_MAX_RATE_UP, CADILLAC_MAX_RATE_DOWN, |
||||
CADILLAC_DRIVER_TORQUE_ALLOWANCE, CADILLAC_DRIVER_TORQUE_FACTOR); |
||||
|
||||
// used next time
|
||||
cadillac_desired_torque_last[idx] = desired_torque; |
||||
|
||||
// *** torque real time rate limit check ***
|
||||
violation |= rt_rate_limit_check(desired_torque, cadillac_rt_torque_last, CADILLAC_MAX_RT_DELTA); |
||||
|
||||
// every RT_INTERVAL set the new limits
|
||||
uint32_t ts_elapsed = get_ts_elapsed(ts, cadillac_ts_last); |
||||
if (ts_elapsed > CADILLAC_RT_INTERVAL) { |
||||
cadillac_rt_torque_last = desired_torque; |
||||
cadillac_ts_last = ts; |
||||
} |
||||
} |
||||
|
||||
// no torque if controls is not allowed
|
||||
if (!controls_allowed && (desired_torque != 0)) { |
||||
violation = 1; |
||||
} |
||||
|
||||
// reset to 0 if either controls is not allowed or there's a violation
|
||||
if (violation || !controls_allowed) { |
||||
cadillac_desired_torque_last[idx] = 0; |
||||
cadillac_rt_torque_last = 0; |
||||
cadillac_ts_last = ts; |
||||
} |
||||
|
||||
if (violation || cadillac_supercruise_on) { |
||||
tx = 0; |
||||
} |
||||
|
||||
} |
||||
return tx; |
||||
} |
||||
|
||||
static void cadillac_init(int16_t param) { |
||||
UNUSED(param); |
||||
controls_allowed = 0; |
||||
cadillac_ign = 0; |
||||
} |
||||
|
||||
static int cadillac_ign_hook(void) { |
||||
return cadillac_ign; |
||||
} |
||||
|
||||
const safety_hooks cadillac_hooks = { |
||||
.init = cadillac_init, |
||||
.rx = cadillac_rx_hook, |
||||
.tx = cadillac_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = cadillac_ign_hook, |
||||
.fwd = default_fwd_hook, |
||||
}; |
@ -0,0 +1,143 @@ |
||||
const int CHRYSLER_MAX_STEER = 261; |
||||
const int CHRYSLER_MAX_RT_DELTA = 112; // max delta torque allowed for real time checks
|
||||
const uint32_t CHRYSLER_RT_INTERVAL = 250000; // 250ms between real time checks
|
||||
const int CHRYSLER_MAX_RATE_UP = 3; |
||||
const int CHRYSLER_MAX_RATE_DOWN = 3; |
||||
const int CHRYSLER_MAX_TORQUE_ERROR = 80; // max torque cmd in excess of torque motor
|
||||
|
||||
bool chrysler_camera_detected = 0; // is giraffe switch 2 high?
|
||||
int chrysler_rt_torque_last = 0; |
||||
int chrysler_desired_torque_last = 0; |
||||
int chrysler_cruise_engaged_last = 0; |
||||
uint32_t chrysler_ts_last = 0; |
||||
struct sample_t chrysler_torque_meas; // last few torques measured
|
||||
|
||||
static void chrysler_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
int bus = GET_BUS(to_push); |
||||
int addr = GET_ADDR(to_push); |
||||
|
||||
// Measured eps torque
|
||||
if (addr == 544) { |
||||
uint32_t rdhr = to_push->RDHR; |
||||
int torque_meas_new = ((rdhr & 0x7U) << 8) + ((rdhr & 0xFF00U) >> 8) - 1024U; |
||||
|
||||
// update array of samples
|
||||
update_sample(&chrysler_torque_meas, torque_meas_new); |
||||
} |
||||
|
||||
// enter controls on rising edge of ACC, exit controls on ACC off
|
||||
if (addr == 0x1F4) { |
||||
int cruise_engaged = ((to_push->RDLR & 0x380000) >> 19) == 7; |
||||
if (cruise_engaged && !chrysler_cruise_engaged_last) { |
||||
controls_allowed = 1; |
||||
} |
||||
if (!cruise_engaged) { |
||||
controls_allowed = 0; |
||||
} |
||||
chrysler_cruise_engaged_last = cruise_engaged; |
||||
} |
||||
|
||||
// check if stock camera ECU is still online
|
||||
if ((bus == 0) && (addr == 0x292)) { |
||||
chrysler_camera_detected = 1; |
||||
controls_allowed = 0; |
||||
} |
||||
} |
||||
|
||||
static int chrysler_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
|
||||
int tx = 1; |
||||
|
||||
// If camera is on bus 0, then nothing can be sent
|
||||
if (chrysler_camera_detected) { |
||||
tx = 0; |
||||
} |
||||
|
||||
int addr = GET_ADDR(to_send); |
||||
|
||||
// LKA STEER
|
||||
if (addr == 0x292) { |
||||
uint32_t rdlr = to_send->RDLR; |
||||
int desired_torque = ((rdlr & 0x7U) << 8) + ((rdlr & 0xFF00U) >> 8) - 1024U; |
||||
uint32_t ts = TIM2->CNT; |
||||
bool violation = 0; |
||||
|
||||
if (controls_allowed) { |
||||
|
||||
// *** global torque limit check ***
|
||||
violation |= max_limit_check(desired_torque, CHRYSLER_MAX_STEER, -CHRYSLER_MAX_STEER); |
||||
|
||||
// *** torque rate limit check ***
|
||||
violation |= dist_to_meas_check(desired_torque, chrysler_desired_torque_last, |
||||
&chrysler_torque_meas, CHRYSLER_MAX_RATE_UP, CHRYSLER_MAX_RATE_DOWN, CHRYSLER_MAX_TORQUE_ERROR); |
||||
|
||||
// used next time
|
||||
chrysler_desired_torque_last = desired_torque; |
||||
|
||||
// *** torque real time rate limit check ***
|
||||
violation |= rt_rate_limit_check(desired_torque, chrysler_rt_torque_last, CHRYSLER_MAX_RT_DELTA); |
||||
|
||||
// every RT_INTERVAL set the new limits
|
||||
uint32_t ts_elapsed = get_ts_elapsed(ts, chrysler_ts_last); |
||||
if (ts_elapsed > CHRYSLER_RT_INTERVAL) { |
||||
chrysler_rt_torque_last = desired_torque; |
||||
chrysler_ts_last = ts; |
||||
} |
||||
} |
||||
|
||||
// no torque if controls is not allowed
|
||||
if (!controls_allowed && (desired_torque != 0)) { |
||||
violation = 1; |
||||
} |
||||
|
||||
// reset to 0 if either controls is not allowed or there's a violation
|
||||
if (violation || !controls_allowed) { |
||||
chrysler_desired_torque_last = 0; |
||||
chrysler_rt_torque_last = 0; |
||||
chrysler_ts_last = ts; |
||||
} |
||||
|
||||
if (violation) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
|
||||
// FORCE CANCEL: safety check only relevant when spamming the cancel button.
|
||||
// ensuring that only the cancel button press is sent when controls are off.
|
||||
// This avoids unintended engagements while still allowing resume spam
|
||||
// TODO: fix bug preventing the button msg to be fwd'd on bus 2
|
||||
|
||||
// 1 allows the message through
|
||||
return tx; |
||||
} |
||||
|
||||
static void chrysler_init(int16_t param) { |
||||
UNUSED(param); |
||||
controls_allowed = 0; |
||||
chrysler_camera_detected = 0; |
||||
} |
||||
|
||||
static int chrysler_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
|
||||
int bus_fwd = -1; |
||||
int addr = GET_ADDR(to_fwd); |
||||
// forward CAN 0 -> 2 so stock LKAS camera sees messages
|
||||
if ((bus_num == 0) && !chrysler_camera_detected) { |
||||
bus_fwd = 2; |
||||
} |
||||
// forward all messages from camera except LKAS_COMMAND and LKAS_HUD
|
||||
if ((bus_num == 2) && !chrysler_camera_detected && (addr != 658) && (addr != 678)) { |
||||
bus_fwd = 0; |
||||
} |
||||
return bus_fwd; |
||||
} |
||||
|
||||
|
||||
const safety_hooks chrysler_hooks = { |
||||
.init = chrysler_init, |
||||
.rx = chrysler_rx_hook, |
||||
.tx = chrysler_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = chrysler_fwd_hook, |
||||
}; |
@ -0,0 +1,69 @@ |
||||
void default_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
UNUSED(to_push); |
||||
} |
||||
|
||||
int default_ign_hook(void) { |
||||
return -1; // use GPIO to determine ignition
|
||||
} |
||||
|
||||
// *** no output safety mode ***
|
||||
|
||||
static void nooutput_init(int16_t param) { |
||||
UNUSED(param); |
||||
controls_allowed = 0; |
||||
} |
||||
|
||||
static int nooutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
UNUSED(to_send); |
||||
return false; |
||||
} |
||||
|
||||
static int nooutput_tx_lin_hook(int lin_num, uint8_t *data, int len) { |
||||
UNUSED(lin_num); |
||||
UNUSED(data); |
||||
UNUSED(len); |
||||
return false; |
||||
} |
||||
|
||||
static int default_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
UNUSED(bus_num); |
||||
UNUSED(to_fwd); |
||||
return -1; |
||||
} |
||||
|
||||
const safety_hooks nooutput_hooks = { |
||||
.init = nooutput_init, |
||||
.rx = default_rx_hook, |
||||
.tx = nooutput_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = default_fwd_hook, |
||||
}; |
||||
|
||||
// *** all output safety mode ***
|
||||
|
||||
static void alloutput_init(int16_t param) { |
||||
UNUSED(param); |
||||
controls_allowed = 1; |
||||
} |
||||
|
||||
static int alloutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
UNUSED(to_send); |
||||
return true; |
||||
} |
||||
|
||||
static int alloutput_tx_lin_hook(int lin_num, uint8_t *data, int len) { |
||||
UNUSED(lin_num); |
||||
UNUSED(data); |
||||
UNUSED(len); |
||||
return true; |
||||
} |
||||
|
||||
const safety_hooks alloutput_hooks = { |
||||
.init = alloutput_init, |
||||
.rx = default_rx_hook, |
||||
.tx = alloutput_tx_hook, |
||||
.tx_lin = alloutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = default_fwd_hook, |
||||
}; |
@ -0,0 +1,49 @@ |
||||
static int elm327_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
|
||||
int tx = 1; |
||||
int bus = GET_BUS(to_send); |
||||
int addr = GET_ADDR(to_send); |
||||
int len = GET_LEN(to_send); |
||||
|
||||
//All ELM traffic must appear on CAN0
|
||||
if (bus != 0) { |
||||
tx = 0; |
||||
} |
||||
|
||||
//All ISO 15765-4 messages must be 8 bytes long
|
||||
if (len != 8) { |
||||
tx = 0; |
||||
} |
||||
|
||||
//Check valid 29 bit send addresses for ISO 15765-4
|
||||
//Check valid 11 bit send addresses for ISO 15765-4
|
||||
if ((addr != 0x18DB33F1) && ((addr & 0x1FFF00FF) != 0x18DA00F1) && |
||||
((addr != 0x7DF) && ((addr & 0x7F8) != 0x7E0))) { |
||||
tx = 0; |
||||
} |
||||
return tx; |
||||
} |
||||
|
||||
static int elm327_tx_lin_hook(int lin_num, uint8_t *data, int len) { |
||||
int tx = 1; |
||||
if (lin_num != 0) { |
||||
tx = 0; //Only operate on LIN 0, aka serial 2
|
||||
} |
||||
if ((len < 5) || (len > 11)) { |
||||
tx = 0; //Valid KWP size
|
||||
} |
||||
if (!(((data[0] & 0xF8U) == 0xC0U) && ((data[0] & 0x07U) != 0U) && |
||||
(data[1] == 0x33U) && (data[2] == 0xF1U))) { |
||||
tx = 0; //Bad msg
|
||||
} |
||||
return tx; |
||||
} |
||||
|
||||
const safety_hooks elm327_hooks = { |
||||
.init = nooutput_init, |
||||
.rx = default_rx_hook, |
||||
.tx = elm327_tx_hook, |
||||
.tx_lin = elm327_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = default_fwd_hook, |
||||
}; |
@ -0,0 +1,100 @@ |
||||
// board enforces
|
||||
// in-state
|
||||
// accel set/resume
|
||||
// out-state
|
||||
// cancel button
|
||||
// accel rising edge
|
||||
// brake rising edge
|
||||
// brake > 0mph
|
||||
|
||||
int ford_brake_prev = 0; |
||||
int ford_gas_prev = 0; |
||||
int ford_is_moving = 0; |
||||
|
||||
static void ford_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
|
||||
int addr = GET_ADDR(to_push); |
||||
|
||||
if (addr == 0x217) { |
||||
// wheel speeds are 14 bits every 16
|
||||
ford_is_moving = 0xFCFF & (to_push->RDLR | (to_push->RDLR >> 16) | |
||||
to_push->RDHR | (to_push->RDHR >> 16)); |
||||
} |
||||
|
||||
// state machine to enter and exit controls
|
||||
if (addr == 0x83) { |
||||
bool cancel = (to_push->RDLR >> 8) & 0x1; |
||||
bool set_or_resume = (to_push->RDLR >> 28) & 0x3; |
||||
if (cancel) { |
||||
controls_allowed = 0; |
||||
} |
||||
if (set_or_resume) { |
||||
controls_allowed = 1; |
||||
} |
||||
} |
||||
|
||||
// exit controls on rising edge of brake press or on brake press when
|
||||
// speed > 0
|
||||
if (addr == 0x165) { |
||||
int brake = to_push->RDLR & 0x20; |
||||
if (brake && (!(ford_brake_prev) || ford_is_moving)) { |
||||
controls_allowed = 0; |
||||
} |
||||
ford_brake_prev = brake; |
||||
} |
||||
|
||||
// exit controls on rising edge of gas press
|
||||
if (addr == 0x204) { |
||||
int gas = to_push->RDLR & 0xFF03; |
||||
if (gas && !(ford_gas_prev)) { |
||||
controls_allowed = 0; |
||||
} |
||||
ford_gas_prev = gas; |
||||
} |
||||
} |
||||
|
||||
// all commands: just steering
|
||||
// if controls_allowed and no pedals pressed
|
||||
// allow all commands up to limit
|
||||
// else
|
||||
// block all commands that produce actuation
|
||||
|
||||
static int ford_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
|
||||
int tx = 1; |
||||
// disallow actuator commands if gas or brake (with vehicle moving) are pressed
|
||||
// and the the latching controls_allowed flag is True
|
||||
int pedal_pressed = ford_gas_prev || (ford_brake_prev && ford_is_moving); |
||||
bool current_controls_allowed = controls_allowed && !(pedal_pressed); |
||||
int addr = GET_ADDR(to_send); |
||||
|
||||
// STEER: safety check
|
||||
if (addr == 0x3CA) { |
||||
if (!current_controls_allowed) { |
||||
// bits 7-4 need to be 0xF to disallow lkas commands
|
||||
if (((to_send->RDLR >> 4) & 0xF) != 0xF) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// FORCE CANCEL: safety check only relevant when spamming the cancel button
|
||||
// ensuring that set and resume aren't sent
|
||||
if (addr == 0x83) { |
||||
if (((to_send->RDLR >> 28) & 0x3) != 0) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
|
||||
// 1 allows the message through
|
||||
return tx; |
||||
} |
||||
|
||||
const safety_hooks ford_hooks = { |
||||
.init = nooutput_init, |
||||
.rx = ford_rx_hook, |
||||
.tx = ford_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = default_fwd_hook, |
||||
}; |
@ -0,0 +1,244 @@ |
||||
// board enforces
|
||||
// in-state
|
||||
// accel set/resume
|
||||
// out-state
|
||||
// cancel button
|
||||
// regen paddle
|
||||
// accel rising edge
|
||||
// brake rising edge
|
||||
// brake > 0mph
|
||||
|
||||
const int GM_MAX_STEER = 300; |
||||
const int GM_MAX_RT_DELTA = 128; // max delta torque allowed for real time checks
|
||||
const uint32_t GM_RT_INTERVAL = 250000; // 250ms between real time checks
|
||||
const int GM_MAX_RATE_UP = 7; |
||||
const int GM_MAX_RATE_DOWN = 17; |
||||
const int GM_DRIVER_TORQUE_ALLOWANCE = 50; |
||||
const int GM_DRIVER_TORQUE_FACTOR = 4; |
||||
const int GM_MAX_GAS = 3072; |
||||
const int GM_MAX_REGEN = 1404; |
||||
const int GM_MAX_BRAKE = 350; |
||||
|
||||
int gm_brake_prev = 0; |
||||
int gm_gas_prev = 0; |
||||
int gm_speed = 0; |
||||
// silence everything if stock car control ECUs are still online
|
||||
bool gm_ascm_detected = 0; |
||||
bool gm_ignition_started = 0; |
||||
int gm_rt_torque_last = 0; |
||||
int gm_desired_torque_last = 0; |
||||
uint32_t gm_ts_last = 0; |
||||
struct sample_t gm_torque_driver; // last few driver torques measured
|
||||
|
||||
static void gm_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
int bus_number = GET_BUS(to_push); |
||||
int addr = GET_ADDR(to_push); |
||||
|
||||
if (addr == 388) { |
||||
int torque_driver_new = (((to_push->RDHR >> 16) & 0x7) << 8) | ((to_push->RDHR >> 24) & 0xFF); |
||||
torque_driver_new = to_signed(torque_driver_new, 11); |
||||
// update array of samples
|
||||
update_sample(&gm_torque_driver, torque_driver_new); |
||||
} |
||||
|
||||
if ((addr == 0x1F1) && (bus_number == 0)) { |
||||
//Bit 5 should be ignition "on"
|
||||
//Backup plan is Bit 2 (accessory power)
|
||||
bool ign = ((to_push->RDLR) & 0x20) != 0; |
||||
gm_ignition_started = ign; |
||||
} |
||||
|
||||
// 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 ASCM or LKA camera are online
|
||||
// on powertrain bus.
|
||||
// 384 = ASCMLKASteeringCmd
|
||||
// 715 = ASCMGasRegenCmd
|
||||
if ((bus_number == 0) && ((addr == 384) || (addr == 715))) { |
||||
gm_ascm_detected = 1; |
||||
controls_allowed = 0; |
||||
} |
||||
|
||||
// ACC steering wheel buttons
|
||||
if (addr == 481) { |
||||
int button = (to_push->RDHR >> 12) & 0x7; |
||||
switch (button) { |
||||
case 2: // resume
|
||||
case 3: // set
|
||||
controls_allowed = 1; |
||||
break; |
||||
case 6: // cancel
|
||||
controls_allowed = 0; |
||||
break; |
||||
default: |
||||
break; // any other button is irrelevant
|
||||
} |
||||
} |
||||
|
||||
// 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 && long_controls_allowed) { |
||||
controls_allowed = 0; |
||||
} |
||||
gm_gas_prev = gas; |
||||
} |
||||
|
||||
// exit controls on regen paddle
|
||||
if (addr == 189) { |
||||
bool 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) { |
||||
|
||||
int tx = 1; |
||||
|
||||
// There can be only one! (ASCM)
|
||||
if (gm_ascm_detected) { |
||||
tx = 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); |
||||
bool current_controls_allowed = controls_allowed && !pedal_pressed; |
||||
|
||||
int addr = GET_ADDR(to_send); |
||||
|
||||
// BRAKE: safety check
|
||||
if (addr == 789) { |
||||
uint32_t rdlr = to_send->RDLR; |
||||
int brake = ((rdlr & 0xFU) << 8) + ((rdlr & 0xFF00U) >> 8); |
||||
brake = (0x1000 - brake) & 0xFFF; |
||||
if (!current_controls_allowed || !long_controls_allowed) { |
||||
if (brake != 0) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
if (brake > GM_MAX_BRAKE) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
|
||||
// LKA STEER: safety check
|
||||
if (addr == 384) { |
||||
uint32_t rdlr = to_send->RDLR; |
||||
int desired_torque = ((rdlr & 0x7U) << 8) + ((rdlr & 0xFF00U) >> 8); |
||||
uint32_t ts = TIM2->CNT; |
||||
bool violation = 0; |
||||
desired_torque = to_signed(desired_torque, 11); |
||||
|
||||
if (current_controls_allowed) { |
||||
|
||||
// *** global torque limit check ***
|
||||
violation |= max_limit_check(desired_torque, GM_MAX_STEER, -GM_MAX_STEER); |
||||
|
||||
// *** torque rate limit check ***
|
||||
violation |= driver_limit_check(desired_torque, gm_desired_torque_last, &gm_torque_driver, |
||||
GM_MAX_STEER, GM_MAX_RATE_UP, GM_MAX_RATE_DOWN, |
||||
GM_DRIVER_TORQUE_ALLOWANCE, GM_DRIVER_TORQUE_FACTOR); |
||||
|
||||
// used next time
|
||||
gm_desired_torque_last = desired_torque; |
||||
|
||||
// *** torque real time rate limit check ***
|
||||
violation |= rt_rate_limit_check(desired_torque, gm_rt_torque_last, GM_MAX_RT_DELTA); |
||||
|
||||
// every RT_INTERVAL set the new limits
|
||||
uint32_t ts_elapsed = get_ts_elapsed(ts, gm_ts_last); |
||||
if (ts_elapsed > GM_RT_INTERVAL) { |
||||
gm_rt_torque_last = desired_torque; |
||||
gm_ts_last = ts; |
||||
} |
||||
} |
||||
|
||||
// no torque if controls is not allowed
|
||||
if (!current_controls_allowed && (desired_torque != 0)) { |
||||
violation = 1; |
||||
} |
||||
|
||||
// reset to 0 if either controls is not allowed or there's a violation
|
||||
if (violation || !current_controls_allowed) { |
||||
gm_desired_torque_last = 0; |
||||
gm_rt_torque_last = 0; |
||||
gm_ts_last = ts; |
||||
} |
||||
|
||||
if (violation) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
|
||||
// PARK ASSIST STEER: unlimited torque, no thanks
|
||||
if (addr == 823) { |
||||
tx = 0; |
||||
} |
||||
|
||||
// GAS/REGEN: safety check
|
||||
if (addr == 715) { |
||||
uint32_t rdlr = to_send->RDLR; |
||||
int gas_regen = ((rdlr & 0x7F0000U) >> 11) + ((rdlr & 0xF8000000U) >> 27); |
||||
// Disabled message is !engaed with gas
|
||||
// value that corresponds to max regen.
|
||||
if (!current_controls_allowed || !long_controls_allowed) { |
||||
bool apply = (rdlr & 1U) != 0U; |
||||
if (apply || (gas_regen != GM_MAX_REGEN)) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
if (gas_regen > GM_MAX_GAS) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
|
||||
// 1 allows the message through
|
||||
return tx; |
||||
} |
||||
|
||||
static void gm_init(int16_t param) { |
||||
UNUSED(param); |
||||
controls_allowed = 0; |
||||
gm_ignition_started = 0; |
||||
} |
||||
|
||||
static int gm_ign_hook(void) { |
||||
return gm_ignition_started; |
||||
} |
||||
|
||||
const safety_hooks gm_hooks = { |
||||
.init = gm_init, |
||||
.rx = gm_rx_hook, |
||||
.tx = gm_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = gm_ign_hook, |
||||
.fwd = default_fwd_hook, |
||||
}; |
@ -0,0 +1,45 @@ |
||||
// BUS 0 is on the LKAS module (ASCM) side
|
||||
// BUS 2 is on the actuator (EPS) side
|
||||
|
||||
static int gm_ascm_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
|
||||
int bus_fwd = -1; |
||||
|
||||
if (bus_num == 0) { |
||||
int addr = GET_ADDR(to_fwd); |
||||
bus_fwd = 2; |
||||
// do not propagate lkas messages from ascm to actuators, unless supercruise is on
|
||||
// block 0x152 and 0x154, which are the lkas command from ASCM1 and ASCM2
|
||||
// block 0x315 and 0x2cb, which are the brake and accel commands from ASCM1
|
||||
//if ((addr == 0x152) || (addr == 0x154) || (addr == 0x315) || (addr == 0x2cb)) {
|
||||
if ((addr == 0x152) || (addr == 0x154)) { |
||||
int supercruise_on = (to_fwd->RDHR >> 4) & 0x1; // bit 36
|
||||
if (!supercruise_on) { |
||||
bus_fwd = -1; |
||||
} |
||||
} |
||||
if ((addr == 0x151) || (addr == 0x153) || (addr == 0x314)) { |
||||
// on the chassis bus, the OBDII port is on the module side, so we need to read
|
||||
// the lkas messages sent by openpilot (put on unused 0x151 ane 0x153 addrs) and send it to
|
||||
// the actuator as 0x152 and 0x154
|
||||
uint32_t fwd_addr = addr + 1; |
||||
to_fwd->RIR = (fwd_addr << 21) | (to_fwd->RIR & 0x1fffff); |
||||
} |
||||
} |
||||
|
||||
if (bus_num == 2) { |
||||
bus_fwd = 0; |
||||
} |
||||
|
||||
return bus_fwd; |
||||
} |
||||
|
||||
const safety_hooks gm_ascm_hooks = { |
||||
.init = nooutput_init, |
||||
.rx = default_rx_hook, |
||||
.tx = alloutput_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = gm_ascm_fwd_hook, |
||||
}; |
||||
|
@ -0,0 +1,220 @@ |
||||
// board enforces
|
||||
// in-state
|
||||
// accel set/resume
|
||||
// out-state
|
||||
// cancel button
|
||||
// accel rising edge
|
||||
// brake rising edge
|
||||
// brake > 0mph
|
||||
|
||||
const int HONDA_GAS_INTERCEPTOR_THRESHOLD = 328; // ratio between offset and gain from dbc file
|
||||
int honda_brake_prev = 0; |
||||
int honda_gas_prev = 0; |
||||
int honda_ego_speed = 0; |
||||
bool honda_bosch_hardware = false; |
||||
bool honda_alt_brake_msg = false; |
||||
|
||||
static void honda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
|
||||
int addr = GET_ADDR(to_push); |
||||
int len = GET_LEN(to_push); |
||||
|
||||
// sample speed
|
||||
if (addr == 0x158) { |
||||
// first 2 bytes
|
||||
honda_ego_speed = to_push->RDLR & 0xFFFF; |
||||
} |
||||
|
||||
// state machine to enter and exit controls
|
||||
// 0x1A6 for the ILX, 0x296 for the Civic Touring
|
||||
if ((addr == 0x1A6) || (addr == 0x296)) { |
||||
int button = (to_push->RDLR & 0xE0) >> 5; |
||||
switch (button) { |
||||
case 2: // cancel
|
||||
controls_allowed = 0; |
||||
break; |
||||
case 3: // set
|
||||
case 4: // resume
|
||||
controls_allowed = 1; |
||||
break; |
||||
default: |
||||
break; // any other button is irrelevant
|
||||
} |
||||
} |
||||
|
||||
// user brake signal on 0x17C reports applied brake from computer brake on accord
|
||||
// and crv, which prevents the usual brake safety from working correctly. these
|
||||
// cars have a signal on 0x1BE which only detects user's brake being applied so
|
||||
// in these cases, this is used instead.
|
||||
// most hondas: 0x17C bit 53
|
||||
// accord, crv: 0x1BE bit 4
|
||||
#define IS_USER_BRAKE_MSG(addr) (!honda_alt_brake_msg ? ((addr) == 0x17C) : ((addr) == 0x1BE)) |
||||
#define USER_BRAKE_VALUE(to_push) (!honda_alt_brake_msg ? ((to_push)->RDHR & 0x200000) : ((to_push)->RDLR & 0x10)) |
||||
// exit controls on rising edge of brake press or on brake press when
|
||||
// speed > 0
|
||||
bool is_user_brake_msg = IS_USER_BRAKE_MSG(addr); // needed to enforce type
|
||||
if (is_user_brake_msg) { |
||||
int brake = USER_BRAKE_VALUE(to_push); |
||||
if (brake && (!(honda_brake_prev) || honda_ego_speed)) { |
||||
controls_allowed = 0; |
||||
} |
||||
honda_brake_prev = brake; |
||||
} |
||||
|
||||
// exit controls on rising edge of gas press if interceptor (0x201 w/ len = 6)
|
||||
// length check because bosch hardware also uses this id (0x201 w/ len = 8)
|
||||
if ((addr == 0x201) && (len == 6)) { |
||||
gas_interceptor_detected = 1; |
||||
int gas_interceptor = ((to_push->RDLR & 0xFF) << 8) | ((to_push->RDLR & 0xFF00) >> 8); |
||||
if ((gas_interceptor > HONDA_GAS_INTERCEPTOR_THRESHOLD) && |
||||
(gas_interceptor_prev <= HONDA_GAS_INTERCEPTOR_THRESHOLD) && |
||||
long_controls_allowed) { |
||||
controls_allowed = 0; |
||||
} |
||||
gas_interceptor_prev = gas_interceptor; |
||||
} |
||||
|
||||
// exit controls on rising edge of gas press if no interceptor
|
||||
if (!gas_interceptor_detected) { |
||||
if (addr == 0x17C) { |
||||
int gas = to_push->RDLR & 0xFF; |
||||
if (gas && !(honda_gas_prev) && long_controls_allowed) { |
||||
controls_allowed = 0; |
||||
} |
||||
honda_gas_prev = gas; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// all commands: gas, 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 honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
|
||||
int tx = 1; |
||||
int addr = GET_ADDR(to_send); |
||||
int bus = GET_BUS(to_send); |
||||
|
||||
// disallow actuator commands if gas or brake (with vehicle moving) are pressed
|
||||
// and the the latching controls_allowed flag is True
|
||||
int pedal_pressed = honda_gas_prev || (gas_interceptor_prev > HONDA_GAS_INTERCEPTOR_THRESHOLD) || |
||||
(honda_brake_prev && honda_ego_speed); |
||||
bool current_controls_allowed = controls_allowed && !(pedal_pressed); |
||||
|
||||
// BRAKE: safety check
|
||||
if (addr == 0x1FA) { |
||||
if (!current_controls_allowed || !long_controls_allowed) { |
||||
if ((to_send->RDLR & 0xFFFF0000) != to_send->RDLR) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
if ((to_send->RDLR & 0xFFFFFF3F) != to_send->RDLR) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
|
||||
// STEER: safety check
|
||||
if ((addr == 0xE4) || (addr == 0x194)) { |
||||
if (!current_controls_allowed) { |
||||
if ((to_send->RDLR & 0xFFFF0000) != to_send->RDLR) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// GAS: safety check
|
||||
if (addr == 0x200) { |
||||
if (!current_controls_allowed || !long_controls_allowed) { |
||||
if ((to_send->RDLR & 0xFFFF0000) != to_send->RDLR) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// FORCE CANCEL: safety check only relevant when spamming the cancel button in Bosch HW
|
||||
// ensuring that only the cancel button press is sent (VAL 2) when controls are off.
|
||||
// This avoids unintended engagements while still allowing resume spam
|
||||
if ((addr == 0x296) && honda_bosch_hardware && |
||||
!current_controls_allowed && (bus == 0)) { |
||||
if (((to_send->RDLR >> 5) & 0x7) != 2) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
|
||||
// 1 allows the message through
|
||||
return tx; |
||||
} |
||||
|
||||
static void honda_init(int16_t param) { |
||||
UNUSED(param); |
||||
controls_allowed = 0; |
||||
honda_bosch_hardware = false; |
||||
honda_alt_brake_msg = false; |
||||
} |
||||
|
||||
static void honda_bosch_init(int16_t param) { |
||||
controls_allowed = 0; |
||||
honda_bosch_hardware = true; |
||||
// Checking for alternate brake override from safety parameter
|
||||
honda_alt_brake_msg = (param == 1) ? true : false; |
||||
} |
||||
|
||||
static int honda_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
// fwd from car to camera. also fwd certain msgs from camera to car
|
||||
// 0xE4 is steering on all cars except CRV and RDX, 0x194 for CRV and RDX,
|
||||
// 0x1FA is brake control, 0x30C is acc hud, 0x33D is lkas hud,
|
||||
// 0x39f is radar hud
|
||||
int bus_fwd = -1; |
||||
|
||||
if (bus_num == 0) { |
||||
bus_fwd = 2; |
||||
} |
||||
if (bus_num == 2) { |
||||
// block stock lkas messages and stock acc messages (if OP is doing ACC)
|
||||
int addr = GET_ADDR(to_fwd); |
||||
int is_lkas_msg = (addr == 0xE4) || (addr == 0x194) || (addr == 0x33D); |
||||
int is_acc_msg = (addr == 0x1FA) || (addr == 0x30C) || (addr == 0x39F); |
||||
int block_fwd = is_lkas_msg || (is_acc_msg && long_controls_allowed); |
||||
if (!block_fwd) { |
||||
bus_fwd = 0; |
||||
} |
||||
} |
||||
return bus_fwd; |
||||
} |
||||
|
||||
static int honda_bosch_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
int bus_fwd = -1; |
||||
|
||||
if (bus_num == 2) { |
||||
bus_fwd = 1; |
||||
} |
||||
if (bus_num == 1) { |
||||
int addr = GET_ADDR(to_fwd); |
||||
int is_lkas_msg = (addr == 0xE4) || (addr == 0x33D); |
||||
if (!is_lkas_msg) { |
||||
bus_fwd = 2; |
||||
} |
||||
} |
||||
return bus_fwd; |
||||
} |
||||
|
||||
const safety_hooks honda_hooks = { |
||||
.init = honda_init, |
||||
.rx = honda_rx_hook, |
||||
.tx = honda_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = honda_fwd_hook, |
||||
}; |
||||
|
||||
const safety_hooks honda_bosch_hooks = { |
||||
.init = honda_bosch_init, |
||||
.rx = honda_rx_hook, |
||||
.tx = honda_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = honda_bosch_fwd_hook, |
||||
}; |
@ -0,0 +1,160 @@ |
||||
const int HYUNDAI_MAX_STEER = 255; // like stock
|
||||
const int HYUNDAI_MAX_RT_DELTA = 112; // max delta torque allowed for real time checks
|
||||
const uint32_t HYUNDAI_RT_INTERVAL = 250000; // 250ms between real time checks
|
||||
const int HYUNDAI_MAX_RATE_UP = 3; |
||||
const int HYUNDAI_MAX_RATE_DOWN = 7; |
||||
const int HYUNDAI_DRIVER_TORQUE_ALLOWANCE = 50; |
||||
const int HYUNDAI_DRIVER_TORQUE_FACTOR = 2; |
||||
|
||||
bool hyundai_camera_detected = 0; |
||||
bool hyundai_giraffe_switch_2 = 0; // is giraffe switch 2 high?
|
||||
int hyundai_camera_bus = 0; |
||||
int hyundai_rt_torque_last = 0; |
||||
int hyundai_desired_torque_last = 0; |
||||
int hyundai_cruise_engaged_last = 0; |
||||
uint32_t hyundai_ts_last = 0; |
||||
struct sample_t hyundai_torque_driver; // last few driver torques measured
|
||||
|
||||
static void hyundai_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
int bus = GET_BUS(to_push); |
||||
int addr = GET_ADDR(to_push); |
||||
|
||||
if (addr == 897) { |
||||
int torque_driver_new = ((to_push->RDLR >> 11) & 0xfff) - 2048; |
||||
// update array of samples
|
||||
update_sample(&hyundai_torque_driver, torque_driver_new); |
||||
} |
||||
|
||||
// check if stock camera ECU is still online
|
||||
if ((bus == 0) && (addr == 832)) { |
||||
hyundai_camera_detected = 1; |
||||
controls_allowed = 0; |
||||
} |
||||
|
||||
// Find out which bus the camera is on
|
||||
if (addr == 832) { |
||||
hyundai_camera_bus = bus; |
||||
} |
||||
|
||||
// enter controls on rising edge of ACC, exit controls on ACC off
|
||||
if (addr == 1057) { |
||||
// 2 bits: 13-14
|
||||
int cruise_engaged = (to_push->RDLR >> 13) & 0x3; |
||||
if (cruise_engaged && !hyundai_cruise_engaged_last) { |
||||
controls_allowed = 1; |
||||
} |
||||
if (!cruise_engaged) { |
||||
controls_allowed = 0; |
||||
} |
||||
hyundai_cruise_engaged_last = cruise_engaged; |
||||
} |
||||
|
||||
// 832 is lkas cmd. If it is on camera bus, then giraffe switch 2 is high
|
||||
if ((addr == 832) && (bus == hyundai_camera_bus) && (hyundai_camera_bus != 0)) { |
||||
hyundai_giraffe_switch_2 = 1; |
||||
} |
||||
} |
||||
|
||||
static int hyundai_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
|
||||
int tx = 1; |
||||
int addr = GET_ADDR(to_send); |
||||
|
||||
// There can be only one! (camera)
|
||||
if (hyundai_camera_detected) { |
||||
tx = 0; |
||||
} |
||||
|
||||
// LKA STEER: safety check
|
||||
if (addr == 832) { |
||||
int desired_torque = ((to_send->RDLR >> 16) & 0x7ff) - 1024; |
||||
uint32_t ts = TIM2->CNT; |
||||
bool violation = 0; |
||||
|
||||
if (controls_allowed) { |
||||
|
||||
// *** global torque limit check ***
|
||||
violation |= max_limit_check(desired_torque, HYUNDAI_MAX_STEER, -HYUNDAI_MAX_STEER); |
||||
|
||||
// *** torque rate limit check ***
|
||||
violation |= driver_limit_check(desired_torque, hyundai_desired_torque_last, &hyundai_torque_driver, |
||||
HYUNDAI_MAX_STEER, HYUNDAI_MAX_RATE_UP, HYUNDAI_MAX_RATE_DOWN, |
||||
HYUNDAI_DRIVER_TORQUE_ALLOWANCE, HYUNDAI_DRIVER_TORQUE_FACTOR); |
||||
|
||||
// used next time
|
||||
hyundai_desired_torque_last = desired_torque; |
||||
|
||||
// *** torque real time rate limit check ***
|
||||
violation |= rt_rate_limit_check(desired_torque, hyundai_rt_torque_last, HYUNDAI_MAX_RT_DELTA); |
||||
|
||||
// every RT_INTERVAL set the new limits
|
||||
uint32_t ts_elapsed = get_ts_elapsed(ts, hyundai_ts_last); |
||||
if (ts_elapsed > HYUNDAI_RT_INTERVAL) { |
||||
hyundai_rt_torque_last = desired_torque; |
||||
hyundai_ts_last = ts; |
||||
} |
||||
} |
||||
|
||||
// no torque if controls is not allowed
|
||||
if (!controls_allowed && (desired_torque != 0)) { |
||||
violation = 1; |
||||
} |
||||
|
||||
// reset to 0 if either controls is not allowed or there's a violation
|
||||
if (violation || !controls_allowed) { |
||||
hyundai_desired_torque_last = 0; |
||||
hyundai_rt_torque_last = 0; |
||||
hyundai_ts_last = ts; |
||||
} |
||||
|
||||
if (violation) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
|
||||
// FORCE CANCEL: safety check only relevant when spamming the cancel button.
|
||||
// ensuring that only the cancel button press is sent (VAL 4) when controls are off.
|
||||
// This avoids unintended engagements while still allowing resume spam
|
||||
// TODO: fix bug preventing the button msg to be fwd'd on bus 2
|
||||
//if ((addr == 1265) && !controls_allowed && (bus == 0) {
|
||||
// if ((to_send->RDLR & 0x7) != 4) {
|
||||
// tx = 0;
|
||||
// }
|
||||
//}
|
||||
|
||||
// 1 allows the message through
|
||||
return tx; |
||||
} |
||||
|
||||
static int hyundai_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
|
||||
int bus_fwd = -1; |
||||
// forward cam to ccan and viceversa, except lkas cmd
|
||||
if (hyundai_giraffe_switch_2) { |
||||
if (bus_num == 0) { |
||||
bus_fwd = hyundai_camera_bus; |
||||
} |
||||
if (bus_num == hyundai_camera_bus) { |
||||
int addr = GET_ADDR(to_fwd); |
||||
if (addr != 832) { |
||||
bus_fwd = 0; |
||||
} |
||||
} |
||||
} |
||||
return bus_fwd; |
||||
} |
||||
|
||||
static void hyundai_init(int16_t param) { |
||||
UNUSED(param); |
||||
controls_allowed = 0; |
||||
hyundai_giraffe_switch_2 = 0; |
||||
} |
||||
|
||||
const safety_hooks hyundai_hooks = { |
||||
.init = hyundai_init, |
||||
.rx = hyundai_rx_hook, |
||||
.tx = hyundai_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = hyundai_fwd_hook, |
||||
}; |
@ -0,0 +1,127 @@ |
||||
const int SUBARU_MAX_STEER = 2047; // 1s
|
||||
// real time torque limit to prevent controls spamming
|
||||
// the real time limit is 1500/sec
|
||||
const int SUBARU_MAX_RT_DELTA = 940; // max delta torque allowed for real time checks
|
||||
const uint32_t SUBARU_RT_INTERVAL = 250000; // 250ms between real time checks
|
||||
const int SUBARU_MAX_RATE_UP = 50; |
||||
const int SUBARU_MAX_RATE_DOWN = 70; |
||||
const int SUBARU_DRIVER_TORQUE_ALLOWANCE = 60; |
||||
const int SUBARU_DRIVER_TORQUE_FACTOR = 10; |
||||
|
||||
int subaru_cruise_engaged_last = 0; |
||||
int subaru_rt_torque_last = 0; |
||||
int subaru_desired_torque_last = 0; |
||||
uint32_t subaru_ts_last = 0; |
||||
struct sample_t subaru_torque_driver; // last few driver torques measured
|
||||
|
||||
|
||||
static void subaru_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
int bus = GET_BUS(to_push); |
||||
int addr = GET_ADDR(to_push); |
||||
|
||||
if ((addr == 0x119) && (bus == 0)){ |
||||
int torque_driver_new = ((to_push->RDLR >> 16) & 0x7FF); |
||||
torque_driver_new = to_signed(torque_driver_new, 11); |
||||
// update array of samples
|
||||
update_sample(&subaru_torque_driver, torque_driver_new); |
||||
} |
||||
|
||||
// enter controls on rising edge of ACC, exit controls on ACC off
|
||||
if ((addr == 0x240) && (bus == 0)) { |
||||
int cruise_engaged = (to_push->RDHR >> 9) & 1; |
||||
if (cruise_engaged && !subaru_cruise_engaged_last) { |
||||
controls_allowed = 1; |
||||
} |
||||
if (!cruise_engaged) { |
||||
controls_allowed = 0; |
||||
} |
||||
subaru_cruise_engaged_last = cruise_engaged; |
||||
} |
||||
} |
||||
|
||||
static int subaru_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
int tx = 1; |
||||
int addr = GET_ADDR(to_send); |
||||
|
||||
// steer cmd checks
|
||||
if (addr == 0x122) { |
||||
int desired_torque = ((to_send->RDLR >> 16) & 0x1FFF); |
||||
bool violation = 0; |
||||
uint32_t ts = TIM2->CNT; |
||||
desired_torque = to_signed(desired_torque, 13); |
||||
|
||||
if (controls_allowed) { |
||||
|
||||
// *** global torque limit check ***
|
||||
violation |= max_limit_check(desired_torque, SUBARU_MAX_STEER, -SUBARU_MAX_STEER); |
||||
|
||||
// *** torque rate limit check ***
|
||||
int desired_torque_last = subaru_desired_torque_last; |
||||
violation |= driver_limit_check(desired_torque, desired_torque_last, &subaru_torque_driver, |
||||
SUBARU_MAX_STEER, SUBARU_MAX_RATE_UP, SUBARU_MAX_RATE_DOWN, |
||||
SUBARU_DRIVER_TORQUE_ALLOWANCE, SUBARU_DRIVER_TORQUE_FACTOR); |
||||
|
||||
// used next time
|
||||
subaru_desired_torque_last = desired_torque; |
||||
|
||||
// *** torque real time rate limit check ***
|
||||
violation |= rt_rate_limit_check(desired_torque, subaru_rt_torque_last, SUBARU_MAX_RT_DELTA); |
||||
|
||||
// every RT_INTERVAL set the new limits
|
||||
uint32_t ts_elapsed = get_ts_elapsed(ts, subaru_ts_last); |
||||
if (ts_elapsed > SUBARU_RT_INTERVAL) { |
||||
subaru_rt_torque_last = desired_torque; |
||||
subaru_ts_last = ts; |
||||
} |
||||
} |
||||
|
||||
// no torque if controls is not allowed
|
||||
if (!controls_allowed && (desired_torque != 0)) { |
||||
violation = 1; |
||||
} |
||||
|
||||
// reset to 0 if either controls is not allowed or there's a violation
|
||||
if (violation || !controls_allowed) { |
||||
subaru_desired_torque_last = 0; |
||||
subaru_rt_torque_last = 0; |
||||
subaru_ts_last = ts; |
||||
} |
||||
|
||||
if (violation) { |
||||
tx = 0; |
||||
} |
||||
|
||||
} |
||||
return tx; |
||||
} |
||||
|
||||
static int subaru_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
|
||||
int bus_fwd = -1; |
||||
if (bus_num == 0) { |
||||
bus_fwd = 2; // Camera CAN
|
||||
} |
||||
if (bus_num == 2) { |
||||
// 356 is LKAS for outback 2015
|
||||
// 356 is LKAS for Global Platform
|
||||
// 545 is ES_Distance
|
||||
// 802 is ES_LKAS
|
||||
int addr = GET_ADDR(to_fwd); |
||||
int block_msg = (addr == 290) || (addr == 356) || (addr == 545) || (addr == 802); |
||||
if (!block_msg) { |
||||
bus_fwd = 0; // Main CAN
|
||||
} |
||||
} |
||||
|
||||
// fallback to do not forward
|
||||
return bus_fwd; |
||||
} |
||||
|
||||
const safety_hooks subaru_hooks = { |
||||
.init = nooutput_init, |
||||
.rx = subaru_rx_hook, |
||||
.tx = subaru_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = subaru_fwd_hook, |
||||
}; |
@ -0,0 +1,229 @@ |
||||
// board enforces
|
||||
// in-state
|
||||
// accel set/resume
|
||||
// out-state
|
||||
// cancel button
|
||||
// regen paddle
|
||||
// accel rising edge
|
||||
// brake rising edge
|
||||
// brake > 0mph
|
||||
//
|
||||
bool fmax_limit_check(float val, const float MAX_VAL, const float MIN_VAL) { |
||||
return (val > MAX_VAL) || (val < MIN_VAL); |
||||
} |
||||
|
||||
// 2m/s are added to be less restrictive
|
||||
const struct lookup_t TESLA_LOOKUP_ANGLE_RATE_UP = { |
||||
{2., 7., 17.}, |
||||
{5., .8, .25}}; |
||||
|
||||
const struct lookup_t TESLA_LOOKUP_ANGLE_RATE_DOWN = { |
||||
{2., 7., 17.}, |
||||
{5., 3.5, .8}}; |
||||
|
||||
const struct lookup_t TESLA_LOOKUP_MAX_ANGLE = { |
||||
{2., 29., 38.}, |
||||
{410., 92., 36.}}; |
||||
|
||||
const uint32_t TESLA_RT_INTERVAL = 250000; // 250ms between real time checks
|
||||
|
||||
// state of angle limits
|
||||
float tesla_desired_angle_last = 0; // last desired steer angle
|
||||
float tesla_rt_angle_last = 0.; // last real time angle
|
||||
float tesla_ts_angle_last = 0; |
||||
|
||||
int tesla_controls_allowed_last = 0; |
||||
|
||||
int tesla_brake_prev = 0; |
||||
int tesla_gas_prev = 0; |
||||
int tesla_speed = 0; |
||||
int eac_status = 0; |
||||
|
||||
int tesla_ignition_started = 0; |
||||
|
||||
|
||||
void set_gmlan_digital_output(int to_set); |
||||
void reset_gmlan_switch_timeout(void); |
||||
void gmlan_switch_init(int timeout_enable); |
||||
|
||||
|
||||
static void tesla_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
set_gmlan_digital_output(0); // #define GMLAN_HIGH 0
|
||||
reset_gmlan_switch_timeout(); //we're still in tesla safety mode, reset the timeout counter and make sure our output is enabled
|
||||
|
||||
int addr = GET_ADDR(to_push); |
||||
|
||||
if (addr == 0x45) { |
||||
// 6 bits starting at position 0
|
||||
int lever_position = (to_push->RDLR & 0x3F); |
||||
if (lever_position == 2) { // pull forward
|
||||
// activate openpilot
|
||||
controls_allowed = 1; |
||||
} |
||||
if (lever_position == 1) { // push towards the back
|
||||
// deactivate openpilot
|
||||
controls_allowed = 0; |
||||
} |
||||
} |
||||
|
||||
// Detect drive rail on (ignition) (start recording)
|
||||
if (addr == 0x348) { |
||||
// GTW_status
|
||||
int drive_rail_on = (to_push->RDLR & 0x0001); |
||||
tesla_ignition_started = drive_rail_on == 1; |
||||
} |
||||
|
||||
// exit controls on brake press
|
||||
// DI_torque2::DI_brakePedal 0x118
|
||||
if (addr == 0x118) { |
||||
// 1 bit at position 16
|
||||
if ((((to_push->RDLR & 0x8000)) >> 15) == 1) { |
||||
// disable break cancel by commenting line below
|
||||
controls_allowed = 0; |
||||
} |
||||
//get vehicle speed in m/s. Tesla gives MPH
|
||||
tesla_speed = ((((((to_push->RDLR >> 24) & 0xF) << 8) + ((to_push->RDLR >> 16) & 0xFF)) * 0.05) - 25) * 1.609 / 3.6; |
||||
if (tesla_speed < 0) { |
||||
tesla_speed = 0; |
||||
} |
||||
} |
||||
|
||||
// exit controls on EPAS error
|
||||
// EPAS_sysStatus::EPAS_eacStatus 0x370
|
||||
if (addr == 0x370) { |
||||
// if EPAS_eacStatus is not 1 or 2, disable control
|
||||
eac_status = ((to_push->RDHR >> 21)) & 0x7; |
||||
// For human steering override we must not disable controls when eac_status == 0
|
||||
// Additional safety: we could only allow eac_status == 0 when we have human steering allowed
|
||||
if (controls_allowed && (eac_status != 0) && (eac_status != 1) && (eac_status != 2)) { |
||||
controls_allowed = 0; |
||||
//puts("EPAS error! \n");
|
||||
} |
||||
} |
||||
//get latest steering wheel angle
|
||||
if (addr == 0x00E) { |
||||
float angle_meas_now = (int)(((((to_push->RDLR & 0x3F) << 8) + ((to_push->RDLR >> 8) & 0xFF)) * 0.1) - 819.2); |
||||
uint32_t ts = TIM2->CNT; |
||||
uint32_t ts_elapsed = get_ts_elapsed(ts, tesla_ts_angle_last); |
||||
|
||||
// *** angle real time check
|
||||
// add 1 to not false trigger the violation and multiply by 25 since the check is done every 250 ms and steer angle is updated at 100Hz
|
||||
float rt_delta_angle_up = (interpolate(TESLA_LOOKUP_ANGLE_RATE_UP, tesla_speed) * 25.) + 1.; |
||||
float rt_delta_angle_down = (interpolate(TESLA_LOOKUP_ANGLE_RATE_DOWN, tesla_speed) * 25.) + 1.; |
||||
float highest_rt_angle = tesla_rt_angle_last + ((tesla_rt_angle_last > 0.) ? rt_delta_angle_up : rt_delta_angle_down); |
||||
float lowest_rt_angle = tesla_rt_angle_last - ((tesla_rt_angle_last > 0.) ? rt_delta_angle_down : rt_delta_angle_up); |
||||
|
||||
if ((ts_elapsed > TESLA_RT_INTERVAL) || (controls_allowed && !tesla_controls_allowed_last)) { |
||||
tesla_rt_angle_last = angle_meas_now; |
||||
tesla_ts_angle_last = ts; |
||||
} |
||||
|
||||
// check for violation;
|
||||
if (fmax_limit_check(angle_meas_now, highest_rt_angle, lowest_rt_angle)) { |
||||
// We should not be able to STEER under these conditions
|
||||
// Other sending is fine (to allow human override)
|
||||
controls_allowed = 0; |
||||
//puts("WARN: RT Angle - No steer allowed! \n");
|
||||
} else { |
||||
controls_allowed = 1; |
||||
} |
||||
|
||||
tesla_controls_allowed_last = controls_allowed; |
||||
} |
||||
} |
||||
|
||||
// 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 tesla_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
|
||||
int tx = 1; |
||||
int addr = GET_ADDR(to_send); |
||||
|
||||
// do not transmit CAN message if steering angle too high
|
||||
// DAS_steeringControl::DAS_steeringAngleRequest
|
||||
if (addr == 0x488) { |
||||
float angle_raw = ((to_send->RDLR & 0x7F) << 8) + ((to_send->RDLR & 0xFF00) >> 8); |
||||
float desired_angle = (angle_raw * 0.1) - 1638.35; |
||||
bool violation = 0; |
||||
int st_enabled = (to_send->RDLR & 0x400000) >> 22; |
||||
|
||||
if (st_enabled == 0) { |
||||
//steering is not enabled, do not check angles and do send
|
||||
tesla_desired_angle_last = desired_angle; |
||||
} else if (controls_allowed) { |
||||
// add 1 to not false trigger the violation
|
||||
float delta_angle_up = interpolate(TESLA_LOOKUP_ANGLE_RATE_UP, tesla_speed) + 1.; |
||||
float delta_angle_down = interpolate(TESLA_LOOKUP_ANGLE_RATE_DOWN, tesla_speed) + 1.; |
||||
float highest_desired_angle = tesla_desired_angle_last + ((tesla_desired_angle_last > 0.) ? delta_angle_up : delta_angle_down); |
||||
float lowest_desired_angle = tesla_desired_angle_last - ((tesla_desired_angle_last > 0.) ? delta_angle_down : delta_angle_up); |
||||
float TESLA_MAX_ANGLE = interpolate(TESLA_LOOKUP_MAX_ANGLE, tesla_speed) + 1.; |
||||
|
||||
//check for max angles
|
||||
violation |= fmax_limit_check(desired_angle, TESLA_MAX_ANGLE, -TESLA_MAX_ANGLE); |
||||
|
||||
//check for angle delta changes
|
||||
violation |= fmax_limit_check(desired_angle, highest_desired_angle, lowest_desired_angle); |
||||
|
||||
if (violation) { |
||||
controls_allowed = 0; |
||||
tx = 0; |
||||
} |
||||
tesla_desired_angle_last = desired_angle; |
||||
} else { |
||||
tx = 0; |
||||
} |
||||
} |
||||
return tx; |
||||
} |
||||
|
||||
static void tesla_init(int16_t param) { |
||||
UNUSED(param); |
||||
controls_allowed = 0; |
||||
tesla_ignition_started = 0; |
||||
gmlan_switch_init(1); //init the gmlan switch with 1s timeout enabled
|
||||
} |
||||
|
||||
static int tesla_ign_hook(void) { |
||||
return tesla_ignition_started; |
||||
} |
||||
|
||||
static int tesla_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
|
||||
int bus_fwd = -1; |
||||
int addr = GET_ADDR(to_fwd); |
||||
|
||||
if (bus_num == 0) { |
||||
// change inhibit of GTW_epasControl
|
||||
|
||||
if (addr != 0x214) { |
||||
// remove EPB_epasControl
|
||||
bus_fwd = 2; // Custom EPAS bus
|
||||
} |
||||
if (addr == 0x101) { |
||||
to_fwd->RDLR = to_fwd->RDLR | 0x4000; // 0x4000: WITH_ANGLE, 0xC000: WITH_BOTH (angle and torque)
|
||||
uint32_t checksum = (((to_fwd->RDLR & 0xFF00) >> 8) + (to_fwd->RDLR & 0xFF) + 2) & 0xFF; |
||||
to_fwd->RDLR = to_fwd->RDLR & 0xFFFF; |
||||
to_fwd->RDLR = to_fwd->RDLR + (checksum << 16); |
||||
} |
||||
} |
||||
if (bus_num == 2) { |
||||
// remove GTW_epasControl in forwards
|
||||
if (addr != 0x101) { |
||||
bus_fwd = 0; // Chassis CAN
|
||||
} |
||||
} |
||||
return bus_fwd; |
||||
} |
||||
|
||||
const safety_hooks tesla_hooks = { |
||||
.init = tesla_init, |
||||
.rx = tesla_rx_hook, |
||||
.tx = tesla_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = tesla_ign_hook, |
||||
.fwd = tesla_fwd_hook, |
||||
}; |
@ -0,0 +1,229 @@ |
||||
// global torque limit
|
||||
const int TOYOTA_MAX_TORQUE = 1500; // max torque cmd allowed ever
|
||||
|
||||
// rate based torque limit + stay within actually applied
|
||||
// packet is sent at 100hz, so this limit is 1000/sec
|
||||
const int TOYOTA_MAX_RATE_UP = 10; // ramp up slow
|
||||
const int TOYOTA_MAX_RATE_DOWN = 25; // ramp down fast
|
||||
const int TOYOTA_MAX_TORQUE_ERROR = 350; // max torque cmd in excess of torque motor
|
||||
|
||||
// real time torque limit to prevent controls spamming
|
||||
// the real time limit is 1500/sec
|
||||
const int TOYOTA_MAX_RT_DELTA = 375; // max delta torque allowed for real time checks
|
||||
const uint32_t TOYOTA_RT_INTERVAL = 250000; // 250ms between real time checks
|
||||
|
||||
// longitudinal limits
|
||||
const int TOYOTA_MAX_ACCEL = 1500; // 1.5 m/s2
|
||||
const int TOYOTA_MIN_ACCEL = -3000; // 3.0 m/s2
|
||||
|
||||
const int TOYOTA_GAS_INTERCEPTOR_THRESHOLD = 475; // ratio between offset and gain from dbc file
|
||||
|
||||
// global actuation limit states
|
||||
int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file
|
||||
|
||||
// states
|
||||
int toyota_giraffe_switch_1 = 0; // is giraffe switch 1 high?
|
||||
int toyota_camera_forwarded = 0; // should we forward the camera bus?
|
||||
int toyota_desired_torque_last = 0; // last desired steer torque
|
||||
int toyota_rt_torque_last = 0; // last desired torque for real time check
|
||||
uint32_t toyota_ts_last = 0; |
||||
int toyota_cruise_engaged_last = 0; // cruise state
|
||||
int toyota_gas_prev = 0; |
||||
struct sample_t toyota_torque_meas; // last 3 motor torques produced by the eps
|
||||
|
||||
|
||||
static void toyota_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
|
||||
int bus = GET_BUS(to_push); |
||||
int addr = GET_ADDR(to_push); |
||||
|
||||
// get eps motor torque (0.66 factor in dbc)
|
||||
if (addr == 0x260) { |
||||
int torque_meas_new = (((to_push->RDHR) & 0xFF00) | ((to_push->RDHR >> 16) & 0xFF)); |
||||
torque_meas_new = to_signed(torque_meas_new, 16); |
||||
|
||||
// scale by dbc_factor
|
||||
torque_meas_new = (torque_meas_new * toyota_dbc_eps_torque_factor) / 100; |
||||
|
||||
// update array of sample
|
||||
update_sample(&toyota_torque_meas, torque_meas_new); |
||||
|
||||
// increase torque_meas by 1 to be conservative on rounding
|
||||
toyota_torque_meas.min--; |
||||
toyota_torque_meas.max++; |
||||
} |
||||
|
||||
// enter controls on rising edge of ACC, exit controls on ACC off
|
||||
if (addr == 0x1D2) { |
||||
// 5th bit is CRUISE_ACTIVE
|
||||
int cruise_engaged = to_push->RDLR & 0x20; |
||||
if (!cruise_engaged) { |
||||
controls_allowed = 0; |
||||
} |
||||
if (cruise_engaged && !toyota_cruise_engaged_last) { |
||||
controls_allowed = 1; |
||||
} |
||||
toyota_cruise_engaged_last = cruise_engaged; |
||||
} |
||||
|
||||
// exit controls on rising edge of interceptor gas press
|
||||
if (addr == 0x201) { |
||||
gas_interceptor_detected = 1; |
||||
int gas_interceptor = ((to_push->RDLR & 0xFF) << 8) | ((to_push->RDLR & 0xFF00) >> 8); |
||||
if ((gas_interceptor > TOYOTA_GAS_INTERCEPTOR_THRESHOLD) && |
||||
(gas_interceptor_prev <= TOYOTA_GAS_INTERCEPTOR_THRESHOLD) && |
||||
long_controls_allowed) { |
||||
controls_allowed = 0; |
||||
} |
||||
gas_interceptor_prev = gas_interceptor; |
||||
} |
||||
|
||||
// exit controls on rising edge of gas press
|
||||
if (addr == 0x2C1) { |
||||
int gas = (to_push->RDHR >> 16) & 0xFF; |
||||
if ((gas > 0) && (toyota_gas_prev == 0) && !gas_interceptor_detected && long_controls_allowed) { |
||||
controls_allowed = 0; |
||||
} |
||||
toyota_gas_prev = gas; |
||||
} |
||||
|
||||
// msgs are only on bus 2 if panda is connected to frc
|
||||
if (bus == 2) { |
||||
toyota_camera_forwarded = 1; |
||||
} |
||||
|
||||
// 0x2E4 is lkas cmd. If it is on bus 0, then giraffe switch 1 is high
|
||||
if ((addr == 0x2E4) && (bus == 0)) { |
||||
toyota_giraffe_switch_1 = 1; |
||||
} |
||||
} |
||||
|
||||
static int toyota_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
|
||||
int tx = 1; |
||||
int addr = GET_ADDR(to_send); |
||||
int bus = GET_BUS(to_send); |
||||
|
||||
// Check if msg is sent on BUS 0
|
||||
if (bus == 0) { |
||||
|
||||
// no IPAS in non IPAS mode
|
||||
if ((addr == 0x266) || (addr == 0x167)) { |
||||
tx = 0; |
||||
} |
||||
|
||||
// GAS PEDAL: safety check
|
||||
if (addr == 0x200) { |
||||
if (!controls_allowed || !long_controls_allowed) { |
||||
if ((to_send->RDLR & 0xFFFF0000) != to_send->RDLR) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// ACCEL: safety check on byte 1-2
|
||||
if (addr == 0x343) { |
||||
int desired_accel = ((to_send->RDLR & 0xFF) << 8) | ((to_send->RDLR >> 8) & 0xFF); |
||||
desired_accel = to_signed(desired_accel, 16); |
||||
if (!controls_allowed || !long_controls_allowed) { |
||||
if (desired_accel != 0) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
bool violation = max_limit_check(desired_accel, TOYOTA_MAX_ACCEL, TOYOTA_MIN_ACCEL); |
||||
if (violation) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
|
||||
// STEER: safety check on bytes 2-3
|
||||
if (addr == 0x2E4) { |
||||
int desired_torque = (to_send->RDLR & 0xFF00) | ((to_send->RDLR >> 16) & 0xFF); |
||||
desired_torque = to_signed(desired_torque, 16); |
||||
bool violation = 0; |
||||
|
||||
uint32_t ts = TIM2->CNT; |
||||
|
||||
if (controls_allowed) { |
||||
|
||||
// *** global torque limit check ***
|
||||
violation |= max_limit_check(desired_torque, TOYOTA_MAX_TORQUE, -TOYOTA_MAX_TORQUE); |
||||
|
||||
// *** torque rate limit check ***
|
||||
violation |= dist_to_meas_check(desired_torque, toyota_desired_torque_last, |
||||
&toyota_torque_meas, TOYOTA_MAX_RATE_UP, TOYOTA_MAX_RATE_DOWN, TOYOTA_MAX_TORQUE_ERROR); |
||||
|
||||
// used next time
|
||||
toyota_desired_torque_last = desired_torque; |
||||
|
||||
// *** torque real time rate limit check ***
|
||||
violation |= rt_rate_limit_check(desired_torque, toyota_rt_torque_last, TOYOTA_MAX_RT_DELTA); |
||||
|
||||
// every RT_INTERVAL set the new limits
|
||||
uint32_t ts_elapsed = get_ts_elapsed(ts, toyota_ts_last); |
||||
if (ts_elapsed > TOYOTA_RT_INTERVAL) { |
||||
toyota_rt_torque_last = desired_torque; |
||||
toyota_ts_last = ts; |
||||
} |
||||
} |
||||
|
||||
// no torque if controls is not allowed
|
||||
if (!controls_allowed && (desired_torque != 0)) { |
||||
violation = 1; |
||||
} |
||||
|
||||
// reset to 0 if either controls is not allowed or there's a violation
|
||||
if (violation || !controls_allowed) { |
||||
toyota_desired_torque_last = 0; |
||||
toyota_rt_torque_last = 0; |
||||
toyota_ts_last = ts; |
||||
} |
||||
|
||||
if (violation) { |
||||
tx = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 1 allows the message through
|
||||
return tx; |
||||
} |
||||
|
||||
static void toyota_init(int16_t param) { |
||||
controls_allowed = 0; |
||||
toyota_giraffe_switch_1 = 0; |
||||
toyota_camera_forwarded = 0; |
||||
toyota_dbc_eps_torque_factor = param; |
||||
} |
||||
|
||||
static int toyota_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { |
||||
|
||||
int bus_fwd = -1; |
||||
if (toyota_camera_forwarded && !toyota_giraffe_switch_1) { |
||||
if (bus_num == 0) { |
||||
bus_fwd = 2; |
||||
} |
||||
if (bus_num == 2) { |
||||
int addr = GET_ADDR(to_fwd); |
||||
// block stock lkas messages and stock acc messages (if OP is doing ACC)
|
||||
// in TSS2, 0.191 is LTA which we need to block to avoid controls collision
|
||||
int is_lkas_msg = ((addr == 0x2E4) || (addr == 0x412) || (addr == 0x191)); |
||||
// in TSS2 the camera does ACC as well, so filter 0x343
|
||||
int is_acc_msg = (addr == 0x343); |
||||
int block_msg = is_lkas_msg || (is_acc_msg && long_controls_allowed); |
||||
if (!block_msg) { |
||||
bus_fwd = 0; |
||||
} |
||||
} |
||||
} |
||||
return bus_fwd; |
||||
} |
||||
|
||||
const safety_hooks toyota_hooks = { |
||||
.init = toyota_init, |
||||
.rx = toyota_rx_hook, |
||||
.tx = toyota_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = toyota_fwd_hook, |
||||
}; |
@ -0,0 +1,169 @@ |
||||
// uses tons from safety_toyota
|
||||
// TODO: refactor to repeat less code
|
||||
|
||||
// IPAS override
|
||||
const int32_t TOYOTA_IPAS_OVERRIDE_THRESHOLD = 200; // disallow controls when user torque exceeds this value
|
||||
|
||||
// 2m/s are added to be less restrictive
|
||||
const struct lookup_t LOOKUP_ANGLE_RATE_UP = { |
||||
{2., 7., 17.}, |
||||
{5., .8, .15}}; |
||||
|
||||
const struct lookup_t LOOKUP_ANGLE_RATE_DOWN = { |
||||
{2., 7., 17.}, |
||||
{5., 3.5, .4}}; |
||||
|
||||
const float RT_ANGLE_FUDGE = 1.5; // for RT checks allow 50% more angle change
|
||||
const float CAN_TO_DEG = 2. / 3.; // convert angles from CAN unit to degrees
|
||||
|
||||
int ipas_state = 1; // 1 disabled, 3 executing angle control, 5 override
|
||||
int angle_control = 0; // 1 if direct angle control packets are seen
|
||||
float speed = 0.; |
||||
|
||||
struct sample_t angle_meas; // last 3 steer angles
|
||||
struct sample_t torque_driver; // last 3 driver steering torque
|
||||
|
||||
// state of angle limits
|
||||
int16_t desired_angle_last = 0; // last desired steer angle
|
||||
int16_t rt_angle_last = 0; // last desired torque for real time check
|
||||
uint32_t ts_angle_last = 0; |
||||
|
||||
int controls_allowed_last = 0; |
||||
|
||||
|
||||
static void toyota_ipas_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { |
||||
// check standard toyota stuff as well
|
||||
toyota_rx_hook(to_push); |
||||
|
||||
int addr = GET_ADDR(to_push); |
||||
|
||||
if (addr == 0x260) { |
||||
// get driver steering torque
|
||||
int16_t torque_driver_new = (((to_push->RDLR) & 0xFF00) | ((to_push->RDLR >> 16) & 0xFF)); |
||||
|
||||
// update array of samples
|
||||
update_sample(&torque_driver, torque_driver_new); |
||||
} |
||||
|
||||
// get steer angle
|
||||
if (addr == 0x25) { |
||||
int angle_meas_new = ((to_push->RDLR & 0xf) << 8) + ((to_push->RDLR & 0xff00) >> 8); |
||||
uint32_t ts = TIM2->CNT; |
||||
|
||||
angle_meas_new = to_signed(angle_meas_new, 12); |
||||
|
||||
// update array of samples
|
||||
update_sample(&angle_meas, angle_meas_new); |
||||
|
||||
// *** angle real time check
|
||||
// add 1 to not false trigger the violation and multiply by 20 since the check is done every 250ms and steer angle is updated at 80Hz
|
||||
int rt_delta_angle_up = ((int)(RT_ANGLE_FUDGE * ((interpolate(LOOKUP_ANGLE_RATE_UP, speed) * 20. * CAN_TO_DEG) + 1.))); |
||||
int rt_delta_angle_down = ((int)(RT_ANGLE_FUDGE * ((interpolate(LOOKUP_ANGLE_RATE_DOWN, speed) * 20. * CAN_TO_DEG) + 1.))); |
||||
int highest_rt_angle = rt_angle_last + ((rt_angle_last > 0) ? rt_delta_angle_up : rt_delta_angle_down); |
||||
int lowest_rt_angle = rt_angle_last - ((rt_angle_last > 0) ? rt_delta_angle_down : rt_delta_angle_up); |
||||
|
||||
// every RT_INTERVAL or when controls are turned on, set the new limits
|
||||
uint32_t ts_elapsed = get_ts_elapsed(ts, ts_angle_last); |
||||
if ((ts_elapsed > TOYOTA_RT_INTERVAL) || (controls_allowed && !controls_allowed_last)) { |
||||
rt_angle_last = angle_meas_new; |
||||
ts_angle_last = ts; |
||||
} |
||||
|
||||
// check for violation
|
||||
if (angle_control && |
||||
((angle_meas_new < lowest_rt_angle) || |
||||
(angle_meas_new > highest_rt_angle))) { |
||||
controls_allowed = 0; |
||||
} |
||||
|
||||
controls_allowed_last = controls_allowed; |
||||
} |
||||
|
||||
// get speed
|
||||
if (addr == 0xb4) { |
||||
speed = ((float) (((to_push->RDHR) & 0xFF00) | ((to_push->RDHR >> 16) & 0xFF))) * 0.01 / 3.6; |
||||
} |
||||
|
||||
// get ipas state
|
||||
if (addr == 0x262) { |
||||
ipas_state = (to_push->RDLR & 0xf); |
||||
} |
||||
|
||||
// exit controls on high steering override
|
||||
if (angle_control && ((torque_driver.min > TOYOTA_IPAS_OVERRIDE_THRESHOLD) || |
||||
(torque_driver.max < -TOYOTA_IPAS_OVERRIDE_THRESHOLD) || |
||||
(ipas_state==5))) { |
||||
controls_allowed = 0; |
||||
} |
||||
} |
||||
|
||||
static int toyota_ipas_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { |
||||
|
||||
int tx = 1; |
||||
int bypass_standard_tx_hook = 0; |
||||
int bus = GET_BUS(to_send); |
||||
int addr = GET_ADDR(to_send); |
||||
|
||||
// Check if msg is sent on BUS 0
|
||||
if (bus == 0) { |
||||
|
||||
// STEER ANGLE
|
||||
if ((addr == 0x266) || (addr == 0x167)) { |
||||
|
||||
angle_control = 1; // we are in angle control mode
|
||||
int desired_angle = ((to_send->RDLR & 0xf) << 8) + ((to_send->RDLR & 0xff00) >> 8); |
||||
int ipas_state_cmd = ((to_send->RDLR & 0xff) >> 4); |
||||
bool violation = 0; |
||||
|
||||
desired_angle = to_signed(desired_angle, 12); |
||||
|
||||
if (controls_allowed) { |
||||
// add 1 to not false trigger the violation
|
||||
float delta_angle_float; |
||||
delta_angle_float = (interpolate(LOOKUP_ANGLE_RATE_UP, speed) * CAN_TO_DEG) + 1.; |
||||
int delta_angle_up = (int) (delta_angle_float); |
||||
delta_angle_float = (interpolate(LOOKUP_ANGLE_RATE_DOWN, speed) * CAN_TO_DEG) + 1.; |
||||
int delta_angle_down = (int) (delta_angle_float); |
||||
|
||||
int highest_desired_angle = desired_angle_last + ((desired_angle_last > 0) ? delta_angle_up : delta_angle_down); |
||||
int lowest_desired_angle = desired_angle_last - ((desired_angle_last > 0) ? delta_angle_down : delta_angle_up); |
||||
if ((desired_angle > highest_desired_angle) || |
||||
(desired_angle < lowest_desired_angle)){ |
||||
violation = 1; |
||||
controls_allowed = 0; |
||||
} |
||||
} |
||||
|
||||
// desired steer angle should be the same as steer angle measured when controls are off
|
||||
if ((!controls_allowed) && |
||||
((desired_angle < (angle_meas.min - 1)) || |
||||
(desired_angle > (angle_meas.max + 1)) || |
||||
(ipas_state_cmd != 1))) { |
||||
violation = 1; |
||||
} |
||||
|
||||
desired_angle_last = desired_angle; |
||||
|
||||
if (violation) { |
||||
tx = 0; |
||||
} |
||||
bypass_standard_tx_hook = 1; |
||||
} |
||||
} |
||||
|
||||
// check standard toyota stuff as well if addr isn't IPAS related
|
||||
if (!bypass_standard_tx_hook) { |
||||
tx &= toyota_tx_hook(to_send); |
||||
} |
||||
|
||||
return tx; |
||||
} |
||||
|
||||
const safety_hooks toyota_ipas_hooks = { |
||||
.init = toyota_init, |
||||
.rx = toyota_ipas_rx_hook, |
||||
.tx = toyota_ipas_tx_hook, |
||||
.tx_lin = nooutput_tx_lin_hook, |
||||
.ignition = default_ign_hook, |
||||
.fwd = toyota_fwd_hook, |
||||
}; |
@ -0,0 +1,56 @@ |
||||
#define GET_BUS(msg) (((msg)->RDTR >> 4) & 0xFF) |
||||
#define GET_LEN(msg) ((msg)->RDTR & 0xf) |
||||
#define GET_ADDR(msg) ((((msg)->RIR & 4) != 0) ? ((msg)->RIR >> 3) : ((msg)->RIR >> 21)) |
||||
|
||||
// sample struct that keeps 3 samples in memory
|
||||
struct sample_t { |
||||
int values[6]; |
||||
int min; |
||||
int max; |
||||
} sample_t_default = {{0}, 0, 0}; |
||||
|
||||
// safety code requires floats
|
||||
struct lookup_t { |
||||
float x[3]; |
||||
float y[3]; |
||||
}; |
||||
|
||||
void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_push); |
||||
int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_send); |
||||
int safety_tx_lin_hook(int lin_num, uint8_t *data, int len); |
||||
int safety_ignition_hook(void); |
||||
uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last); |
||||
int to_signed(int d, int bits); |
||||
void update_sample(struct sample_t *sample, int sample_new); |
||||
bool max_limit_check(int val, const int MAX, const int MIN); |
||||
bool dist_to_meas_check(int val, int val_last, struct sample_t *val_meas, |
||||
const int MAX_RATE_UP, const int MAX_RATE_DOWN, const int MAX_ERROR); |
||||
bool driver_limit_check(int val, int val_last, struct sample_t *val_driver, |
||||
const int MAX, const int MAX_RATE_UP, const int MAX_RATE_DOWN, |
||||
const int MAX_ALLOWANCE, const int DRIVER_FACTOR); |
||||
bool rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA); |
||||
float interpolate(struct lookup_t xy, float x); |
||||
|
||||
typedef void (*safety_hook_init)(int16_t param); |
||||
typedef void (*rx_hook)(CAN_FIFOMailBox_TypeDef *to_push); |
||||
typedef int (*tx_hook)(CAN_FIFOMailBox_TypeDef *to_send); |
||||
typedef int (*tx_lin_hook)(int lin_num, uint8_t *data, int len); |
||||
typedef int (*ign_hook)(void); |
||||
typedef int (*fwd_hook)(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd); |
||||
|
||||
typedef struct { |
||||
safety_hook_init init; |
||||
ign_hook ignition; |
||||
rx_hook rx; |
||||
tx_hook tx; |
||||
tx_lin_hook tx_lin; |
||||
fwd_hook fwd; |
||||
} safety_hooks; |
||||
|
||||
// This can be set by the safety hooks.
|
||||
bool controls_allowed = 0; |
||||
bool gas_interceptor_detected = 0; |
||||
int gas_interceptor_prev = 0; |
||||
|
||||
// This is set by USB command 0xdf
|
||||
bool long_controls_allowed = 1; |
@ -0,0 +1,317 @@ |
||||
// flasher state variables
|
||||
uint32_t *prog_ptr = NULL; |
||||
int unlocked = 0; |
||||
|
||||
#ifdef uart_ring |
||||
void debug_ring_callback(uart_ring *ring) {} |
||||
#endif |
||||
|
||||
int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired) { |
||||
int resp_len = 0; |
||||
|
||||
// flasher machine
|
||||
memset(resp, 0, 4); |
||||
memcpy(resp+4, "\xde\xad\xd0\x0d", 4); |
||||
resp[0] = 0xff; |
||||
resp[2] = setup->b.bRequest; |
||||
resp[3] = ~setup->b.bRequest; |
||||
*((uint32_t **)&resp[8]) = prog_ptr; |
||||
resp_len = 0xc; |
||||
|
||||
int sec; |
||||
switch (setup->b.bRequest) { |
||||
// **** 0xb0: flasher echo
|
||||
case 0xb0: |
||||
resp[1] = 0xff; |
||||
break; |
||||
// **** 0xb1: unlock flash
|
||||
case 0xb1: |
||||
if (FLASH->CR & FLASH_CR_LOCK) { |
||||
FLASH->KEYR = 0x45670123; |
||||
FLASH->KEYR = 0xCDEF89AB; |
||||
resp[1] = 0xff; |
||||
} |
||||
set_led(LED_GREEN, 1); |
||||
unlocked = 1; |
||||
prog_ptr = (uint32_t *)0x8004000; |
||||
break; |
||||
// **** 0xb2: erase sector
|
||||
case 0xb2: |
||||
sec = setup->b.wValue.w; |
||||
// don't erase the bootloader
|
||||
if (sec != 0 && sec < 12 && unlocked) { |
||||
FLASH->CR = (sec << 3) | FLASH_CR_SER; |
||||
FLASH->CR |= FLASH_CR_STRT; |
||||
while (FLASH->SR & FLASH_SR_BSY); |
||||
resp[1] = 0xff; |
||||
} |
||||
break; |
||||
// **** 0xd0: fetch serial number
|
||||
case 0xd0: |
||||
#ifdef STM32F4 |
||||
// 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; |
||||
// **** 0xd6: get version
|
||||
case 0xd6: |
||||
COMPILE_TIME_ASSERT(sizeof(gitversion) <= MAX_RESP_LEN); |
||||
memcpy(resp, gitversion, sizeof(gitversion)); |
||||
resp_len = sizeof(gitversion); |
||||
break; |
||||
// **** 0xd8: reset ST
|
||||
case 0xd8: |
||||
NVIC_SystemReset(); |
||||
break; |
||||
} |
||||
return resp_len; |
||||
} |
||||
|
||||
int usb_cb_ep1_in(uint8_t *usbdata, int len, bool hardwired) { return 0; } |
||||
void usb_cb_ep3_out(uint8_t *usbdata, int len, bool hardwired) { } |
||||
|
||||
int is_enumerated = 0; |
||||
void usb_cb_enumeration_complete() { |
||||
puts("USB enumeration complete\n"); |
||||
is_enumerated = 1; |
||||
} |
||||
|
||||
void usb_cb_ep2_out(uint8_t *usbdata, int len, bool hardwired) { |
||||
set_led(LED_RED, 0); |
||||
for (int i = 0; i < len/4; i++) { |
||||
// program byte 1
|
||||
FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG; |
||||
|
||||
*prog_ptr = *(uint32_t*)(usbdata+(i*4)); |
||||
while (FLASH->SR & FLASH_SR_BSY); |
||||
|
||||
//*(uint64_t*)(&spi_tx_buf[0x30+(i*4)]) = *prog_ptr;
|
||||
prog_ptr++; |
||||
} |
||||
set_led(LED_RED, 1); |
||||
} |
||||
|
||||
|
||||
int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) { |
||||
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 2: |
||||
// ep 2, flash!
|
||||
usb_cb_ep2_out(data+4, data[2], 0); |
||||
break; |
||||
} |
||||
return resp_len; |
||||
} |
||||
|
||||
#ifdef PEDAL |
||||
|
||||
#include "drivers/llcan.h" |
||||
#define CAN CAN1 |
||||
|
||||
#define CAN_BL_INPUT 0x1 |
||||
#define CAN_BL_OUTPUT 0x2 |
||||
|
||||
void CAN1_TX_IRQHandler() { |
||||
// clear interrupt
|
||||
CAN->TSR |= CAN_TSR_RQCP0; |
||||
} |
||||
|
||||
#define ISOTP_BUF_SIZE 0x110 |
||||
|
||||
uint8_t isotp_buf[ISOTP_BUF_SIZE]; |
||||
uint8_t *isotp_buf_ptr = NULL; |
||||
int isotp_buf_remain = 0; |
||||
|
||||
uint8_t isotp_buf_out[ISOTP_BUF_SIZE]; |
||||
uint8_t *isotp_buf_out_ptr = NULL; |
||||
int isotp_buf_out_remain = 0; |
||||
int isotp_buf_out_idx = 0; |
||||
|
||||
void bl_can_send(uint8_t *odat) { |
||||
// wait for send
|
||||
while (!(CAN->TSR & CAN_TSR_TME0)); |
||||
|
||||
// send continue
|
||||
CAN->sTxMailBox[0].TDLR = ((uint32_t*)odat)[0]; |
||||
CAN->sTxMailBox[0].TDHR = ((uint32_t*)odat)[1]; |
||||
CAN->sTxMailBox[0].TDTR = 8; |
||||
CAN->sTxMailBox[0].TIR = (CAN_BL_OUTPUT << 21) | 1; |
||||
} |
||||
|
||||
void CAN1_RX0_IRQHandler() { |
||||
while (CAN->RF0R & CAN_RF0R_FMP0) { |
||||
if ((CAN->sFIFOMailBox[0].RIR>>21) == CAN_BL_INPUT) { |
||||
uint8_t dat[8]; |
||||
((uint32_t*)dat)[0] = CAN->sFIFOMailBox[0].RDLR; |
||||
((uint32_t*)dat)[1] = CAN->sFIFOMailBox[0].RDHR; |
||||
uint8_t odat[8]; |
||||
uint8_t type = dat[0] & 0xF0; |
||||
if (type == 0x30) { |
||||
// continue
|
||||
while (isotp_buf_out_remain > 0) { |
||||
// wait for send
|
||||
while (!(CAN->TSR & CAN_TSR_TME0)); |
||||
|
||||
odat[0] = 0x20 | isotp_buf_out_idx; |
||||
memcpy(odat+1, isotp_buf_out_ptr, 7); |
||||
isotp_buf_out_remain -= 7; |
||||
isotp_buf_out_ptr += 7; |
||||
isotp_buf_out_idx++; |
||||
|
||||
bl_can_send(odat); |
||||
} |
||||
} else if (type == 0x20) { |
||||
if (isotp_buf_remain > 0) { |
||||
memcpy(isotp_buf_ptr, dat+1, 7); |
||||
isotp_buf_ptr += 7; |
||||
isotp_buf_remain -= 7; |
||||
} |
||||
if (isotp_buf_remain <= 0) { |
||||
int len = isotp_buf_ptr - isotp_buf + isotp_buf_remain; |
||||
|
||||
// call the function
|
||||
memset(isotp_buf_out, 0, ISOTP_BUF_SIZE); |
||||
isotp_buf_out_remain = spi_cb_rx(isotp_buf, len, isotp_buf_out); |
||||
isotp_buf_out_ptr = isotp_buf_out; |
||||
isotp_buf_out_idx = 0; |
||||
|
||||
// send initial
|
||||
if (isotp_buf_out_remain <= 7) { |
||||
odat[0] = isotp_buf_out_remain; |
||||
memcpy(odat+1, isotp_buf_out_ptr, isotp_buf_out_remain); |
||||
} else { |
||||
odat[0] = 0x10 | (isotp_buf_out_remain>>8); |
||||
odat[1] = isotp_buf_out_remain & 0xFF; |
||||
memcpy(odat+2, isotp_buf_out_ptr, 6); |
||||
isotp_buf_out_remain -= 6; |
||||
isotp_buf_out_ptr += 6; |
||||
isotp_buf_out_idx++; |
||||
} |
||||
|
||||
bl_can_send(odat); |
||||
} |
||||
} else if (type == 0x10) { |
||||
int len = ((dat[0]&0xF)<<8) | dat[1]; |
||||
|
||||
// setup buffer
|
||||
isotp_buf_ptr = isotp_buf; |
||||
memcpy(isotp_buf_ptr, dat+2, 6); |
||||
|
||||
if (len < (ISOTP_BUF_SIZE-0x10)) { |
||||
isotp_buf_ptr += 6; |
||||
isotp_buf_remain = len-6; |
||||
} |
||||
|
||||
memset(odat, 0, 8); |
||||
odat[0] = 0x30; |
||||
bl_can_send(odat); |
||||
} |
||||
} |
||||
// next
|
||||
CAN->RF0R |= CAN_RF0R_RFOM0; |
||||
} |
||||
} |
||||
|
||||
void CAN1_SCE_IRQHandler() { |
||||
llcan_clear_send(CAN); |
||||
} |
||||
|
||||
#endif |
||||
|
||||
void soft_flasher_start() { |
||||
puts("\n\n\n************************ FLASHER START ************************\n"); |
||||
|
||||
enter_bootloader_mode = 0; |
||||
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; |
||||
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; |
||||
RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; |
||||
RCC->APB1ENR |= RCC_APB1ENR_USART2EN; |
||||
|
||||
// pedal has the canloader
|
||||
#ifdef PEDAL |
||||
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; |
||||
|
||||
// B8,B9: CAN 1
|
||||
set_gpio_alternate(GPIOB, 8, GPIO_AF9_CAN1); |
||||
set_gpio_alternate(GPIOB, 9, GPIO_AF9_CAN1); |
||||
set_can_enable(CAN1, 1); |
||||
|
||||
// init can
|
||||
llcan_set_speed(CAN1, 5000, false, false); |
||||
llcan_init(CAN1); |
||||
#endif |
||||
|
||||
// A4,A5,A6,A7: setup SPI
|
||||
set_gpio_alternate(GPIOA, 4, GPIO_AF5_SPI1); |
||||
set_gpio_alternate(GPIOA, 5, GPIO_AF5_SPI1); |
||||
set_gpio_alternate(GPIOA, 6, GPIO_AF5_SPI1); |
||||
set_gpio_alternate(GPIOA, 7, GPIO_AF5_SPI1); |
||||
|
||||
// A2,A3: USART 2 for debugging
|
||||
set_gpio_alternate(GPIOA, 2, GPIO_AF7_USART2); |
||||
set_gpio_alternate(GPIOA, 3, GPIO_AF7_USART2); |
||||
|
||||
// A11,A12: USB
|
||||
set_gpio_alternate(GPIOA, 11, GPIO_AF10_OTG_FS); |
||||
set_gpio_alternate(GPIOA, 12, GPIO_AF10_OTG_FS); |
||||
GPIOA->OSPEEDR = GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12; |
||||
|
||||
// flasher
|
||||
spi_init(); |
||||
|
||||
// enable USB
|
||||
usb_init(); |
||||
|
||||
// green LED on for flashing
|
||||
set_led(LED_GREEN, 1); |
||||
|
||||
__enable_irq(); |
||||
|
||||
uint64_t cnt = 0; |
||||
|
||||
for (cnt=0;;cnt++) { |
||||
if (cnt == 35 && !is_enumerated && usb_power_mode == USB_POWER_CLIENT) { |
||||
// if you are connected through a hub to the phone
|
||||
// you need power to be able to see the device
|
||||
puts("USBP: didn't enumerate, switching to CDP mode\n"); |
||||
set_usb_power_mode(USB_POWER_CDP); |
||||
set_led(LED_BLUE, 1); |
||||
} |
||||
// blink the green LED fast
|
||||
set_led(LED_GREEN, 0); |
||||
delay(500000); |
||||
set_led(LED_GREEN, 1); |
||||
delay(500000); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,511 @@ |
||||
/** |
||||
****************************************************************************** |
||||
* @file startup_stm32f205xx.s
|
||||
* @author MCD Application Team
|
||||
* @version V2.1.2
|
||||
* @date 29-June-2016
|
||||
* @brief STM32F205xx Devices vector table for Atollic TrueSTUDIO toolchain.
|
||||
* This module performs: |
||||
* - Set the initial SP |
||||
* - Set the initial PC == Reset_Handler, |
||||
* - Set the vector table entries with the exceptions ISR address |
||||
* - Branches to main in the C library (which eventually |
||||
* calls main()). |
||||
* After Reset the Cortex-M3 processor is in Thread mode, |
||||
* priority is Privileged, and the Stack is set to Main. |
||||
****************************************************************************** |
||||
* @attention
|
||||
* |
||||
* <h2><center>© COPYRIGHT 2016 STMicroelectronics</center></h2>
|
||||
* |
||||
* Redistribution and use in source and binary forms, with or without modification, |
||||
* are permitted provided that the following conditions are met: |
||||
* 1. Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, |
||||
* this list of conditions and the following disclaimer in the documentation |
||||
* and/or other materials provided with the distribution. |
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
****************************************************************************** |
||||
*/ |
||||
|
||||
.syntax unified
|
||||
.cpu cortex-m3 |
||||
.thumb |
||||
|
||||
.global g_pfnVectors
|
||||
.global Default_Handler
|
||||
|
||||
/* start address for the initialization values of the .data section.
|
||||
defined in linker script */ |
||||
.word _sidata
|
||||
/* start address for the .data section. defined in linker script */
|
||||
.word _sdata
|
||||
/* end address for the .data section. defined in linker script */ |
||||
.word _edata
|
||||
/* start address for the .bss section. defined in linker script */ |
||||
.word _sbss
|
||||
/* end address for the .bss section. defined in linker script */ |
||||
.word _ebss
|
||||
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ |
||||
|
||||
/** |
||||
* @brief This is the code that gets called when the processor first
|
||||
* starts execution following a reset event. Only the absolutely |
||||
* necessary set is performed, after which the application |
||||
* supplied main() routine is called.
|
||||
* @param None
|
||||
* @retval : None
|
||||
*/ |
||||
|
||||
.section .text.Reset_Handler |
||||
.weak Reset_Handler
|
||||
.type Reset_Handler, %function |
||||
Reset_Handler:
|
||||
ldr sp, =_estack /* set stack pointer */ |
||||
bl __initialize_hardware_early |
||||
|
||||
/* Copy the data segment initializers from flash to SRAM */ |
||||
movs r1, #0 |
||||
b LoopCopyDataInit |
||||
|
||||
CopyDataInit: |
||||
ldr r3, =_sidata |
||||
ldr r3, [r3, r1] |
||||
str r3, [r0, r1] |
||||
adds r1, r1, #4 |
||||
|
||||
LoopCopyDataInit: |
||||
ldr r0, =_sdata |
||||
ldr r3, =_edata |
||||
adds r2, r0, r1 |
||||
cmp r2, r3 |
||||
bcc CopyDataInit |
||||
ldr r2, =_sbss |
||||
b LoopFillZerobss |
||||
/* Zero fill the bss segment. */ |
||||
FillZerobss: |
||||
movs r3, #0 |
||||
str r3, [r2], #4 |
||||
|
||||
LoopFillZerobss: |
||||
ldr r3, = _ebss |
||||
cmp r2, r3 |
||||
bcc FillZerobss |
||||
|
||||
/* Call the clock system initialization function.*/ |
||||
/*bl SystemInit */ |
||||
/* Call static constructors */ |
||||
/*bl __libc_init_array*/ |
||||
/* Call the application's entry point.*/ |
||||
bl main |
||||
bx lr
|
||||
.size Reset_Handler, .-Reset_Handler |
||||
|
||||
/** |
||||
* @brief This is the code that gets called when the processor receives an
|
||||
* unexpected interrupt. This simply enters an infinite loop, preserving |
||||
* the system state for examination by a debugger. |
||||
* @param None
|
||||
* @retval None
|
||||
*/ |
||||
.section .text.Default_Handler,"ax",%progbits |
||||
Default_Handler: |
||||
Infinite_Loop: |
||||
b Infinite_Loop |
||||
.size Default_Handler, .-Default_Handler |
||||
/****************************************************************************** |
||||
* |
||||
* The minimal vector table for a Cortex M3. Note that the proper constructs |
||||
* must be placed on this to ensure that it ends up at physical address |
||||
* 0x0000.0000. |
||||
*
|
||||
*******************************************************************************/ |
||||
.section .isr_vector,"a",%progbits |
||||
.type g_pfnVectors, %object |
||||
.size g_pfnVectors, .-g_pfnVectors |
||||
|
||||
|
||||
|
||||
g_pfnVectors: |
||||
.word _estack
|
||||
.word Reset_Handler
|
||||
|
||||
.word NMI_Handler
|
||||
.word HardFault_Handler
|
||||
.word MemManage_Handler
|
||||
.word BusFault_Handler
|
||||
.word UsageFault_Handler
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word SVC_Handler
|
||||
.word DebugMon_Handler
|
||||
.word 0
|
||||
.word PendSV_Handler
|
||||
.word SysTick_Handler
|
||||
|
||||
/* External Interrupts */ |
||||
.word WWDG_IRQHandler /* Window WatchDog */ |
||||
.word PVD_IRQHandler /* PVD through EXTI Line detection */ |
||||
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */ |
||||
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ |
||||
.word FLASH_IRQHandler /* FLASH */ |
||||
.word RCC_IRQHandler /* RCC */ |
||||
.word EXTI0_IRQHandler /* EXTI Line0 */ |
||||
.word EXTI1_IRQHandler /* EXTI Line1 */ |
||||
.word EXTI2_IRQHandler /* EXTI Line2 */ |
||||
.word EXTI3_IRQHandler /* EXTI Line3 */ |
||||
.word EXTI4_IRQHandler /* EXTI Line4 */ |
||||
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */ |
||||
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */ |
||||
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */ |
||||
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */ |
||||
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */ |
||||
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */ |
||||
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */ |
||||
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */ |
||||
.word CAN1_TX_IRQHandler /* CAN1 TX */ |
||||
.word CAN1_RX0_IRQHandler /* CAN1 RX0 */ |
||||
.word CAN1_RX1_IRQHandler /* CAN1 RX1 */ |
||||
.word CAN1_SCE_IRQHandler /* CAN1 SCE */ |
||||
.word EXTI9_5_IRQHandler /* External Line[9:5]s */ |
||||
.word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */ |
||||
.word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */ |
||||
.word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */ |
||||
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ |
||||
.word TIM2_IRQHandler /* TIM2 */ |
||||
.word TIM3_IRQHandler /* TIM3 */ |
||||
.word TIM4_IRQHandler /* TIM4 */ |
||||
.word I2C1_EV_IRQHandler /* I2C1 Event */ |
||||
.word I2C1_ER_IRQHandler /* I2C1 Error */ |
||||
.word I2C2_EV_IRQHandler /* I2C2 Event */ |
||||
.word I2C2_ER_IRQHandler /* I2C2 Error */ |
||||
.word SPI1_IRQHandler /* SPI1 */ |
||||
.word SPI2_IRQHandler /* SPI2 */ |
||||
.word USART1_IRQHandler /* USART1 */ |
||||
.word USART2_IRQHandler /* USART2 */ |
||||
.word USART3_IRQHandler /* USART3 */ |
||||
.word EXTI15_10_IRQHandler /* External Line[15:10]s */ |
||||
.word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */ |
||||
.word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */ |
||||
.word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */ |
||||
.word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */ |
||||
.word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */ |
||||
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare */ |
||||
.word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ |
||||
.word FSMC_IRQHandler /* FSMC */ |
||||
.word SDIO_IRQHandler /* SDIO */ |
||||
.word TIM5_IRQHandler /* TIM5 */ |
||||
.word SPI3_IRQHandler /* SPI3 */ |
||||
.word UART4_IRQHandler /* UART4 */ |
||||
.word UART5_IRQHandler /* UART5 */ |
||||
.word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */ |
||||
.word TIM7_IRQHandler /* TIM7 */ |
||||
.word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */ |
||||
.word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */ |
||||
.word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */ |
||||
.word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */ |
||||
.word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */ |
||||
.word 0 /* Reserved */ |
||||
.word 0 /* Reserved */ |
||||
.word CAN2_TX_IRQHandler /* CAN2 TX */ |
||||
.word CAN2_RX0_IRQHandler /* CAN2 RX0 */ |
||||
.word CAN2_RX1_IRQHandler /* CAN2 RX1 */ |
||||
.word CAN2_SCE_IRQHandler /* CAN2 SCE */ |
||||
.word OTG_FS_IRQHandler /* USB OTG FS */ |
||||
.word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */ |
||||
.word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */ |
||||
.word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */ |
||||
.word USART6_IRQHandler /* USART6 */ |
||||
.word I2C3_EV_IRQHandler /* I2C3 event */ |
||||
.word I2C3_ER_IRQHandler /* I2C3 error */ |
||||
.word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */ |
||||
.word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */ |
||||
.word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */ |
||||
.word OTG_HS_IRQHandler /* USB OTG HS */ |
||||
.word 0 /* Reserved */ |
||||
.word 0 /* Reserved */ |
||||
.word HASH_RNG_IRQHandler /* Hash and Rng */ |
||||
|
||||
/******************************************************************************* |
||||
* |
||||
* Provide weak aliases for each Exception handler to the Default_Handler. |
||||
* As they are weak aliases, any function with the same name will override |
||||
* this definition. |
||||
*
|
||||
*******************************************************************************/ |
||||
.weak NMI_Handler
|
||||
.thumb_set NMI_Handler,Default_Handler |
||||
|
||||
.weak HardFault_Handler
|
||||
.thumb_set HardFault_Handler,Default_Handler |
||||
|
||||
.weak MemManage_Handler
|
||||
.thumb_set MemManage_Handler,Default_Handler |
||||
|
||||
.weak BusFault_Handler
|
||||
.thumb_set BusFault_Handler,Default_Handler |
||||
|
||||
.weak UsageFault_Handler
|
||||
.thumb_set UsageFault_Handler,Default_Handler |
||||
|
||||
.weak SVC_Handler
|
||||
.thumb_set SVC_Handler,Default_Handler |
||||
|
||||
.weak DebugMon_Handler
|
||||
.thumb_set DebugMon_Handler,Default_Handler |
||||
|
||||
.weak PendSV_Handler
|
||||
.thumb_set PendSV_Handler,Default_Handler |
||||
|
||||
.weak SysTick_Handler
|
||||
.thumb_set SysTick_Handler,Default_Handler |
||||
|
||||
.weak WWDG_IRQHandler
|
||||
.thumb_set WWDG_IRQHandler,Default_Handler |
||||
|
||||
.weak PVD_IRQHandler
|
||||
.thumb_set PVD_IRQHandler,Default_Handler |
||||
|
||||
.weak TAMP_STAMP_IRQHandler
|
||||
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler |
||||
|
||||
.weak RTC_WKUP_IRQHandler
|
||||
.thumb_set RTC_WKUP_IRQHandler,Default_Handler |
||||
|
||||
.weak FLASH_IRQHandler
|
||||
.thumb_set FLASH_IRQHandler,Default_Handler |
||||
|
||||
.weak RCC_IRQHandler
|
||||
.thumb_set RCC_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI0_IRQHandler
|
||||
.thumb_set EXTI0_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI1_IRQHandler
|
||||
.thumb_set EXTI1_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI2_IRQHandler
|
||||
.thumb_set EXTI2_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI3_IRQHandler
|
||||
.thumb_set EXTI3_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI4_IRQHandler
|
||||
.thumb_set EXTI4_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream0_IRQHandler
|
||||
.thumb_set DMA1_Stream0_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream1_IRQHandler
|
||||
.thumb_set DMA1_Stream1_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream2_IRQHandler
|
||||
.thumb_set DMA1_Stream2_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream3_IRQHandler
|
||||
.thumb_set DMA1_Stream3_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream4_IRQHandler
|
||||
.thumb_set DMA1_Stream4_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream5_IRQHandler
|
||||
.thumb_set DMA1_Stream5_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream6_IRQHandler
|
||||
.thumb_set DMA1_Stream6_IRQHandler,Default_Handler |
||||
|
||||
.weak ADC_IRQHandler
|
||||
.thumb_set ADC_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN1_TX_IRQHandler
|
||||
.thumb_set CAN1_TX_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN1_RX0_IRQHandler
|
||||
.thumb_set CAN1_RX0_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN1_RX1_IRQHandler
|
||||
.thumb_set CAN1_RX1_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN1_SCE_IRQHandler
|
||||
.thumb_set CAN1_SCE_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI9_5_IRQHandler
|
||||
.thumb_set EXTI9_5_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM1_BRK_TIM9_IRQHandler
|
||||
.thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM1_UP_TIM10_IRQHandler
|
||||
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM1_TRG_COM_TIM11_IRQHandler
|
||||
.thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM1_CC_IRQHandler
|
||||
.thumb_set TIM1_CC_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM2_IRQHandler
|
||||
.thumb_set TIM2_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM3_IRQHandler
|
||||
.thumb_set TIM3_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM4_IRQHandler
|
||||
.thumb_set TIM4_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C1_EV_IRQHandler
|
||||
.thumb_set I2C1_EV_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C1_ER_IRQHandler
|
||||
.thumb_set I2C1_ER_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C2_EV_IRQHandler
|
||||
.thumb_set I2C2_EV_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C2_ER_IRQHandler
|
||||
.thumb_set I2C2_ER_IRQHandler,Default_Handler |
||||
|
||||
.weak SPI1_IRQHandler
|
||||
.thumb_set SPI1_IRQHandler,Default_Handler |
||||
|
||||
.weak SPI2_IRQHandler
|
||||
.thumb_set SPI2_IRQHandler,Default_Handler |
||||
|
||||
.weak USART1_IRQHandler
|
||||
.thumb_set USART1_IRQHandler,Default_Handler |
||||
|
||||
.weak USART2_IRQHandler
|
||||
.thumb_set USART2_IRQHandler,Default_Handler |
||||
|
||||
.weak USART3_IRQHandler
|
||||
.thumb_set USART3_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI15_10_IRQHandler
|
||||
.thumb_set EXTI15_10_IRQHandler,Default_Handler |
||||
|
||||
.weak RTC_Alarm_IRQHandler
|
||||
.thumb_set RTC_Alarm_IRQHandler,Default_Handler |
||||
|
||||
.weak OTG_FS_WKUP_IRQHandler
|
||||
.thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM8_BRK_TIM12_IRQHandler
|
||||
.thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM8_UP_TIM13_IRQHandler
|
||||
.thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM8_TRG_COM_TIM14_IRQHandler
|
||||
.thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM8_CC_IRQHandler
|
||||
.thumb_set TIM8_CC_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream7_IRQHandler
|
||||
.thumb_set DMA1_Stream7_IRQHandler,Default_Handler |
||||
|
||||
.weak FSMC_IRQHandler
|
||||
.thumb_set FSMC_IRQHandler,Default_Handler |
||||
|
||||
.weak SDIO_IRQHandler
|
||||
.thumb_set SDIO_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM5_IRQHandler
|
||||
.thumb_set TIM5_IRQHandler,Default_Handler |
||||
|
||||
.weak SPI3_IRQHandler
|
||||
.thumb_set SPI3_IRQHandler,Default_Handler |
||||
|
||||
.weak UART4_IRQHandler
|
||||
.thumb_set UART4_IRQHandler,Default_Handler |
||||
|
||||
.weak UART5_IRQHandler
|
||||
.thumb_set UART5_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM6_DAC_IRQHandler
|
||||
.thumb_set TIM6_DAC_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM7_IRQHandler
|
||||
.thumb_set TIM7_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream0_IRQHandler
|
||||
.thumb_set DMA2_Stream0_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream1_IRQHandler
|
||||
.thumb_set DMA2_Stream1_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream2_IRQHandler
|
||||
.thumb_set DMA2_Stream2_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream3_IRQHandler
|
||||
.thumb_set DMA2_Stream3_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream4_IRQHandler
|
||||
.thumb_set DMA2_Stream4_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN2_TX_IRQHandler
|
||||
.thumb_set CAN2_TX_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN2_RX0_IRQHandler
|
||||
.thumb_set CAN2_RX0_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN2_RX1_IRQHandler
|
||||
.thumb_set CAN2_RX1_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN2_SCE_IRQHandler
|
||||
.thumb_set CAN2_SCE_IRQHandler,Default_Handler |
||||
|
||||
.weak OTG_FS_IRQHandler
|
||||
.thumb_set OTG_FS_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream5_IRQHandler
|
||||
.thumb_set DMA2_Stream5_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream6_IRQHandler
|
||||
.thumb_set DMA2_Stream6_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream7_IRQHandler
|
||||
.thumb_set DMA2_Stream7_IRQHandler,Default_Handler |
||||
|
||||
.weak USART6_IRQHandler
|
||||
.thumb_set USART6_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C3_EV_IRQHandler
|
||||
.thumb_set I2C3_EV_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C3_ER_IRQHandler
|
||||
.thumb_set I2C3_ER_IRQHandler,Default_Handler |
||||
|
||||
.weak OTG_HS_EP1_OUT_IRQHandler
|
||||
.thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler |
||||
|
||||
.weak OTG_HS_EP1_IN_IRQHandler
|
||||
.thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler |
||||
|
||||
.weak OTG_HS_WKUP_IRQHandler
|
||||
.thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler |
||||
|
||||
.weak OTG_HS_IRQHandler
|
||||
.thumb_set OTG_HS_IRQHandler,Default_Handler |
||||
|
||||
.weak HASH_RNG_IRQHandler
|
||||
.thumb_set HASH_RNG_IRQHandler,Default_Handler |
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,583 @@ |
||||
/** |
||||
****************************************************************************** |
||||
* @file startup_stm32f413xx.s
|
||||
* @author MCD Application Team
|
||||
* @version V2.6.0
|
||||
* @date 04-November-2016
|
||||
* @brief STM32F413xx Devices vector table for GCC based toolchains.
|
||||
* This module performs: |
||||
* - Set the initial SP |
||||
* - Set the initial PC == Reset_Handler, |
||||
* - Set the vector table entries with the exceptions ISR address |
||||
* - Branches to main in the C library (which eventually |
||||
* calls main()). |
||||
* After Reset the Cortex-M4 processor is in Thread mode, |
||||
* priority is Privileged, and the Stack is set to Main. |
||||
****************************************************************************** |
||||
* @attention
|
||||
* |
||||
* <h2><center>© COPYRIGHT 2016 STMicroelectronics</center></h2>
|
||||
* |
||||
* Redistribution and use in source and binary forms, with or without modification, |
||||
* are permitted provided that the following conditions are met: |
||||
* 1. Redistributions of source code must retain the above copyright notice, |
||||
* this list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, |
||||
* this list of conditions and the following disclaimer in the documentation |
||||
* and/or other materials provided with the distribution. |
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
****************************************************************************** |
||||
*/ |
||||
|
||||
.syntax unified
|
||||
.cpu cortex-m4 |
||||
.fpu softvfp
|
||||
.thumb |
||||
|
||||
.global g_pfnVectors
|
||||
.global Default_Handler
|
||||
|
||||
/* start address for the initialization values of the .data section.
|
||||
defined in linker script */ |
||||
.word _sidata
|
||||
/* start address for the .data section. defined in linker script */ |
||||
.word _sdata
|
||||
/* end address for the .data section. defined in linker script */ |
||||
.word _edata
|
||||
/* start address for the .bss section. defined in linker script */ |
||||
.word _sbss
|
||||
/* end address for the .bss section. defined in linker script */ |
||||
.word _ebss
|
||||
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ |
||||
|
||||
/** |
||||
* @brief This is the code that gets called when the processor first
|
||||
* starts execution following a reset event. Only the absolutely |
||||
* necessary set is performed, after which the application |
||||
* supplied main() routine is called.
|
||||
* @param None
|
||||
* @retval : None
|
||||
*/ |
||||
|
||||
.section .text.Reset_Handler |
||||
.weak Reset_Handler
|
||||
.type Reset_Handler, %function |
||||
Reset_Handler:
|
||||
ldr sp, =_estack /* set stack pointer */ |
||||
bl __initialize_hardware_early |
||||
|
||||
/* Copy the data segment initializers from flash to SRAM */ |
||||
movs r1, #0 |
||||
b LoopCopyDataInit |
||||
|
||||
CopyDataInit: |
||||
ldr r3, =_sidata |
||||
ldr r3, [r3, r1] |
||||
str r3, [r0, r1] |
||||
adds r1, r1, #4 |
||||
|
||||
LoopCopyDataInit: |
||||
ldr r0, =_sdata |
||||
ldr r3, =_edata |
||||
adds r2, r0, r1 |
||||
cmp r2, r3 |
||||
bcc CopyDataInit |
||||
ldr r2, =_sbss |
||||
b LoopFillZerobss |
||||
/* Zero fill the bss segment. */ |
||||
FillZerobss: |
||||
movs r3, #0 |
||||
str r3, [r2], #4 |
||||
|
||||
LoopFillZerobss: |
||||
ldr r3, = _ebss |
||||
cmp r2, r3 |
||||
bcc FillZerobss |
||||
|
||||
/* Call the clock system intitialization function.*/ |
||||
/* bl SystemInit */ |
||||
/* Call static constructors */ |
||||
/* bl __libc_init_array */ |
||||
/* Call the application's entry point.*/ |
||||
bl main |
||||
bx lr
|
||||
.size Reset_Handler, .-Reset_Handler |
||||
|
||||
/** |
||||
* @brief This is the code that gets called when the processor receives an
|
||||
* unexpected interrupt. This simply enters an infinite loop, preserving |
||||
* the system state for examination by a debugger. |
||||
* @param None
|
||||
* @retval None
|
||||
*/ |
||||
.section .text.Default_Handler,"ax",%progbits |
||||
Default_Handler: |
||||
Infinite_Loop: |
||||
b Infinite_Loop |
||||
.size Default_Handler, .-Default_Handler |
||||
/****************************************************************************** |
||||
* |
||||
* The minimal vector table for a Cortex M3. Note that the proper constructs |
||||
* must be placed on this to ensure that it ends up at physical address |
||||
* 0x0000.0000. |
||||
*
|
||||
*******************************************************************************/ |
||||
.section .isr_vector,"a",%progbits |
||||
.type g_pfnVectors, %object |
||||
.size g_pfnVectors, .-g_pfnVectors |
||||
|
||||
g_pfnVectors: |
||||
.word _estack
|
||||
.word Reset_Handler
|
||||
.word NMI_Handler
|
||||
.word HardFault_Handler
|
||||
.word MemManage_Handler
|
||||
.word BusFault_Handler
|
||||
.word UsageFault_Handler
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word SVC_Handler
|
||||
.word DebugMon_Handler
|
||||
.word 0
|
||||
.word PendSV_Handler
|
||||
.word SysTick_Handler
|
||||
|
||||
/* External Interrupts */ |
||||
.word WWDG_IRQHandler /* Window WatchDog */ |
||||
.word PVD_IRQHandler /* PVD through EXTI Line detection */ |
||||
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */ |
||||
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ |
||||
.word FLASH_IRQHandler /* FLASH */ |
||||
.word RCC_IRQHandler /* RCC */ |
||||
.word EXTI0_IRQHandler /* EXTI Line0 */ |
||||
.word EXTI1_IRQHandler /* EXTI Line1 */ |
||||
.word EXTI2_IRQHandler /* EXTI Line2 */ |
||||
.word EXTI3_IRQHandler /* EXTI Line3 */ |
||||
.word EXTI4_IRQHandler /* EXTI Line4 */ |
||||
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */ |
||||
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */ |
||||
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */ |
||||
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */ |
||||
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */ |
||||
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */ |
||||
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */ |
||||
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */ |
||||
.word CAN1_TX_IRQHandler /* CAN1 TX */ |
||||
.word CAN1_RX0_IRQHandler /* CAN1 RX0 */ |
||||
.word CAN1_RX1_IRQHandler /* CAN1 RX1 */ |
||||
.word CAN1_SCE_IRQHandler /* CAN1 SCE */ |
||||
.word EXTI9_5_IRQHandler /* External Line[9:5]s */ |
||||
.word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */ |
||||
.word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */ |
||||
.word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */ |
||||
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ |
||||
.word TIM2_IRQHandler /* TIM2 */ |
||||
.word TIM3_IRQHandler /* TIM3 */ |
||||
.word TIM4_IRQHandler /* TIM4 */ |
||||
.word I2C1_EV_IRQHandler /* I2C1 Event */ |
||||
.word I2C1_ER_IRQHandler /* I2C1 Error */ |
||||
.word I2C2_EV_IRQHandler /* I2C2 Event */ |
||||
.word I2C2_ER_IRQHandler /* I2C2 Error */ |
||||
.word SPI1_IRQHandler /* SPI1 */ |
||||
.word SPI2_IRQHandler /* SPI2 */ |
||||
.word USART1_IRQHandler /* USART1 */ |
||||
.word USART2_IRQHandler /* USART2 */ |
||||
.word USART3_IRQHandler /* USART3 */ |
||||
.word EXTI15_10_IRQHandler /* External Line[15:10]s */ |
||||
.word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */ |
||||
.word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */ |
||||
.word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */ |
||||
.word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */ |
||||
.word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */ |
||||
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare */ |
||||
.word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ |
||||
.word FSMC_IRQHandler /* FSMC */ |
||||
.word SDIO_IRQHandler /* SDIO */ |
||||
.word TIM5_IRQHandler /* TIM5 */ |
||||
.word SPI3_IRQHandler /* SPI3 */ |
||||
.word UART4_IRQHandler /* UART4 */ |
||||
.word UART5_IRQHandler /* UART5 */ |
||||
.word TIM6_DAC_IRQHandler /* TIM6, DAC1 and DAC2 */ |
||||
.word TIM7_IRQHandler /* TIM7 */ |
||||
.word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */ |
||||
.word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */ |
||||
.word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */ |
||||
.word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */ |
||||
.word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */ |
||||
.word DFSDM1_FLT0_IRQHandler /* DFSDM1 Filter0 */ |
||||
.word DFSDM1_FLT1_IRQHandler /* DFSDM1 Filter1 */ |
||||
.word CAN2_TX_IRQHandler /* CAN2 TX */ |
||||
.word CAN2_RX0_IRQHandler /* CAN2 RX0 */ |
||||
.word CAN2_RX1_IRQHandler /* CAN2 RX1 */ |
||||
.word CAN2_SCE_IRQHandler /* CAN2 SCE */ |
||||
.word OTG_FS_IRQHandler /* USB OTG FS */ |
||||
.word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */ |
||||
.word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */ |
||||
.word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */ |
||||
.word USART6_IRQHandler /* USART6 */ |
||||
.word I2C3_EV_IRQHandler /* I2C3 event */ |
||||
.word I2C3_ER_IRQHandler /* I2C3 error */ |
||||
.word CAN3_TX_IRQHandler /* CAN3 TX */ |
||||
.word CAN3_RX0_IRQHandler /* CAN3 RX0 */ |
||||
.word CAN3_RX1_IRQHandler /* CAN3 RX1 */ |
||||
.word CAN3_SCE_IRQHandler /* CAN3 SCE */ |
||||
.word 0 /* Reserved */ |
||||
.word 0 /* Reserved */ |
||||
.word RNG_IRQHandler /* RNG */ |
||||
.word FPU_IRQHandler /* FPU */ |
||||
.word UART7_IRQHandler /* UART7 */ |
||||
.word UART8_IRQHandler /* UART8 */ |
||||
.word SPI4_IRQHandler /* SPI4 */ |
||||
.word SPI5_IRQHandler /* SPI5 */ |
||||
.word 0 /* Reserved */ |
||||
.word SAI1_IRQHandler /* SAI1 */ |
||||
.word UART9_IRQHandler /* UART9 */ |
||||
.word UART10_IRQHandler /* UART10 */ |
||||
.word 0 /* Reserved */ |
||||
.word 0 /* Reserved */ |
||||
.word QUADSPI_IRQHandler /* QuadSPI */ |
||||
.word 0 /* Reserved */ |
||||
.word 0 /* Reserved */ |
||||
.word FMPI2C1_EV_IRQHandler /* FMPI2C1 Event */ |
||||
.word FMPI2C1_ER_IRQHandler /* FMPI2C1 Error */ |
||||
.word LPTIM1_IRQHandler /* LPTIM1 */ |
||||
.word DFSDM2_FLT0_IRQHandler /* DFSDM2 Filter0 */ |
||||
.word DFSDM2_FLT1_IRQHandler /* DFSDM2 Filter1 */ |
||||
.word DFSDM2_FLT2_IRQHandler /* DFSDM2 Filter2 */ |
||||
.word DFSDM2_FLT3_IRQHandler /* DFSDM2 Filter3 */ |
||||
|
||||
/******************************************************************************* |
||||
* |
||||
* Provide weak aliases for each Exception handler to the Default_Handler.
|
||||
* As they are weak aliases, any function with the same name will override
|
||||
* this definition. |
||||
* |
||||
*******************************************************************************/ |
||||
.weak NMI_Handler
|
||||
.thumb_set NMI_Handler,Default_Handler |
||||
|
||||
.weak HardFault_Handler
|
||||
.thumb_set HardFault_Handler,Default_Handler |
||||
|
||||
.weak MemManage_Handler
|
||||
.thumb_set MemManage_Handler,Default_Handler |
||||
|
||||
.weak BusFault_Handler
|
||||
.thumb_set BusFault_Handler,Default_Handler |
||||
|
||||
.weak UsageFault_Handler
|
||||
.thumb_set UsageFault_Handler,Default_Handler |
||||
|
||||
.weak SVC_Handler
|
||||
.thumb_set SVC_Handler,Default_Handler |
||||
|
||||
.weak DebugMon_Handler
|
||||
.thumb_set DebugMon_Handler,Default_Handler |
||||
|
||||
.weak PendSV_Handler
|
||||
.thumb_set PendSV_Handler,Default_Handler |
||||
|
||||
.weak SysTick_Handler
|
||||
.thumb_set SysTick_Handler,Default_Handler |
||||
|
||||
.weak WWDG_IRQHandler
|
||||
.thumb_set WWDG_IRQHandler,Default_Handler |
||||
|
||||
.weak PVD_IRQHandler
|
||||
.thumb_set PVD_IRQHandler,Default_Handler |
||||
|
||||
.weak TAMP_STAMP_IRQHandler
|
||||
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler |
||||
|
||||
.weak RTC_WKUP_IRQHandler
|
||||
.thumb_set RTC_WKUP_IRQHandler,Default_Handler |
||||
|
||||
.weak FLASH_IRQHandler
|
||||
.thumb_set FLASH_IRQHandler,Default_Handler |
||||
|
||||
.weak RCC_IRQHandler
|
||||
.thumb_set RCC_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI0_IRQHandler
|
||||
.thumb_set EXTI0_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI1_IRQHandler
|
||||
.thumb_set EXTI1_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI2_IRQHandler
|
||||
.thumb_set EXTI2_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI3_IRQHandler
|
||||
.thumb_set EXTI3_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI4_IRQHandler
|
||||
.thumb_set EXTI4_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream0_IRQHandler
|
||||
.thumb_set DMA1_Stream0_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream1_IRQHandler
|
||||
.thumb_set DMA1_Stream1_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream2_IRQHandler
|
||||
.thumb_set DMA1_Stream2_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream3_IRQHandler
|
||||
.thumb_set DMA1_Stream3_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream4_IRQHandler
|
||||
.thumb_set DMA1_Stream4_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream5_IRQHandler
|
||||
.thumb_set DMA1_Stream5_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream6_IRQHandler
|
||||
.thumb_set DMA1_Stream6_IRQHandler,Default_Handler |
||||
|
||||
.weak ADC_IRQHandler
|
||||
.thumb_set ADC_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN1_TX_IRQHandler
|
||||
.thumb_set CAN1_TX_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN1_RX0_IRQHandler
|
||||
.thumb_set CAN1_RX0_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN1_RX1_IRQHandler
|
||||
.thumb_set CAN1_RX1_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN1_SCE_IRQHandler
|
||||
.thumb_set CAN1_SCE_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI9_5_IRQHandler
|
||||
.thumb_set EXTI9_5_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM1_BRK_TIM9_IRQHandler
|
||||
.thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM1_UP_TIM10_IRQHandler
|
||||
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM1_TRG_COM_TIM11_IRQHandler
|
||||
.thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM1_CC_IRQHandler
|
||||
.thumb_set TIM1_CC_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM2_IRQHandler
|
||||
.thumb_set TIM2_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM3_IRQHandler
|
||||
.thumb_set TIM3_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM4_IRQHandler
|
||||
.thumb_set TIM4_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C1_EV_IRQHandler
|
||||
.thumb_set I2C1_EV_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C1_ER_IRQHandler
|
||||
.thumb_set I2C1_ER_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C2_EV_IRQHandler
|
||||
.thumb_set I2C2_EV_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C2_ER_IRQHandler
|
||||
.thumb_set I2C2_ER_IRQHandler,Default_Handler |
||||
|
||||
.weak SPI1_IRQHandler
|
||||
.thumb_set SPI1_IRQHandler,Default_Handler |
||||
|
||||
.weak SPI2_IRQHandler
|
||||
.thumb_set SPI2_IRQHandler,Default_Handler |
||||
|
||||
.weak USART1_IRQHandler
|
||||
.thumb_set USART1_IRQHandler,Default_Handler |
||||
|
||||
.weak USART2_IRQHandler
|
||||
.thumb_set USART2_IRQHandler,Default_Handler |
||||
|
||||
.weak USART3_IRQHandler
|
||||
.thumb_set USART3_IRQHandler,Default_Handler |
||||
|
||||
.weak EXTI15_10_IRQHandler
|
||||
.thumb_set EXTI15_10_IRQHandler,Default_Handler |
||||
|
||||
.weak RTC_Alarm_IRQHandler
|
||||
.thumb_set RTC_Alarm_IRQHandler,Default_Handler |
||||
|
||||
.weak OTG_FS_WKUP_IRQHandler
|
||||
.thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM8_BRK_TIM12_IRQHandler
|
||||
.thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM8_UP_TIM13_IRQHandler
|
||||
.thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM8_TRG_COM_TIM14_IRQHandler
|
||||
.thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM8_CC_IRQHandler
|
||||
.thumb_set TIM8_CC_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA1_Stream7_IRQHandler
|
||||
.thumb_set DMA1_Stream7_IRQHandler,Default_Handler |
||||
|
||||
.weak FSMC_IRQHandler
|
||||
.thumb_set FSMC_IRQHandler,Default_Handler
|
||||
|
||||
.weak SDIO_IRQHandler
|
||||
.thumb_set SDIO_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM5_IRQHandler
|
||||
.thumb_set TIM5_IRQHandler,Default_Handler |
||||
|
||||
.weak SPI3_IRQHandler
|
||||
.thumb_set SPI3_IRQHandler,Default_Handler |
||||
|
||||
.weak UART4_IRQHandler
|
||||
.thumb_set UART4_IRQHandler,Default_Handler |
||||
|
||||
.weak UART5_IRQHandler
|
||||
.thumb_set UART5_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM6_DAC_IRQHandler
|
||||
.thumb_set TIM6_DAC_IRQHandler,Default_Handler |
||||
|
||||
.weak TIM7_IRQHandler
|
||||
.thumb_set TIM7_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream0_IRQHandler
|
||||
.thumb_set DMA2_Stream0_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream1_IRQHandler
|
||||
.thumb_set DMA2_Stream1_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream2_IRQHandler
|
||||
.thumb_set DMA2_Stream2_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream3_IRQHandler
|
||||
.thumb_set DMA2_Stream3_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream4_IRQHandler
|
||||
.thumb_set DMA2_Stream4_IRQHandler,Default_Handler |
||||
|
||||
.weak DFSDM1_FLT0_IRQHandler
|
||||
.thumb_set DFSDM1_FLT0_IRQHandler,Default_Handler |
||||
|
||||
.weak DFSDM1_FLT1_IRQHandler
|
||||
.thumb_set DFSDM1_FLT1_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN2_TX_IRQHandler
|
||||
.thumb_set CAN2_TX_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN2_RX0_IRQHandler
|
||||
.thumb_set CAN2_RX0_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN2_RX1_IRQHandler
|
||||
.thumb_set CAN2_RX1_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN2_SCE_IRQHandler
|
||||
.thumb_set CAN2_SCE_IRQHandler,Default_Handler |
||||
|
||||
.weak OTG_FS_IRQHandler
|
||||
.thumb_set OTG_FS_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream5_IRQHandler
|
||||
.thumb_set DMA2_Stream5_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream6_IRQHandler
|
||||
.thumb_set DMA2_Stream6_IRQHandler,Default_Handler |
||||
|
||||
.weak DMA2_Stream7_IRQHandler
|
||||
.thumb_set DMA2_Stream7_IRQHandler,Default_Handler |
||||
|
||||
.weak USART6_IRQHandler
|
||||
.thumb_set USART6_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C3_EV_IRQHandler
|
||||
.thumb_set I2C3_EV_IRQHandler,Default_Handler |
||||
|
||||
.weak I2C3_ER_IRQHandler
|
||||
.thumb_set I2C3_ER_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN3_TX_IRQHandler
|
||||
.thumb_set CAN3_TX_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN3_RX0_IRQHandler
|
||||
.thumb_set CAN3_RX0_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN3_RX1_IRQHandler
|
||||
.thumb_set CAN3_RX1_IRQHandler,Default_Handler |
||||
|
||||
.weak CAN3_SCE_IRQHandler
|
||||
.thumb_set CAN3_SCE_IRQHandler,Default_Handler
|
||||
|
||||
.weak RNG_IRQHandler
|
||||
.thumb_set RNG_IRQHandler,Default_Handler |
||||
|
||||
.weak FPU_IRQHandler
|
||||
.thumb_set FPU_IRQHandler,Default_Handler |
||||
|
||||
.weak UART7_IRQHandler
|
||||
.thumb_set UART7_IRQHandler,Default_Handler |
||||
|
||||
.weak UART8_IRQHandler
|
||||
.thumb_set UART8_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI4_IRQHandler
|
||||
.thumb_set SPI4_IRQHandler,Default_Handler |
||||
|
||||
.weak SPI5_IRQHandler
|
||||
.thumb_set SPI5_IRQHandler,Default_Handler |
||||
|
||||
.weak SAI1_IRQHandler
|
||||
.thumb_set SAI1_IRQHandler,Default_Handler |
||||
|
||||
.weak UART9_IRQHandler
|
||||
.thumb_set UART9_IRQHandler,Default_Handler |
||||
|
||||
.weak UART10_IRQHandler
|
||||
.thumb_set UART10_IRQHandler,Default_Handler
|
||||
|
||||
.weak QUADSPI_IRQHandler
|
||||
.thumb_set QUADSPI_IRQHandler,Default_Handler |
||||
|
||||
.weak FMPI2C1_EV_IRQHandler
|
||||
.thumb_set FMPI2C1_EV_IRQHandler,Default_Handler |
||||
|
||||
.weak FMPI2C1_ER_IRQHandler
|
||||
.thumb_set FMPI2C1_ER_IRQHandler,Default_Handler |
||||
|
||||
.weak LPTIM1_IRQHandler
|
||||
.thumb_set LPTIM1_IRQHandler,Default_Handler |
||||
|
||||
.weak DFSDM2_FLT0_IRQHandler
|
||||
.thumb_set DFSDM2_FLT0_IRQHandler,Default_Handler |
||||
|
||||
.weak DFSDM2_FLT1_IRQHandler
|
||||
.thumb_set DFSDM2_FLT1_IRQHandler,Default_Handler |
||||
|
||||
.weak DFSDM2_FLT2_IRQHandler
|
||||
.thumb_set DFSDM2_FLT2_IRQHandler,Default_Handler |
||||
|
||||
.weak DFSDM2_FLT3_IRQHandler
|
||||
.thumb_set DFSDM2_FLT3_IRQHandler,Default_Handler |
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@ -0,0 +1,165 @@ |
||||
/* |
||||
***************************************************************************** |
||||
** |
||||
** File : stm32_flash.ld |
||||
** |
||||
** Abstract : Linker script for STM32F407VG Device with |
||||
** 1024KByte FLASH, 192KByte RAM |
||||
** |
||||
** Set heap size, stack size and stack location according |
||||
** to application requirements. |
||||
** |
||||
** Set memory bank area and size if external memory is used. |
||||
** |
||||
** Target : STMicroelectronics STM32 |
||||
** |
||||
** Environment : Atollic TrueSTUDIO(R) |
||||
** |
||||
** Distribution: The file is distributed “as is,” without any warranty |
||||
** of any kind. |
||||
** |
||||
** (c)Copyright Atollic AB. |
||||
** You may use this file as-is or modify it according to the needs of your |
||||
** project. Distribution of this file (unmodified or modified) is not |
||||
** permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the |
||||
** rights to distribute the assembled, compiled & linked contents of this |
||||
** file as part of an application binary file, provided that it is built |
||||
** using the Atollic TrueSTUDIO(R) toolchain. |
||||
** |
||||
***************************************************************************** |
||||
*/ |
||||
|
||||
/* Entry Point */ |
||||
ENTRY(Reset_Handler) |
||||
|
||||
/* Highest address of the user mode stack */ |
||||
enter_bootloader_mode = 0x2001FFFC; |
||||
_estack = 0x2001FFFC; /* end of 128K RAM on AHB bus*/ |
||||
_app_start = 0x08004000; /* Reserve 16K for bootloader */ |
||||
|
||||
/* Generate a link error if heap and stack don't fit into RAM */ |
||||
_Min_Heap_Size = 0; /* required amount of heap */ |
||||
_Min_Stack_Size = 0x400; /* required amount of stack */ |
||||
|
||||
/* Specify the memory areas */ |
||||
MEMORY |
||||
{ |
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K |
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K |
||||
MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K |
||||
} |
||||
|
||||
/* Define output sections */ |
||||
SECTIONS |
||||
{ |
||||
/* The startup code goes first into FLASH */ |
||||
.isr_vector : |
||||
{ |
||||
. = ALIGN(4); |
||||
KEEP(*(.isr_vector)) /* Startup code */ |
||||
. = ALIGN(4); |
||||
} >FLASH |
||||
|
||||
/* The program code and other data goes into FLASH */ |
||||
.text : |
||||
{ |
||||
. = ALIGN(4); |
||||
*(.text) /* .text sections (code) */ |
||||
*(.text*) /* .text* sections (code) */ |
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */ |
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */ |
||||
*(.glue_7) /* glue arm to thumb code */ |
||||
*(.glue_7t) /* glue thumb to arm code */ |
||||
*(.eh_frame) |
||||
|
||||
KEEP (*(.init)) |
||||
KEEP (*(.fini)) |
||||
|
||||
. = ALIGN(4); |
||||
_etext = .; /* define a global symbols at end of code */ |
||||
_exit = .; |
||||
} >FLASH |
||||
|
||||
|
||||
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH |
||||
.ARM : { |
||||
__exidx_start = .; |
||||
*(.ARM.exidx*) |
||||
__exidx_end = .; |
||||
} >FLASH |
||||
|
||||
.preinit_array : |
||||
{ |
||||
PROVIDE_HIDDEN (__preinit_array_start = .); |
||||
KEEP (*(.preinit_array*)) |
||||
PROVIDE_HIDDEN (__preinit_array_end = .); |
||||
} >FLASH |
||||
.init_array : |
||||
{ |
||||
PROVIDE_HIDDEN (__init_array_start = .); |
||||
KEEP (*(SORT(.init_array.*))) |
||||
KEEP (*(.init_array*)) |
||||
PROVIDE_HIDDEN (__init_array_end = .); |
||||
} >FLASH |
||||
.fini_array : |
||||
{ |
||||
PROVIDE_HIDDEN (__fini_array_start = .); |
||||
KEEP (*(.fini_array*)) |
||||
KEEP (*(SORT(.fini_array.*))) |
||||
PROVIDE_HIDDEN (__fini_array_end = .); |
||||
} >FLASH |
||||
|
||||
/* used by the startup to initialize data */ |
||||
_sidata = .; |
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */ |
||||
.data : AT ( _sidata ) |
||||
{ |
||||
. = ALIGN(4); |
||||
_sdata = .; /* create a global symbol at data start */ |
||||
*(.data) /* .data sections */ |
||||
*(.data*) /* .data* sections */ |
||||
|
||||
. = ALIGN(4); |
||||
_edata = .; /* define a global symbol at data end */ |
||||
} >RAM |
||||
|
||||
/* Uninitialized data section */ |
||||
. = ALIGN(4); |
||||
.bss : |
||||
{ |
||||
/* This is used by the startup in order to initialize the .bss secion */ |
||||
_sbss = .; /* define a global symbol at bss start */ |
||||
__bss_start__ = _sbss; |
||||
*(.bss) |
||||
*(.bss*) |
||||
*(COMMON) |
||||
|
||||
. = ALIGN(4); |
||||
_ebss = .; /* define a global symbol at bss end */ |
||||
__bss_end__ = _ebss; |
||||
} >RAM |
||||
|
||||
/* User_heap_stack section, used to check that there is enough RAM left */ |
||||
._user_heap_stack : |
||||
{ |
||||
. = ALIGN(4); |
||||
PROVIDE ( end = . ); |
||||
PROVIDE ( _end = . ); |
||||
. = . + _Min_Heap_Size; |
||||
. = . + _Min_Stack_Size; |
||||
. = ALIGN(4); |
||||
} >RAM |
||||
|
||||
/* MEMORY_bank1 section, code must be located here explicitly */ |
||||
/* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ |
||||
.memory_b1_text : |
||||
{ |
||||
*(.mb1text) /* .mb1text sections (code) */ |
||||
*(.mb1text*) /* .mb1text* sections (code) */ |
||||
*(.mb1rodata) /* read-only data (constants) */ |
||||
*(.mb1rodata*) |
||||
} >MEMORY_B1 |
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) } |
||||
} |
@ -0,0 +1,35 @@ |
||||
/*
|
||||
gcc -DTEST_RSA test_rsa.c ../crypto/rsa.c ../crypto/sha.c && ./a.out |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
#define MAX_LEN 0x40000 |
||||
char buf[MAX_LEN]; |
||||
|
||||
#include "../crypto/sha.h" |
||||
#include "../crypto/rsa.h" |
||||
#include "../obj/cert.h" |
||||
|
||||
int main() { |
||||
FILE *f = fopen("../obj/panda.bin", "rb"); |
||||
int tlen = fread(buf, 1, MAX_LEN, f); |
||||
fclose(f); |
||||
printf("read %d\n", tlen); |
||||
uint32_t *_app_start = (uint32_t *)buf; |
||||
|
||||
int len = _app_start[0]; |
||||
char digest[SHA_DIGEST_SIZE]; |
||||
SHA_hash(&_app_start[1], len-4, digest); |
||||
printf("SHA hash done\n"); |
||||
|
||||
if (!RSA_verify(&rsa_key, ((void*)&_app_start[0]) + len, RSANUMBYTES, digest, SHA_DIGEST_SIZE)) { |
||||
printf("RSA fail\n"); |
||||
} else { |
||||
printf("RSA match!!!\n"); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:76a07bd598aacbfbc81b95b9b33ef1282fedfe6e49819179445a457c4982a979 |
||||
size 116048 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:ea03146271626a42997a38a0c2d53000b7c788347bb7ead973b7aeb652168bae |
||||
size 159256 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:d33372170341cf50a79ec72e6c0dca51d6116c73203beaaf307c729d2753d178 |
||||
size 152156 |
@ -0,0 +1,33 @@ |
||||
#!/usr/bin/env python |
||||
from __future__ import print_function |
||||
|
||||
import sys |
||||
import time |
||||
import usb1 |
||||
|
||||
def enter_download_mode(device): |
||||
handle = device.open() |
||||
handle.claimInterface(0) |
||||
|
||||
try: |
||||
handle.controlWrite(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xd1, 0, 0, b'') |
||||
except (usb1.USBErrorIO, usb1.USBErrorPipe) as e: |
||||
print("Device download mode enabled.") |
||||
time.sleep(1) |
||||
else: |
||||
print("Device failed to enter download mode.") |
||||
sys.exit(1) |
||||
|
||||
def find_first_panda(context=None): |
||||
context = context or usb1.USBContext() |
||||
for device in context.getDeviceList(skip_on_error=True): |
||||
if device.getVendorID() == 0xbbaa and device.getProductID()&0xFF00 == 0xdd00: |
||||
return device |
||||
|
||||
if __name__ == "__main__": |
||||
panda_dev = find_first_panda() |
||||
if panda_dev == None: |
||||
print("no device found") |
||||
sys.exit(0) |
||||
print("found device") |
||||
enter_download_mode(panda_dev) |
@ -0,0 +1,8 @@ |
||||
proxy |
||||
*.bin |
||||
esp-open-sdk |
||||
a.out |
||||
cert.h |
||||
gitversion.h |
||||
esp-open-sdk.dmg |
||||
obj/* |
@ -0,0 +1,101 @@ |
||||
ELM327 support for panda |
||||
====== |
||||
The panda now has basic ELM327 support. |
||||
|
||||
### What is ELM327? |
||||
|
||||
ELM327 is a command protocol for interfacing with cars using an OBD-II |
||||
port to read [list](standard vehicle diagnostic codes). ELM327 |
||||
originally referred to a line of programmable microcontrollers that |
||||
implemented the ELM327 command protocol, and are still being |
||||
developed. |
||||
|
||||
ELM327 devices present a shell and commands are sent via a UART. The |
||||
official ELM327 chips only support raw UART communication, but most |
||||
devices built with the ELM327 devices (official or clones) expose the |
||||
UART in a more modern way (Wifi, USB, etc). |
||||
|
||||
Mechanics use ELM to diagnose vehicles, and reset fault codes in the |
||||
car's computer after fixing the issue (turning off the dreaded check |
||||
engine light). Car owners can use ELM devices to perform the same |
||||
diagnostics in their garage using either a raw terminal to send |
||||
commands to the ELM device directly, or using a GUI (like one of |
||||
several popular smart phone apps) to translate the OBD error codes |
||||
into readable messages. These GUIs also often allow monitoring of the |
||||
performance of the car's speed, engine rpm, etc. |
||||
|
||||
The panda natively supports sending all the important OBD diagnostic |
||||
messages, but ELM327 support removes the need for the user to manually |
||||
craft CAN or LIN packets using the native panda API, and grants |
||||
compatibility with many existing tools. |
||||
|
||||
[Wikipedia](https://en.wikipedia.org/wiki/ELM327) can provide |
||||
additional information. |
||||
|
||||
### OBD Protocols? |
||||
|
||||
While the commands that the OBD standard describe are in fact |
||||
standard, there are several different protocols that those messages |
||||
can be sent and received with. All cars after 1991 support one of |
||||
these protocols. Which one depends on the car's year and country, as |
||||
legal requirements change over the years. |
||||
|
||||
The panda supports the most popular/modern of these protocols, and all |
||||
but two can be added as needed. Below is a chart of the OBD-II |
||||
protocols supported by the panda. |
||||
|
||||
| Protocol | Support Status | |
||||
| --- | --- | |
||||
| SAE J1850 PWM (41.6 kbit/s) | Never/Obsolete | |
||||
| SAE J1850 VPW (10.4 kbit/s) | Never/Obsolete | |
||||
| ISO 9141-2 (5 baud init, 10.4 kbit/s) | Unsupported | |
||||
| ISO 14230-4 KWP (5 baud init, 10.4 kbit/s) | Unsupported | |
||||
| ISO 14230-4 KWP (fast init, 10.4 kbit/s) | Supported | |
||||
| ISO 15765-4 CAN (11 bit ID, 500 kbit/s) | Supported | |
||||
| ISO 15765-4 CAN (29 bit ID, 500 kbit/s) | Supported | |
||||
| ISO 15765-4 CAN (11 bit ID, 250 kbit/s) | Supported | |
||||
| ISO 15765-4 CAN (29 bit ID, 250 kbit/s) | Supported | |
||||
| SAE J1939 (250kbps) | Unsupported | |
||||
|
||||
### The Implementation |
||||
|
||||
The panda ELM327 implementation is not a full implementation of all |
||||
the features of the official ELM327 microcontroller. Like most ELM327 |
||||
clones, the panda reports its ELM version as the unreleased version |
||||
1.5, despite only implementing commands from protocol version 1.0. |
||||
|
||||
|
||||
### Testing |
||||
|
||||
These tests require two pandas. One to be tested, and the second to |
||||
simulate the vehicle. |
||||
|
||||
The panda used to simulate the vehicle must be plugged into a USB port |
||||
of the testing computer. |
||||
|
||||
The computer running the tests must be connected to the panda being |
||||
tested's wifi network. |
||||
|
||||
The following command will run the tests (nosetest should work fine |
||||
instead of pytest if you still prefer using that). The CANSIMSERIAL |
||||
environment variable will force the car simulator to use the correct |
||||
panda as the simulator if multiple pandas are attached via usb to the |
||||
host computer. |
||||
|
||||
``` |
||||
CANSIMSERIAL=car_sim_panda_serial pytest tests/automated/elm_wifi.py |
||||
``` |
||||
|
||||
A single test can be run by putting the test name after the file name |
||||
and two colons, like so: |
||||
|
||||
``` |
||||
CANSIMSERIAL=car_sim_panda_serial pytest tests/automated/elm_wifi.py::test_important_thing |
||||
``` |
||||
|
||||
For more detail, provide the -s (show output) and the -vv (very |
||||
verbose) flags. |
||||
|
||||
``` |
||||
CANSIMSERIAL=car_sim_panda_serial pytest -s -vv tests/automated/elm_wifi.py |
||||
``` |
@ -0,0 +1,74 @@ |
||||
PATH := esp-open-sdk/xtensa-lx106-elf/bin:$(PATH)
|
||||
CC = esp-open-sdk/xtensa-lx106-elf/bin/xtensa-lx106-elf-gcc
|
||||
CFLAGS = -Iinclude/ -I. -I../ -mlongcalls -Iesp-open-sdk/ESP8266_NONOS_SDK_V1.5.4_16_05_20/driver_lib/include -std=c99 -DICACHE_FLASH
|
||||
LDLIBS = -nostdlib -Wl,--start-group -lmain -lnet80211 -lwpa -llwip -lpp -lphy -Wl,--end-group -lgcc -ldriver -Wl,--gc-sections
|
||||
LDFLAGS = -Teagle.app.v6.ld
|
||||
OBJCP = esp-open-sdk/xtensa-lx106-elf/bin/xtensa-lx106-elf-objcopy
|
||||
SDK_BASE = esp-open-sdk/ESP8266_NONOS_SDK_V1.5.4_16_05_20
|
||||
|
||||
ifeq ($(RELEASE),1) |
||||
CERT = ../../pandaextra/certs/releaseesp
|
||||
else |
||||
CERT = ../certs/debugesp
|
||||
CFLAGS += "-DALLOW_DEBUG"
|
||||
endif |
||||
|
||||
flashall: user1.bin user2.bin |
||||
../python/esptool.py write_flash 0 $(SDK_BASE)/bin/boot_v1.5.bin 0x01000 user1.bin 0x81000 user2.bin 0x3FE000 $(SDK_BASE)/bin/blank.bin
|
||||
|
||||
proxy-0x00000.bin: proxy |
||||
../python/esptool.py elf2image $^
|
||||
|
||||
proxy: proxy.o elm327.o webserver.o sha.o |
||||
|
||||
obj/proxy.o: proxy.c |
||||
$(CC) $(CFLAGS) -c $^ -o $@
|
||||
|
||||
obj/elm327.o: elm327.c |
||||
$(CC) $(CFLAGS) -c $^ -o $@
|
||||
|
||||
obj/webserver.o: webserver.c obj/cert.h obj/gitversion.h |
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
obj/cert.h: ../crypto/getcertheader.py |
||||
../crypto/getcertheader.py ../certs/debugesp.pub ../certs/releaseesp.pub > obj/cert.h
|
||||
|
||||
include ../common/version.mk |
||||
|
||||
obj/sha.o: ../crypto/sha.c |
||||
$(CC) $(CFLAGS) -c $^ -o $@
|
||||
|
||||
obj/rsa.o: ../crypto/rsa.c |
||||
$(CC) $(CFLAGS) -c $^ -o $@
|
||||
|
||||
oldflash: proxy-0x00000.bin |
||||
../python/esptool.py write_flash 0 proxy-0x00000.bin 0x40000 proxy-0x40000.bin
|
||||
|
||||
user1.bin: obj/proxy.o obj/elm327.o obj/webserver.o obj/sha.o obj/rsa.o |
||||
$(CC) $(CFLAGS) $^ -o a.out -L$(SDK_BASE)/ld -T$(SDK_BASE)/ld/eagle.app.v6.new.1024.app1.ld $(LDLIBS)
|
||||
$(OBJCP) --only-section .text -O binary a.out eagle.app.v6.text.bin
|
||||
$(OBJCP) --only-section .data -O binary a.out eagle.app.v6.data.bin
|
||||
$(OBJCP) --only-section .rodata -O binary a.out eagle.app.v6.rodata.bin
|
||||
$(OBJCP) --only-section .irom0.text -O binary a.out eagle.app.v6.irom0text.bin
|
||||
COMPILE=gcc python ./esp-open-sdk/ESP8266_NONOS_SDK_V1.5.4_16_05_20/tools/gen_appbin.py a.out 2 0 32 4 0
|
||||
rm -f eagle.app.v6.*.bin
|
||||
mv eagle.app.flash.bin $@
|
||||
../crypto/sign.py $@ $@ $(CERT)
|
||||
|
||||
user2.bin: obj/proxy.o obj/elm327.o obj/webserver.o obj/sha.o obj/rsa.o |
||||
$(CC) $(CFLAGS) $^ -o a.out -L$(SDK_BASE)/ld -T$(SDK_BASE)/ld/eagle.app.v6.new.1024.app2.ld $(LDLIBS)
|
||||
$(OBJCP) --only-section .text -O binary a.out eagle.app.v6.text.bin
|
||||
$(OBJCP) --only-section .data -O binary a.out eagle.app.v6.data.bin
|
||||
$(OBJCP) --only-section .rodata -O binary a.out eagle.app.v6.rodata.bin
|
||||
$(OBJCP) --only-section .irom0.text -O binary a.out eagle.app.v6.irom0text.bin
|
||||
COMPILE=gcc python ./esp-open-sdk/ESP8266_NONOS_SDK_V1.5.4_16_05_20/tools/gen_appbin.py a.out 2 0 32 4 0
|
||||
rm -f eagle.app.v6.*.bin
|
||||
mv eagle.app.flash.bin $@
|
||||
../crypto/sign.py $@ $@ $(CERT)
|
||||
|
||||
ota: user1.bin user2.bin |
||||
curl http://192.168.0.10/espupdate1 --upload-file user1.bin
|
||||
curl http://192.168.0.10/espupdate2 --upload-file user2.bin
|
||||
|
||||
clean: |
||||
rm -f proxy proxy.o proxy-0x00000.bin proxy-0x40000.bin eagle.app.* user1.bin user2.bin a.out obj/*
|
@ -0,0 +1,22 @@ |
||||
|
||||
Dependencies |
||||
----- |
||||
|
||||
**Debian / Ubuntu** |
||||
|
||||
``` |
||||
./get_sdk.sh |
||||
``` |
||||
|
||||
**Mac** |
||||
|
||||
``` |
||||
./get_sdk_mac.sh |
||||
``` |
||||
|
||||
Programming |
||||
----- |
||||
|
||||
``` |
||||
make |
||||
``` |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@ |
||||
#!/bin/bash |
||||
sudo apt-get install make unrar-free autoconf automake libtool gcc g++ gperf \ |
||||
flex bison texinfo gawk ncurses-dev libexpat-dev python-dev python python-serial \ |
||||
sed git unzip bash help2man wget bzip2 |
||||
# huh? |
||||
sudo apt-get install libtool |
||||
sudo apt-get install libtool-bin |
||||
git clone --recursive https://github.com/pfalcon/esp-open-sdk.git |
||||
cd esp-open-sdk |
||||
git checkout 03f5e898a059451ec5f3de30e7feff30455f7cec |
||||
LD_LIBRARY_PATH="" make STANDALONE=y |
||||
|
@ -0,0 +1,5 @@ |
||||
#!/bin/bash |
||||
git clone --recursive https://github.com/pfalcon/esp-open-sdk.git |
||||
cd esp-open-sdk |
||||
git checkout 03f5e898a059451ec5f3de30e7feff30455f7cec |
||||
LD_LIBRARY_PATH="" make STANDALONE=y |
@ -0,0 +1,32 @@ |
||||
#!/bin/bash |
||||
|
||||
# from http://www.esp8266.com/wiki/doku.php?id=setup-osx-compiler-esp8266 |
||||
|
||||
brew install gnu-sed --with-default-names |
||||
brew tap homebrew/dupes |
||||
brew install gperf |
||||
brew install grep |
||||
brew install autoconf |
||||
brew install binutils |
||||
brew install gawk |
||||
brew install wget |
||||
brew install automake |
||||
brew install libtool |
||||
brew install help2man |
||||
|
||||
brew uninstall gperf |
||||
|
||||
hdiutil create esp-open-sdk.dmg -volname "esp-open-sdk" -size 10g -fs "Case-sensitive HFS+" |
||||
hdiutil mount esp-open-sdk.dmg |
||||
ln -s /Volumes/esp-open-sdk esp-open-sdk |
||||
cd esp-open-sdk |
||||
|
||||
git init |
||||
git remote add origin https://github.com/pfalcon/esp-open-sdk.git |
||||
git fetch origin |
||||
git checkout 03f5e898a059451ec5f3de30e7feff30455f7cec |
||||
git submodule init |
||||
git submodule update --recursive |
||||
|
||||
make STANDALONE=y |
||||
|
@ -0,0 +1,78 @@ |
||||
#ifndef ESPMISSINGINCLUDES_H |
||||
#define ESPMISSINGINCLUDES_H |
||||
|
||||
#include <stdint.h> |
||||
#include <c_types.h> |
||||
#include <os_type.h> |
||||
|
||||
|
||||
int strcasecmp(const char *a, const char *b); |
||||
#ifndef FREERTOS |
||||
#include <eagle_soc.h> |
||||
#include <ets_sys.h> |
||||
//Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere.
|
||||
//MOST OF THESE ARE GUESSED! but they seem to swork and shut up the compiler.
|
||||
typedef struct espconn espconn; |
||||
|
||||
int atoi(const char *nptr); |
||||
void ets_install_putc1(void *routine); |
||||
void ets_isr_attach(int intr, void *handler, void *arg); |
||||
void ets_isr_mask(unsigned intr); |
||||
void ets_isr_unmask(unsigned intr); |
||||
int ets_memcmp(const void *s1, const void *s2, size_t n); |
||||
void *ets_memcpy(void *dest, const void *src, size_t n); |
||||
void *ets_memset(void *s, int c, size_t n); |
||||
int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (printf, 2, 3))); |
||||
int ets_str2macaddr(void *, void *); |
||||
int ets_strcmp(const char *s1, const char *s2); |
||||
char *ets_strcpy(char *dest, const char *src); |
||||
size_t ets_strlen(const char *s); |
||||
int ets_strncmp(const char *s1, const char *s2, int len); |
||||
char *ets_strncpy(char *dest, const char *src, size_t n); |
||||
char *ets_strstr(const char *haystack, const char *needle); |
||||
void ets_timer_arm_new(os_timer_t *a, int b, int c, int isMstimer); |
||||
void ets_timer_disarm(os_timer_t *a); |
||||
void ets_timer_setfn(os_timer_t *t, ETSTimerFunc *fn, void *parg); |
||||
void ets_update_cpu_frequency(int freqmhz); |
||||
void *os_memmove(void *dest, const void *src, size_t n); |
||||
int os_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); |
||||
int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format (printf, 3, 4))); |
||||
int os_printf_plus(const char *format, ...) __attribute__ ((format (printf, 1, 2))); |
||||
void uart_div_modify(int no, unsigned int freq); |
||||
uint8 wifi_get_opmode(void); |
||||
uint32 system_get_time(); |
||||
int rand(void); |
||||
void ets_bzero(void *s, size_t n); |
||||
void ets_delay_us(int ms); |
||||
|
||||
//Hack: this is defined in SDK 1.4.0 and undefined in 1.3.0. It's only used for this, the symbol itself
|
||||
//has no meaning here.
|
||||
#ifndef RC_LIMIT_P2P_11N |
||||
//Defs for SDK <1.4.0
|
||||
void *pvPortMalloc(size_t xWantedSize); |
||||
void *pvPortZalloc(size_t); |
||||
void vPortFree(void *ptr); |
||||
void *vPortMalloc(size_t xWantedSize); |
||||
void pvPortFree(void *ptr); |
||||
#else |
||||
void *pvPortMalloc(size_t xWantedSize, const char *file, int line); |
||||
void *pvPortZalloc(size_t, const char *file, int line); |
||||
void vPortFree(void *ptr, const char *file, int line); |
||||
void *vPortMalloc(size_t xWantedSize, const char *file, int line); |
||||
void pvPortFree(void *ptr, const char *file, int line); |
||||
#endif |
||||
|
||||
//Standard PIN_FUNC_SELECT gives a warning. Replace by a non-warning one.
|
||||
#ifdef PIN_FUNC_SELECT |
||||
#undef PIN_FUNC_SELECT |
||||
#define PIN_FUNC_SELECT(PIN_NAME, FUNC) do { \ |
||||
WRITE_PERI_REG(PIN_NAME, \
|
||||
(READ_PERI_REG(PIN_NAME) \
|
||||
& (~(PERIPHS_IO_MUX_FUNC<<PERIPHS_IO_MUX_FUNC_S))) \
|
||||
|( (((FUNC&BIT2)<<2)|(FUNC&0x3))<<PERIPHS_IO_MUX_FUNC_S) ); \
|
||||
} while (0) |
||||
#endif |
||||
|
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,373 @@ |
||||
#include "ets_sys.h" |
||||
#include "osapi.h" |
||||
#include "gpio.h" |
||||
#include "os_type.h" |
||||
#include "user_interface.h" |
||||
#include "espconn.h" |
||||
|
||||
#include "driver/spi_interface.h" |
||||
#include "driver/uart.h" |
||||
#include "crypto/sha.h" |
||||
|
||||
#define MIN(a,b) \ |
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; }) |
||||
|
||||
#define MAX(a,b) \ |
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; }) |
||||
|
||||
char ssid[32]; |
||||
char password[] = "testing123"; |
||||
int wifi_secure_mode = 0; |
||||
|
||||
static const int pin = 2; |
||||
|
||||
// Structure holding the TCP connection information.
|
||||
struct espconn tcp_conn; |
||||
// TCP specific protocol structure.
|
||||
esp_tcp tcp_proto; |
||||
|
||||
// interrupt communication on port 1338, UDP!
|
||||
struct espconn inter_conn; |
||||
esp_udp inter_proto; |
||||
|
||||
uint32_t sendData[0x14] = {0}; |
||||
uint32_t recvData[0x40] = {0}; |
||||
|
||||
static int ICACHE_FLASH_ATTR __spi_comm(char *dat, int len, uint32_t *recvData, int recvDataLen) { |
||||
unsigned int length = 0; |
||||
|
||||
SpiData spiData; |
||||
|
||||
spiData.cmd = 2; |
||||
spiData.cmdLen = 0; |
||||
spiData.addr = NULL; |
||||
spiData.addrLen = 0; |
||||
|
||||
// float boot pin
|
||||
gpio_output_set(0, 0, 0, (1 << 4)); |
||||
|
||||
// manual CS pin
|
||||
gpio_output_set(0, (1 << 5), 0, 0); |
||||
memset(sendData, 0xCC, 0x14); |
||||
|
||||
// wait for ST to respond to CS interrupt
|
||||
os_delay_us(50); |
||||
|
||||
// send request
|
||||
memcpy(((void*)sendData), dat, len); |
||||
spiData.data = sendData; |
||||
spiData.dataLen = 0x14; |
||||
SPIMasterSendData(SpiNum_HSPI, &spiData); |
||||
|
||||
#define SPI_TIMEOUT 50000 |
||||
// give the ST time to be ready, up to 500ms
|
||||
int i; |
||||
for (i = 0; (gpio_input_get() & (1 << 4)) && i < SPI_TIMEOUT; i++) { |
||||
os_delay_us(10); |
||||
system_soft_wdt_feed(); |
||||
} |
||||
|
||||
// TODO: handle this better
|
||||
if (i == SPI_TIMEOUT) { |
||||
os_printf("ERROR: SPI receive failed\n"); |
||||
goto fail; |
||||
} |
||||
|
||||
// blank out recvData
|
||||
memset(recvData, 0x00, 0x44); |
||||
|
||||
// receive the length
|
||||
spiData.data = recvData; |
||||
spiData.dataLen = 4; |
||||
if(SPIMasterRecvData(SpiNum_HSPI, &spiData) == -1) { |
||||
// TODO: Handle gracefully. Maybe fail if len read fails?
|
||||
os_printf("SPI: Failed to recv length\n"); |
||||
goto fail; |
||||
} |
||||
|
||||
length = recvData[0]; |
||||
if (length > 0x40) { |
||||
os_printf("SPI: BAD LENGTH RECEIVED %x\n", length); |
||||
length = 0; |
||||
goto fail; |
||||
} |
||||
|
||||
// got response, 0x40 works, 0x44 does not
|
||||
spiData.data = recvData+1; |
||||
spiData.dataLen = (length+3)&(~3); // recvDataLen;
|
||||
if(SPIMasterRecvData(SpiNum_HSPI, &spiData) == -1) { |
||||
// TODO: Handle gracefully. Maybe retry if payload failed.
|
||||
os_printf("SPI: Failed to recv payload\n"); |
||||
length = 0; |
||||
goto fail; |
||||
} |
||||
|
||||
fail: |
||||
// clear CS
|
||||
gpio_output_set((1 << 5), 0, 0, 0); |
||||
|
||||
// set boot pin back
|
||||
gpio_output_set((1 << 4), 0, (1 << 4), 0); |
||||
|
||||
return length; |
||||
} |
||||
|
||||
int ICACHE_FLASH_ATTR spi_comm(char *dat, int len, uint32_t *recvData, int recvDataLen) { |
||||
// blink the led during SPI comm
|
||||
if (GPIO_REG_READ(GPIO_OUT_ADDRESS) & (1 << pin)) { |
||||
// set gpio low
|
||||
gpio_output_set(0, (1 << pin), 0, 0); |
||||
} else { |
||||
// set gpio high
|
||||
gpio_output_set((1 << pin), 0, 0, 0); |
||||
} |
||||
|
||||
return __spi_comm(dat, len, recvData, recvDataLen); |
||||
} |
||||
|
||||
static void ICACHE_FLASH_ATTR tcp_rx_cb(void *arg, char *data, uint16_t len) { |
||||
// nothing too big
|
||||
if (len > 0x14) return; |
||||
|
||||
// do the SPI comm
|
||||
spi_comm(data, len, recvData, 0x40); |
||||
|
||||
espconn_send(&tcp_conn, recvData, 0x44); |
||||
} |
||||
|
||||
void ICACHE_FLASH_ATTR tcp_connect_cb(void *arg) { |
||||
struct espconn *conn = (struct espconn *)arg; |
||||
espconn_set_opt(&tcp_conn, ESPCONN_NODELAY); |
||||
espconn_regist_recvcb(conn, tcp_rx_cb); |
||||
} |
||||
|
||||
// must be 0x44, because we can fit 4 more
|
||||
uint8_t buf[0x44*0x10]; |
||||
int queue_send_len = -1; |
||||
|
||||
void ICACHE_FLASH_ATTR poll_can(void *arg) { |
||||
uint8_t timerRecvData[0x44] = {0}; |
||||
int i = 0; |
||||
int j; |
||||
|
||||
while (i < 0x40) { |
||||
int len = spi_comm("\x01\x00\x00\x00", 4, timerRecvData, 0x40); |
||||
if (len == 0) break; |
||||
if (len > 0x40) { os_printf("SPI LENGTH ERROR!"); break; } |
||||
|
||||
// if it sends it, assume it's valid CAN
|
||||
for (j = 0; j < len; j += 0x10) { |
||||
memcpy(buf + i*0x10, (timerRecvData+4)+j, 0x10); |
||||
i++; |
||||
} |
||||
} |
||||
|
||||
if (i != 0) { |
||||
int ret = espconn_sendto(&inter_conn, buf, i*0x10); |
||||
if (ret != 0) { |
||||
os_printf("send failed: %d\n", ret); |
||||
queue_send_len = i*0x10; |
||||
} else { |
||||
queue_send_len = -1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
int udp_countdown = 0; |
||||
|
||||
static volatile os_timer_t udp_callback; |
||||
void ICACHE_FLASH_ATTR udp_callback_func(void *arg) { |
||||
if (queue_send_len == -1) { |
||||
poll_can(NULL); |
||||
} else { |
||||
int ret = espconn_sendto(&inter_conn, buf, queue_send_len); |
||||
if (ret == 0) { |
||||
queue_send_len = -1; |
||||
} |
||||
} |
||||
if (udp_countdown > 0) { |
||||
os_timer_arm(&udp_callback, 5, 0); |
||||
udp_countdown--; |
||||
} else { |
||||
os_printf("UDP timeout\n"); |
||||
} |
||||
} |
||||
|
||||
void ICACHE_FLASH_ATTR inter_recv_cb(void *arg, char *pusrdata, unsigned short length) { |
||||
remot_info *premot = NULL; |
||||
if (espconn_get_connection_info(&inter_conn,&premot,0) == ESPCONN_OK) { |
||||
inter_conn.proto.udp->remote_port = premot->remote_port; |
||||
inter_conn.proto.udp->remote_ip[0] = premot->remote_ip[0]; |
||||
inter_conn.proto.udp->remote_ip[1] = premot->remote_ip[1]; |
||||
inter_conn.proto.udp->remote_ip[2] = premot->remote_ip[2]; |
||||
inter_conn.proto.udp->remote_ip[3] = premot->remote_ip[3]; |
||||
|
||||
|
||||
if (udp_countdown == 0) { |
||||
os_printf("UDP recv\n"); |
||||
udp_countdown = 200*5; |
||||
|
||||
// start 5 second timer
|
||||
os_timer_disarm(&udp_callback); |
||||
os_timer_setfn(&udp_callback, (os_timer_func_t *)udp_callback_func, NULL); |
||||
os_timer_arm(&udp_callback, 5, 0); |
||||
} else { |
||||
udp_countdown = 200*5; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void ICACHE_FLASH_ATTR wifi_configure(int secure) { |
||||
wifi_secure_mode = secure; |
||||
|
||||
// start wifi AP
|
||||
wifi_set_opmode(SOFTAP_MODE); |
||||
struct softap_config config = {0}; |
||||
wifi_softap_get_config(&config); |
||||
strcpy(config.ssid, ssid); |
||||
if (wifi_secure_mode == 0) strcat(config.ssid, "-pair"); |
||||
strcpy(config.password, password); |
||||
config.ssid_len = strlen(config.ssid); |
||||
config.authmode = wifi_secure_mode ? AUTH_WPA2_PSK : AUTH_OPEN; |
||||
config.beacon_interval = 100; |
||||
config.max_connection = 4; |
||||
wifi_softap_set_config(&config); |
||||
|
||||
if (wifi_secure_mode) { |
||||
// setup tcp server
|
||||
tcp_proto.local_port = 1337; |
||||
tcp_conn.type = ESPCONN_TCP; |
||||
tcp_conn.state = ESPCONN_NONE; |
||||
tcp_conn.proto.tcp = &tcp_proto; |
||||
espconn_regist_connectcb(&tcp_conn, tcp_connect_cb); |
||||
espconn_accept(&tcp_conn); |
||||
espconn_regist_time(&tcp_conn, 60, 0); // 60s timeout for all connections
|
||||
|
||||
// setup inter server
|
||||
inter_proto.local_port = 1338; |
||||
const char udp_remote_ip[4] = {255, 255, 255, 255}; |
||||
os_memcpy(inter_proto.remote_ip, udp_remote_ip, 4); |
||||
inter_proto.remote_port = 1338; |
||||
|
||||
inter_conn.type = ESPCONN_UDP; |
||||
inter_conn.proto.udp = &inter_proto; |
||||
|
||||
espconn_regist_recvcb(&inter_conn, inter_recv_cb); |
||||
espconn_create(&inter_conn); |
||||
} |
||||
} |
||||
|
||||
void ICACHE_FLASH_ATTR wifi_init() { |
||||
// default ssid and password
|
||||
memset(ssid, 0, 32); |
||||
os_sprintf(ssid, "panda-%08x-BROKEN", system_get_chip_id()); |
||||
|
||||
// fetch secure ssid and password
|
||||
// update, try 20 times, for 1 second
|
||||
for (int i = 0; i < 20; i++) { |
||||
uint8_t digest[SHA_DIGEST_SIZE]; |
||||
char resp[0x20]; |
||||
__spi_comm("\x00\x00\x00\x00\x40\xD0\x00\x00\x00\x00\x20\x00", 0xC, recvData, 0x40); |
||||
memcpy(resp, recvData+1, 0x20); |
||||
|
||||
SHA_hash(resp, 0x1C, digest); |
||||
if (memcmp(digest, resp+0x1C, 4) == 0) { |
||||
// OTP is valid
|
||||
memcpy(ssid+6, resp, 0x10); |
||||
memcpy(password, resp+0x10, 10); |
||||
break; |
||||
} |
||||
os_delay_us(50000); |
||||
} |
||||
os_printf("Finished getting SID\n"); |
||||
os_printf(ssid); |
||||
os_printf("\n"); |
||||
|
||||
// set IP
|
||||
wifi_softap_dhcps_stop(); //stop DHCP before setting static IP
|
||||
struct ip_info ip_config; |
||||
IP4_ADDR(&ip_config.ip, 192, 168, 0, 10); |
||||
IP4_ADDR(&ip_config.gw, 0, 0, 0, 0); |
||||
IP4_ADDR(&ip_config.netmask, 255, 255, 255, 0); |
||||
wifi_set_ip_info(SOFTAP_IF, &ip_config); |
||||
int stupid_gateway = 0; |
||||
wifi_softap_set_dhcps_offer_option(OFFER_ROUTER, &stupid_gateway); |
||||
wifi_softap_dhcps_start(); |
||||
|
||||
wifi_configure(0); |
||||
} |
||||
|
||||
#define LOOP_PRIO 2 |
||||
#define QUEUE_SIZE 1 |
||||
static os_event_t my_queue[QUEUE_SIZE]; |
||||
void loop(); |
||||
|
||||
void ICACHE_FLASH_ATTR web_init(); |
||||
void ICACHE_FLASH_ATTR elm327_init(); |
||||
|
||||
void ICACHE_FLASH_ATTR user_init() { |
||||
// init gpio subsystem
|
||||
gpio_init(); |
||||
|
||||
// configure UART TXD to be GPIO1, set as output
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1); |
||||
gpio_output_set(0, 0, (1 << pin), 0); |
||||
|
||||
// configure SPI
|
||||
SpiAttr hSpiAttr; |
||||
hSpiAttr.bitOrder = SpiBitOrder_MSBFirst; |
||||
hSpiAttr.speed = SpiSpeed_10MHz; |
||||
hSpiAttr.mode = SpiMode_Master; |
||||
hSpiAttr.subMode = SpiSubMode_0; |
||||
|
||||
// TODO: is one of these CS?
|
||||
WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); |
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2); // configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2); // configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2); // configure io to spi mode
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2); // configure io to spi mode
|
||||
SPIInit(SpiNum_HSPI, &hSpiAttr); |
||||
//SPICsPinSelect(SpiNum_HSPI, SpiPinCS_1);
|
||||
|
||||
// configure UART TXD to be GPIO1, set as output
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5); |
||||
gpio_output_set(0, 0, (1 << 5), 0); |
||||
gpio_output_set((1 << 5), 0, 0, 0); |
||||
|
||||
// uart init
|
||||
uart_init(BIT_RATE_115200, BIT_RATE_115200); |
||||
|
||||
// led init
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); |
||||
gpio_output_set(0, (1 << pin), (1 << pin), 0); |
||||
|
||||
os_printf("hello\n"); |
||||
|
||||
// needs SPI
|
||||
wifi_init(); |
||||
|
||||
// support ota upgrades
|
||||
elm327_init(); |
||||
web_init(); |
||||
|
||||
// set gpio high, so LED is off by default
|
||||
for (int i = 0; i < 5; i++) { |
||||
gpio_output_set(0, (1 << pin), 0, 0); |
||||
os_delay_us(50000); |
||||
gpio_output_set((1 << pin), 0, 0, 0); |
||||
os_delay_us(50000); |
||||
} |
||||
|
||||
// jump to OS
|
||||
system_os_task(loop, LOOP_PRIO, my_queue, QUEUE_SIZE); |
||||
system_os_post(LOOP_PRIO, 0, 0); |
||||
} |
||||
|
||||
void ICACHE_FLASH_ATTR loop(os_event_t *events) { |
||||
system_os_post(LOOP_PRIO, 0, 0); |
||||
} |
||||
|
@ -0,0 +1,381 @@ |
||||
#include "stdlib.h" |
||||
#include "ets_sys.h" |
||||
#include "osapi.h" |
||||
#include "gpio.h" |
||||
#include "mem.h" |
||||
#include "os_type.h" |
||||
#include "user_interface.h" |
||||
#include "espconn.h" |
||||
#include "upgrade.h" |
||||
|
||||
#include "crypto/rsa.h" |
||||
#include "crypto/sha.h" |
||||
|
||||
#include "obj/gitversion.h" |
||||
#include "obj/cert.h" |
||||
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b)) |
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b)) |
||||
#define espconn_send_string(conn, x) espconn_send(conn, x, strlen(x)) |
||||
|
||||
#define MAX_RESP 0x800 |
||||
char resp[MAX_RESP]; |
||||
char pageheader[] = "HTTP/1.0 200 OK\nContent-Type: text/html\n\n" |
||||
"<!DOCTYPE html>\n" |
||||
"<html>\n" |
||||
"<head>\n" |
||||
"<title>Panda</title>\n" |
||||
"</head>\n" |
||||
"<body>\n" |
||||
"<pre>This is your comma.ai panda\n\n" |
||||
"It's open source. Find the code <a href=\"https://github.com/commaai/panda\">here</a>\n" |
||||
"Designed to work with our dashcam, <a href=\"http://chffr.comma.ai\">chffr</a>\n"; |
||||
|
||||
char pagefooter[] = "</pre>\n" |
||||
"</body>\n" |
||||
"</html>\n"; |
||||
|
||||
char OK_header[] = "HTTP/1.0 200 OK\nContent-Type: text/html\n\n"; |
||||
|
||||
static struct espconn web_conn; |
||||
static esp_tcp web_proto; |
||||
extern char ssid[]; |
||||
extern int wifi_secure_mode; |
||||
|
||||
char *st_firmware; |
||||
int real_content_length, content_length = 0; |
||||
char *st_firmware_ptr; |
||||
LOCAL os_timer_t ota_reboot_timer; |
||||
|
||||
#define FIRMWARE_SIZE 503808 |
||||
|
||||
typedef struct { |
||||
uint16_t ep; |
||||
uint16_t extra_len; |
||||
union { |
||||
struct { |
||||
uint8_t request_type; |
||||
uint8_t request; |
||||
uint16_t value; |
||||
uint16_t index; |
||||
uint16_t length; |
||||
} control; |
||||
uint8_t data[0x10]; |
||||
} u; |
||||
} usb_msg; |
||||
|
||||
int ICACHE_FLASH_ATTR usb_cmd(int ep, int len, int request, |
||||
int value, int index, char *data) { |
||||
usb_msg usb = {0}; |
||||
|
||||
usb.ep = ep; |
||||
usb.extra_len = (ep == 0) ? 0 : len; |
||||
if (ep == 0) { |
||||
usb.u.control.request_type = 0xc0; |
||||
usb.u.control.request = request; |
||||
usb.u.control.value = value; |
||||
usb.u.control.index = index; |
||||
} else { |
||||
memcpy(&usb.u.data, data, usb.extra_len); |
||||
} |
||||
|
||||
uint32_t recv[0x44/4]; |
||||
spi_comm(&usb, sizeof(usb), recv, 0x40); |
||||
|
||||
return recv[0]; |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR st_flash() { |
||||
if (st_firmware != NULL) { |
||||
// boot mode
|
||||
os_printf("st_flash: enter boot mode\n"); |
||||
st_set_boot_mode(1); |
||||
|
||||
// echo
|
||||
os_printf("st_flash: wait for echo\n"); |
||||
for (int i = 0; i < 10; i++) { |
||||
os_printf(" attempt: %d\n", i); |
||||
if (usb_cmd(0, 0, 0xb0, 0, 0, NULL) > 0) break; |
||||
} |
||||
|
||||
// unlock flash
|
||||
os_printf("st_flash: unlock flash\n"); |
||||
usb_cmd(0, 0, 0xb1, 0, 0, NULL); |
||||
|
||||
// erase sector 1
|
||||
os_printf("st_flash: erase sector 1\n"); |
||||
usb_cmd(0, 0, 0xb2, 1, 0, NULL); |
||||
|
||||
if (real_content_length >= 16384) { |
||||
// erase sector 2
|
||||
os_printf("st_flash: erase sector 2\n"); |
||||
usb_cmd(0, 0, 0xb2, 2, 0, NULL); |
||||
} |
||||
|
||||
// real content length will always be 0x10 aligned
|
||||
os_printf("st_flash: flashing\n"); |
||||
for (int i = 0; i < real_content_length; i += 0x10) { |
||||
int rl = MIN(0x10, real_content_length-i); |
||||
usb_cmd(2, rl, 0, 0, 0, &st_firmware[i]); |
||||
system_soft_wdt_feed(); |
||||
} |
||||
|
||||
// reboot into normal mode
|
||||
os_printf("st_flash: rebooting\n"); |
||||
usb_cmd(0, 0, 0xd8, 0, 0, NULL); |
||||
|
||||
// done with this
|
||||
os_free(st_firmware); |
||||
st_firmware = NULL; |
||||
} |
||||
} |
||||
|
||||
typedef enum { |
||||
NOT_STARTED, |
||||
CONNECTION_ESTABLISHED, |
||||
RECEIVING_HEADER, |
||||
RECEIVING_ST_FIRMWARE, |
||||
RECEIVING_ESP_FIRMWARE, |
||||
REBOOTING, |
||||
ERROR |
||||
} web_state_t; |
||||
|
||||
web_state_t state = NOT_STARTED; |
||||
int esp_address, esp_address_erase_limit, start_address; |
||||
|
||||
void ICACHE_FLASH_ATTR hexdump(char *data, int len) { |
||||
int i; |
||||
for (i=0;i<len;i++) { |
||||
if (i!=0 && (i%0x10)==0) os_printf("\n"); |
||||
os_printf("%02X ", data[i]); |
||||
} |
||||
os_printf("\n"); |
||||
} |
||||
|
||||
void ICACHE_FLASH_ATTR st_reset() { |
||||
// reset the ST
|
||||
gpio16_output_conf(); |
||||
gpio16_output_set(0); |
||||
os_delay_us(1000); |
||||
gpio16_output_set(1); |
||||
os_delay_us(10000); |
||||
} |
||||
|
||||
void ICACHE_FLASH_ATTR st_set_boot_mode(int boot_mode) { |
||||
if (boot_mode) { |
||||
// boot mode (pull low)
|
||||
gpio_output_set(0, (1 << 4), (1 << 4), 0); |
||||
st_reset(); |
||||
} else { |
||||
// no boot mode (pull high)
|
||||
gpio_output_set((1 << 4), 0, (1 << 4), 0); |
||||
st_reset(); |
||||
} |
||||
|
||||
// float boot pin
|
||||
gpio_output_set(0, 0, 0, (1 << 4)); |
||||
} |
||||
|
||||
static void ICACHE_FLASH_ATTR web_rx_cb(void *arg, char *data, uint16_t len) { |
||||
int i; |
||||
struct espconn *conn = (struct espconn *)arg; |
||||
if (state == CONNECTION_ESTABLISHED) { |
||||
state = RECEIVING_HEADER; |
||||
os_printf("%s %d\n", data, len); |
||||
|
||||
// index
|
||||
if (memcmp(data, "GET / ", 6) == 0) { |
||||
memset(resp, 0, MAX_RESP); |
||||
|
||||
strcpy(resp, pageheader); |
||||
ets_strcat(resp, "\nssid: "); |
||||
ets_strcat(resp, ssid); |
||||
ets_strcat(resp, "\n"); |
||||
|
||||
ets_strcat(resp, "\nst version: "); |
||||
uint32_t recvData[0x11]; |
||||
int len = spi_comm("\x00\x00\x00\x00\x40\xD6\x00\x00\x00\x00\x40\x00", 0xC, recvData, 0x40); |
||||
ets_memcpy(resp+strlen(resp), recvData+1, len); |
||||
|
||||
ets_strcat(resp, "\nesp version: "); |
||||
ets_strcat(resp, gitversion); |
||||
uint8_t current = system_upgrade_userbin_check(); |
||||
if (current == UPGRADE_FW_BIN1) { |
||||
ets_strcat(resp, "\nesp flash file: user2.bin"); |
||||
} else { |
||||
ets_strcat(resp, "\nesp flash file: user1.bin"); |
||||
} |
||||
|
||||
if (wifi_secure_mode) { |
||||
ets_strcat(resp, "\nin secure mode"); |
||||
} else { |
||||
ets_strcat(resp, "\nin INSECURE mode...<a href=\"/secure\">secure it</a>"); |
||||
} |
||||
|
||||
ets_strcat(resp,"\nSet USB Mode:" |
||||
"<button onclick=\"var xhr = new XMLHttpRequest(); xhr.open('GET', 'set_property?usb_mode=0'); xhr.send()\" type='button'>Client</button>" |
||||
"<button onclick=\"var xhr = new XMLHttpRequest(); xhr.open('GET', 'set_property?usb_mode=1'); xhr.send()\" type='button'>CDP</button>" |
||||
"<button onclick=\"var xhr = new XMLHttpRequest(); xhr.open('GET', 'set_property?usb_mode=2'); xhr.send()\" type='button'>DCP</button>\n"); |
||||
|
||||
ets_strcat(resp, pagefooter); |
||||
|
||||
espconn_send_string(&web_conn, resp); |
||||
espconn_disconnect(conn); |
||||
} else if (memcmp(data, "GET /secure", 11) == 0 && !wifi_secure_mode) { |
||||
wifi_configure(1); |
||||
} else if (memcmp(data, "GET /set_property?usb_mode=", 27) == 0 && wifi_secure_mode) { |
||||
char mode_value = data[27] - '0'; |
||||
if (mode_value >= '\x00' && mode_value <= '\x02') { |
||||
memset(resp, 0, MAX_RESP); |
||||
char set_usb_mode_packet[] = "\x00\x00\x00\x00\x40\xE6\x00\x00\x00\x00\x40\x00"; |
||||
set_usb_mode_packet[6] = mode_value; |
||||
uint32_t recvData[1]; |
||||
spi_comm(set_usb_mode_packet, 0xC, recvData, 0); |
||||
os_sprintf(resp, "%sUSB Mode set to %02x\n\n", OK_header, mode_value); |
||||
espconn_send_string(&web_conn, resp); |
||||
espconn_disconnect(conn); |
||||
}
|
||||
} else if (memcmp(data, "PUT /stupdate ", 14) == 0 && wifi_secure_mode) { |
||||
os_printf("init st firmware\n"); |
||||
char *cl = strstr(data, "Content-Length: "); |
||||
if (cl != NULL) { |
||||
// get content length
|
||||
cl += strlen("Content-Length: "); |
||||
content_length = skip_atoi(&cl); |
||||
os_printf("with content length %d\n", content_length); |
||||
|
||||
// should be small enough to fit in RAM
|
||||
real_content_length = (content_length+0xF)&(~0xF); |
||||
st_firmware_ptr = st_firmware = os_malloc(real_content_length); |
||||
memset(st_firmware, 0, real_content_length); |
||||
state = RECEIVING_ST_FIRMWARE; |
||||
} |
||||
|
||||
} else if (((memcmp(data, "PUT /espupdate1 ", 16) == 0) || |
||||
(memcmp(data, "PUT /espupdate2 ", 16) == 0)) && wifi_secure_mode) { |
||||
// 0x1000 = user1.bin
|
||||
// 0x81000 = user2.bin
|
||||
// 0x3FE000 = blank.bin
|
||||
os_printf("init esp firmware\n"); |
||||
char *cl = strstr(data, "Content-Length: "); |
||||
if (cl != NULL) { |
||||
// get content length
|
||||
cl += strlen("Content-Length: "); |
||||
content_length = skip_atoi(&cl); |
||||
os_printf("with content length %d\n", content_length); |
||||
|
||||
// setup flashing
|
||||
uint8_t current = system_upgrade_userbin_check(); |
||||
if (data[14] == '2' && current == UPGRADE_FW_BIN1) { |
||||
os_printf("flashing boot2.bin\n"); |
||||
state = RECEIVING_ESP_FIRMWARE; |
||||
esp_address = 4*1024 + FIRMWARE_SIZE + 16*1024 + 4*1024; |
||||
} else if (data[14] == '1' && current == UPGRADE_FW_BIN2) { |
||||
os_printf("flashing boot1.bin\n"); |
||||
state = RECEIVING_ESP_FIRMWARE; |
||||
esp_address = 4*1024; |
||||
} else { |
||||
espconn_send_string(&web_conn, "HTTP/1.0 404 Not Found\nContent-Type: text/html\n\nwrong!\n"); |
||||
espconn_disconnect(conn); |
||||
} |
||||
esp_address_erase_limit = esp_address; |
||||
start_address = esp_address; |
||||
} |
||||
} else { |
||||
espconn_send_string(&web_conn, "HTTP/1.0 404 Not Found\nContent-Type: text/html\n\n404 Not Found!\n"); |
||||
espconn_disconnect(conn); |
||||
} |
||||
} else if (state == RECEIVING_ST_FIRMWARE) { |
||||
os_printf("receiving st firmware: %d/%d\n", len, content_length); |
||||
memcpy(st_firmware_ptr, data, MIN(content_length, len)); |
||||
st_firmware_ptr += len; |
||||
content_length -= len; |
||||
|
||||
if (content_length <= 0 && real_content_length > 1000) { |
||||
state = NOT_STARTED; |
||||
os_printf("done!\n"); |
||||
espconn_send_string(&web_conn, "HTTP/1.0 200 OK\nContent-Type: text/html\n\nsuccess!\n"); |
||||
espconn_disconnect(conn); |
||||
|
||||
// reboot
|
||||
os_printf("Scheduling st_flash in 100ms.\n"); |
||||
os_timer_disarm(&ota_reboot_timer); |
||||
os_timer_setfn(&ota_reboot_timer, (os_timer_func_t *)st_flash, NULL); |
||||
os_timer_arm(&ota_reboot_timer, 100, 0); |
||||
} |
||||
} else if (state == RECEIVING_ESP_FIRMWARE) { |
||||
if ((esp_address+len) < (start_address + FIRMWARE_SIZE)) { |
||||
os_printf("receiving esp firmware: %d/%d -- 0x%x - 0x%x\n", len, content_length, |
||||
esp_address, esp_address_erase_limit); |
||||
content_length -= len; |
||||
while (esp_address_erase_limit < (esp_address + len)) { |
||||
os_printf("erasing 0x%X\n", esp_address_erase_limit); |
||||
spi_flash_erase_sector(esp_address_erase_limit / SPI_FLASH_SEC_SIZE); |
||||
esp_address_erase_limit += SPI_FLASH_SEC_SIZE; |
||||
} |
||||
SpiFlashOpResult res = spi_flash_write(esp_address, data, len); |
||||
if (res != SPI_FLASH_RESULT_OK) { |
||||
os_printf("flash fail @ 0x%x\n", esp_address); |
||||
} |
||||
esp_address += len; |
||||
|
||||
if (content_length == 0) { |
||||
|
||||
char digest[SHA_DIGEST_SIZE]; |
||||
uint32_t rsa[RSANUMBYTES/4]; |
||||
uint32_t dat[0x80/4]; |
||||
int ll; |
||||
spi_flash_read(esp_address-RSANUMBYTES, rsa, RSANUMBYTES); |
||||
|
||||
// 32-bit aligned accesses only
|
||||
SHA_CTX ctx; |
||||
SHA_init(&ctx); |
||||
for (ll = start_address; ll < esp_address-RSANUMBYTES; ll += 0x80) { |
||||
spi_flash_read(ll, dat, 0x80); |
||||
SHA_update(&ctx, dat, MIN((esp_address-RSANUMBYTES)-ll, 0x80)); |
||||
} |
||||
memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE); |
||||
|
||||
if (RSA_verify(&releaseesp_rsa_key, rsa, RSANUMBYTES, digest, SHA_DIGEST_SIZE) || |
||||
#ifdef ALLOW_DEBUG |
||||
RSA_verify(&debugesp_rsa_key, rsa, RSANUMBYTES, digest, SHA_DIGEST_SIZE) |
||||
#else |
||||
false |
||||
#endif |
||||
) { |
||||
os_printf("RSA verify success!\n"); |
||||
espconn_send_string(&web_conn, "HTTP/1.0 200 OK\nContent-Type: text/html\n\nsuccess!\n"); |
||||
system_upgrade_flag_set(UPGRADE_FLAG_FINISH); |
||||
|
||||
// reboot
|
||||
os_printf("Scheduling reboot.\n"); |
||||
os_timer_disarm(&ota_reboot_timer); |
||||
os_timer_setfn(&ota_reboot_timer, (os_timer_func_t *)system_upgrade_reboot, NULL); |
||||
os_timer_arm(&ota_reboot_timer, 2000, 0); |
||||
} else { |
||||
os_printf("RSA verify FAILURE\n"); |
||||
espconn_send_string(&web_conn, "HTTP/1.0 500 Internal Server Error\nContent-Type: text/html\n\nrsa verify fail\n"); |
||||
} |
||||
espconn_disconnect(conn); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void ICACHE_FLASH_ATTR web_tcp_connect_cb(void *arg) { |
||||
state = CONNECTION_ESTABLISHED; |
||||
struct espconn *conn = (struct espconn *)arg; |
||||
espconn_set_opt(&web_conn, ESPCONN_NODELAY); |
||||
espconn_regist_recvcb(conn, web_rx_cb); |
||||
} |
||||
|
||||
void ICACHE_FLASH_ATTR web_init() { |
||||
web_proto.local_port = 80; |
||||
web_conn.type = ESPCONN_TCP; |
||||
web_conn.state = ESPCONN_NONE; |
||||
web_conn.proto.tcp = &web_proto; |
||||
espconn_regist_connectcb(&web_conn, web_tcp_connect_cb); |
||||
espconn_accept(&web_conn); |
||||
} |
||||
|
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:7e5c26f88d0afd2f2591292dcaa7993f3524e889c4716cfa2294ae6e483e93b3 |
||||
size 12200 |
@ -0,0 +1,15 @@ |
||||
-----BEGIN RSA PRIVATE KEY----- |
||||
MIICXQIBAAKBgQC948lnRo4x44Rd7Y8bQAML4aKDC4XRx958fHV8K6+FbCaP1Z42 |
||||
U2kX0yygak0LjoDutpgObmGHZA+Iz3HeUD6VGjr/teN24vPk+A95cRsjt8rgmGQ9 |
||||
6HNjaNgjR+gl1F9XxFimMzir82Xpl1ekTueJNXa7ia5HVH1nFdiksOKHGQIDAQAB |
||||
AoGAQuPw2I6EHJLW1/eNB75e1FqhUqRGeYV8nEGDaUBCTi+wzc4kM2LijF/5QnDv |
||||
vvht9qkfm0XK2VSoHDtnEzcVM/l1ksb68n4R/1nUooAWY6cQI7dCSk/A6yS1EJFg |
||||
BXsgGbT/65khw9pzBW2zVtMVcVNWFayqfCO1I9WcDdA1x1kCQQDfrhoZTZNoDEUE |
||||
JKM4fiUdWr1h3Aw8KLJFFexSWeGDwo+qqnujYcKWkHa9qaH1RG5x8Kir9s9Oi4Js |
||||
mzKwov8fAkEA2VPJPWxJ4vVQpXle6wC1nyoL7s739yxMWFcabvkzDDhlIVBNdVJd |
||||
gZKsFWV7QnVNdDMjn9D27FwKu3i2D+kKxwJBANp1SMojqO765MEKI1t+YDNONx6H |
||||
cm+i85Fjuv4nCIjOEdCGVuCYDxtMFpxgO2y3HAMuHx5sm8XDnWsDHLvFRdMCQD7V |
||||
XqWHnYUk8AAnqy2+ssQl3/VXmZG5GQmhhV74Za3u0C5ljT+SZL6FrYMyKAT67T3f |
||||
WzllrT6BDglNyTWoZxkCQQCt0XSoGM3603GGYNt6AUlGSgtXSo/2Px7odGUtQoKA |
||||
FH9q6FVMYpQJ38spZxIGufZJmLP8LLg6YIWJj1F+akxr |
||||
-----END RSA PRIVATE KEY----- |
@ -0,0 +1 @@ |
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC948lnRo4x44Rd7Y8bQAML4aKDC4XRx958fHV8K6+FbCaP1Z42U2kX0yygak0LjoDutpgObmGHZA+Iz3HeUD6VGjr/teN24vPk+A95cRsjt8rgmGQ96HNjaNgjR+gl1F9XxFimMzir82Xpl1ekTueJNXa7ia5HVH1nFdiksOKHGQ== batman@y840 |
@ -0,0 +1,15 @@ |
||||
-----BEGIN RSA PRIVATE KEY----- |
||||
MIICXAIBAAKBgQCjIHvrSCWN0Nec6ozbImYik30PIF7JSWgdwDKTxSJ05RM3pj5E |
||||
LQEGt3qcaVrTokO68tpt5Gu1p6ZsNqWg7iVTW9M7Qj7IH45YDzQP/PSRjgSosQA6 |
||||
6f5Gokba5QrW38myqimvj+0p+YH+CNGCBRlTUQGCO8uLCspMZneRSLPW9QIDAQAB |
||||
AoGADaUn+HRef9BaWMvd4G6uMHI54cwJYbj8NpDfKjExQqnuw5bqWnWRQmiSnwbJ |
||||
DC7kj3zE/LBAuj890ot3q1CAWqh47ZICZfoX9Qbi5TpvIHFCGy6YkOliF6iIQhR2 |
||||
4+zNKTAA0zNKskOM25PdI+grK1Ni/bEofSA6TrqvEwsmxnkCQQDVp9FUUor2Bo/h |
||||
/3oAIP51LTw7vfpztYbJr+BDV63czV2DLXzSwzeNrwH4sA3oy1mjUgMBBgAarNGE |
||||
DYlc4H5jAkEAw3UCHzzXPlxkw2QGp7nBly5y3p80Uqc31NuYz8rdX/U8KTngi2No |
||||
Ft/SGCEXNpeYbToj+WK3RJJ2Ey0mK8+IxwJAcpGd/5CPsaQNLcw4WK9Yo+8Q2Jxk |
||||
G/4gfDCSmqn+smNxnLEcuUwzkwdgkEGgA9BfjeOhdsAH+EXpx90WZrZ/LwJBAK0k |
||||
jq+rTqUQZbZsejTEKYjJ/bnV4BzDwoKN0Q1pkLc7X4LJoW74rTFuLgdv8MdMfRtt |
||||
IIb/eoeFEpGkMicnHesCQHgR7BTUGBM6Uxam7RCdsgVsxoHBma21E/44ivWUMZzN |
||||
3oVt0mPnjS4speOlqwED5pCJ7yw7jwLPFMs8kNxuIKU= |
||||
-----END RSA PRIVATE KEY----- |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue