commit dba8e01e540f8b6b231be082ee6d36304aa1997d Author: Vehicle Researcher Date: Fri Jun 28 21:09:12 2019 +0000 Squashed 'panda/' content from commit ae816c104 git-subtree-dir: panda git-subtree-split: ae816c104a99a8cd4d508ccd6abdc7b93053529c diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..e36aaa142a --- /dev/null +++ b/.circleci/config.yml @@ -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 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..f04ae5c0aa --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.git +.DS_Store +boardesp/esp-open-sdk diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..70d010fabf --- /dev/null +++ b/.gitignore @@ -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 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..1a0d924661 --- /dev/null +++ b/Dockerfile @@ -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 diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000..0a861a5fa1 --- /dev/null +++ b/Jenkinsfile @@ -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" + } + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..8a6c6976b7 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..42f432dfc3 --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +Welcome to panda +====== + +[panda](http://github.com/commaai/panda) is the nicest universal car interface ever. + + + + + +It supports 3x CAN, 2x LIN, and 1x GMLAN. It also charges a phone. On the computer side, it has both USB and Wi-Fi. + +It uses an [STM32F413](http://www.st.com/en/microcontrollers/stm32f413-423.html?querycriteria=productId=LN2004) for low level stuff and an [ESP8266](https://en.wikipedia.org/wiki/ESP8266) for Wi-Fi. They are connected over high speed SPI, so the panda is actually capable of dumping the full contents of the busses over Wi-Fi, unlike every other dongle on amazon. ELM327 is weak, panda is strong. + +It is 2nd gen hardware, reusing code and parts from the [NEO](https://github.com/commaai/neo) interface board. + +[![CircleCI](https://circleci.com/gh/commaai/panda.svg?style=svg)](https://circleci.com/gh/commaai/panda) + +Usage (Python) +------ + +To install the library: +``` +# pip install pandacan +``` + +See [this class](https://github.com/commaai/panda/blob/master/python/__init__.py#L80) for how to interact with the panda. + +For example, to receive CAN messages: +``` +>>> from panda import Panda +>>> panda = Panda() +>>> panda.can_recv() +``` +And to send one on bus 0: +``` +>>> panda.can_send(0x1aa, "message", 0) +``` +Find user made scripts on the [wiki](https://community.comma.ai/wiki/index.php/Panda_scripts) + +Usage (JavaScript) +------- + +See [PandaJS](https://github.com/commaai/pandajs) + + +Software interface support +------ + +As a universal car interface, it should support every reasonable software interface. + +- [User space](https://github.com/commaai/panda/tree/master/python) +- [socketcan in kernel](https://github.com/commaai/panda/tree/master/drivers/linux) (alpha) +- [ELM327](https://github.com/commaai/panda/blob/master/boardesp/elm327.c) +- [Windows J2534](https://github.com/commaai/panda/tree/master/drivers/windows) + +Directory structure +------ + +- board -- Code that runs on the STM32 +- boardesp -- Code that runs on the ESP8266 +- drivers -- Drivers (not needed for use with python) +- python   -- Python userspace library for interfacing with the panda +- tests -- Tests and helper programs for panda + +Programming (over USB) +------ + +[Programming the Board (STM32)](board/README.md) + +[Programming the ESP](boardesp/README.md) + + +Debugging +------ + +To print out the serial console from the STM32, run tests/debug_console.py + +To print out the serial console from the ESP8266, run PORT=1 tests/debug_console.py + +Safety Model +------ + +When a panda powers up, by default it's in "SAFETY_NOOUTPUT" mode. While in no output mode, the buses are also forced to be silent. In order to send messages, you have to select a safety mode. Currently, setting safety modes is only supported over USB. + +Safety modes can also optionally support "controls_allowed", which allows or blocks a subset of messages based on a piece of state in the board. + +Hardware +------ + +Check out the hardware [guide](https://github.com/commaai/panda/blob/master/docs/guide.pdf) + +Licensing +------ + +panda software is released under the MIT license unless otherwise specified. diff --git a/TODO b/TODO new file mode 100644 index 0000000000..4e4a355154 --- /dev/null +++ b/TODO @@ -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. + diff --git a/UPDATING.md b/UPDATING.md new file mode 100644 index 0000000000..100acc6bab --- /dev/null +++ b/UPDATING.md @@ -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.) diff --git a/VERSION b/VERSION new file mode 100644 index 0000000000..ec7b967829 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +v1.4.0 \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000000..b802cf5a59 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +from .python import Panda, PandaWifiStreaming, PandaDFU, ESPROM, CesantaFlasher, flash_release, BASEDIR, ensure_st_up_to_date, build_st, PandaSerial diff --git a/board/Makefile b/board/Makefile new file mode 100644 index 0000000000..ed6dcaa030 --- /dev/null +++ b/board/Makefile @@ -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 diff --git a/board/Makefile.strict b/board/Makefile.strict new file mode 100644 index 0000000000..b6c72b58b1 --- /dev/null +++ b/board/Makefile.strict @@ -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 diff --git a/board/README.md b/board/README.md new file mode 100644 index 0000000000..5fe2e4b238 --- /dev/null +++ b/board/README.md @@ -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 diff --git a/board/__init__.py b/board/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/board/bootstub.c b/board/bootstub.c new file mode 100644 index 0000000000..691d0d02e3 --- /dev/null +++ b/board/bootstub.c @@ -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; +} + diff --git a/board/build.mk b/board/build.mk new file mode 100644 index 0000000000..af2bffeb0a --- /dev/null +++ b/board/build.mk @@ -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) diff --git a/board/config.h b/board/config.h new file mode 100644 index 0000000000..219ce64c24 --- /dev/null +++ b/board/config.h @@ -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 +#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 + diff --git a/board/drivers/adc.h b/board/drivers/adc.h new file mode 100644 index 0000000000..3e5f1b32a0 --- /dev/null +++ b/board/drivers/adc.h @@ -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; +} + diff --git a/board/drivers/can.h b/board/drivers/can.h new file mode 100644 index 0000000000..7d0e63058a --- /dev/null +++ b/board/drivers/can.h @@ -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; +} + diff --git a/board/drivers/clock.h b/board/drivers/clock.h new file mode 100644 index 0000000000..a9f2dea730 --- /dev/null +++ b/board/drivers/clock.h @@ -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; +} + diff --git a/board/drivers/dac.h b/board/drivers/dac.h new file mode 100644 index 0000000000..b8833dc4a6 --- /dev/null +++ b/board/drivers/dac.h @@ -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; + } +} + diff --git a/board/drivers/gmlan_alt.h b/board/drivers/gmlan_alt.h new file mode 100644 index 0000000000..357cffcd99 --- /dev/null +++ b/board/drivers/gmlan_alt.h @@ -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<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(); + } +} + diff --git a/board/drivers/llcan.h b/board/drivers/llcan.h new file mode 100644 index 0000000000..5d0b888458 --- /dev/null +++ b/board/drivers/llcan.h @@ -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; +} + diff --git a/board/drivers/llgpio.h b/board/drivers/llgpio.h new file mode 100644 index 0000000000..172776eb35 --- /dev/null +++ b/board/drivers/llgpio.h @@ -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); +} + diff --git a/board/drivers/spi.h b/board/drivers/spi.h new file mode 100644 index 0000000000..b61cd719a1 --- /dev/null +++ b/board/drivers/spi.h @@ -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; +} + diff --git a/board/drivers/timer.h b/board/drivers/timer.h new file mode 100644 index 0000000000..a14b619e4b --- /dev/null +++ b/board/drivers/timer.h @@ -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; +} + diff --git a/board/drivers/uart.h b/board/drivers/uart.h new file mode 100644 index 0000000000..f86c5f1593 --- /dev/null +++ b/board/drivers/uart.h @@ -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> 8) & 0xFF) + +// take in string length and return the first 2 bytes of a string descriptor +#define STRING_DESCRIPTOR_HEADER(size)\ + (((((size) * 2) + 2) & 0xFF) | 0x0300) + +uint8_t device_desc[] = { + DSCR_DEVICE_LEN, USB_DESC_TYPE_DEVICE, //Length, Type + 0x10, 0x02, // bcdUSB max version of USB supported (2.1) + 0xFF, 0xFF, 0xFF, 0x40, // Class, Subclass, Protocol, Max Packet Size + TOUSBORDER(USB_VID), // idVendor + TOUSBORDER(USB_PID), // idProduct +#ifdef STM32F4 + 0x00, 0x23, // bcdDevice +#else + 0x00, 0x22, // bcdDevice +#endif + 0x01, 0x02, // Manufacturer, Product + 0x03, 0x01 // Serial Number, Num Configurations +}; + +uint8_t device_qualifier[] = { + 0x0a, USB_DESC_TYPE_DEVICE_QUALIFIER, //Length, Type + 0x10, 0x02, // bcdUSB max version of USB supported (2.1) + 0xFF, 0xFF, 0xFF, 0x40, // bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0 + 0x01, 0x00 // bNumConfigurations, bReserved +}; + +#define ENDPOINT_RCV 0x80 +#define ENDPOINT_SND 0x00 + +uint8_t configuration_desc[] = { + DSCR_CONFIG_LEN, USB_DESC_TYPE_CONFIGURATION, // Length, Type, + TOUSBORDER(0x0045), // Total Len (uint16) + 0x01, 0x01, STRING_OFFSET_ICONFIGURATION, // Num Interface, Config Value, Configuration + 0xc0, 0x32, // Attributes, Max Power + // interface 0 ALT 0 + DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type + 0x00, 0x00, 0x03, // Index, Alt Index idx, Endpoint count + 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol + 0x00, // Interface + // endpoint 1, read CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_RCV | 1, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040), // Max Packet (0x0040) + 0x00, // Polling Interval (NA) + // endpoint 2, send serial + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040), // Max Packet (0x0040) + 0x00, // Polling Interval + // endpoint 3, send CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040), // Max Packet (0x0040) + 0x00, // Polling Interval + // interface 0 ALT 1 + DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type + 0x00, 0x01, 0x03, // Index, Alt Index idx, Endpoint count + 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol + 0x00, // Interface + // endpoint 1, read CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_RCV | 1, ENDPOINT_TYPE_INT, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040), // Max Packet (0x0040) + 0x05, // Polling Interval (5 frames) + // endpoint 2, send serial + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040), // Max Packet (0x0040) + 0x00, // Polling Interval + // endpoint 3, send CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040), // Max Packet (0x0040) + 0x00, // Polling Interval +}; + +// STRING_DESCRIPTOR_HEADER is for uint16 string descriptors +// it takes in a string length, which is bytes/2 because unicode +uint16_t string_language_desc[] = { + STRING_DESCRIPTOR_HEADER(1), + 0x0409 // american english +}; + +// these strings are all uint16's so that we don't need to spam ,0 after every character +uint16_t string_manufacturer_desc[] = { + STRING_DESCRIPTOR_HEADER(8), + 'c', 'o', 'm', 'm', 'a', '.', 'a', 'i' +}; + +uint16_t string_product_desc[] = { + STRING_DESCRIPTOR_HEADER(5), + 'p', 'a', 'n', 'd', 'a' +}; + +// default serial number when we're not a panda +uint16_t string_serial_desc[] = { + STRING_DESCRIPTOR_HEADER(4), + 'n', 'o', 'n', 'e' +}; + +// a string containing the default configuration index +uint16_t string_configuration_desc[] = { + STRING_DESCRIPTOR_HEADER(2), + '0', '1' // "01" +}; + +// WCID (auto install WinUSB driver) +// https://github.com/pbatard/libwdi/wiki/WCID-Devices +// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-installation#automatic-installation-of--winusb-without-an-inf-file +// WinUSB 1.0 descriptors, this is mostly used by Windows XP +uint8_t string_238_desc[] = { + 0x12, USB_DESC_TYPE_STRING, // bLength, bDescriptorType + 'M',0, 'S',0, 'F',0, 'T',0, '1',0, '0',0, '0',0, // qwSignature (MSFT100) + MS_VENDOR_CODE, 0x00 // bMS_VendorCode, bPad +}; +uint8_t winusb_ext_compatid_os_desc[] = { + 0x28, 0x00, 0x00, 0x00, // dwLength + 0x00, 0x01, // bcdVersion + 0x04, 0x00, // wIndex + 0x01, // bCount + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved + 0x00, // bFirstInterfaceNumber + 0x00, // Reserved + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subcompatible ID (none) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved +}; +uint8_t winusb_ext_prop_os_desc[] = { + 0x8e, 0x00, 0x00, 0x00, // dwLength + 0x00, 0x01, // bcdVersion + 0x05, 0x00, // wIndex + 0x01, 0x00, // wCount + // first property + 0x84, 0x00, 0x00, 0x00, // dwSize + 0x01, 0x00, 0x00, 0x00, // dwPropertyDataType + 0x28, 0x00, // wPropertyNameLength + 'D',0, 'e',0, 'v',0, 'i',0, 'c',0, 'e',0, 'I',0, 'n',0, 't',0, 'e',0, 'r',0, 'f',0, 'a',0, 'c',0, 'e',0, 'G',0, 'U',0, 'I',0, 'D',0, 0, 0, // bPropertyName (DeviceInterfaceGUID) + 0x4e, 0x00, 0x00, 0x00, // dwPropertyDataLength + '{',0, 'c',0, 'c',0, 'e',0, '5',0, '2',0, '9',0, '1',0, 'c',0, '-',0, 'a',0, '6',0, '9',0, 'f',0, '-',0, '4',0 ,'9',0 ,'9',0 ,'5',0 ,'-',0, 'a',0, '4',0, 'c',0, '2',0, '-',0, '2',0, 'a',0, 'e',0, '5',0, '7',0, 'a',0, '5',0, '1',0, 'a',0, 'd',0, 'e',0, '9',0, '}',0, 0, 0, // bPropertyData ({CCE5291C-A69F-4995-A4C2-2AE57A51ADE9}) +}; + +/* +Binary Object Store descriptor used to expose WebUSB (and more WinUSB) metadata +comments are from the wicg spec +References used: + https://wicg.github.io/webusb/#webusb-platform-capability-descriptor + https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c + https://os.mbed.com/users/larsgk/code/USBDevice_WebUSB/file/1d8a6665d607/WebUSBDevice/ + +*/ +uint8_t binary_object_store_desc[] = { + // BOS header + BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH, // bLength, this is only the length of the header + BINARY_OBJECT_STORE_DESCRIPTOR, // bDescriptorType + 0x40, 0x00, // wTotalLength (LSB, MSB) + 0x03, // bNumDeviceCaps (USB 2.0 + WebUSB + WinUSB) + + // ------------------------------------------------- + // USB 2.0 extension descriptor + 0x07, // bLength, Descriptor size + 0x10, // bDescriptorType, Device Capability Descriptor Type + 0x02, // bDevCapabilityType, USB 2.0 extension capability type + 0x00, 0x00, 0x00, 0x00, // bmAttributes, LIBUSB_BM_LPM_SUPPORT = 2 and its the only option + + // ------------------------------------------------- + // WebUSB descriptor + // header + 0x18, // bLength, Size of this descriptor. Must be set to 24. + 0x10, // bDescriptorType, DEVICE CAPABILITY descriptor + 0x05, // bDevCapabilityType, PLATFORM capability + 0x00, // bReserved, This field is reserved and shall be set to zero. + + // PlatformCapabilityUUID, Must be set to {3408b638-09a9-47a0-8bfd-a0768815b665}. + 0x38, 0xB6, 0x08, 0x34, + 0xA9, 0x09, 0xA0, 0x47, + 0x8B, 0xFD, 0xA0, 0x76, + 0x88, 0x15, 0xB6, 0x65, + // + + 0x00, 0x01, // bcdVersion, Protocol version supported. Must be set to 0x0100. + WEBUSB_VENDOR_CODE, // bVendorCode, bRequest value used for issuing WebUSB requests. + // there used to be a concept of "allowed origins", but it was removed from the spec + // it was intended to be a security feature, but then the entire security model relies on domain ownership + // https://github.com/WICG/webusb/issues/49 + // other implementations use various other indexed to leverate this no-longer-valid feature. we wont. + // the spec says we *must* reply to index 0x03 with the url, so we'll hint that that's the right index + 0x03, // iLandingPage, URL descriptor index of the device’s landing page. + + // ------------------------------------------------- + // WinUSB descriptor + // header + 0x1C, // Descriptor size (28 bytes) + 0x10, // Descriptor type (Device Capability) + 0x05, // Capability type (Platform) + 0x00, // Reserved + + // MS OS 2.0 Platform Capability ID (D8DD60DF-4589-4CC7-9CD2-659D9E648A9F) + // Indicates the device supports the Microsoft OS 2.0 descriptor + 0xDF, 0x60, 0xDD, 0xD8, + 0x89, 0x45, 0xC7, 0x4C, + 0x9C, 0xD2, 0x65, 0x9D, + 0x9E, 0x64, 0x8A, 0x9F, + + 0x00, 0x00, 0x03, 0x06, // Windows version, currently set to 8.1 (0x06030000) + + WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // MS OS 2.0 descriptor size (word) + MS_VENDOR_CODE, 0x00 // vendor code, no alternate enumeration +}; + +uint8_t webusb_url_descriptor[] = { + 0x14, /* bLength */ + WEBUSB_DESC_TYPE_URL, // bDescriptorType + WEBUSB_URL_SCHEME_HTTPS, // bScheme + 'u', 's', 'b', 'p', 'a', 'n', 'd', 'a', '.', 'c', 'o', 'm', 'm', 'a', '.', 'a', 'i' +}; + +// WinUSB 2.0 descriptor. This is what modern systems use +// https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c +// http://janaxelson.com/files/ms_os_20_descriptors.c +// https://books.google.com/books?id=pkefBgAAQBAJ&pg=PA353&lpg=PA353 +uint8_t winusb_20_desc[WINUSB_PLATFORM_DESCRIPTOR_LENGTH] = { + // Microsoft OS 2.0 descriptor set header (table 10) + 0x0A, 0x00, // Descriptor size (10 bytes) + 0x00, 0x00, // MS OS 2.0 descriptor set header + + 0x00, 0x00, 0x03, 0x06, // Windows version (8.1) (0x06030000) + WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // Total size of MS OS 2.0 descriptor set + + // Microsoft OS 2.0 compatible ID descriptor + 0x14, 0x00, // Descriptor size (20 bytes) + 0x03, 0x00, // MS OS 2.0 compatible ID descriptor + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Sub-compatible ID + + // Registry property descriptor + 0x80, 0x00, // Descriptor size (130 bytes) + 0x04, 0x00, // Registry Property descriptor + 0x01, 0x00, // Strings are null-terminated Unicode + 0x28, 0x00, // Size of Property Name (40 bytes) "DeviceInterfaceGUID" + + // bPropertyName (DeviceInterfaceGUID) + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, + 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, + 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, + + 0x4E, 0x00, // Size of Property Data (78 bytes) + + // Vendor-defined property data: {CCE5291C-A69F-4995-A4C2-2AE57A51ADE9} + '{', 0x00, 'c', 0x00, 'c', 0x00, 'e', 0x00, '5', 0x00, '2', 0x00, '9', 0x00, '1', 0x00, // 16 + 'c', 0x00, '-', 0x00, 'a', 0x00, '6', 0x00, '9', 0x00, 'f', 0x00, '-', 0x00, '4', 0x00, // 32 + '9', 0x00, '9', 0x00, '5', 0x00, '-', 0x00, 'a', 0x00, '4', 0x00, 'c', 0x00, '2', 0x00, // 48 + '-', 0x00, '2', 0x00, 'a', 0x00, 'e', 0x00, '5', 0x00, '7', 0x00, 'a', 0x00, '5', 0x00, // 64 + '1', 0x00, 'a', 0x00, 'd', 0x00, 'e', 0x00, '9', 0x00, '}', 0x00, 0x00, 0x00 // 78 bytes +}; + +// current packet +USB_Setup_TypeDef setup; +uint8_t usbdata[0x100]; +uint8_t* ep0_txdata = NULL; +uint16_t ep0_txlen = 0; + +// Store the current interface alt setting. +int current_int0_alt_setting = 0; + +// packet read and write + +void *USB_ReadPacket(void *dest, uint16_t len) { + uint32_t i=0; + uint32_t count32b = (len + 3) / 4; + + for ( i = 0; i < count32b; i++) { + // packed? + *(__attribute__((__packed__)) uint32_t *)dest = USBx_DFIFO(0); + dest += 4; + } + return ((void *)dest); +} + +void USB_WritePacket(const uint8_t *src, uint16_t len, uint32_t ep) { + #ifdef DEBUG_USB + puts("writing "); + hexdump(src, len); + #endif + + uint8_t numpacket = (len+(MAX_RESP_LEN-1))/MAX_RESP_LEN; + uint32_t count32b = 0, i = 0; + count32b = (len + 3) / 4; + + // bullshit + USBx_INEP(ep)->DIEPTSIZ = ((numpacket << 19) & USB_OTG_DIEPTSIZ_PKTCNT) | + (len & USB_OTG_DIEPTSIZ_XFRSIZ); + USBx_INEP(ep)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA); + + // load the FIFO + for (i = 0; i < count32b; i++) { + USBx_DFIFO(ep) = *((__attribute__((__packed__)) uint32_t *)src); + src += 4; + } +} + +// IN EP 0 TX FIFO has a max size of 127 bytes (much smaller than the rest) +// so use TX FIFO empty interrupt to send larger amounts of data +void USB_WritePacket_EP0(uint8_t *src, uint16_t len) { + #ifdef DEBUG_USB + puts("writing "); + hexdump(src, len); + #endif + + uint16_t wplen = MIN(len, 0x40); + USB_WritePacket(src, wplen, 0); + + if (wplen < len) { + ep0_txdata = src + wplen; + ep0_txlen = len - wplen; + USBx_DEVICE->DIEPEMPMSK |= 1; + } else { + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } +} + +void usb_reset(void) { + // unmask endpoint interrupts, so many sets + USBx_DEVICE->DAINT = 0xFFFFFFFF; + USBx_DEVICE->DAINTMSK = 0xFFFFFFFF; + //USBx_DEVICE->DOEPMSK = (USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_EPDM); + //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM | USB_OTG_DIEPMSK_ITTXFEMSK); + //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM); + + // all interrupts for debugging + USBx_DEVICE->DIEPMSK = 0xFFFFFFFF; + USBx_DEVICE->DOEPMSK = 0xFFFFFFFF; + + // clear interrupts + USBx_INEP(0)->DIEPINT = 0xFF; + USBx_OUTEP(0)->DOEPINT = 0xFF; + + // unset the address + USBx_DEVICE->DCFG &= ~USB_OTG_DCFG_DAD; + + // set up USB FIFOs + // RX start address is fixed to 0 + USBx->GRXFSIZ = 0x40; + + // 0x100 to offset past GRXFSIZ + USBx->DIEPTXF0_HNPTXFSIZ = (0x40 << 16) | 0x40; + + // EP1, massive + USBx->DIEPTXF[0] = (0x40 << 16) | 0x80; + + // flush TX fifo + USBx->GRSTCTL = USB_OTG_GRSTCTL_TXFFLSH | USB_OTG_GRSTCTL_TXFNUM_4; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH); + // flush RX FIFO + USBx->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH) == USB_OTG_GRSTCTL_RXFFLSH); + + // no global NAK + USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGINAK; + + // ready to receive setup packets + USBx_OUTEP(0)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1 << 19)) | (3 * 8); +} + +char to_hex_char(int a) { + char ret; + if (a < 10) { + ret = '0' + a; + } else { + ret = 'a' + (a - 10); + } + return ret; +} + +void usb_setup(void) { + int resp_len; + // setup packet is ready + switch (setup.b.bRequest) { + case USB_REQ_SET_CONFIGURATION: + // enable other endpoints, has to be here? + USBx_INEP(1)->DIEPCTL = (0x40 & USB_OTG_DIEPCTL_MPSIZ) | (2 << 18) | (1 << 22) | + USB_OTG_DIEPCTL_SD0PID_SEVNFRM | USB_OTG_DIEPCTL_USBAEP; + USBx_INEP(1)->DIEPINT = 0xFF; + + USBx_OUTEP(2)->DOEPTSIZ = (1 << 19) | 0x40; + USBx_OUTEP(2)->DOEPCTL = (0x40 & USB_OTG_DOEPCTL_MPSIZ) | (2 << 18) | + USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; + USBx_OUTEP(2)->DOEPINT = 0xFF; + + USBx_OUTEP(3)->DOEPTSIZ = (1 << 19) | 0x40; + USBx_OUTEP(3)->DOEPCTL = (0x40 & USB_OTG_DOEPCTL_MPSIZ) | (2 << 18) | + USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; + USBx_OUTEP(3)->DOEPINT = 0xFF; + + // mark ready to receive + USBx_OUTEP(2)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + USBx_OUTEP(3)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_REQ_SET_ADDRESS: + // set now? + USBx_DEVICE->DCFG |= ((setup.b.wValue.w & 0x7f) << 4); + + #ifdef DEBUG_USB + puts(" set address\n"); + #endif + + // TODO: this isn't enumeration complete + // moved here to work better on OS X + usb_cb_enumeration_complete(); + + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + + break; + case USB_REQ_GET_DESCRIPTOR: + switch (setup.b.wValue.bw.lsb) { + case USB_DESC_TYPE_DEVICE: + //puts(" writing device descriptor\n"); + + // setup transfer + USB_WritePacket(device_desc, MIN(sizeof(device_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + + //puts("D"); + break; + case USB_DESC_TYPE_CONFIGURATION: + USB_WritePacket(configuration_desc, MIN(sizeof(configuration_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_DEVICE_QUALIFIER: + USB_WritePacket(device_qualifier, MIN(sizeof(device_qualifier), setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_STRING: + switch (setup.b.wValue.bw.msb) { + case STRING_OFFSET_LANGID: + USB_WritePacket((uint8_t*)string_language_desc, MIN(sizeof(string_language_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_IMANUFACTURER: + USB_WritePacket((uint8_t*)string_manufacturer_desc, MIN(sizeof(string_manufacturer_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_IPRODUCT: + USB_WritePacket((uint8_t*)string_product_desc, MIN(sizeof(string_product_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_ISERIAL: + #ifdef UID_BASE + resp[0] = 0x02 + 12*4; + resp[1] = 0x03; + + // 96 bits = 12 bytes + for (int i = 0; i < 12; i++){ + uint8_t cc = ((uint8_t *)UID_BASE)[i]; + resp[2 + i*4 + 0] = to_hex_char((cc>>4)&0xF); + resp[2 + i*4 + 1] = '\0'; + resp[2 + i*4 + 2] = to_hex_char((cc>>0)&0xF); + resp[2 + i*4 + 3] = '\0'; + } + + USB_WritePacket(resp, MIN(resp[0], setup.b.wLength.w), 0); + #else + USB_WritePacket((const uint8_t *)string_serial_desc, MIN(sizeof(string_serial_desc), setup.b.wLength.w), 0); + #endif + break; + case STRING_OFFSET_ICONFIGURATION: + USB_WritePacket((uint8_t*)string_configuration_desc, MIN(sizeof(string_configuration_desc), setup.b.wLength.w), 0); + break; + case 238: + USB_WritePacket((uint8_t*)string_238_desc, MIN(sizeof(string_238_desc), setup.b.wLength.w), 0); + break; + default: + // nothing + USB_WritePacket(0, 0, 0); + break; + } + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_BINARY_OBJECT_STORE: + USB_WritePacket(binary_object_store_desc, MIN(sizeof(binary_object_store_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + default: + // nothing here? + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + } + break; + case USB_REQ_GET_STATUS: + // empty resp? + resp[0] = 0; + resp[1] = 0; + USB_WritePacket((void*)&resp, 2, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_REQ_SET_INTERFACE: + // Store the alt setting number for IN EP behavior. + current_int0_alt_setting = setup.b.wValue.w; + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case WEBUSB_VENDOR_CODE: + switch (setup.b.wIndex.w) { + case WEBUSB_REQ_GET_URL: + USB_WritePacket(webusb_url_descriptor, MIN(sizeof(webusb_url_descriptor), setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + default: + // probably asking for allowed origins, which was removed from the spec + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + } + break; + case MS_VENDOR_CODE: + switch (setup.b.wIndex.w) { + // winusb 2.0 descriptor from BOS + case WINUSB_REQ_GET_DESCRIPTOR: + USB_WritePacket_EP0((uint8_t*)winusb_20_desc, MIN(sizeof(winusb_20_desc), setup.b.wLength.w)); + break; + // Extended Compat ID OS Descriptor + case WINUSB_REQ_GET_COMPATID_DESCRIPTOR: + USB_WritePacket_EP0((uint8_t*)winusb_ext_compatid_os_desc, MIN(sizeof(winusb_ext_compatid_os_desc), setup.b.wLength.w)); + break; + // Extended Properties OS Descriptor + case WINUSB_REQ_GET_EXT_PROPS_OS: + USB_WritePacket_EP0((uint8_t*)winusb_ext_prop_os_desc, MIN(sizeof(winusb_ext_prop_os_desc), setup.b.wLength.w)); + break; + default: + USB_WritePacket_EP0(0, 0); + } + break; + default: + resp_len = usb_cb_control_msg(&setup, resp, 1); + USB_WritePacket(resp, MIN(resp_len, setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } +} + +void usb_init(void) { + // full speed PHY, do reset and remove power down + /*puth(USBx->GRSTCTL); + puts(" resetting PHY\n");*/ + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0); + //puts("AHB idle\n"); + + // reset PHY here + USBx->GRSTCTL |= USB_OTG_GRSTCTL_CSRST; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST); + //puts("reset done\n"); + + // internal PHY, force device mode + USBx->GUSBCFG = USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_FDMOD; + + // slowest timings + USBx->GUSBCFG |= (uint32_t)((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT); + + // power up the PHY +#ifdef STM32F4 + USBx->GCCFG = USB_OTG_GCCFG_PWRDWN; + + //USBx->GCCFG |= USB_OTG_GCCFG_VBDEN | USB_OTG_GCCFG_SDEN |USB_OTG_GCCFG_PDEN | USB_OTG_GCCFG_DCDEN; + + /* B-peripheral session valid override enable*/ + USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; + USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN; +#else + USBx->GCCFG = USB_OTG_GCCFG_PWRDWN | USB_OTG_GCCFG_NOVBUSSENS; +#endif + + // be a device, slowest timings + //USBx->GUSBCFG = USB_OTG_GUSBCFG_FDMOD | USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_TRDT | USB_OTG_GUSBCFG_TOCAL; + //USBx->GUSBCFG |= (uint32_t)((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT); + //USBx->GUSBCFG = USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_TRDT | USB_OTG_GUSBCFG_TOCAL; + + // **** for debugging, doesn't seem to work **** + //USBx->GUSBCFG |= USB_OTG_GUSBCFG_CTXPKT; + + // reset PHY clock + USBx_PCGCCTL = 0; + + // enable the fancy OTG things + // DCFG_FRAME_INTERVAL_80 is 0 + //USBx->GUSBCFG |= USB_OTG_GUSBCFG_HNPCAP | USB_OTG_GUSBCFG_SRPCAP; + USBx_DEVICE->DCFG |= USB_OTG_SPEED_FULL | USB_OTG_DCFG_NZLSOHSK; + + //USBx_DEVICE->DCFG = USB_OTG_DCFG_NZLSOHSK | USB_OTG_DCFG_DSPD; + //USBx_DEVICE->DCFG = USB_OTG_DCFG_DSPD; + + // clear pending interrupts + USBx->GINTSTS = 0xBFFFFFFFU; + + // setup USB interrupts + // all interrupts except TXFIFO EMPTY + //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF); + //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM); + USBx->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_OTGINT | + USB_OTG_GINTMSK_RXFLVLM | USB_OTG_GINTMSK_GONAKEFFM | USB_OTG_GINTMSK_GINAKEFFM | + USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_USBSUSPM | + USB_OTG_GINTMSK_CIDSCHGM | USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_MMISM; + + USBx->GAHBCFG = USB_OTG_GAHBCFG_GINT; + + // DCTL startup value is 2 on new chip, 0 on old chip + // THIS IS FUCKING BULLSHIT + USBx_DEVICE->DCTL = 0; + + // enable the IRQ + NVIC_EnableIRQ(OTG_FS_IRQn); +} + +// ***************************** USB port ***************************** + +void usb_irqhandler(void) { + //USBx->GINTMSK = 0; + + unsigned int gintsts = USBx->GINTSTS; + unsigned int gotgint = USBx->GOTGINT; + unsigned int daint = USBx_DEVICE->DAINT; + + // gintsts SUSPEND? 04008428 + #ifdef DEBUG_USB + puth(gintsts); + puts(" "); + /*puth(USBx->GCCFG); + puts(" ");*/ + puth(gotgint); + puts(" ep "); + puth(daint); + puts(" USB interrupt!\n"); + #endif + + if ((gintsts & USB_OTG_GINTSTS_CIDSCHG) != 0) { + puts("connector ID status change\n"); + } + + if ((gintsts & USB_OTG_GINTSTS_ESUSP) != 0) { + puts("ESUSP detected\n"); + } + + if ((gintsts & USB_OTG_GINTSTS_USBRST) != 0) { + puts("USB reset\n"); + usb_reset(); + } + + if ((gintsts & USB_OTG_GINTSTS_ENUMDNE) != 0) { + puts("enumeration done"); + // Full speed, ENUMSPD + //puth(USBx_DEVICE->DSTS); + puts("\n"); + } + + if ((gintsts & USB_OTG_GINTSTS_OTGINT) != 0) { + puts("OTG int:"); + puth(USBx->GOTGINT); + puts("\n"); + + // getting ADTOCHG + //USBx->GOTGINT = USBx->GOTGINT; + } + + // RX FIFO first + if ((gintsts & USB_OTG_GINTSTS_RXFLVL) != 0) { + // 1. Read the Receive status pop register + volatile unsigned int rxst = USBx->GRXSTSP; + + #ifdef DEBUG_USB + puts(" RX FIFO:"); + puth(rxst); + puts(" status: "); + puth((rxst & USB_OTG_GRXSTSP_PKTSTS) >> 17); + puts(" len: "); + puth((rxst & USB_OTG_GRXSTSP_BCNT) >> 4); + puts("\n"); + #endif + + if (((rxst & USB_OTG_GRXSTSP_PKTSTS) >> 17) == STS_DATA_UPDT) { + int endpoint = (rxst & USB_OTG_GRXSTSP_EPNUM); + int len = (rxst & USB_OTG_GRXSTSP_BCNT) >> 4; + USB_ReadPacket(&usbdata, len); + #ifdef DEBUG_USB + puts(" data "); + puth(len); + puts("\n"); + hexdump(&usbdata, len); + #endif + + if (endpoint == 2) { + usb_cb_ep2_out(usbdata, len, 1); + } + + if (endpoint == 3) { + usb_cb_ep3_out(usbdata, len, 1); + } + } else if (((rxst & USB_OTG_GRXSTSP_PKTSTS) >> 17) == STS_SETUP_UPDT) { + USB_ReadPacket(&setup, 8); + #ifdef DEBUG_USB + puts(" setup "); + hexdump(&setup, 8); + puts("\n"); + #endif + } + } + + /*if (gintsts & USB_OTG_GINTSTS_HPRTINT) { + // host + puts("HPRT:"); + puth(USBx_HOST_PORT->HPRT); + puts("\n"); + if (USBx_HOST_PORT->HPRT & USB_OTG_HPRT_PCDET) { + USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PRST; + USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PCDET; + } + + }*/ + + if ((gintsts & USB_OTG_GINTSTS_BOUTNAKEFF) || (gintsts & USB_OTG_GINTSTS_GINAKEFF)) { + // no global NAK, why is this getting set? + #ifdef DEBUG_USB + puts("GLOBAL NAK\n"); + #endif + USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK | USB_OTG_DCTL_CGINAK; + } + + if ((gintsts & USB_OTG_GINTSTS_SRQINT) != 0) { + // we want to do "A-device host negotiation protocol" since we are the A-device + /*puts("start request\n"); + puth(USBx->GOTGCTL); + puts("\n");*/ + //USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; + //USBx_HOST_PORT->HPRT = USB_OTG_HPRT_PPWR | USB_OTG_HPRT_PENA; + //USBx->GOTGCTL |= USB_OTG_GOTGCTL_SRQ; + } + + // out endpoint hit + if ((gintsts & USB_OTG_GINTSTS_OEPINT) != 0) { + #ifdef DEBUG_USB + puts(" 0:"); + puth(USBx_OUTEP(0)->DOEPINT); + puts(" 2:"); + puth(USBx_OUTEP(2)->DOEPINT); + puts(" 3:"); + puth(USBx_OUTEP(3)->DOEPINT); + puts(" "); + puth(USBx_OUTEP(3)->DOEPCTL); + puts(" 4:"); + puth(USBx_OUTEP(4)->DOEPINT); + puts(" OUT ENDPOINT\n"); + #endif + + if ((USBx_OUTEP(2)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0) { + #ifdef DEBUG_USB + puts(" OUT2 PACKET XFRC\n"); + #endif + USBx_OUTEP(2)->DOEPTSIZ = (1 << 19) | 0x40; + USBx_OUTEP(2)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + } + + if ((USBx_OUTEP(3)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0) { + #ifdef DEBUG_USB + puts(" OUT3 PACKET XFRC\n"); + #endif + USBx_OUTEP(3)->DOEPTSIZ = (1 << 19) | 0x40; + USBx_OUTEP(3)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + } else if ((USBx_OUTEP(3)->DOEPINT & 0x2000) != 0) { + #ifdef DEBUG_USB + puts(" OUT3 PACKET WTF\n"); + #endif + // if NAK was set trigger this, unknown interrupt + USBx_OUTEP(3)->DOEPTSIZ = (1 << 19) | 0x40; + USBx_OUTEP(3)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } else if ((USBx_OUTEP(3)->DOEPINT) != 0) { + puts("OUTEP3 error "); + puth(USBx_OUTEP(3)->DOEPINT); + puts("\n"); + } + + if ((USBx_OUTEP(0)->DOEPINT & USB_OTG_DIEPINT_XFRC) != 0) { + // ready for next packet + USBx_OUTEP(0)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1 << 19)) | (1 * 8); + } + + // respond to setup packets + if ((USBx_OUTEP(0)->DOEPINT & USB_OTG_DOEPINT_STUP) != 0) { + usb_setup(); + } + + USBx_OUTEP(0)->DOEPINT = USBx_OUTEP(0)->DOEPINT; + USBx_OUTEP(2)->DOEPINT = USBx_OUTEP(2)->DOEPINT; + USBx_OUTEP(3)->DOEPINT = USBx_OUTEP(3)->DOEPINT; + } + + // interrupt endpoint hit (Page 1221) + if ((gintsts & USB_OTG_GINTSTS_IEPINT) != 0) { + #ifdef DEBUG_USB + puts(" "); + puth(USBx_INEP(0)->DIEPINT); + puts(" "); + puth(USBx_INEP(1)->DIEPINT); + puts(" IN ENDPOINT\n"); + #endif + + // Should likely check the EP of the IN request even if there is + // only one IN endpoint. + + // No need to set NAK in OTG_DIEPCTL0 when nothing to send, + // Appears USB core automatically sets NAK. WritePacket clears it. + + // Handle the two interface alternate settings. Setting 0 has EP1 + // as bulk. Setting 1 has EP1 as interrupt. The code to handle + // these two EP variations are very similar and can be + // restructured for smaller code footprint. Keeping split out for + // now for clarity. + + //TODO add default case. Should it NAK? + switch (current_int0_alt_setting) { + case 0: ////// Bulk config + // *** IN token received when TxFIFO is empty + if ((USBx_INEP(1)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0) { + #ifdef DEBUG_USB + puts(" IN PACKET QUEUE\n"); + #endif + // TODO: always assuming max len, can we get the length? + USB_WritePacket((void *)resp, usb_cb_ep1_in(resp, 0x40, 1), 1); + } + break; + + case 1: ////// Interrupt config + // *** IN token received when TxFIFO is empty + if ((USBx_INEP(1)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0) { + #ifdef DEBUG_USB + puts(" IN PACKET QUEUE\n"); + #endif + // TODO: always assuming max len, can we get the length? + int len = usb_cb_ep1_in(resp, 0x40, 1); + if (len > 0) { + USB_WritePacket((void *)resp, len, 1); + } + } + break; + default: + puts("current_int0_alt_setting value invalid\n"); + break; + } + + if ((USBx_INEP(0)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0) { + #ifdef DEBUG_USB + puts(" IN PACKET QUEUE\n"); + #endif + + if ((ep0_txlen != 0) && ((USBx_INEP(0)->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV) >= 0x40)) { + uint16_t len = MIN(ep0_txlen, 0x40); + USB_WritePacket(ep0_txdata, len, 0); + ep0_txdata += len; + ep0_txlen -= len; + if (ep0_txlen == 0) { + ep0_txdata = NULL; + USBx_DEVICE->DIEPEMPMSK &= ~1; + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } + } + } + + // clear interrupts + USBx_INEP(0)->DIEPINT = USBx_INEP(0)->DIEPINT; // Why ep0? + USBx_INEP(1)->DIEPINT = USBx_INEP(1)->DIEPINT; + } + + // clear all interrupts we handled + USBx_DEVICE->DAINT = daint; + USBx->GOTGINT = gotgint; + USBx->GINTSTS = gintsts; + + //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF); +} + +void OTG_FS_IRQHandler(void) { + NVIC_DisableIRQ(OTG_FS_IRQn); + //__disable_irq(); + usb_irqhandler(); + //__enable_irq(); + NVIC_EnableIRQ(OTG_FS_IRQn); +} + diff --git a/board/get_sdk.sh b/board/get_sdk.sh new file mode 100755 index 0000000000..7b8d1f9154 --- /dev/null +++ b/board/get_sdk.sh @@ -0,0 +1,3 @@ +#!/bin/bash +sudo apt-get install gcc-arm-none-eabi python-pip +sudo pip2 install libusb1 pycrypto requests diff --git a/board/get_sdk_mac.sh b/board/get_sdk_mac.sh new file mode 100755 index 0000000000..a6f641dce1 --- /dev/null +++ b/board/get_sdk_mac.sh @@ -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 diff --git a/board/gpio.h b/board/gpio.h new file mode 100644 index 0000000000..26a82d6275 --- /dev/null +++ b/board/gpio.h @@ -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; iAHB1ENR |= 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; + } +} diff --git a/board/inc/cmsis_gcc.h b/board/inc/cmsis_gcc.h new file mode 100644 index 0000000000..bb89fbba9e --- /dev/null +++ b/board/inc/cmsis_gcc.h @@ -0,0 +1,1373 @@ +/**************************************************************************//** + * @file cmsis_gcc.h + * @brief CMSIS Cortex-M Core Function/Instruction Header File + * @version V4.30 + * @date 20. October 2015 + ******************************************************************************/ +/* Copyright (c) 2009 - 2015 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - 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. + - Neither the name of ARM 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 COPYRIGHT HOLDERS AND 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. + ---------------------------------------------------------------------------*/ + + +#ifndef __CMSIS_GCC_H +#define __CMSIS_GCC_H + +/* ignore some GCC warnings */ +#if defined ( __GNUC__ ) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); +} + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + + \return xPSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PSP(void) +{ + register uint32_t result; + + __ASM volatile ("MRS %0, psp\n" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0\n" : : "r" (topOfProcStack) : "sp"); +} + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void) +{ + register uint32_t result; + + __ASM volatile ("MRS %0, msp\n" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : "sp"); +} + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (__CORTEX_M >= 0x03U) + +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI(uint32_t value) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); +} + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI_MAX(uint32_t value) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (value) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + +#endif /* (__CORTEX_M >= 0x03U) */ + + +#if (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FPSCR(void) +{ +#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) + uint32_t result; + + /* Empty asm statement works as a scheduling barrier */ + __ASM volatile (""); + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + __ASM volatile (""); + return(result); +#else + return(0); +#endif +} + + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) +{ +#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) + /* Empty asm statement works as a scheduling barrier */ + __ASM volatile (""); + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); + __ASM volatile (""); +#endif +} + +#endif /* (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) */ + + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +__attribute__((always_inline)) __STATIC_INLINE void __NOP(void) +{ + __ASM volatile ("nop"); +} + + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +__attribute__((always_inline)) __STATIC_INLINE void __WFI(void) +{ + __ASM volatile ("wfi"); +} + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +__attribute__((always_inline)) __STATIC_INLINE void __WFE(void) +{ + __ASM volatile ("wfe"); +} + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +__attribute__((always_inline)) __STATIC_INLINE void __SEV(void) +{ + __ASM volatile ("sev"); +} + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +__attribute__((always_inline)) __STATIC_INLINE void __ISB(void) +{ + __ASM volatile ("isb 0xF":::"memory"); +} + + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__attribute__((always_inline)) __STATIC_INLINE void __DSB(void) +{ + __ASM volatile ("dsb 0xF":::"memory"); +} + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__attribute__((always_inline)) __STATIC_INLINE void __DMB(void) +{ + __ASM volatile ("dmb 0xF":::"memory"); +} + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in integer value. + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +#endif +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in two unsigned short values. + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief Reverse byte order in signed short value + \details Reverses the byte order in a signed short value with sign extension to integer. + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__((always_inline)) __STATIC_INLINE int32_t __REVSH(int32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (short)__builtin_bswap16(value); +#else + int32_t result; + + __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +#endif +} + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] value Value to rotate + \param [in] value Number of Bits to rotate + \return Rotated value + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + +#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); +#else + int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */ + + result = value; /* r will be reversed bits of v; first get LSB of v */ + for (value >>= 1U; value; value >>= 1U) + { + result <<= 1U; + result |= value & 1U; + s--; + } + result <<= s; /* shift when v's highest bits are zero */ +#endif + return(result); +} + + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +#define __CLZ __builtin_clz + + +#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) + +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +__attribute__((always_inline)) __STATIC_INLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDRBT(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDRHT(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDRT(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__attribute__((always_inline)) __STATIC_INLINE void __STRBT(uint8_t value, volatile uint8_t *addr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*addr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__attribute__((always_inline)) __STATIC_INLINE void __STRHT(uint16_t value, volatile uint16_t *addr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*addr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__attribute__((always_inline)) __STATIC_INLINE void __STRT(uint32_t value, volatile uint32_t *addr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*addr) : "r" (value) ); +} + +#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (__CORTEX_M >= 0x04U) /* only for Cortex-M4 and above */ + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1,ARG2) \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +#define __USAT16(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __QADD( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __QSUB( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +#define __PKHBT(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +#define __PKHTB(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + if (ARG3 == 0) \ + __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ + else \ + __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__CORTEX_M >= 0x04) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#if defined ( __GNUC__ ) +#pragma GCC diagnostic pop +#endif + +#endif /* __CMSIS_GCC_H */ diff --git a/board/inc/core_cm3.h b/board/inc/core_cm3.h new file mode 100644 index 0000000000..b4ac4c7b05 --- /dev/null +++ b/board/inc/core_cm3.h @@ -0,0 +1,1763 @@ +/**************************************************************************//** + * @file core_cm3.h + * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Header File + * @version V4.30 + * @date 20. October 2015 + ******************************************************************************/ +/* Copyright (c) 2009 - 2015 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - 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. + - Neither the name of ARM 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 COPYRIGHT HOLDERS AND 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. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM3_H_GENERIC +#define __CORE_CM3_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M3 + @{ + */ + +/* CMSIS CM3 definitions */ +#define __CM3_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ +#define __CM3_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __CM3_CMSIS_VERSION ((__CM3_CMSIS_VERSION_MAIN << 16U) | \ + __CM3_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + +#define __CORTEX_M (0x03U) /*!< Cortex-M Core */ + + +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ + #define __STATIC_INLINE static inline + +#elif defined ( __TMS470__ ) + #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __CSMC__ ) + #define __packed + #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ + #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ + #define __STATIC_INLINE static inline + +#else + #error Unknown compiler +#endif + +/** __FPU_USED indicates whether an FPU is used or not. + This core does not support an FPU at all +*/ +#define __FPU_USED 0U + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_PCS_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TMS470__ ) + #if defined __TI_VFP_SUPPORT__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#endif + +#include "core_cmInstr.h" /* Core Instruction Access */ +#include "core_cmFunc.h" /* Core Function Access */ + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM3_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM3_H_DEPENDANT +#define __CORE_CM3_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM3_REV + #define __CM3_REV 0x0200U + #warning "__CM3_REV not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 4U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M3 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ +#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24U]; + __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[24U]; + __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24U]; + __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24U]; + __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56U]; + __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHP[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5U]; + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#if (__CM3_REV < 0x0201U) /* core r2p1 */ +#define SCB_VTOR_TBLBASE_Pos 29U /*!< SCB VTOR: TBLBASE Position */ +#define SCB_VTOR_TBLBASE_Msk (1UL << SCB_VTOR_TBLBASE_Pos) /*!< SCB VTOR: TBLBASE Mask */ + +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x3FFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#else +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#endif + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ +#if ((defined __CM3_REV) && (__CM3_REV >= 0x200U)) + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +#else + uint32_t RESERVED1[1U]; +#endif +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2U /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1U /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29U]; + __OM uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __IM uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IOM uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8U /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0U /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL /*<< ITM_IWR_ATVALIDM_Pos*/) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0U /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL /*<< ITM_IRR_ATREADYM_Pos*/) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0U /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL /*<< ITM_IMCR_INTEGRATION_Pos*/) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ +#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ +#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register Definitions */ +#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Cortex-M3 Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable External Interrupt + \details Enables a device-specific interrupt in the NVIC interrupt controller. + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +{ + NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); +} + + +/** + \brief Disable External Interrupt + \details Disables a device-specific interrupt in the NVIC interrupt controller. + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); +} + + +/** + \brief Get Pending Interrupt + \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. + \param [in] IRQn Interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + */ +__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of an external interrupt. + \param [in] IRQn Interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of an external interrupt. + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in NVIC and returns the active bit. + \param [in] IRQn Interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + */ +__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +{ + return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); +} + + +/** + \brief Set Interrupt Priority + \details Sets the priority of an interrupt. + \note The priority cannot be set for every core interrupt. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + */ +__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) < 0) + { + SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of an interrupt. + The interrupt number can be positive to specify an external (device specific) interrupt, + or negative to specify an internal (core) interrupt. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) < 0) + { + return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__STATIC_INLINE void NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM3_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/board/inc/core_cm4.h b/board/inc/core_cm4.h new file mode 100644 index 0000000000..dc840ebf22 --- /dev/null +++ b/board/inc/core_cm4.h @@ -0,0 +1,1937 @@ +/**************************************************************************//** + * @file core_cm4.h + * @brief CMSIS Cortex-M4 Core Peripheral Access Layer Header File + * @version V4.30 + * @date 20. October 2015 + ******************************************************************************/ +/* Copyright (c) 2009 - 2015 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - 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. + - Neither the name of ARM 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 COPYRIGHT HOLDERS AND 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. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM4_H_GENERIC +#define __CORE_CM4_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M4 + @{ + */ + +/* CMSIS CM4 definitions */ +#define __CM4_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ +#define __CM4_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16U) | \ + __CM4_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + +#define __CORTEX_M (0x04U) /*!< Cortex-M Core */ + + +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ + #define __STATIC_INLINE static inline + +#elif defined ( __TMS470__ ) + #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __CSMC__ ) + #define __packed + #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ + #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ + #define __STATIC_INLINE static inline + +#else + #error Unknown compiler +#endif + +/** __FPU_USED indicates whether an FPU is used or not. + For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #if (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_PCS_VFP + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #if (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TMS470__ ) + #if defined __TI_VFP_SUPPORT__ + #if (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #if (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #if (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#endif + +#include "core_cmInstr.h" /* Core Instruction Access */ +#include "core_cmFunc.h" /* Core Function Access */ +#include "core_cmSimd.h" /* Compiler specific SIMD Intrinsics */ + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM4_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM4_H_DEPENDANT +#define __CORE_CM4_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM4_REV + #define __CM4_REV 0x0000U + #warning "__CM4_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 4U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M4 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core FPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + +#define APSR_GE_Pos 16U /*!< APSR: GE Position */ +#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ +#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ +#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ +#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ + +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24U]; + __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[24U]; + __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24U]; + __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24U]; + __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56U]; + __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHP[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5U]; + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ +#define SCnSCB_ACTLR_DISOOFP_Pos 9U /*!< ACTLR: DISOOFP Position */ +#define SCnSCB_ACTLR_DISOOFP_Msk (1UL << SCnSCB_ACTLR_DISOOFP_Pos) /*!< ACTLR: DISOOFP Mask */ + +#define SCnSCB_ACTLR_DISFPCA_Pos 8U /*!< ACTLR: DISFPCA Position */ +#define SCnSCB_ACTLR_DISFPCA_Msk (1UL << SCnSCB_ACTLR_DISFPCA_Pos) /*!< ACTLR: DISFPCA Mask */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2U /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1U /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29U]; + __OM uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __IM uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IOM uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8U /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0U /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL /*<< ITM_IWR_ATVALIDM_Pos*/) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0U /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL /*<< ITM_IRR_ATREADYM_Pos*/) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0U /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL /*<< ITM_IMCR_INTEGRATION_Pos*/) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ +#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ +#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register Definitions */ +#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if (__FPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** + \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ +} FPU_Type; + +/* Floating-Point Context Control Register Definitions */ +#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register Definitions */ +#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register Definitions */ +#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 Definitions */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 Definitions */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ + +/*@} end of group CMSIS_FPU */ +#endif + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Cortex-M4 Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +#if (__FPU_PRESENT == 1U) + #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ + #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ +#endif + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable External Interrupt + \details Enables a device-specific interrupt in the NVIC interrupt controller. + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +{ + NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); +} + + +/** + \brief Disable External Interrupt + \details Disables a device-specific interrupt in the NVIC interrupt controller. + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); +} + + +/** + \brief Get Pending Interrupt + \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. + \param [in] IRQn Interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + */ +__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of an external interrupt. + \param [in] IRQn Interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of an external interrupt. + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in NVIC and returns the active bit. + \param [in] IRQn Interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + */ +__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +{ + return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); +} + + +/** + \brief Set Interrupt Priority + \details Sets the priority of an interrupt. + \note The priority cannot be set for every core interrupt. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + */ +__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) < 0) + { + SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of an interrupt. + The interrupt number can be positive to specify an external (device specific) interrupt, + or negative to specify an internal (core) interrupt. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) < 0) + { + return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__STATIC_INLINE void NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM4_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/board/inc/core_cmFunc.h b/board/inc/core_cmFunc.h new file mode 100644 index 0000000000..652a48af07 --- /dev/null +++ b/board/inc/core_cmFunc.h @@ -0,0 +1,87 @@ +/**************************************************************************//** + * @file core_cmFunc.h + * @brief CMSIS Cortex-M Core Function Access Header File + * @version V4.30 + * @date 20. October 2015 + ******************************************************************************/ +/* Copyright (c) 2009 - 2015 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - 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. + - Neither the name of ARM 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 COPYRIGHT HOLDERS AND 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. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CMFUNC_H +#define __CORE_CMFUNC_H + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ +*/ + +/*------------------ RealView Compiler -----------------*/ +#if defined ( __CC_ARM ) + #include "cmsis_armcc.h" + +/*------------------ ARM Compiler V6 -------------------*/ +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #include "cmsis_armcc_V6.h" + +/*------------------ GNU Compiler ----------------------*/ +#elif defined ( __GNUC__ ) + #include "cmsis_gcc.h" + +/*------------------ ICC Compiler ----------------------*/ +#elif defined ( __ICCARM__ ) + #include + +/*------------------ TI CCS Compiler -------------------*/ +#elif defined ( __TMS470__ ) + #include + +/*------------------ TASKING Compiler ------------------*/ +#elif defined ( __TASKING__ ) + /* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + +/*------------------ COSMIC Compiler -------------------*/ +#elif defined ( __CSMC__ ) + #include + +#endif + +/*@} end of CMSIS_Core_RegAccFunctions */ + +#endif /* __CORE_CMFUNC_H */ diff --git a/board/inc/core_cmInstr.h b/board/inc/core_cmInstr.h new file mode 100644 index 0000000000..f474b0e6f3 --- /dev/null +++ b/board/inc/core_cmInstr.h @@ -0,0 +1,87 @@ +/**************************************************************************//** + * @file core_cmInstr.h + * @brief CMSIS Cortex-M Core Instruction Access Header File + * @version V4.30 + * @date 20. October 2015 + ******************************************************************************/ +/* Copyright (c) 2009 - 2015 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - 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. + - Neither the name of ARM 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 COPYRIGHT HOLDERS AND 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. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CMINSTR_H +#define __CORE_CMINSTR_H + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/*------------------ RealView Compiler -----------------*/ +#if defined ( __CC_ARM ) + #include "cmsis_armcc.h" + +/*------------------ ARM Compiler V6 -------------------*/ +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #include "cmsis_armcc_V6.h" + +/*------------------ GNU Compiler ----------------------*/ +#elif defined ( __GNUC__ ) + #include "cmsis_gcc.h" + +/*------------------ ICC Compiler ----------------------*/ +#elif defined ( __ICCARM__ ) + #include + +/*------------------ TI CCS Compiler -------------------*/ +#elif defined ( __TMS470__ ) + #include + +/*------------------ TASKING Compiler ------------------*/ +#elif defined ( __TASKING__ ) + /* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + +/*------------------ COSMIC Compiler -------------------*/ +#elif defined ( __CSMC__ ) + #include + +#endif + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + +#endif /* __CORE_CMINSTR_H */ diff --git a/board/inc/core_cmSimd.h b/board/inc/core_cmSimd.h new file mode 100644 index 0000000000..66bf5c2a72 --- /dev/null +++ b/board/inc/core_cmSimd.h @@ -0,0 +1,96 @@ +/**************************************************************************//** + * @file core_cmSimd.h + * @brief CMSIS Cortex-M SIMD Header File + * @version V4.30 + * @date 20. October 2015 + ******************************************************************************/ +/* Copyright (c) 2009 - 2015 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - 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. + - Neither the name of ARM 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 COPYRIGHT HOLDERS AND 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. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CMSIMD_H +#define __CORE_CMSIMD_H + +#ifdef __cplusplus + extern "C" { +#endif + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +/*------------------ RealView Compiler -----------------*/ +#if defined ( __CC_ARM ) + #include "cmsis_armcc.h" + +/*------------------ ARM Compiler V6 -------------------*/ +#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #include "cmsis_armcc_V6.h" + +/*------------------ GNU Compiler ----------------------*/ +#elif defined ( __GNUC__ ) + #include "cmsis_gcc.h" + +/*------------------ ICC Compiler ----------------------*/ +#elif defined ( __ICCARM__ ) + #include + +/*------------------ TI CCS Compiler -------------------*/ +#elif defined ( __TMS470__ ) + #include + +/*------------------ TASKING Compiler ------------------*/ +#elif defined ( __TASKING__ ) + /* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + +/*------------------ COSMIC Compiler -------------------*/ +#elif defined ( __CSMC__ ) + #include + +#endif + +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CMSIMD_H */ diff --git a/board/inc/stm32f205xx.h b/board/inc/stm32f205xx.h new file mode 100644 index 0000000000..ba825d1970 --- /dev/null +++ b/board/inc/stm32f205xx.h @@ -0,0 +1,7666 @@ +/** + ****************************************************************************** + * @file stm32f205xx.h + * @author MCD Application Team + * @version V2.1.2 + * @date 29-June-2016 + * @brief CMSIS STM32F205xx Device Peripheral Access Layer Header File. + * This file contains : + * - Data structures and the address mapping for all peripherals + * - Peripherals registers declarations and bits definition + * - Macros to access peripheral’s registers hardware + * + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * 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. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f205xx + * @{ + */ + +#ifndef __STM32F205xx_H +#define __STM32F205xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/** @addtogroup Configuration_section_for_CMSIS + * @{ + */ + +/** + * @brief Configuration of the Cortex-M3 Processor and Core Peripherals + */ +#define __CM3_REV 0x0200U /*!< Core revision r0p1 */ +#define __MPU_PRESENT 1U /*!< STM32F2XX provides an MPU */ +#define __NVIC_PRIO_BITS 4U /*!< STM32F2XX uses 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0U /*!< Set to 1 if different SysTick Config is used */ + +/** + * @} + */ + +/** @addtogroup Peripheral_interrupt_number_definition + * @{ + */ + +/** + * @brief STM32F2XX Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ +/****** Cortex-M3 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */ +/****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */ + DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */ + DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */ + DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */ + DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */ + DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */ + DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */ + ADC_IRQn = 18, /*!< ADC1, ADC2 and ADC3 global Interrupts */ + CAN1_TX_IRQn = 19, /*!< CAN1 TX Interrupt */ + CAN1_RX0_IRQn = 20, /*!< CAN1 RX0 Interrupt */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM9_IRQn = 24, /*!< TIM1 Break interrupt and TIM9 global interrupt */ + TIM1_UP_TIM10_IRQn = 25, /*!< TIM1 Update Interrupt and TIM10 global interrupt */ + TIM1_TRG_COM_TIM11_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */ + OTG_FS_WKUP_IRQn = 42, /*!< USB OTG FS Wakeup through EXTI line interrupt */ + TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */ + TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */ + TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */ + FSMC_IRQn = 48, /*!< FSMC global Interrupt */ + SDIO_IRQn = 49, /*!< SDIO global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn = 55, /*!< TIM7 global interrupt */ + DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */ + DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */ + DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */ + DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */ + DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */ + CAN2_TX_IRQn = 63, /*!< CAN2 TX Interrupt */ + CAN2_RX0_IRQn = 64, /*!< CAN2 RX0 Interrupt */ + CAN2_RX1_IRQn = 65, /*!< CAN2 RX1 Interrupt */ + CAN2_SCE_IRQn = 66, /*!< CAN2 SCE Interrupt */ + OTG_FS_IRQn = 67, /*!< USB OTG FS global Interrupt */ + DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */ + DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */ + DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */ + USART6_IRQn = 71, /*!< USART6 global interrupt */ + I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */ + I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */ + OTG_HS_EP1_OUT_IRQn = 74, /*!< USB OTG HS End Point 1 Out global interrupt */ + OTG_HS_EP1_IN_IRQn = 75, /*!< USB OTG HS End Point 1 In global interrupt */ + OTG_HS_WKUP_IRQn = 76, /*!< USB OTG HS Wakeup through EXTI interrupt */ + OTG_HS_IRQn = 77, /*!< USB OTG HS global interrupt */ + HASH_RNG_IRQn = 80 /*!< Hash and RNG global interrupt */ +} IRQn_Type; + +/** + * @} + */ + +#include "core_cm3.h" +#include "system_stm32f2xx.h" +#include + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< ADC status register, Address offset: 0x00 */ + __IO uint32_t CR1; /*!< ADC control register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< ADC control register 2, Address offset: 0x08 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x0C */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x10 */ + __IO uint32_t JOFR1; /*!< ADC injected channel data offset register 1, Address offset: 0x14 */ + __IO uint32_t JOFR2; /*!< ADC injected channel data offset register 2, Address offset: 0x18 */ + __IO uint32_t JOFR3; /*!< ADC injected channel data offset register 3, Address offset: 0x1C */ + __IO uint32_t JOFR4; /*!< ADC injected channel data offset register 4, Address offset: 0x20 */ + __IO uint32_t HTR; /*!< ADC watchdog higher threshold register, Address offset: 0x24 */ + __IO uint32_t LTR; /*!< ADC watchdog lower threshold register, Address offset: 0x28 */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x2C */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x30 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x34 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x38*/ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x3C */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x40 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x44 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x48 */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x4C */ +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1 base address + 0x300 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1 base address + 0x304 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1 base address + 0x308 */ +} ADC_Common_TypeDef; + + +/** + * @brief Controller Area Network TxMailBox + */ + +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** + * @brief Controller Area Network FIFOMailBox + */ + +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** + * @brief Controller Area Network FilterRegister + */ + +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** + * @brief Controller Area Network + */ + +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ +} CRC_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +}DBGMCU_TypeDef; + + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA stream x configuration register */ + __IO uint32_t NDTR; /*!< DMA stream x number of data register */ + __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ + __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ + __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ + __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ +} DMA_Stream_TypeDef; + +typedef struct +{ + __IO uint32_t LISR; /*!< DMA low interrupt status register, Address offset: 0x00 */ + __IO uint32_t HISR; /*!< DMA high interrupt status register, Address offset: 0x04 */ + __IO uint32_t LIFCR; /*!< DMA low interrupt flag clear register, Address offset: 0x08 */ + __IO uint32_t HIFCR; /*!< DMA high interrupt flag clear register, Address offset: 0x0C */ +} DMA_TypeDef; + + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR; /*!< EXTI Interrupt mask register, Address offset: 0x00 */ + __IO uint32_t EMR; /*!< EXTI Event mask register, Address offset: 0x04 */ + __IO uint32_t RTSR; /*!< EXTI Rising trigger selection register, Address offset: 0x08 */ + __IO uint32_t FTSR; /*!< EXTI Falling trigger selection register, Address offset: 0x0C */ + __IO uint32_t SWIER; /*!< EXTI Software interrupt event register, Address offset: 0x10 */ + __IO uint32_t PR; /*!< EXTI Pending register, Address offset: 0x14 */ +} EXTI_TypeDef; + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t KEYR; /*!< FLASH key register, Address offset: 0x04 */ + __IO uint32_t OPTKEYR; /*!< FLASH option key register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< FLASH status register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FLASH control register, Address offset: 0x10 */ + __IO uint32_t OPTCR; /*!< FLASH option control register, Address offset: 0x14 */ +} FLASH_TypeDef; + + +/** + * @brief Flexible Static Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ +} FSMC_Bank1_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FSMC_Bank1E_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank2 + */ + +typedef struct +{ + __IO uint32_t PCR2; /*!< NAND Flash control register 2, Address offset: 0x60 */ + __IO uint32_t SR2; /*!< NAND Flash FIFO status and interrupt register 2, Address offset: 0x64 */ + __IO uint32_t PMEM2; /*!< NAND Flash Common memory space timing register 2, Address offset: 0x68 */ + __IO uint32_t PATT2; /*!< NAND Flash Attribute memory space timing register 2, Address offset: 0x6C */ + uint32_t RESERVED0; /*!< Reserved, 0x70 */ + __IO uint32_t ECCR2; /*!< NAND Flash ECC result registers 2, Address offset: 0x74 */ + uint32_t RESERVED1; /*!< Reserved, 0x78 */ + uint32_t RESERVED2; /*!< Reserved, 0x7C */ + __IO uint32_t PCR3; /*!< NAND Flash control register 3, Address offset: 0x80 */ + __IO uint32_t SR3; /*!< NAND Flash FIFO status and interrupt register 3, Address offset: 0x84 */ + __IO uint32_t PMEM3; /*!< NAND Flash Common memory space timing register 3, Address offset: 0x88 */ + __IO uint32_t PATT3; /*!< NAND Flash Attribute memory space timing register 3, Address offset: 0x8C */ + uint32_t RESERVED3; /*!< Reserved, 0x90 */ + __IO uint32_t ECCR3; /*!< NAND Flash ECC result registers 3, Address offset: 0x94 */ +} FSMC_Bank2_3_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank4 + */ + +typedef struct +{ + __IO uint32_t PCR4; /*!< PC Card control register 4, Address offset: 0xA0 */ + __IO uint32_t SR4; /*!< PC Card FIFO status and interrupt register 4, Address offset: 0xA4 */ + __IO uint32_t PMEM4; /*!< PC Card Common memory space timing register 4, Address offset: 0xA8 */ + __IO uint32_t PATT4; /*!< PC Card Attribute memory space timing register 4, Address offset: 0xAC */ + __IO uint32_t PIO4; /*!< PC Card I/O space timing register 4, Address offset: 0xB0 */ +} FSMC_Bank4_TypeDef; + + +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ +} GPIO_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + __IO uint32_t MEMRMP; /*!< SYSCFG memory remap register, Address offset: 0x00 */ + __IO uint32_t PMC; /*!< SYSCFG peripheral mode configuration register, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + uint32_t RESERVED[2]; /*!< Reserved, 0x18-0x1C */ + __IO uint32_t CMPCR; /*!< SYSCFG Compensation cell control register, Address offset: 0x20 */ +} SYSCFG_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address register 1, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address register 2, Address offset: 0x0C */ + __IO uint32_t DR; /*!< I2C Data register, Address offset: 0x10 */ + __IO uint32_t SR1; /*!< I2C Status register 1, Address offset: 0x14 */ + __IO uint32_t SR2; /*!< I2C Status register 2, Address offset: 0x18 */ + __IO uint32_t CCR; /*!< I2C Clock control register, Address offset: 0x1C */ + __IO uint32_t TRISE; /*!< I2C TRISE register, Address offset: 0x20 */ +} I2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ +} IWDG_TypeDef; + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< PWR power control register, Address offset: 0x00 */ + __IO uint32_t CSR; /*!< PWR power control/status register, Address offset: 0x04 */ +} PWR_TypeDef; + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t PLLCFGR; /*!< RCC PLL configuration register, Address offset: 0x04 */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ + __IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x0C */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x10 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x14 */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved, 0x1C */ + __IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x20 */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x24 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x28-0x2C */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0x30 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0x34 */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0x38 */ + uint32_t RESERVED2; /*!< Reserved, 0x3C */ + __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x40 */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x44 */ + uint32_t RESERVED3[2]; /*!< Reserved, 0x48-0x4C */ + __IO uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */ + __IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */ + __IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */ + uint32_t RESERVED4; /*!< Reserved, 0x5C */ + __IO uint32_t APB1LPENR; /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */ + __IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */ + uint32_t RESERVED5[2]; /*!< Reserved, 0x68-0x6C */ + __IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x70 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */ + uint32_t RESERVED6[2]; /*!< Reserved, 0x78-0x7C */ + __IO uint32_t SSCGR; /*!< RCC spread spectrum clock generation register, Address offset: 0x80 */ + __IO uint32_t PLLI2SCFGR; /*!< RCC PLLI2S configuration register, Address offset: 0x84 */ + +} RCC_TypeDef; + +/** + * @brief Real-Time Clock + */ + +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x08 */ + __IO uint32_t ISR; /*!< RTC initialization and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + __IO uint32_t CALIBR; /*!< RTC calibration register, Address offset: 0x18 */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x1C */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + uint32_t RESERVED1; /*!< Reserved, 0x28 */ + uint32_t RESERVED2; /*!< Reserved, 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + uint32_t RESERVED3; /*!< Reserved, 0x38 */ + uint32_t RESERVED4; /*!< Reserved, 0x3C */ + __IO uint32_t TAFCR; /*!< RTC tamper and alternate function configuration register, Address offset: 0x40 */ + uint32_t RESERVED5; /*!< Reserved, 0x44 */ + uint32_t RESERVED6; /*!< Reserved, 0x48 */ + uint32_t RESERVED7; /*!< Reserved, 0x4C */ + __IO uint32_t BKP0R; /*!< RTC backup register 1, Address offset: 0x50 */ + __IO uint32_t BKP1R; /*!< RTC backup register 1, Address offset: 0x54 */ + __IO uint32_t BKP2R; /*!< RTC backup register 2, Address offset: 0x58 */ + __IO uint32_t BKP3R; /*!< RTC backup register 3, Address offset: 0x5C */ + __IO uint32_t BKP4R; /*!< RTC backup register 4, Address offset: 0x60 */ + __IO uint32_t BKP5R; /*!< RTC backup register 5, Address offset: 0x64 */ + __IO uint32_t BKP6R; /*!< RTC backup register 6, Address offset: 0x68 */ + __IO uint32_t BKP7R; /*!< RTC backup register 7, Address offset: 0x6C */ + __IO uint32_t BKP8R; /*!< RTC backup register 8, Address offset: 0x70 */ + __IO uint32_t BKP9R; /*!< RTC backup register 9, Address offset: 0x74 */ + __IO uint32_t BKP10R; /*!< RTC backup register 10, Address offset: 0x78 */ + __IO uint32_t BKP11R; /*!< RTC backup register 11, Address offset: 0x7C */ + __IO uint32_t BKP12R; /*!< RTC backup register 12, Address offset: 0x80 */ + __IO uint32_t BKP13R; /*!< RTC backup register 13, Address offset: 0x84 */ + __IO uint32_t BKP14R; /*!< RTC backup register 14, Address offset: 0x88 */ + __IO uint32_t BKP15R; /*!< RTC backup register 15, Address offset: 0x8C */ + __IO uint32_t BKP16R; /*!< RTC backup register 16, Address offset: 0x90 */ + __IO uint32_t BKP17R; /*!< RTC backup register 17, Address offset: 0x94 */ + __IO uint32_t BKP18R; /*!< RTC backup register 18, Address offset: 0x98 */ + __IO uint32_t BKP19R; /*!< RTC backup register 19, Address offset: 0x9C */ +} RTC_TypeDef; + + +/** + * @brief SD host Interface + */ + +typedef struct +{ + __IO uint32_t POWER; /*!< SDIO power control register, Address offset: 0x00 */ + __IO uint32_t CLKCR; /*!< SDI clock control register, Address offset: 0x04 */ + __IO uint32_t ARG; /*!< SDIO argument register, Address offset: 0x08 */ + __IO uint32_t CMD; /*!< SDIO command register, Address offset: 0x0C */ + __IO const uint32_t RESPCMD; /*!< SDIO command response register, Address offset: 0x10 */ + __IO const uint32_t RESP1; /*!< SDIO response 1 register, Address offset: 0x14 */ + __IO const uint32_t RESP2; /*!< SDIO response 2 register, Address offset: 0x18 */ + __IO const uint32_t RESP3; /*!< SDIO response 3 register, Address offset: 0x1C */ + __IO const uint32_t RESP4; /*!< SDIO response 4 register, Address offset: 0x20 */ + __IO uint32_t DTIMER; /*!< SDIO data timer register, Address offset: 0x24 */ + __IO uint32_t DLEN; /*!< SDIO data length register, Address offset: 0x28 */ + __IO uint32_t DCTRL; /*!< SDIO data control register, Address offset: 0x2C */ + __IO const uint32_t DCOUNT; /*!< SDIO data counter register, Address offset: 0x30 */ + __IO const uint32_t STA; /*!< SDIO status register, Address offset: 0x34 */ + __IO uint32_t ICR; /*!< SDIO interrupt clear register, Address offset: 0x38 */ + __IO uint32_t MASK; /*!< SDIO mask register, Address offset: 0x3C */ + uint32_t RESERVED0[2]; /*!< Reserved, 0x40-0x44 */ + __IO const uint32_t FIFOCNT; /*!< SDIO FIFO counter register, Address offset: 0x48 */ + uint32_t RESERVED1[13]; /*!< Reserved, 0x4C-0x7C */ + __IO uint32_t FIFO; /*!< SDIO data FIFO register, Address offset: 0x80 */ +} SDIO_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI control register 1 (not used in I2S mode), Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI control register 2, Address offset: 0x04 */ + __IO uint32_t SR; /*!< SPI status register, Address offset: 0x08 */ + __IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */ + __IO uint32_t CRCPR; /*!< SPI CRC polynomial register (not used in I2S mode), Address offset: 0x10 */ + __IO uint32_t RXCRCR; /*!< SPI RX CRC register (not used in I2S mode), Address offset: 0x14 */ + __IO uint32_t TXCRCR; /*!< SPI TX CRC register (not used in I2S mode), Address offset: 0x18 */ + __IO uint32_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */ + __IO uint32_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */ +} SPI_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ + __IO uint32_t OR; /*!< TIM option register, Address offset: 0x50 */ +} TIM_TypeDef; + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< USART Status register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< USART Data register, Address offset: 0x04 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */ +} USART_TypeDef; + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + + +/** + * @brief RNG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ +} RNG_TypeDef; + + + +/** + * @brief __USB_OTG_Core_register + */ +typedef struct +{ + __IO uint32_t GOTGCTL; /*!< USB_OTG Control and Status Register Address offset : 0x00 */ + __IO uint32_t GOTGINT; /*!< USB_OTG Interrupt Register Address offset : 0x04 */ + __IO uint32_t GAHBCFG; /*!< Core AHB Configuration Register Address offset : 0x08 */ + __IO uint32_t GUSBCFG; /*!< Core USB Configuration Register Address offset : 0x0C */ + __IO uint32_t GRSTCTL; /*!< Core Reset Register Address offset : 0x10 */ + __IO uint32_t GINTSTS; /*!< Core Interrupt Register Address offset : 0x14 */ + __IO uint32_t GINTMSK; /*!< Core Interrupt Mask Register Address offset : 0x18 */ + __IO uint32_t GRXSTSR; /*!< Receive Sts Q Read Register Address offset : 0x1C */ + __IO uint32_t GRXSTSP; /*!< Receive Sts Q Read & POP Register Address offset : 0x20 */ + __IO uint32_t GRXFSIZ; /* Receive FIFO Size Register Address offset : 0x24 */ + __IO uint32_t DIEPTXF0_HNPTXFSIZ; /*!< EP0 / Non Periodic Tx FIFO Size Register Address offset : 0x28 */ + __IO uint32_t HNPTXSTS; /*!< Non Periodic Tx FIFO/Queue Sts reg Address offset : 0x2C */ + uint32_t Reserved30[2]; /* Reserved Address offset : 0x30 */ + __IO uint32_t GCCFG; /*!< General Purpose IO Register Address offset : 0x38 */ + __IO uint32_t CID; /*!< User ID Register Address offset : 0x3C */ + uint32_t Reserved40[48]; /*!< Reserved Address offset : 0x40-0xFF */ + __IO uint32_t HPTXFSIZ; /*!< Host Periodic Tx FIFO Size Reg Address offset : 0x100 */ + __IO uint32_t DIEPTXF[0x0F]; /*!< dev Periodic Transmit FIFO */ +} +USB_OTG_GlobalTypeDef; + + + +/** + * @brief __device_Registers + */ +typedef struct +{ + __IO uint32_t DCFG; /*!< dev Configuration Register Address offset : 0x800 */ + __IO uint32_t DCTL; /*!< dev Control Register Address offset : 0x804 */ + __IO uint32_t DSTS; /*!< dev Status Register (RO) Address offset : 0x808 */ + uint32_t Reserved0C; /*!< Reserved Address offset : 0x80C */ + __IO uint32_t DIEPMSK; /* !< dev IN Endpoint Mask Address offset : 0x810 */ + __IO uint32_t DOEPMSK; /*!< dev OUT Endpoint Mask Address offset : 0x814 */ + __IO uint32_t DAINT; /*!< dev All Endpoints Itr Reg Address offset : 0x818 */ + __IO uint32_t DAINTMSK; /*!< dev All Endpoints Itr Mask Address offset : 0x81C */ + uint32_t Reserved20; /*!< Reserved Address offset : 0x820 */ + uint32_t Reserved9; /*!< Reserved Address offset : 0x824 */ + __IO uint32_t DVBUSDIS; /*!< dev VBUS discharge Register Address offset : 0x828 */ + __IO uint32_t DVBUSPULSE; /*!< dev VBUS Pulse Register Address offset : 0x82C */ + __IO uint32_t DTHRCTL; /*!< dev thr Address offset : 0x830 */ + __IO uint32_t DIEPEMPMSK; /*!< dev empty msk Address offset : 0x834 */ + __IO uint32_t DEACHINT; /*!< dedicated EP interrupt Address offset : 0x838 */ + __IO uint32_t DEACHMSK; /*!< dedicated EP msk Address offset : 0x83C */ + uint32_t Reserved40; /*!< dedicated EP mask Address offset : 0x840 */ + __IO uint32_t DINEP1MSK; /*!< dedicated EP mask Address offset : 0x844 */ + uint32_t Reserved44[15]; /*!< Reserved Address offset : 0x844-0x87C */ + __IO uint32_t DOUTEP1MSK; /*!< dedicated EP msk Address offset : 0x884 */ +} +USB_OTG_DeviceTypeDef; + + +/** + * @brief __IN_Endpoint-Specific_Register + */ +typedef struct +{ + __IO uint32_t DIEPCTL; /* dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /* Reserved 900h + (ep_num * 20h) + 04h */ + __IO uint32_t DIEPINT; /* dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /* Reserved 900h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DIEPTSIZ; /* IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h */ + __IO uint32_t DIEPDMA; /* IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h */ + __IO uint32_t DTXFSTS; /*IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h */ + uint32_t Reserved18; /* Reserved 900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch */ +} +USB_OTG_INEndpointTypeDef; + + +/** + * @brief __OUT_Endpoint-Specific_Registers + */ +typedef struct +{ + __IO uint32_t DOEPCTL; /* dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h*/ + uint32_t Reserved04; /* Reserved B00h + (ep_num * 20h) + 04h*/ + __IO uint32_t DOEPINT; /* dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h*/ + uint32_t Reserved0C; /* Reserved B00h + (ep_num * 20h) + 0Ch*/ + __IO uint32_t DOEPTSIZ; /* dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h*/ + __IO uint32_t DOEPDMA; /* dev OUT Endpoint DMA Address B00h + (ep_num * 20h) + 14h*/ + uint32_t Reserved18[2]; /* Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch*/ +} +USB_OTG_OUTEndpointTypeDef; + + +/** + * @brief __Host_Mode_Register_Structures + */ +typedef struct +{ + __IO uint32_t HCFG; /* Host Configuration Register 400h*/ + __IO uint32_t HFIR; /* Host Frame Interval Register 404h*/ + __IO uint32_t HFNUM; /* Host Frame Nbr/Frame Remaining 408h*/ + uint32_t Reserved40C; /* Reserved 40Ch*/ + __IO uint32_t HPTXSTS; /* Host Periodic Tx FIFO/ Queue Status 410h*/ + __IO uint32_t HAINT; /* Host All Channels Interrupt Register 414h*/ + __IO uint32_t HAINTMSK; /* Host All Channels Interrupt Mask 418h*/ +} +USB_OTG_HostTypeDef; + + +/** + * @brief __Host_Channel_Specific_Registers + */ +typedef struct +{ + __IO uint32_t HCCHAR; + __IO uint32_t HCSPLT; + __IO uint32_t HCINT; + __IO uint32_t HCINTMSK; + __IO uint32_t HCTSIZ; + __IO uint32_t HCDMA; + uint32_t Reserved[2]; +} +USB_OTG_HostChannelTypeDef; + + +/** + * @brief Peripheral_memory_map + */ +#define FLASH_BASE 0x08000000U /*!< FLASH(up to 1 MB) base address in the alias region */ +#define SRAM1_BASE 0x20000000U /*!< SRAM1(112 KB) base address in the alias region */ +#define SRAM2_BASE 0x2001C000U /*!< SRAM2(16 KB) base address in the alias region */ +#define PERIPH_BASE 0x40000000U /*!< Peripheral base address in the alias region */ +#define BKPSRAM_BASE 0x40024000U /*!< Backup SRAM(4 KB) base address in the alias region */ +#define FSMC_R_BASE 0xA0000000U /*!< FSMC registers base address */ +#define SRAM1_BB_BASE 0x22000000U /*!< SRAM1(112 KB) base address in the bit-band region */ +#define SRAM2_BB_BASE 0x22380000U /*!< SRAM2(16 KB) base address in the bit-band region */ +#define PERIPH_BB_BASE 0x42000000U /*!< Peripheral base address in the bit-band region */ +#define BKPSRAM_BB_BASE 0x42480000U /*!< Backup SRAM(4 KB) base address in the bit-band region */ +#define FLASH_END 0x080FFFFFU /*!< FLASH end address */ + +/* Legacy defines */ +#define SRAM_BASE SRAM1_BASE +#define SRAM_BB_BASE SRAM1_BB_BASE + + +/*!< Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000U) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000U) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x10000000U) + +/*!< APB1 peripherals */ +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000U) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400U) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800U) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00U) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000U) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400U) +#define TIM12_BASE (APB1PERIPH_BASE + 0x1800U) +#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00U) +#define TIM14_BASE (APB1PERIPH_BASE + 0x2000U) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800U) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00U) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000U) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800U) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00U) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400U) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800U) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00U) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000U) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400U) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800U) +#define I2C3_BASE (APB1PERIPH_BASE + 0x5C00U) +#define CAN1_BASE (APB1PERIPH_BASE + 0x6400U) +#define CAN2_BASE (APB1PERIPH_BASE + 0x6800U) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000U) +#define DAC_BASE (APB1PERIPH_BASE + 0x7400U) + +/*!< APB2 peripherals */ +#define TIM1_BASE (APB2PERIPH_BASE + 0x0000U) +#define TIM8_BASE (APB2PERIPH_BASE + 0x0400U) +#define USART1_BASE (APB2PERIPH_BASE + 0x1000U) +#define USART6_BASE (APB2PERIPH_BASE + 0x1400U) +#define ADC1_BASE (APB2PERIPH_BASE + 0x2000U) +#define ADC2_BASE (APB2PERIPH_BASE + 0x2100U) +#define ADC3_BASE (APB2PERIPH_BASE + 0x2200U) +#define ADC_BASE (APB2PERIPH_BASE + 0x2300U) +#define SDIO_BASE (APB2PERIPH_BASE + 0x2C00U) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000U) +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x3800U) +#define EXTI_BASE (APB2PERIPH_BASE + 0x3C00U) +#define TIM9_BASE (APB2PERIPH_BASE + 0x4000U) +#define TIM10_BASE (APB2PERIPH_BASE + 0x4400U) +#define TIM11_BASE (APB2PERIPH_BASE + 0x4800U) + +/*!< AHB1 peripherals */ +#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000U) +#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400U) +#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800U) +#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00U) +#define GPIOE_BASE (AHB1PERIPH_BASE + 0x1000U) +#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400U) +#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800U) +#define GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00U) +#define GPIOI_BASE (AHB1PERIPH_BASE + 0x2000U) +#define CRC_BASE (AHB1PERIPH_BASE + 0x3000U) +#define RCC_BASE (AHB1PERIPH_BASE + 0x3800U) +#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x3C00U) +#define DMA1_BASE (AHB1PERIPH_BASE + 0x6000U) +#define DMA1_Stream0_BASE (DMA1_BASE + 0x010U) +#define DMA1_Stream1_BASE (DMA1_BASE + 0x028U) +#define DMA1_Stream2_BASE (DMA1_BASE + 0x040U) +#define DMA1_Stream3_BASE (DMA1_BASE + 0x058U) +#define DMA1_Stream4_BASE (DMA1_BASE + 0x070U) +#define DMA1_Stream5_BASE (DMA1_BASE + 0x088U) +#define DMA1_Stream6_BASE (DMA1_BASE + 0x0A0U) +#define DMA1_Stream7_BASE (DMA1_BASE + 0x0B8U) +#define DMA2_BASE (AHB1PERIPH_BASE + 0x6400U) +#define DMA2_Stream0_BASE (DMA2_BASE + 0x010U) +#define DMA2_Stream1_BASE (DMA2_BASE + 0x028U) +#define DMA2_Stream2_BASE (DMA2_BASE + 0x040U) +#define DMA2_Stream3_BASE (DMA2_BASE + 0x058U) +#define DMA2_Stream4_BASE (DMA2_BASE + 0x070U) +#define DMA2_Stream5_BASE (DMA2_BASE + 0x088U) +#define DMA2_Stream6_BASE (DMA2_BASE + 0x0A0U) +#define DMA2_Stream7_BASE (DMA2_BASE + 0x0B8U) + +/*!< AHB2 peripherals */ +#define RNG_BASE (AHB2PERIPH_BASE + 0x60800U) + +/*!< FSMC Bankx registers base address */ +#define FSMC_Bank1_R_BASE (FSMC_R_BASE + 0x0000U) +#define FSMC_Bank1E_R_BASE (FSMC_R_BASE + 0x0104U) +#define FSMC_Bank2_3_R_BASE (FSMC_R_BASE + 0x0060U) +#define FSMC_Bank4_R_BASE (FSMC_R_BASE + 0x00A0U) + +/* Debug MCU registers base address */ +#define DBGMCU_BASE 0xE0042000U + +/*!< USB registers base address */ +#define USB_OTG_HS_PERIPH_BASE 0x40040000U +#define USB_OTG_FS_PERIPH_BASE 0x50000000U + +#define USB_OTG_GLOBAL_BASE 0x000U +#define USB_OTG_DEVICE_BASE 0x800U +#define USB_OTG_IN_ENDPOINT_BASE 0x900U +#define USB_OTG_OUT_ENDPOINT_BASE 0xB00U +#define USB_OTG_EP_REG_SIZE 0x20U +#define USB_OTG_HOST_BASE 0x400U +#define USB_OTG_HOST_PORT_BASE 0x440U +#define USB_OTG_HOST_CHANNEL_BASE 0x500U +#define USB_OTG_HOST_CHANNEL_SIZE 0x20U +#define USB_OTG_PCGCCTL_BASE 0xE00U +#define USB_OTG_FIFO_BASE 0x1000U +#define USB_OTG_FIFO_SIZE 0x1000U + +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) +#define TIM13 ((TIM_TypeDef *) TIM13_BASE) +#define TIM14 ((TIM_TypeDef *) TIM14_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG ((WWDG_TypeDef *) WWDG_BASE) +#define IWDG ((IWDG_TypeDef *) IWDG_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define CAN1 ((CAN_TypeDef *) CAN1_BASE) +#define CAN2 ((CAN_TypeDef *) CAN2_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define DAC ((DAC_TypeDef *) DAC_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define USART6 ((USART_TypeDef *) USART6_BASE) +#define ADC ((ADC_Common_TypeDef *) ADC_BASE) +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define ADC2 ((ADC_TypeDef *) ADC2_BASE) +#define ADC3 ((ADC_TypeDef *) ADC3_BASE) +#define SDIO ((SDIO_TypeDef *) SDIO_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define TIM9 ((TIM_TypeDef *) TIM9_BASE) +#define TIM10 ((TIM_TypeDef *) TIM10_BASE) +#define TIM11 ((TIM_TypeDef *) TIM11_BASE) +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) +#define GPIOI ((GPIO_TypeDef *) GPIOI_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA1_Stream0 ((DMA_Stream_TypeDef *) DMA1_Stream0_BASE) +#define DMA1_Stream1 ((DMA_Stream_TypeDef *) DMA1_Stream1_BASE) +#define DMA1_Stream2 ((DMA_Stream_TypeDef *) DMA1_Stream2_BASE) +#define DMA1_Stream3 ((DMA_Stream_TypeDef *) DMA1_Stream3_BASE) +#define DMA1_Stream4 ((DMA_Stream_TypeDef *) DMA1_Stream4_BASE) +#define DMA1_Stream5 ((DMA_Stream_TypeDef *) DMA1_Stream5_BASE) +#define DMA1_Stream6 ((DMA_Stream_TypeDef *) DMA1_Stream6_BASE) +#define DMA1_Stream7 ((DMA_Stream_TypeDef *) DMA1_Stream7_BASE) +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMA2_Stream0 ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE) +#define DMA2_Stream1 ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE) +#define DMA2_Stream2 ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE) +#define DMA2_Stream3 ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE) +#define DMA2_Stream4 ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE) +#define DMA2_Stream5 ((DMA_Stream_TypeDef *) DMA2_Stream5_BASE) +#define DMA2_Stream6 ((DMA_Stream_TypeDef *) DMA2_Stream6_BASE) +#define DMA2_Stream7 ((DMA_Stream_TypeDef *) DMA2_Stream7_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) +#define FSMC_Bank1 ((FSMC_Bank1_TypeDef *) FSMC_Bank1_R_BASE) +#define FSMC_Bank1E ((FSMC_Bank1E_TypeDef *) FSMC_Bank1E_R_BASE) +#define FSMC_Bank2_3 ((FSMC_Bank2_3_TypeDef *) FSMC_Bank2_3_R_BASE) +#define FSMC_Bank4 ((FSMC_Bank4_TypeDef *) FSMC_Bank4_R_BASE) + +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) + +#define USB_OTG_FS ((USB_OTG_GlobalTypeDef *) USB_OTG_FS_PERIPH_BASE) +#define USB_OTG_HS ((USB_OTG_GlobalTypeDef *) USB_OTG_HS_PERIPH_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + + /** @addtogroup Peripheral_Registers_Bits_Definition + * @{ + */ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ +/******************** Bit definition for ADC_SR register ********************/ +#define ADC_SR_AWD 0x00000001U /*!
© COPYRIGHT(c) 2016 STMicroelectronics
+ * + * 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. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f2xx + * @{ + */ + +#ifndef __STM32F2xx_H +#define __STM32F2xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Library_configuration_section + * @{ + */ + +/** + * @brief STM32 Family + */ +#if !defined (STM32F2) +#define STM32F2 +#endif /* STM32F2 */ + +/* Uncomment the line below according to the target STM32 device used in your + application + */ +#if !defined (STM32F205xx) && !defined (STM32F215xx) && !defined (STM32F207xx) && !defined (STM32F217xx) + + /* #define STM32F205xx */ /*!< STM32F205RG, STM32F205VG, STM32F205ZG, STM32F205RF, STM32F205VF, STM32F205ZF, + STM32F205RE, STM32F205VE, STM32F205ZE, STM32F205RC, STM32F205VC, STM32F205ZC, + STM32F205RB and STM32F205VB Devices */ + /* #define STM32F215xx */ /*!< STM32F215RG, STM32F215VG, STM32F215ZG, STM32F215RE, STM32F215VE and STM32F215ZE Devices */ + /* #define STM32F207xx */ /*!< STM32F207VG, STM32F207ZG, STM32F207IG, STM32F207VF, STM32F207ZF, STM32F207IF, + STM32F207VE, STM32F207ZE, STM32F207IE, STM32F207VC, STM32F207ZC and STM32F207IC Devices */ + /* #define STM32F217xx */ /*!< STM32F217VG, STM32F217ZG, STM32F217IG, STM32F217VE, STM32F217ZE and STM32F217IE Devices */ + +#endif + +/* Tip: To avoid modifying this file each time you need to switch between these + devices, you can define the device in your toolchain compiler preprocessor. + */ +#if !defined (USE_HAL_DRIVER) +/** + * @brief Comment the line below if you will not use the peripherals drivers. + In this case, these drivers will not be included and the application code will + be based on direct access to peripherals registers + */ + /*#define USE_HAL_DRIVER */ +#endif /* USE_HAL_DRIVER */ + +/** + * @brief CMSIS Device version number V2.1.2 + */ +#define __STM32F2xx_CMSIS_VERSION_MAIN (0x02U) /*!< [31:24] main version */ +#define __STM32F2xx_CMSIS_VERSION_SUB1 (0x01U) /*!< [23:16] sub1 version */ +#define __STM32F2xx_CMSIS_VERSION_SUB2 (0x02U) /*!< [15:8] sub2 version */ +#define __STM32F2xx_CMSIS_VERSION_RC (0x00U) /*!< [7:0] release candidate */ +#define __STM32F2xx_CMSIS_VERSION ((__STM32F2xx_CMSIS_VERSION_MAIN << 24)\ + |(__STM32F2xx_CMSIS_VERSION_SUB1 << 16)\ + |(__STM32F2xx_CMSIS_VERSION_SUB2 << 8 )\ + |(__STM32F2xx_CMSIS_VERSION)) + +/** + * @} + */ + +/** @addtogroup Device_Included + * @{ + */ + +#if defined(STM32F205xx) + #include "stm32f205xx.h" +#elif defined(STM32F215xx) + #include "stm32f215xx.h" +#elif defined(STM32F207xx) + #include "stm32f207xx.h" +#elif defined(STM32F217xx) + #include "stm32f217xx.h" +#else + #error "Please select first the target STM32F2xx device used in your application (in stm32f2xx.h file)" +#endif + +/** + * @} + */ + +/** @addtogroup Exported_types + * @{ + */ +typedef enum +{ + RESET = 0, + SET = !RESET +} FlagStatus, ITStatus; + +typedef enum +{ + DISABLE = 0, + ENABLE = !DISABLE +} FunctionalState; +#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE)) + +typedef enum +{ + ERROR = 0, + SUCCESS = !ERROR +} ErrorStatus; + +/** + * @} + */ + + +/** @addtogroup Exported_macro + * @{ + */ +#define SET_BIT(REG, BIT) ((REG) |= (BIT)) + +#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) + +#define READ_BIT(REG, BIT) ((REG) & (BIT)) + +#define CLEAR_REG(REG) ((REG) = (0x0)) + +#define WRITE_REG(REG, VAL) ((REG) = (VAL)) + +#define READ_REG(REG) ((REG)) + +#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) + +#define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL))) + + +/** + * @} + */ + +#if defined (USE_HAL_DRIVER) + #include "stm32f2xx_hal.h" +#endif /* USE_HAL_DRIVER */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __STM32F2xx_H */ + +/** + * @} + */ + +/** + * @} + */ + + + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/inc/stm32f2xx_hal_def.h b/board/inc/stm32f2xx_hal_def.h new file mode 100644 index 0000000000..231aa74ae2 --- /dev/null +++ b/board/inc/stm32f2xx_hal_def.h @@ -0,0 +1,181 @@ +/** + ****************************************************************************** + * @file stm32f2xx_hal_def.h + * @author MCD Application Team + * @version V1.1.3 + * @date 29-June-2016 + * @brief This file contains HAL common defines, enumeration, macros and + * structures definitions. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * 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. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F2xx_HAL_DEF +#define __STM32F2xx_HAL_DEF + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f2xx.h" +//#include "Legacy/stm32_hal_legacy.h" +//#include + +/* Exported types ------------------------------------------------------------*/ + +/** + * @brief HAL Status structures definition + */ +typedef enum +{ + HAL_OK = 0x00U, + HAL_ERROR = 0x01U, + HAL_BUSY = 0x02U, + HAL_TIMEOUT = 0x03U +} HAL_StatusTypeDef; + +/** + * @brief HAL Lock structures definition + */ +typedef enum +{ + HAL_UNLOCKED = 0x00U, + HAL_LOCKED = 0x01U +} HAL_LockTypeDef; + +/* Exported macro ------------------------------------------------------------*/ +#define HAL_MAX_DELAY 0xFFFFFFFFU + +#define HAL_IS_BIT_SET(REG, BIT) (((REG) & (BIT)) != RESET) +#define HAL_IS_BIT_CLR(REG, BIT) (((REG) & (BIT)) == RESET) + +#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD_, __DMA_HANDLE_) \ + do{ \ + (__HANDLE__)->__PPP_DMA_FIELD_ = &(__DMA_HANDLE_); \ + (__DMA_HANDLE_).Parent = (__HANDLE__); \ + } while(0) + +#define UNUSED(x) ((void)(x)) + +/** @brief Reset the Handle's State field. + * @param __HANDLE__: specifies the Peripheral Handle. + * @note This macro can be used for the following purpose: + * - When the Handle is declared as local variable; before passing it as parameter + * to HAL_PPP_Init() for the first time, it is mandatory to use this macro + * to set to 0 the Handle's "State" field. + * Otherwise, "State" field may have any random value and the first time the function + * HAL_PPP_Init() is called, the low level hardware initialization will be missed + * (i.e. HAL_PPP_MspInit() will not be executed). + * - When there is a need to reconfigure the low level hardware: instead of calling + * HAL_PPP_DeInit() then HAL_PPP_Init(), user can make a call to this macro then HAL_PPP_Init(). + * In this later function, when the Handle's "State" field is set to 0, it will execute the function + * HAL_PPP_MspInit() which will reconfigure the low level hardware. + * @retval None + */ +#define __HAL_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = 0U) + +#if (USE_RTOS == 1) + /* Reserved for future use */ + #error " USE_RTOS should be 0 in the current HAL release " +#else + #define __HAL_LOCK(__HANDLE__) \ + do{ \ + if((__HANDLE__)->Lock == HAL_LOCKED) \ + { \ + return HAL_BUSY; \ + } \ + else \ + { \ + (__HANDLE__)->Lock = HAL_LOCKED; \ + } \ + }while (0) + + #define __HAL_UNLOCK(__HANDLE__) \ + do{ \ + (__HANDLE__)->Lock = HAL_UNLOCKED; \ + }while (0) +#endif /* USE_RTOS */ + +#if defined ( __GNUC__ ) + #ifndef __weak + #define __weak __attribute__((weak)) + #endif /* __weak */ + #ifndef __packed + #define __packed __attribute__((__packed__)) + #endif /* __packed */ +#endif /* __GNUC__ */ + + +/* Macro to get variable aligned on 4-bytes, for __ICCARM__ the directive "#pragma data_alignment=4" must be used instead */ +#if defined (__GNUC__) /* GNU Compiler */ + #ifndef __ALIGN_END + #define __ALIGN_END __attribute__ ((aligned (4))) + #endif /* __ALIGN_END */ + #ifndef __ALIGN_BEGIN + #define __ALIGN_BEGIN + #endif /* __ALIGN_BEGIN */ +#else + #ifndef __ALIGN_END + #define __ALIGN_END + #endif /* __ALIGN_END */ + #ifndef __ALIGN_BEGIN + #if defined (__CC_ARM) /* ARM Compiler */ + #define __ALIGN_BEGIN __align(4) + #elif defined (__ICCARM__) /* IAR Compiler */ + #define __ALIGN_BEGIN + #endif /* __CC_ARM */ + #endif /* __ALIGN_BEGIN */ +#endif /* __GNUC__ */ + +/** + * @brief __NOINLINE definition + */ +#if defined ( __CC_ARM ) || defined ( __GNUC__ ) +/* ARM & GNUCompiler + ---------------- +*/ +#define __NOINLINE __attribute__ ( (noinline) ) + +#elif defined ( __ICCARM__ ) +/* ICCARM Compiler + --------------- +*/ +#define __NOINLINE _Pragma("optimize = no_inline") + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ___STM32F2xx_HAL_DEF */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/inc/stm32f2xx_hal_gpio_ex.h b/board/inc/stm32f2xx_hal_gpio_ex.h new file mode 100644 index 0000000000..a94f56caf2 --- /dev/null +++ b/board/inc/stm32f2xx_hal_gpio_ex.h @@ -0,0 +1,299 @@ +/** + ****************************************************************************** + * @file stm32f2xx_hal_gpio_ex.h + * @author MCD Application Team + * @version V1.1.3 + * @date 29-June-2016 + * @brief Header file of GPIO HAL Extension module. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * 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. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F2xx_HAL_GPIO_EX_H +#define __STM32F2xx_HAL_GPIO_EX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f2xx_hal_def.h" + +/** @addtogroup STM32F2xx_HAL_Driver + * @{ + */ + +/** @defgroup GPIOEx GPIOEx + * @{ + */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/** @defgroup GPIOEx_Exported_Constants GPIO Exported Constants + * @{ + */ + +/** @defgroup GPIO_Alternate_function_selection GPIO Alternate function selection + * @{ + */ + +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0xAU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0xAU) /* OTG_HS Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#if defined(STM32F207xx) || defined(STM32F217xx) +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ +#endif /* STM32F207xx || STM32F217xx */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FSMC ((uint8_t)0xCU) /* FSMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0xCU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0xCU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#if defined(STM32F207xx) || defined(STM32F217xx) +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ +#endif /* STM32F207xx || STM32F217xx */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ + +/** + * @} + */ + +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Macros GPIO Exported Macros + * @{ + */ +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Functions GPIO Exported Functions + * @{ + */ +/** + * @} + */ + +/* Private types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private constants ---------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Constants GPIO Private Constants + * @{ + */ +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Macros GPIO Private Macros + * @{ + */ +/** @defgroup GPIOEx_Get_Port_Index GPIO Get Port Index + * @{ + */ +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U :\ + ((__GPIOx__) == (GPIOD))? 3U :\ + ((__GPIOx__) == (GPIOE))? 4U :\ + ((__GPIOx__) == (GPIOF))? 5U :\ + ((__GPIOx__) == (GPIOG))? 6U :\ + ((__GPIOx__) == (GPIOH))? 7U :\ + ((__GPIOx__) == (GPIOI))? 8U : 9U) +/** + * @} + */ + +/** @defgroup GPIOEx_IS_Alternat_function_selection GPIO Check Alternate Function + * @{ + */ +#if defined(STM32F207xx) || defined(STM32F217xx) + +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF12_FSMC) || ((AF) == GPIO_AF15_EVENTOUT)) +#else /* STM32F207xx || STM32F217xx */ +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF12_OTG_HS_FS) || ((AF) == GPIO_AF12_SDIO) || \ + ((AF) == GPIO_AF12_FSMC) || ((AF) == GPIO_AF15_EVENTOUT)) +#endif /* STM32F207xx || STM32F217xx */ + +/** + * @} + */ + +/** + * @} + */ + +/* Private functions ---------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Functions GPIO Private Functions + * @{ + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F2xx_HAL_GPIO_EX_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/inc/stm32f413xx.h b/board/inc/stm32f413xx.h new file mode 100644 index 0000000000..f520dbf920 --- /dev/null +++ b/board/inc/stm32f413xx.h @@ -0,0 +1,14994 @@ +/** + ****************************************************************************** + * @file stm32f413xx.h + * @author MCD Application Team + * @version V2.6.0 + * @date 04-November-2016 + * @brief CMSIS STM32F413xx Device Peripheral Access Layer Header File. + * + * This file contains: + * - Data structures and the address mapping for all peripherals + * - peripherals registers declarations and bits definition + * - Macros to access peripheral’s registers hardware + * + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * 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. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS_Device + * @{ + */ + +/** @addtogroup stm32f413xx + * @{ + */ + +#ifndef __STM32F413xx_H +#define __STM32F413xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Configuration_section_for_CMSIS + * @{ + */ + +/** + * @brief Configuration of the Cortex-M4 Processor and Core Peripherals + */ +#define __CM4_REV 0x0001U /*!< Core revision r0p1 */ +#define __MPU_PRESENT 1U /*!< STM32F4XX provides an MPU */ +#define __NVIC_PRIO_BITS 4U /*!< STM32F4XX uses 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0U /*!< Set to 1 if different SysTick Config is used */ +#define __FPU_PRESENT 1U /*!< FPU present */ + +/** + * @} + */ + +/** @addtogroup Peripheral_interrupt_number_definition + * @{ + */ + +/** + * @brief STM32F4XX Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ +/****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */ +/****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */ + DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */ + DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */ + DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */ + DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */ + DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */ + DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */ + ADC_IRQn = 18, /*!< ADC1, ADC2 and ADC3 global Interrupts */ + CAN1_TX_IRQn = 19, /*!< CAN1 TX Interrupt */ + CAN1_RX0_IRQn = 20, /*!< CAN1 RX0 Interrupt */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM9_IRQn = 24, /*!< TIM1 Break interrupt and TIM9 global interrupt */ + TIM1_UP_TIM10_IRQn = 25, /*!< TIM1 Update Interrupt and TIM10 global interrupt */ + TIM1_TRG_COM_TIM11_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */ + OTG_FS_WKUP_IRQn = 42, /*!< USB OTG FS Wakeup through EXTI line interrupt */ + TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */ + TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */ + TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare global interrupt */ + DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */ + SDIO_IRQn = 49, /*!< SDIO global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn = 55, /*!< TIM7 global interrupt */ + DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */ + DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */ + DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */ + DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */ + DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */ + DFSDM1_FLT0_IRQn = 61, /*!< DFSDM1 Filter 0 global Interrupt */ + DFSDM1_FLT1_IRQn = 62, /*!< DFSDM1 Filter 1 global Interrupt */ + CAN2_TX_IRQn = 63, /*!< CAN2 TX Interrupt */ + CAN2_RX0_IRQn = 64, /*!< CAN2 RX0 Interrupt */ + CAN2_RX1_IRQn = 65, /*!< CAN2 RX1 Interrupt */ + CAN2_SCE_IRQn = 66, /*!< CAN2 SCE Interrupt */ + OTG_FS_IRQn = 67, /*!< USB OTG FS global Interrupt */ + DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */ + DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */ + DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */ + USART6_IRQn = 71, /*!< USART6 global interrupt */ + I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */ + I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */ + CAN3_TX_IRQn = 74, /*!< CAN3 TX Interrupt */ + CAN3_RX0_IRQn = 75, /*!< CAN3 RX0 Interrupt */ + CAN3_RX1_IRQn = 76, /*!< CAN3 RX1 Interrupt */ + CAN3_SCE_IRQn = 77, /*!< CAN3 SCE Interrupt */ + RNG_IRQn = 80, /*!< RNG global Interrupt */ + FPU_IRQn = 81, /*!< FPU global interrupt */ + UART7_IRQn = 82, /*!< UART7 global interrupt */ + UART8_IRQn = 83, /*!< UART8 global interrupt */ + SPI4_IRQn = 84, /*!< SPI4 global Interrupt */ + SPI5_IRQn = 85, /*!< SPI5 global Interrupt */ + SAI1_IRQn = 87, /*!< SAI1 global Interrupt */ + UART9_IRQn = 88, /*!< UART9 global Interrupt */ + UART10_IRQn = 89, /*!< UART10 global Interrupt */ + QUADSPI_IRQn = 92, /*!< QuadSPI global Interrupt */ + FMPI2C1_EV_IRQn = 95, /*!< FMPI2C1 Event Interrupt */ + FMPI2C1_ER_IRQn = 96, /*!< FMPI2C1 Error Interrupt */ + LPTIM1_IRQn = 97, /*!< LP TIM1 interrupt */ + DFSDM2_FLT0_IRQn = 98, /*!< DFSDM2 Filter 0 global Interrupt */ + DFSDM2_FLT1_IRQn = 99, /*!< DFSDM2 Filter 1 global Interrupt */ + DFSDM2_FLT2_IRQn = 100, /*!< DFSDM2 Filter 2 global Interrupt */ + DFSDM2_FLT3_IRQn = 101 /*!< DFSDM2 Filter 3 global Interrupt */ +} IRQn_Type; + +/** + * @} + */ + +#include "core_cm4.h" /* Cortex-M4 processor and core peripherals */ +#include "system_stm32f4xx.h" +#include + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< ADC status register, Address offset: 0x00 */ + __IO uint32_t CR1; /*!< ADC control register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< ADC control register 2, Address offset: 0x08 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x0C */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x10 */ + __IO uint32_t JOFR1; /*!< ADC injected channel data offset register 1, Address offset: 0x14 */ + __IO uint32_t JOFR2; /*!< ADC injected channel data offset register 2, Address offset: 0x18 */ + __IO uint32_t JOFR3; /*!< ADC injected channel data offset register 3, Address offset: 0x1C */ + __IO uint32_t JOFR4; /*!< ADC injected channel data offset register 4, Address offset: 0x20 */ + __IO uint32_t HTR; /*!< ADC watchdog higher threshold register, Address offset: 0x24 */ + __IO uint32_t LTR; /*!< ADC watchdog lower threshold register, Address offset: 0x28 */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x2C */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x30 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x34 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x38*/ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x3C */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x40 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x44 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x48 */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x4C */ +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1 base address + 0x300 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1 base address + 0x304 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1 base address + 0x308 */ +} ADC_Common_TypeDef; + + +/** + * @brief Controller Area Network TxMailBox + */ + +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** + * @brief Controller Area Network FIFOMailBox + */ + +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** + * @brief Controller Area Network FilterRegister + */ + +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** + * @brief Controller Area Network + */ + +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ +} CRC_TypeDef; + +/** + * @brief DFSDM module registers + */ +typedef struct +{ + __IO uint32_t FLTCR1; /*!< DFSDM control register1, Address offset: 0x100 */ + __IO uint32_t FLTCR2; /*!< DFSDM control register2, Address offset: 0x104 */ + __IO uint32_t FLTISR; /*!< DFSDM interrupt and status register, Address offset: 0x108 */ + __IO uint32_t FLTICR; /*!< DFSDM interrupt flag clear register, Address offset: 0x10C */ + __IO uint32_t FLTJCHGR; /*!< DFSDM injected channel group selection register, Address offset: 0x110 */ + __IO uint32_t FLTFCR; /*!< DFSDM filter control register, Address offset: 0x114 */ + __IO uint32_t FLTJDATAR; /*!< DFSDM data register for injected group, Address offset: 0x118 */ + __IO uint32_t FLTRDATAR; /*!< DFSDM data register for regular group, Address offset: 0x11C */ + __IO uint32_t FLTAWHTR; /*!< DFSDM analog watchdog high threshold register, Address offset: 0x120 */ + __IO uint32_t FLTAWLTR; /*!< DFSDM analog watchdog low threshold register, Address offset: 0x124 */ + __IO uint32_t FLTAWSR; /*!< DFSDM analog watchdog status register Address offset: 0x128 */ + __IO uint32_t FLTAWCFR; /*!< DFSDM analog watchdog clear flag register Address offset: 0x12C */ + __IO uint32_t FLTEXMAX; /*!< DFSDM extreme detector maximum register, Address offset: 0x130 */ + __IO uint32_t FLTEXMIN; /*!< DFSDM extreme detector minimum register Address offset: 0x134 */ + __IO uint32_t FLTCNVTIMR; /*!< DFSDM conversion timer, Address offset: 0x138 */ +} DFSDM_Filter_TypeDef; + +/** + * @brief DFSDM channel configuration registers + */ +typedef struct +{ + __IO uint32_t CHCFGR1; /*!< DFSDM channel configuration register1, Address offset: 0x00 */ + __IO uint32_t CHCFGR2; /*!< DFSDM channel configuration register2, Address offset: 0x04 */ + __IO uint32_t CHAWSCDR; /*!< DFSDM channel analog watchdog and + short circuit detector register, Address offset: 0x08 */ + __IO uint32_t CHWDATAR; /*!< DFSDM channel watchdog filter data register, Address offset: 0x0C */ + __IO uint32_t CHDATINR; /*!< DFSDM channel data input register, Address offset: 0x10 */ +} DFSDM_Channel_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +}DBGMCU_TypeDef; + + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA stream x configuration register */ + __IO uint32_t NDTR; /*!< DMA stream x number of data register */ + __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ + __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ + __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ + __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ +} DMA_Stream_TypeDef; + +typedef struct +{ + __IO uint32_t LISR; /*!< DMA low interrupt status register, Address offset: 0x00 */ + __IO uint32_t HISR; /*!< DMA high interrupt status register, Address offset: 0x04 */ + __IO uint32_t LIFCR; /*!< DMA low interrupt flag clear register, Address offset: 0x08 */ + __IO uint32_t HIFCR; /*!< DMA high interrupt flag clear register, Address offset: 0x0C */ +} DMA_TypeDef; + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR; /*!< EXTI Interrupt mask register, Address offset: 0x00 */ + __IO uint32_t EMR; /*!< EXTI Event mask register, Address offset: 0x04 */ + __IO uint32_t RTSR; /*!< EXTI Rising trigger selection register, Address offset: 0x08 */ + __IO uint32_t FTSR; /*!< EXTI Falling trigger selection register, Address offset: 0x0C */ + __IO uint32_t SWIER; /*!< EXTI Software interrupt event register, Address offset: 0x10 */ + __IO uint32_t PR; /*!< EXTI Pending register, Address offset: 0x14 */ +} EXTI_TypeDef; + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t KEYR; /*!< FLASH key register, Address offset: 0x04 */ + __IO uint32_t OPTKEYR; /*!< FLASH option key register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< FLASH status register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FLASH control register, Address offset: 0x10 */ + __IO uint32_t OPTCR; /*!< FLASH option control register , Address offset: 0x14 */ + __IO uint32_t OPTCR1; /*!< FLASH option control register 1, Address offset: 0x18 */ +} FLASH_TypeDef; + + + +/** + * @brief Flexible Static Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ +} FSMC_Bank1_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FSMC_Bank1E_TypeDef; +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ +} GPIO_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + __IO uint32_t MEMRMP; /*!< SYSCFG memory remap register, Address offset: 0x00 */ + __IO uint32_t PMC; /*!< SYSCFG peripheral mode configuration register, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + uint32_t RESERVED; /*!< Reserved, 0x18 */ + __IO uint32_t CFGR2; /*!< SYSCFG Configuration register2, Address offset: 0x1C */ + __IO uint32_t CMPCR; /*!< SYSCFG Compensation cell control register, Address offset: 0x20 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x24-0x28 */ + __IO uint32_t CFGR; /*!< SYSCFG Configuration register, Address offset: 0x2C */ + __IO uint32_t MCHDLYCR; /*!< SYSCFG multi-channel delay register, Address offset: 0x30 */ +} SYSCFG_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address register 1, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address register 2, Address offset: 0x0C */ + __IO uint32_t DR; /*!< I2C Data register, Address offset: 0x10 */ + __IO uint32_t SR1; /*!< I2C Status register 1, Address offset: 0x14 */ + __IO uint32_t SR2; /*!< I2C Status register 2, Address offset: 0x18 */ + __IO uint32_t CCR; /*!< I2C Clock control register, Address offset: 0x1C */ + __IO uint32_t TRISE; /*!< I2C TRISE register, Address offset: 0x20 */ + __IO uint32_t FLTR; /*!< I2C FLTR register, Address offset: 0x24 */ +} I2C_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< FMPI2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< FMPI2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< FMPI2C Own address 1 register, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< FMPI2C Own address 2 register, Address offset: 0x0C */ + __IO uint32_t TIMINGR; /*!< FMPI2C Timing register, Address offset: 0x10 */ + __IO uint32_t TIMEOUTR; /*!< FMPI2C Timeout register, Address offset: 0x14 */ + __IO uint32_t ISR; /*!< FMPI2C Interrupt and status register, Address offset: 0x18 */ + __IO uint32_t ICR; /*!< FMPI2C Interrupt clear register, Address offset: 0x1C */ + __IO uint32_t PECR; /*!< FMPI2C PEC register, Address offset: 0x20 */ + __IO uint32_t RXDR; /*!< FMPI2C Receive data register, Address offset: 0x24 */ + __IO uint32_t TXDR; /*!< FMPI2C Transmit data register, Address offset: 0x28 */ +} FMPI2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ +} IWDG_TypeDef; + + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< PWR power control register, Address offset: 0x00 */ + __IO uint32_t CSR; /*!< PWR power control/status register, Address offset: 0x04 */ +} PWR_TypeDef; + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t PLLCFGR; /*!< RCC PLL configuration register, Address offset: 0x04 */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ + __IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x0C */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x10 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x14 */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved, 0x1C */ + __IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x20 */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x24 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x28-0x2C */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0x30 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0x34 */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0x38 */ + uint32_t RESERVED2; /*!< Reserved, 0x3C */ + __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x40 */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x44 */ + uint32_t RESERVED3[2]; /*!< Reserved, 0x48-0x4C */ + __IO uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */ + __IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */ + __IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */ + uint32_t RESERVED4; /*!< Reserved, 0x5C */ + __IO uint32_t APB1LPENR; /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */ + __IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */ + uint32_t RESERVED5[2]; /*!< Reserved, 0x68-0x6C */ + __IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x70 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */ + uint32_t RESERVED6[2]; /*!< Reserved, 0x78-0x7C */ + __IO uint32_t SSCGR; /*!< RCC spread spectrum clock generation register, Address offset: 0x80 */ + __IO uint32_t PLLI2SCFGR; /*!< RCC PLLI2S configuration register, Address offset: 0x84 */ + uint32_t RESERVED7; /*!< Reserved, 0x84 */ + __IO uint32_t DCKCFGR; /*!< RCC Dedicated Clocks configuration register, Address offset: 0x8C */ + __IO uint32_t CKGATENR; /*!< RCC Clocks Gated ENable Register, Address offset: 0x90 */ + __IO uint32_t DCKCFGR2; /*!< RCC Dedicated Clocks configuration register 2, Address offset: 0x94 */ +} RCC_TypeDef; + +/** + * @brief Real-Time Clock + */ + +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x08 */ + __IO uint32_t ISR; /*!< RTC initialization and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + __IO uint32_t CALIBR; /*!< RTC calibration register, Address offset: 0x18 */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x1C */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + __IO uint32_t SSR; /*!< RTC sub second register, Address offset: 0x28 */ + __IO uint32_t SHIFTR; /*!< RTC shift control register, Address offset: 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + __IO uint32_t TSSSR; /*!< RTC time-stamp sub second register, Address offset: 0x38 */ + __IO uint32_t CALR; /*!< RTC calibration register, Address offset: 0x3C */ + __IO uint32_t TAFCR; /*!< RTC tamper and alternate function configuration register, Address offset: 0x40 */ + __IO uint32_t ALRMASSR;/*!< RTC alarm A sub second register, Address offset: 0x44 */ + __IO uint32_t ALRMBSSR;/*!< RTC alarm B sub second register, Address offset: 0x48 */ + uint32_t RESERVED7; /*!< Reserved, 0x4C */ + __IO uint32_t BKP0R; /*!< RTC backup register 1, Address offset: 0x50 */ + __IO uint32_t BKP1R; /*!< RTC backup register 1, Address offset: 0x54 */ + __IO uint32_t BKP2R; /*!< RTC backup register 2, Address offset: 0x58 */ + __IO uint32_t BKP3R; /*!< RTC backup register 3, Address offset: 0x5C */ + __IO uint32_t BKP4R; /*!< RTC backup register 4, Address offset: 0x60 */ + __IO uint32_t BKP5R; /*!< RTC backup register 5, Address offset: 0x64 */ + __IO uint32_t BKP6R; /*!< RTC backup register 6, Address offset: 0x68 */ + __IO uint32_t BKP7R; /*!< RTC backup register 7, Address offset: 0x6C */ + __IO uint32_t BKP8R; /*!< RTC backup register 8, Address offset: 0x70 */ + __IO uint32_t BKP9R; /*!< RTC backup register 9, Address offset: 0x74 */ + __IO uint32_t BKP10R; /*!< RTC backup register 10, Address offset: 0x78 */ + __IO uint32_t BKP11R; /*!< RTC backup register 11, Address offset: 0x7C */ + __IO uint32_t BKP12R; /*!< RTC backup register 12, Address offset: 0x80 */ + __IO uint32_t BKP13R; /*!< RTC backup register 13, Address offset: 0x84 */ + __IO uint32_t BKP14R; /*!< RTC backup register 14, Address offset: 0x88 */ + __IO uint32_t BKP15R; /*!< RTC backup register 15, Address offset: 0x8C */ + __IO uint32_t BKP16R; /*!< RTC backup register 16, Address offset: 0x90 */ + __IO uint32_t BKP17R; /*!< RTC backup register 17, Address offset: 0x94 */ + __IO uint32_t BKP18R; /*!< RTC backup register 18, Address offset: 0x98 */ + __IO uint32_t BKP19R; /*!< RTC backup register 19, Address offset: 0x9C */ +} RTC_TypeDef; + +/** + * @brief Serial Audio Interface + */ + +typedef struct +{ + __IO uint32_t GCR; /*!< SAI global configuration register, Address offset: 0x00 */ +} SAI_TypeDef; + +typedef struct +{ + __IO uint32_t CR1; /*!< SAI block x configuration register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< SAI block x configuration register 2, Address offset: 0x08 */ + __IO uint32_t FRCR; /*!< SAI block x frame configuration register, Address offset: 0x0C */ + __IO uint32_t SLOTR; /*!< SAI block x slot register, Address offset: 0x10 */ + __IO uint32_t IMR; /*!< SAI block x interrupt mask register, Address offset: 0x14 */ + __IO uint32_t SR; /*!< SAI block x status register, Address offset: 0x18 */ + __IO uint32_t CLRFR; /*!< SAI block x clear flag register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< SAI block x data register, Address offset: 0x20 */ +} SAI_Block_TypeDef; + +/** + * @brief SD host Interface + */ + +typedef struct +{ + __IO uint32_t POWER; /*!< SDIO power control register, Address offset: 0x00 */ + __IO uint32_t CLKCR; /*!< SDI clock control register, Address offset: 0x04 */ + __IO uint32_t ARG; /*!< SDIO argument register, Address offset: 0x08 */ + __IO uint32_t CMD; /*!< SDIO command register, Address offset: 0x0C */ + __IO const uint32_t RESPCMD; /*!< SDIO command response register, Address offset: 0x10 */ + __IO const uint32_t RESP1; /*!< SDIO response 1 register, Address offset: 0x14 */ + __IO const uint32_t RESP2; /*!< SDIO response 2 register, Address offset: 0x18 */ + __IO const uint32_t RESP3; /*!< SDIO response 3 register, Address offset: 0x1C */ + __IO const uint32_t RESP4; /*!< SDIO response 4 register, Address offset: 0x20 */ + __IO uint32_t DTIMER; /*!< SDIO data timer register, Address offset: 0x24 */ + __IO uint32_t DLEN; /*!< SDIO data length register, Address offset: 0x28 */ + __IO uint32_t DCTRL; /*!< SDIO data control register, Address offset: 0x2C */ + __IO const uint32_t DCOUNT; /*!< SDIO data counter register, Address offset: 0x30 */ + __IO const uint32_t STA; /*!< SDIO status register, Address offset: 0x34 */ + __IO uint32_t ICR; /*!< SDIO interrupt clear register, Address offset: 0x38 */ + __IO uint32_t MASK; /*!< SDIO mask register, Address offset: 0x3C */ + uint32_t RESERVED0[2]; /*!< Reserved, 0x40-0x44 */ + __IO const uint32_t FIFOCNT; /*!< SDIO FIFO counter register, Address offset: 0x48 */ + uint32_t RESERVED1[13]; /*!< Reserved, 0x4C-0x7C */ + __IO uint32_t FIFO; /*!< SDIO data FIFO register, Address offset: 0x80 */ +} SDIO_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI control register 1 (not used in I2S mode), Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI control register 2, Address offset: 0x04 */ + __IO uint32_t SR; /*!< SPI status register, Address offset: 0x08 */ + __IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */ + __IO uint32_t CRCPR; /*!< SPI CRC polynomial register (not used in I2S mode), Address offset: 0x10 */ + __IO uint32_t RXCRCR; /*!< SPI RX CRC register (not used in I2S mode), Address offset: 0x14 */ + __IO uint32_t TXCRCR; /*!< SPI TX CRC register (not used in I2S mode), Address offset: 0x18 */ + __IO uint32_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */ + __IO uint32_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */ +} SPI_TypeDef; + +/** + * @brief QUAD Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< QUADSPI Control register, Address offset: 0x00 */ + __IO uint32_t DCR; /*!< QUADSPI Device Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< QUADSPI Status register, Address offset: 0x08 */ + __IO uint32_t FCR; /*!< QUADSPI Flag Clear register, Address offset: 0x0C */ + __IO uint32_t DLR; /*!< QUADSPI Data Length register, Address offset: 0x10 */ + __IO uint32_t CCR; /*!< QUADSPI Communication Configuration register, Address offset: 0x14 */ + __IO uint32_t AR; /*!< QUADSPI Address register, Address offset: 0x18 */ + __IO uint32_t ABR; /*!< QUADSPI Alternate Bytes register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< QUADSPI Data register, Address offset: 0x20 */ + __IO uint32_t PSMKR; /*!< QUADSPI Polling Status Mask register, Address offset: 0x24 */ + __IO uint32_t PSMAR; /*!< QUADSPI Polling Status Match register, Address offset: 0x28 */ + __IO uint32_t PIR; /*!< QUADSPI Polling Interval register, Address offset: 0x2C */ + __IO uint32_t LPTR; /*!< QUADSPI Low Power Timeout register, Address offset: 0x30 */ +} QUADSPI_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ + __IO uint32_t OR; /*!< TIM option register, Address offset: 0x50 */ +} TIM_TypeDef; + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< USART Status register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< USART Data register, Address offset: 0x04 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */ +} USART_TypeDef; + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + +/** + * @brief RNG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ +} RNG_TypeDef; + +/** + * @brief USB_OTG_Core_Registers + */ +typedef struct +{ + __IO uint32_t GOTGCTL; /*!< USB_OTG Control and Status Register 000h */ + __IO uint32_t GOTGINT; /*!< USB_OTG Interrupt Register 004h */ + __IO uint32_t GAHBCFG; /*!< Core AHB Configuration Register 008h */ + __IO uint32_t GUSBCFG; /*!< Core USB Configuration Register 00Ch */ + __IO uint32_t GRSTCTL; /*!< Core Reset Register 010h */ + __IO uint32_t GINTSTS; /*!< Core Interrupt Register 014h */ + __IO uint32_t GINTMSK; /*!< Core Interrupt Mask Register 018h */ + __IO uint32_t GRXSTSR; /*!< Receive Sts Q Read Register 01Ch */ + __IO uint32_t GRXSTSP; /*!< Receive Sts Q Read & POP Register 020h */ + __IO uint32_t GRXFSIZ; /*!< Receive FIFO Size Register 024h */ + __IO uint32_t DIEPTXF0_HNPTXFSIZ; /*!< EP0 / Non Periodic Tx FIFO Size Register 028h */ + __IO uint32_t HNPTXSTS; /*!< Non Periodic Tx FIFO/Queue Sts reg 02Ch */ + uint32_t Reserved30[2]; /*!< Reserved 030h */ + __IO uint32_t GCCFG; /*!< General Purpose IO Register 038h */ + __IO uint32_t CID; /*!< User ID Register 03Ch */ + uint32_t Reserved5[3]; /*!< Reserved 040h-048h */ + __IO uint32_t GHWCFG3; /*!< User HW config3 04Ch */ + uint32_t Reserved6; /*!< Reserved 050h */ + __IO uint32_t GLPMCFG; /*!< LPM Register 054h */ + uint32_t Reserved; /*!< Reserved 058h */ + __IO uint32_t GDFIFOCFG; /*!< DFIFO Software Config Register 05Ch */ + uint32_t Reserved43[40]; /*!< Reserved 058h-0FFh */ + __IO uint32_t HPTXFSIZ; /*!< Host Periodic Tx FIFO Size Reg 100h */ + __IO uint32_t DIEPTXF[0x0F]; /*!< dev Periodic Transmit FIFO */ +} USB_OTG_GlobalTypeDef; + +/** + * @brief USB_OTG_device_Registers + */ +typedef struct +{ + __IO uint32_t DCFG; /*!< dev Configuration Register 800h */ + __IO uint32_t DCTL; /*!< dev Control Register 804h */ + __IO uint32_t DSTS; /*!< dev Status Register (RO) 808h */ + uint32_t Reserved0C; /*!< Reserved 80Ch */ + __IO uint32_t DIEPMSK; /*!< dev IN Endpoint Mask 810h */ + __IO uint32_t DOEPMSK; /*!< dev OUT Endpoint Mask 814h */ + __IO uint32_t DAINT; /*!< dev All Endpoints Itr Reg 818h */ + __IO uint32_t DAINTMSK; /*!< dev All Endpoints Itr Mask 81Ch */ + uint32_t Reserved20; /*!< Reserved 820h */ + uint32_t Reserved9; /*!< Reserved 824h */ + __IO uint32_t DVBUSDIS; /*!< dev VBUS discharge Register 828h */ + __IO uint32_t DVBUSPULSE; /*!< dev VBUS Pulse Register 82Ch */ + __IO uint32_t DTHRCTL; /*!< dev threshold 830h */ + __IO uint32_t DIEPEMPMSK; /*!< dev empty msk 834h */ + __IO uint32_t DEACHINT; /*!< dedicated EP interrupt 838h */ + __IO uint32_t DEACHMSK; /*!< dedicated EP msk 83Ch */ + uint32_t Reserved40; /*!< dedicated EP mask 840h */ + __IO uint32_t DINEP1MSK; /*!< dedicated EP mask 844h */ + uint32_t Reserved44[15]; /*!< Reserved 844-87Ch */ + __IO uint32_t DOUTEP1MSK; /*!< dedicated EP msk 884h */ +} USB_OTG_DeviceTypeDef; + +/** + * @brief USB_OTG_IN_Endpoint-Specific_Register + */ +typedef struct +{ + __IO uint32_t DIEPCTL; /*!< dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved 900h + (ep_num * 20h) + 04h */ + __IO uint32_t DIEPINT; /*!< dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved 900h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DIEPTSIZ; /*!< IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h */ + __IO uint32_t DIEPDMA; /*!< IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h */ + __IO uint32_t DTXFSTS; /*!< IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h */ + uint32_t Reserved18; /*!< Reserved 900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch */ +} USB_OTG_INEndpointTypeDef; + +/** + * @brief USB_OTG_OUT_Endpoint-Specific_Registers + */ +typedef struct +{ + __IO uint32_t DOEPCTL; /*!< dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved B00h + (ep_num * 20h) + 04h */ + __IO uint32_t DOEPINT; /*!< dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved B00h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DOEPTSIZ; /*!< dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h */ + __IO uint32_t DOEPDMA; /*!< dev OUT Endpoint DMA Address B00h + (ep_num * 20h) + 14h */ + uint32_t Reserved18[2]; /*!< Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch */ +} USB_OTG_OUTEndpointTypeDef; + +/** + * @brief USB_OTG_Host_Mode_Register_Structures + */ +typedef struct +{ + __IO uint32_t HCFG; /*!< Host Configuration Register 400h */ + __IO uint32_t HFIR; /*!< Host Frame Interval Register 404h */ + __IO uint32_t HFNUM; /*!< Host Frame Nbr/Frame Remaining 408h */ + uint32_t Reserved40C; /*!< Reserved 40Ch */ + __IO uint32_t HPTXSTS; /*!< Host Periodic Tx FIFO/ Queue Status 410h */ + __IO uint32_t HAINT; /*!< Host All Channels Interrupt Register 414h */ + __IO uint32_t HAINTMSK; /*!< Host All Channels Interrupt Mask 418h */ +} USB_OTG_HostTypeDef; + +/** + * @brief USB_OTG_Host_Channel_Specific_Registers + */ +typedef struct +{ + __IO uint32_t HCCHAR; /*!< Host Channel Characteristics Register 500h */ + __IO uint32_t HCSPLT; /*!< Host Channel Split Control Register 504h */ + __IO uint32_t HCINT; /*!< Host Channel Interrupt Register 508h */ + __IO uint32_t HCINTMSK; /*!< Host Channel Interrupt Mask Register 50Ch */ + __IO uint32_t HCTSIZ; /*!< Host Channel Transfer Size Register 510h */ + __IO uint32_t HCDMA; /*!< Host Channel DMA Address Register 514h */ + uint32_t Reserved[2]; /*!< Reserved */ +} USB_OTG_HostChannelTypeDef; + +/** + * @brief LPTIMER + */ +typedef struct +{ + __IO uint32_t ISR; /*!< LPTIM Interrupt and Status register, Address offset: 0x00 */ + __IO uint32_t ICR; /*!< LPTIM Interrupt Clear register, Address offset: 0x04 */ + __IO uint32_t IER; /*!< LPTIM Interrupt Enable register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< LPTIM Configuration register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< LPTIM Control register, Address offset: 0x10 */ + __IO uint32_t CMP; /*!< LPTIM Compare register, Address offset: 0x14 */ + __IO uint32_t ARR; /*!< LPTIM Autoreload register, Address offset: 0x18 */ + __IO uint32_t CNT; /*!< LPTIM Counter register, Address offset: 0x1C */ + __IO uint32_t OR; /*!< LPTIM Option register, Address offset: 0x20 */ +} LPTIM_TypeDef; + +/** + * @} + */ + +/** @addtogroup Peripheral_memory_map + * @{ + */ +#define FLASH_BASE 0x08000000U /*!< FLASH (up to 1.5 MB) base address in the alias region */ +#define SRAM1_BASE 0x20000000U /*!< SRAM1(256 KB) base address in the alias region */ +#define SRAM2_BASE 0x20040000U /*!< SRAM2(64 KB) base address in the alias region */ +#define PERIPH_BASE 0x40000000U /*!< Peripheral base address in the alias region */ +#define FSMC_R_BASE 0xA0000000U /*!< FSMC registers base address */ +#define QSPI_R_BASE 0xA0001000U /*!< QuadSPI registers base address */ +#define SRAM1_BB_BASE 0x22000000U /*!< SRAM1(256 KB) base address in the bit-band region */ +#define SRAM2_BB_BASE 0x22800000U /*!< SRAM2(64 KB) base address in the bit-band region */ +#define PERIPH_BB_BASE 0x42000000U /*!< Peripheral base address in the bit-band region */ +#define FLASH_END 0x0817FFFFU /*!< FLASH end address */ + +/* Legacy defines */ +#define SRAM_BASE SRAM1_BASE +#define SRAM_BB_BASE SRAM1_BB_BASE + + +/*!< Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000U) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000U) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x10000000U) + +/*!< APB1 peripherals */ +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000U) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400U) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800U) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00U) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000U) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400U) +#define TIM12_BASE (APB1PERIPH_BASE + 0x1800U) +#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00U) +#define TIM14_BASE (APB1PERIPH_BASE + 0x2000U) +#define LPTIM1_BASE (APB1PERIPH_BASE + 0x2400U) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800U) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00U) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000U) +#define I2S2ext_BASE (APB1PERIPH_BASE + 0x3400U) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800U) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00U) +#define I2S3ext_BASE (APB1PERIPH_BASE + 0x4000U) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400U) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800U) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00U) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000U) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400U) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800U) +#define I2C3_BASE (APB1PERIPH_BASE + 0x5C00U) +#define FMPI2C1_BASE (APB1PERIPH_BASE + 0x6000U) +#define CAN1_BASE (APB1PERIPH_BASE + 0x6400U) +#define CAN2_BASE (APB1PERIPH_BASE + 0x6800U) +#define CAN3_BASE (APB1PERIPH_BASE + 0x6C00U) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000U) +#define DAC_BASE (APB1PERIPH_BASE + 0x7400U) +#define UART7_BASE (APB1PERIPH_BASE + 0x7800U) +#define UART8_BASE (APB1PERIPH_BASE + 0x7C00U) + +/*!< APB2 peripherals */ +#define TIM1_BASE (APB2PERIPH_BASE + 0x0000U) +#define TIM8_BASE (APB2PERIPH_BASE + 0x0400U) +#define USART1_BASE (APB2PERIPH_BASE + 0x1000U) +#define USART6_BASE (APB2PERIPH_BASE + 0x1400U) +#define UART9_BASE (APB2PERIPH_BASE + 0x1800U) +#define UART10_BASE (APB2PERIPH_BASE + 0x1C00U) +#define ADC1_BASE (APB2PERIPH_BASE + 0x2000U) +#define ADC_BASE (APB2PERIPH_BASE + 0x2300U) +#define SDIO_BASE (APB2PERIPH_BASE + 0x2C00U) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000U) +#define SPI4_BASE (APB2PERIPH_BASE + 0x3400U) +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x3800U) +#define EXTI_BASE (APB2PERIPH_BASE + 0x3C00U) +#define TIM9_BASE (APB2PERIPH_BASE + 0x4000U) +#define TIM10_BASE (APB2PERIPH_BASE + 0x4400U) +#define TIM11_BASE (APB2PERIPH_BASE + 0x4800U) +#define SPI5_BASE (APB2PERIPH_BASE + 0x5000U) +#define DFSDM1_BASE (APB2PERIPH_BASE + 0x6000U) +#define DFSDM2_BASE (APB2PERIPH_BASE + 0x6400U) +#define DFSDM1_Channel0_BASE (DFSDM1_BASE + 0x00U) +#define DFSDM1_Channel1_BASE (DFSDM1_BASE + 0x20U) +#define DFSDM1_Channel2_BASE (DFSDM1_BASE + 0x40U) +#define DFSDM1_Channel3_BASE (DFSDM1_BASE + 0x60U) +#define DFSDM1_Filter0_BASE (DFSDM1_BASE + 0x100U) +#define DFSDM1_Filter1_BASE (DFSDM1_BASE + 0x180U) +#define DFSDM2_Channel0_BASE (DFSDM2_BASE + 0x00U) +#define DFSDM2_Channel1_BASE (DFSDM2_BASE + 0x20U) +#define DFSDM2_Channel2_BASE (DFSDM2_BASE + 0x40U) +#define DFSDM2_Channel3_BASE (DFSDM2_BASE + 0x60U) +#define DFSDM2_Channel4_BASE (DFSDM2_BASE + 0x80U) +#define DFSDM2_Channel5_BASE (DFSDM2_BASE + 0xA0U) +#define DFSDM2_Channel6_BASE (DFSDM2_BASE + 0xC0U) +#define DFSDM2_Channel7_BASE (DFSDM2_BASE + 0xE0U) +#define DFSDM2_Filter0_BASE (DFSDM2_BASE + 0x100U) +#define DFSDM2_Filter1_BASE (DFSDM2_BASE + 0x180U) +#define DFSDM2_Filter2_BASE (DFSDM2_BASE + 0x200U) +#define DFSDM2_Filter3_BASE (DFSDM2_BASE + 0x280U) +#define SAI1_BASE (APB2PERIPH_BASE + 0x5800U) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x004U) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x024U) + +/*!< AHB1 peripherals */ +#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000U) +#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400U) +#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800U) +#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00U) +#define GPIOE_BASE (AHB1PERIPH_BASE + 0x1000U) +#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400U) +#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800U) +#define GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00U) +#define CRC_BASE (AHB1PERIPH_BASE + 0x3000U) +#define RCC_BASE (AHB1PERIPH_BASE + 0x3800U) +#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x3C00U) +#define DMA1_BASE (AHB1PERIPH_BASE + 0x6000U) +#define DMA1_Stream0_BASE (DMA1_BASE + 0x010U) +#define DMA1_Stream1_BASE (DMA1_BASE + 0x028U) +#define DMA1_Stream2_BASE (DMA1_BASE + 0x040U) +#define DMA1_Stream3_BASE (DMA1_BASE + 0x058U) +#define DMA1_Stream4_BASE (DMA1_BASE + 0x070U) +#define DMA1_Stream5_BASE (DMA1_BASE + 0x088U) +#define DMA1_Stream6_BASE (DMA1_BASE + 0x0A0U) +#define DMA1_Stream7_BASE (DMA1_BASE + 0x0B8U) +#define DMA2_BASE (AHB1PERIPH_BASE + 0x6400U) +#define DMA2_Stream0_BASE (DMA2_BASE + 0x010U) +#define DMA2_Stream1_BASE (DMA2_BASE + 0x028U) +#define DMA2_Stream2_BASE (DMA2_BASE + 0x040U) +#define DMA2_Stream3_BASE (DMA2_BASE + 0x058U) +#define DMA2_Stream4_BASE (DMA2_BASE + 0x070U) +#define DMA2_Stream5_BASE (DMA2_BASE + 0x088U) +#define DMA2_Stream6_BASE (DMA2_BASE + 0x0A0U) +#define DMA2_Stream7_BASE (DMA2_BASE + 0x0B8U) + +/*!< AHB2 peripherals */ +#define RNG_BASE (AHB2PERIPH_BASE + 0x60800U) + + +/*!< FSMC Bankx registers base address */ +#define FSMC_Bank1_R_BASE (FSMC_R_BASE + 0x0000U) +#define FSMC_Bank1E_R_BASE (FSMC_R_BASE + 0x0104U) + +/*!< Debug MCU registers base address */ +#define DBGMCU_BASE 0xE0042000U +/*!< USB registers base address */ +#define USB_OTG_FS_PERIPH_BASE 0x50000000U + +#define USB_OTG_GLOBAL_BASE 0x000U +#define USB_OTG_DEVICE_BASE 0x800U +#define USB_OTG_IN_ENDPOINT_BASE 0x900U +#define USB_OTG_OUT_ENDPOINT_BASE 0xB00U +#define USB_OTG_EP_REG_SIZE 0x20U +#define USB_OTG_HOST_BASE 0x400U +#define USB_OTG_HOST_PORT_BASE 0x440U +#define USB_OTG_HOST_CHANNEL_BASE 0x500U +#define USB_OTG_HOST_CHANNEL_SIZE 0x20U +#define USB_OTG_PCGCCTL_BASE 0xE00U +#define USB_OTG_FIFO_BASE 0x1000U +#define USB_OTG_FIFO_SIZE 0x1000U + +#define UID_BASE 0x1FFF7A10U /*!< Unique device ID register base address */ +#define FLASHSIZE_BASE 0x1FFF7A22U /*!< FLASH Size register base address */ +#define PACKAGE_BASE 0x1FFF7BF0U /*!< Package size register base address */ +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) +#define TIM13 ((TIM_TypeDef *) TIM13_BASE) +#define TIM14 ((TIM_TypeDef *) TIM14_BASE) +#define LPTIM1 ((LPTIM_TypeDef *) LPTIM1_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG ((WWDG_TypeDef *) WWDG_BASE) +#define IWDG ((IWDG_TypeDef *) IWDG_BASE) +#define I2S2ext ((SPI_TypeDef *) I2S2ext_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define I2S3ext ((SPI_TypeDef *) I2S3ext_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define FMPI2C1 ((FMPI2C_TypeDef *) FMPI2C1_BASE) +#define CAN1 ((CAN_TypeDef *) CAN1_BASE) +#define CAN2 ((CAN_TypeDef *) CAN2_BASE) +#define CAN3 ((CAN_TypeDef *) CAN3_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define DAC1 ((DAC_TypeDef *) DAC_BASE) +#define DAC ((DAC_TypeDef *) DAC_BASE) /* Kept for legacy purpose */ +#define UART7 ((USART_TypeDef *) UART7_BASE) +#define UART8 ((USART_TypeDef *) UART8_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define USART6 ((USART_TypeDef *) USART6_BASE) +#define UART9 ((USART_TypeDef *) UART9_BASE) +#define UART10 ((USART_TypeDef *) UART10_BASE) +#define ADC ((ADC_Common_TypeDef *) ADC_BASE) +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define SDIO ((SDIO_TypeDef *) SDIO_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define SPI4 ((SPI_TypeDef *) SPI4_BASE) +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define TIM9 ((TIM_TypeDef *) TIM9_BASE) +#define TIM10 ((TIM_TypeDef *) TIM10_BASE) +#define TIM11 ((TIM_TypeDef *) TIM11_BASE) +#define SPI5 ((SPI_TypeDef *) SPI5_BASE) +#define DFSDM1_Channel0 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel0_BASE) +#define DFSDM1_Channel1 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel1_BASE) +#define DFSDM1_Channel2 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel2_BASE) +#define DFSDM1_Channel3 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel3_BASE) +#define DFSDM1_Filter0 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter0_BASE) +#define DFSDM1_Filter1 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter1_BASE) +#define DFSDM2_Channel0 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel0_BASE) +#define DFSDM2_Channel1 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel1_BASE) +#define DFSDM2_Channel2 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel2_BASE) +#define DFSDM2_Channel3 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel3_BASE) +#define DFSDM2_Channel4 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel4_BASE) +#define DFSDM2_Channel5 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel5_BASE) +#define DFSDM2_Channel6 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel6_BASE) +#define DFSDM2_Channel7 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel7_BASE) +#define DFSDM2_Filter0 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter0_BASE) +#define DFSDM2_Filter1 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter1_BASE) +#define DFSDM2_Filter2 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter2_BASE) +#define DFSDM2_Filter3 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter3_BASE) +#define SAI1 ((SAI_TypeDef *) SAI1_BASE) +#define SAI1_Block_A ((SAI_Block_TypeDef *)SAI1_Block_A_BASE) +#define SAI1_Block_B ((SAI_Block_TypeDef *)SAI1_Block_B_BASE) +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA1_Stream0 ((DMA_Stream_TypeDef *) DMA1_Stream0_BASE) +#define DMA1_Stream1 ((DMA_Stream_TypeDef *) DMA1_Stream1_BASE) +#define DMA1_Stream2 ((DMA_Stream_TypeDef *) DMA1_Stream2_BASE) +#define DMA1_Stream3 ((DMA_Stream_TypeDef *) DMA1_Stream3_BASE) +#define DMA1_Stream4 ((DMA_Stream_TypeDef *) DMA1_Stream4_BASE) +#define DMA1_Stream5 ((DMA_Stream_TypeDef *) DMA1_Stream5_BASE) +#define DMA1_Stream6 ((DMA_Stream_TypeDef *) DMA1_Stream6_BASE) +#define DMA1_Stream7 ((DMA_Stream_TypeDef *) DMA1_Stream7_BASE) +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMA2_Stream0 ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE) +#define DMA2_Stream1 ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE) +#define DMA2_Stream2 ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE) +#define DMA2_Stream3 ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE) +#define DMA2_Stream4 ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE) +#define DMA2_Stream5 ((DMA_Stream_TypeDef *) DMA2_Stream5_BASE) +#define DMA2_Stream6 ((DMA_Stream_TypeDef *) DMA2_Stream6_BASE) +#define DMA2_Stream7 ((DMA_Stream_TypeDef *) DMA2_Stream7_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) +#define FSMC_Bank1 ((FSMC_Bank1_TypeDef *) FSMC_Bank1_R_BASE) +#define FSMC_Bank1E ((FSMC_Bank1E_TypeDef *) FSMC_Bank1E_R_BASE) +#define QUADSPI ((QUADSPI_TypeDef *) QSPI_R_BASE) +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) +#define USB_OTG_FS ((USB_OTG_GlobalTypeDef *) USB_OTG_FS_PERIPH_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + + /** @addtogroup Peripheral_Registers_Bits_Definition + * @{ + */ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ +/******************** Bit definition for ADC_SR register ********************/ +#define ADC_SR_AWD_Pos (0U) +#define ADC_SR_AWD_Msk (0x1U << ADC_SR_AWD_Pos) /*!< 0x00000001 */ +#define ADC_SR_AWD ADC_SR_AWD_Msk /*!
© COPYRIGHT(c) 2016 STMicroelectronics
+ * + * 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. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f4xx + * @{ + */ + +#ifndef __STM32F4xx_H +#define __STM32F4xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Library_configuration_section + * @{ + */ + +/** + * @brief STM32 Family + */ +#if !defined (STM32F4) +#define STM32F4 +#endif /* STM32F4 */ + +/* Uncomment the line below according to the target STM32 device used in your + application + */ +#if !defined (STM32F405xx) && !defined (STM32F415xx) && !defined (STM32F407xx) && !defined (STM32F417xx) && \ + !defined (STM32F427xx) && !defined (STM32F437xx) && !defined (STM32F429xx) && !defined (STM32F439xx) && \ + !defined (STM32F401xC) && !defined (STM32F401xE) && !defined (STM32F410Tx) && !defined (STM32F410Cx) && \ + !defined (STM32F410Rx) && !defined (STM32F411xE) && !defined (STM32F446xx) && !defined (STM32F469xx) && \ + !defined (STM32F479xx) && !defined (STM32F412Cx) && !defined (STM32F412Rx) && !defined (STM32F412Vx) && \ + !defined (STM32F412Zx) && !defined (STM32F413xx) && !defined (STM32F423xx) + /* #define STM32F405xx */ /*!< STM32F405RG, STM32F405VG and STM32F405ZG Devices */ + /* #define STM32F415xx */ /*!< STM32F415RG, STM32F415VG and STM32F415ZG Devices */ + /* #define STM32F407xx */ /*!< STM32F407VG, STM32F407VE, STM32F407ZG, STM32F407ZE, STM32F407IG and STM32F407IE Devices */ + /* #define STM32F417xx */ /*!< STM32F417VG, STM32F417VE, STM32F417ZG, STM32F417ZE, STM32F417IG and STM32F417IE Devices */ + /* #define STM32F427xx */ /*!< STM32F427VG, STM32F427VI, STM32F427ZG, STM32F427ZI, STM32F427IG and STM32F427II Devices */ + /* #define STM32F437xx */ /*!< STM32F437VG, STM32F437VI, STM32F437ZG, STM32F437ZI, STM32F437IG and STM32F437II Devices */ + /* #define STM32F429xx */ /*!< STM32F429VG, STM32F429VI, STM32F429ZG, STM32F429ZI, STM32F429BG, STM32F429BI, STM32F429NG, + STM32F439NI, STM32F429IG and STM32F429II Devices */ + /* #define STM32F439xx */ /*!< STM32F439VG, STM32F439VI, STM32F439ZG, STM32F439ZI, STM32F439BG, STM32F439BI, STM32F439NG, + STM32F439NI, STM32F439IG and STM32F439II Devices */ + /* #define STM32F401xC */ /*!< STM32F401CB, STM32F401CC, STM32F401RB, STM32F401RC, STM32F401VB and STM32F401VC Devices */ + /* #define STM32F401xE */ /*!< STM32F401CD, STM32F401RD, STM32F401VD, STM32F401CE, STM32F401RE and STM32F401VE Devices */ + /* #define STM32F410Tx */ /*!< STM32F410T8 and STM32F410TB Devices */ + /* #define STM32F410Cx */ /*!< STM32F410C8 and STM32F410CB Devices */ + /* #define STM32F410Rx */ /*!< STM32F410R8 and STM32F410RB Devices */ + /* #define STM32F411xE */ /*!< STM32F411CC, STM32F411RC, STM32F411VC, STM32F411CE, STM32F411RE and STM32F411VE Devices */ + /* #define STM32F446xx */ /*!< STM32F446MC, STM32F446ME, STM32F446RC, STM32F446RE, STM32F446VC, STM32F446VE, STM32F446ZC, + and STM32F446ZE Devices */ + /* #define STM32F469xx */ /*!< STM32F469AI, STM32F469II, STM32F469BI, STM32F469NI, STM32F469AG, STM32F469IG, STM32F469BG, + STM32F469NG, STM32F469AE, STM32F469IE, STM32F469BE and STM32F469NE Devices */ + /* #define STM32F479xx */ /*!< STM32F479AI, STM32F479II, STM32F479BI, STM32F479NI, STM32F479AG, STM32F479IG, STM32F479BG + and STM32F479NG Devices */ + /* #define STM32F412Cx */ /*!< STM32F412CEU and STM32F412CGU Devices */ + /* #define STM32F412Zx */ /*!< STM32F412ZET, STM32F412ZGT, STM32F412ZEJ and STM32F412ZGJ Devices */ + /* #define STM32F412Vx */ /*!< STM32F412VET, STM32F412VGT, STM32F412VEH and STM32F412VGH Devices */ + /* #define STM32F412Rx */ /*!< STM32F412RET, STM32F412RGT, STM32F412REY and STM32F412RGY Devices */ + /* #define STM32F413xx */ /*!< STM32F413CH, STM32F413MH, STM32F413RH, STM32F413VH, STM32F413ZH, STM32F413CG, STM32F413MG, + STM32F413RG, STM32F413VG and STM32F413ZG Devices */ + /* #define STM32F423xx */ /*!< STM32F423CH, STM32F423RH, STM32F423VH and STM32F423ZH Devices */ +#endif + +/* Tip: To avoid modifying this file each time you need to switch between these + devices, you can define the device in your toolchain compiler preprocessor. + */ +#if !defined (USE_HAL_DRIVER) +/** + * @brief Comment the line below if you will not use the peripherals drivers. + In this case, these drivers will not be included and the application code will + be based on direct access to peripherals registers + */ + /*#define USE_HAL_DRIVER */ +#endif /* USE_HAL_DRIVER */ + +/** + * @brief CMSIS version number V2.6.0 + */ +#define __STM32F4xx_CMSIS_VERSION_MAIN (0x02U) /*!< [31:24] main version */ +#define __STM32F4xx_CMSIS_VERSION_SUB1 (0x06U) /*!< [23:16] sub1 version */ +#define __STM32F4xx_CMSIS_VERSION_SUB2 (0x00U) /*!< [15:8] sub2 version */ +#define __STM32F4xx_CMSIS_VERSION_RC (0x00U) /*!< [7:0] release candidate */ +#define __STM32F4xx_CMSIS_VERSION ((__STM32F4xx_CMSIS_VERSION_MAIN << 24)\ + |(__STM32F4xx_CMSIS_VERSION_SUB1 << 16)\ + |(__STM32F4xx_CMSIS_VERSION_SUB2 << 8 )\ + |(__STM32F4xx_CMSIS_VERSION)) + +/** + * @} + */ + +/** @addtogroup Device_Included + * @{ + */ + +#if defined(STM32F405xx) + #include "stm32f405xx.h" +#elif defined(STM32F415xx) + #include "stm32f415xx.h" +#elif defined(STM32F407xx) + #include "stm32f407xx.h" +#elif defined(STM32F417xx) + #include "stm32f417xx.h" +#elif defined(STM32F427xx) + #include "stm32f427xx.h" +#elif defined(STM32F437xx) + #include "stm32f437xx.h" +#elif defined(STM32F429xx) + #include "stm32f429xx.h" +#elif defined(STM32F439xx) + #include "stm32f439xx.h" +#elif defined(STM32F401xC) + #include "stm32f401xc.h" +#elif defined(STM32F401xE) + #include "stm32f401xe.h" +#elif defined(STM32F410Tx) + #include "stm32f410tx.h" +#elif defined(STM32F410Cx) + #include "stm32f410cx.h" +#elif defined(STM32F410Rx) + #include "stm32f410rx.h" +#elif defined(STM32F411xE) + #include "stm32f411xe.h" +#elif defined(STM32F446xx) + #include "stm32f446xx.h" +#elif defined(STM32F469xx) + #include "stm32f469xx.h" +#elif defined(STM32F479xx) + #include "stm32f479xx.h" +#elif defined(STM32F412Cx) + #include "stm32f412cx.h" +#elif defined(STM32F412Zx) + #include "stm32f412zx.h" +#elif defined(STM32F412Rx) + #include "stm32f412rx.h" +#elif defined(STM32F412Vx) + #include "stm32f412vx.h" +#elif defined(STM32F413xx) + #include "stm32f413xx.h" +#elif defined(STM32F423xx) + #include "stm32f423xx.h" +#else + #error "Please select first the target STM32F4xx device used in your application (in stm32f4xx.h file)" +#endif + +/** + * @} + */ + +/** @addtogroup Exported_types + * @{ + */ +typedef enum +{ + RESET = 0U, + SET = !RESET +} FlagStatus, ITStatus; + +typedef enum +{ + DISABLE = 0U, + ENABLE = !DISABLE +} FunctionalState; +#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE)) + +typedef enum +{ + ERROR = 0U, + SUCCESS = !ERROR +} ErrorStatus; + +/** + * @} + */ + + +/** @addtogroup Exported_macro + * @{ + */ +#define SET_BIT(REG, BIT) ((REG) |= (BIT)) + +#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) + +#define READ_BIT(REG, BIT) ((REG) & (BIT)) + +#define CLEAR_REG(REG) ((REG) = (0x0)) + +#define WRITE_REG(REG, VAL) ((REG) = (VAL)) + +#define READ_REG(REG) ((REG)) + +#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) + +#define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL))) + + +/** + * @} + */ + +#if defined (USE_HAL_DRIVER) + #include "stm32f4xx_hal.h" +#endif /* USE_HAL_DRIVER */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __STM32F4xx_H */ +/** + * @} + */ + +/** + * @} + */ + + + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/inc/stm32f4xx_hal_def.h b/board/inc/stm32f4xx_hal_def.h new file mode 100644 index 0000000000..c9f0baac28 --- /dev/null +++ b/board/inc/stm32f4xx_hal_def.h @@ -0,0 +1,214 @@ +/** + ****************************************************************************** + * @file stm32f4xx_hal_def.h + * @author MCD Application Team + * @version V1.6.0 + * @date 04-November-2016 + * @brief This file contains HAL common defines, enumeration, macros and + * structures definitions. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * 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. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_HAL_DEF +#define __STM32F4xx_HAL_DEF + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx.h" +//#include "Legacy/stm32_hal_legacy.h" +//#include + +/* Exported types ------------------------------------------------------------*/ + +/** + * @brief HAL Status structures definition + */ +typedef enum +{ + HAL_OK = 0x00U, + HAL_ERROR = 0x01U, + HAL_BUSY = 0x02U, + HAL_TIMEOUT = 0x03U +} HAL_StatusTypeDef; + +/** + * @brief HAL Lock structures definition + */ +typedef enum +{ + HAL_UNLOCKED = 0x00U, + HAL_LOCKED = 0x01U +} HAL_LockTypeDef; + +/* Exported macro ------------------------------------------------------------*/ +#define HAL_MAX_DELAY 0xFFFFFFFFU + +#define HAL_IS_BIT_SET(REG, BIT) (((REG) & (BIT)) != RESET) +#define HAL_IS_BIT_CLR(REG, BIT) (((REG) & (BIT)) == RESET) + +#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__) \ + do{ \ + (__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \ + (__DMA_HANDLE__).Parent = (__HANDLE__); \ + } while(0) + +#define UNUSED(x) ((void)(x)) + +/** @brief Reset the Handle's State field. + * @param __HANDLE__: specifies the Peripheral Handle. + * @note This macro can be used for the following purpose: + * - When the Handle is declared as local variable; before passing it as parameter + * to HAL_PPP_Init() for the first time, it is mandatory to use this macro + * to set to 0 the Handle's "State" field. + * Otherwise, "State" field may have any random value and the first time the function + * HAL_PPP_Init() is called, the low level hardware initialization will be missed + * (i.e. HAL_PPP_MspInit() will not be executed). + * - When there is a need to reconfigure the low level hardware: instead of calling + * HAL_PPP_DeInit() then HAL_PPP_Init(), user can make a call to this macro then HAL_PPP_Init(). + * In this later function, when the Handle's "State" field is set to 0, it will execute the function + * HAL_PPP_MspInit() which will reconfigure the low level hardware. + * @retval None + */ +#define __HAL_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = 0U) + +#if (USE_RTOS == 1) + /* Reserved for future use */ + #error "USE_RTOS should be 0 in the current HAL release" +#else + #define __HAL_LOCK(__HANDLE__) \ + do{ \ + if((__HANDLE__)->Lock == HAL_LOCKED) \ + { \ + return HAL_BUSY; \ + } \ + else \ + { \ + (__HANDLE__)->Lock = HAL_LOCKED; \ + } \ + }while (0) + + #define __HAL_UNLOCK(__HANDLE__) \ + do{ \ + (__HANDLE__)->Lock = HAL_UNLOCKED; \ + }while (0) +#endif /* USE_RTOS */ + +#if defined ( __GNUC__ ) + #ifndef __weak + #define __weak __attribute__((weak)) + #endif /* __weak */ + #ifndef __packed + #define __packed __attribute__((__packed__)) + #endif /* __packed */ +#endif /* __GNUC__ */ + + +/* Macro to get variable aligned on 4-bytes, for __ICCARM__ the directive "#pragma data_alignment=4" must be used instead */ +#if defined (__GNUC__) /* GNU Compiler */ + #ifndef __ALIGN_END + #define __ALIGN_END __attribute__ ((aligned (4))) + #endif /* __ALIGN_END */ + #ifndef __ALIGN_BEGIN + #define __ALIGN_BEGIN + #endif /* __ALIGN_BEGIN */ +#else + #ifndef __ALIGN_END + #define __ALIGN_END + #endif /* __ALIGN_END */ + #ifndef __ALIGN_BEGIN + #if defined (__CC_ARM) /* ARM Compiler */ + #define __ALIGN_BEGIN __align(4) + #elif defined (__ICCARM__) /* IAR Compiler */ + #define __ALIGN_BEGIN + #endif /* __CC_ARM */ + #endif /* __ALIGN_BEGIN */ +#endif /* __GNUC__ */ + + +/** + * @brief __RAM_FUNC definition + */ +#if defined ( __CC_ARM ) +/* ARM Compiler + ------------ + RAM functions are defined using the toolchain options. + Functions that are executed in RAM should reside in a separate source module. + Using the 'Options for File' dialog you can simply change the 'Code / Const' + area of a module to a memory space in physical RAM. + Available memory areas are declared in the 'Target' tab of the 'Options for Target' + dialog. +*/ +#define __RAM_FUNC HAL_StatusTypeDef + +#elif defined ( __ICCARM__ ) +/* ICCARM Compiler + --------------- + RAM functions are defined using a specific toolchain keyword "__ramfunc". +*/ +#define __RAM_FUNC __ramfunc HAL_StatusTypeDef + +#elif defined ( __GNUC__ ) +/* GNU Compiler + ------------ + RAM functions are defined using a specific toolchain attribute + "__attribute__((section(".RamFunc")))". +*/ +#define __RAM_FUNC HAL_StatusTypeDef __attribute__((section(".RamFunc"))) + +#endif + +/** + * @brief __NOINLINE definition + */ +#if defined ( __CC_ARM ) || defined ( __GNUC__ ) +/* ARM & GNUCompiler + ---------------- +*/ +#define __NOINLINE __attribute__ ( (noinline) ) + +#elif defined ( __ICCARM__ ) +/* ICCARM Compiler + --------------- +*/ +#define __NOINLINE _Pragma("optimize = no_inline") + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ___STM32F4xx_HAL_DEF */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/inc/stm32f4xx_hal_gpio_ex.h b/board/inc/stm32f4xx_hal_gpio_ex.h new file mode 100644 index 0000000000..50c6588462 --- /dev/null +++ b/board/inc/stm32f4xx_hal_gpio_ex.h @@ -0,0 +1,1591 @@ +/** + ****************************************************************************** + * @file stm32f4xx_hal_gpio_ex.h + * @author MCD Application Team + * @version V1.6.0 + * @date 04-November-2016 + * @brief Header file of GPIO HAL Extension module. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * 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. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_HAL_GPIO_EX_H +#define __STM32F4xx_HAL_GPIO_EX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx_hal_def.h" + +/** @addtogroup STM32F4xx_HAL_Driver + * @{ + */ + +/** @defgroup GPIOEx GPIOEx + * @{ + */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Constants GPIO Exported Constants + * @{ + */ + +/** @defgroup GPIO_Alternate_function_selection GPIO Alternate Function Selection + * @{ + */ + +/*------------------------------------------ STM32F429xx/STM32F439xx ---------*/ +#if defined(STM32F429xx) || defined(STM32F439xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_SPI5 ((uint8_t)0x05U) /* SPI5 Alternate Function mapping */ +#define GPIO_AF5_SPI6 ((uint8_t)0x05U) /* SPI6 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ +#define GPIO_AF6_SAI1 ((uint8_t)0x06U) /* SAI1 Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_UART7 ((uint8_t)0x08U) /* UART7 Alternate Function mapping */ +#define GPIO_AF8_UART8 ((uint8_t)0x08U) /* UART8 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_LTDC ((uint8_t)0x09U) /* LCD-TFT Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ + +/** + * @brief AF 14 selection + */ +#define GPIO_AF14_LTDC ((uint8_t)0x0EU) /* LCD-TFT Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F429xx || STM32F439xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F427xx/STM32F437xx------------------*/ +#if defined(STM32F427xx) || defined(STM32F437xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_SPI5 ((uint8_t)0x05U) /* SPI5 Alternate Function mapping */ +#define GPIO_AF5_SPI6 ((uint8_t)0x05U) /* SPI6 Alternate Function mapping */ +/** @brief GPIO_Legacy + */ +#define GPIO_AF5_I2S3ext GPIO_AF5_SPI3 /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ +#define GPIO_AF6_SAI1 ((uint8_t)0x06U) /* SAI1 Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_UART7 ((uint8_t)0x08U) /* UART7 Alternate Function mapping */ +#define GPIO_AF8_UART8 ((uint8_t)0x08U) /* UART8 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F427xx || STM32F437xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F407xx/STM32F417xx------------------*/ +#if defined(STM32F407xx) || defined(STM32F417xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FSMC ((uint8_t)0x0CU) /* FSMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F407xx || STM32F417xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F405xx/STM32F415xx------------------*/ +#if defined(STM32F405xx) || defined(STM32F415xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FSMC ((uint8_t)0x0CU) /* FSMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F405xx || STM32F415xx */ + +/*----------------------------------------------------------------------------*/ + +/*---------------------------------------- STM32F401xx------------------------*/ +#if defined(STM32F401xC) || defined(STM32F401xE) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_I2C2 ((uint8_t)0x09U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF9_I2C3 ((uint8_t)0x09U) /* I2C3 Alternate Function mapping */ + + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F401xC || STM32F401xE */ +/*----------------------------------------------------------------------------*/ + +/*--------------- STM32F412Zx/STM32F412Vx/STM32F412Rx/STM32F412Cx-------------*/ +#if defined(STM32F412Zx) || defined(STM32F412Vx) || defined(STM32F412Rx) || defined(STM32F412Cx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ +#define GPIO_AF4_FMPI2C1 ((uint8_t)0x04U) /* FMPI2C1 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1/I2S1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4/I2S4 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI2 ((uint8_t)0x06U) /* I2S2 Alternate Function mapping */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_SPI4 ((uint8_t)0x06U) /* SPI4/I2S4 Alternate Function mapping */ +#define GPIO_AF6_SPI5 ((uint8_t)0x06U) /* SPI5/I2S5 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ +#define GPIO_AF6_DFSDM1 ((uint8_t)0x06U) /* DFSDM1 Alternate Function mapping */ +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_SPI3 ((uint8_t)0x07U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_USART3 ((uint8_t)0x08U) /* USART3 Alternate Function mapping */ +#define GPIO_AF8_DFSDM1 ((uint8_t)0x08U) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF8_CAN1 ((uint8_t)0x08U) /* CAN1 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_I2C2 ((uint8_t)0x09U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF9_I2C3 ((uint8_t)0x09U) /* I2C3 Alternate Function mapping */ +#define GPIO_AF9_FMPI2C1 ((uint8_t)0x09U) /* FMPI2C1 Alternate Function mapping */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_QSPI ((uint8_t)0x09U) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_DFSDM1 ((uint8_t)0x0AU) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF10_QSPI ((uint8_t)0x0AU) /* QSPI Alternate Function mapping */ +#define GPIO_AF10_FMC ((uint8_t)0x0AU) /* FMC Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ +#define GPIO_AF12_FSMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F412Zx || STM32F412Vx || STM32F412Rx || STM32F412Cx */ + +/*----------------------------------------------------------------------------*/ + +/*--------------- STM32F413xx/STM32F423xx-------------------------------------*/ +#if defined(STM32F413xx) || defined(STM32F423xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ +#define GPIO_AF1_LPTIM1 ((uint8_t)0x01U) /* LPTIM1 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ +#define GPIO_AF3_DFSDM2 ((uint8_t)0x03U) /* DFSDM2 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ +#define GPIO_AF4_FMPI2C1 ((uint8_t)0x04U) /* FMPI2C1 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1/I2S1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4/I2S4 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI2 ((uint8_t)0x06U) /* I2S2 Alternate Function mapping */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_SPI4 ((uint8_t)0x06U) /* SPI4/I2S4 Alternate Function mapping */ +#define GPIO_AF6_SPI5 ((uint8_t)0x06U) /* SPI5/I2S5 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ +#define GPIO_AF6_DFSDM1 ((uint8_t)0x06U) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF6_DFSDM2 ((uint8_t)0x06U) /* DFSDM2 Alternate Function mapping */ +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_SPI3 ((uint8_t)0x07U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF7_SAI1 ((uint8_t)0x07U) /* SAI1 Alternate Function mapping */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ +#define GPIO_AF7_DFSDM2 ((uint8_t)0x07U) /* DFSDM2 Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_USART3 ((uint8_t)0x08U) /* USART3 Alternate Function mapping */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_UART7 ((uint8_t)0x08U) /* UART8 Alternate Function mapping */ +#define GPIO_AF8_UART8 ((uint8_t)0x08U) /* UART8 Alternate Function mapping */ +#define GPIO_AF8_DFSDM1 ((uint8_t)0x08U) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF8_CAN1 ((uint8_t)0x08U) /* CAN1 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_I2C2 ((uint8_t)0x09U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF9_I2C3 ((uint8_t)0x09U) /* I2C3 Alternate Function mapping */ +#define GPIO_AF9_FMPI2C1 ((uint8_t)0x09U) /* FMPI2C1 Alternate Function mapping */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_QSPI ((uint8_t)0x09U) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_SAI1 ((uint8_t)0x0AU) /* SAI1 Alternate Function mapping */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_DFSDM1 ((uint8_t)0x0AU) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF10_DFSDM2 ((uint8_t)0x0AU) /* DFSDM2 Alternate Function mapping */ +#define GPIO_AF10_QSPI ((uint8_t)0x0AU) /* QSPI Alternate Function mapping */ +#define GPIO_AF10_FSMC ((uint8_t)0x0AU) /* FSMC Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_UART4 ((uint8_t)0x0BU) /* UART4 Alternate Function mapping */ +#define GPIO_AF11_UART5 ((uint8_t)0x0BU) /* UART5 Alternate Function mapping */ +#define GPIO_AF11_UART9 ((uint8_t)0x0BU) /* UART9 Alternate Function mapping */ +#define GPIO_AF11_UART10 ((uint8_t)0x0BU) /* UART10 Alternate Function mapping */ +#define GPIO_AF11_CAN3 ((uint8_t)0x0BU) /* CAN3 Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ +#define GPIO_AF12_FSMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ + +/** + * @brief AF 14 selection + */ +#define GPIO_AF14_RNG ((uint8_t)0x0EU) /* RNG Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F413xx || STM32F423xx */ + +/*---------------------------------------- STM32F411xx------------------------*/ +#if defined(STM32F411xE) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1/I2S1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI2 ((uint8_t)0x06U) /* I2S2 Alternate Function mapping */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_SPI4 ((uint8_t)0x06U) /* SPI4/I2S4 Alternate Function mapping */ +#define GPIO_AF6_SPI5 ((uint8_t)0x06U) /* SPI5/I2S5 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_SPI3 ((uint8_t)0x07U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_I2C2 ((uint8_t)0x09U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF9_I2C3 ((uint8_t)0x09U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F411xE */ + +/*---------------------------------------- STM32F410xx------------------------*/ +#if defined(STM32F410Tx) || defined(STM32F410Cx) || defined(STM32F410Rx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_LPTIM1 ((uint8_t)0x01U) /* LPTIM1 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_FMPI2C1 ((uint8_t)0x04U) /* FMPI2C1 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1/I2S1 Alternate Function mapping */ +#if defined(STM32F410Cx) || defined(STM32F410Rx) +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#endif /* STM32F410Cx || STM32F410Rx */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI1 ((uint8_t)0x06U) /* SPI1 Alternate Function mapping */ +#if defined(STM32F410Cx) || defined(STM32F410Rx) +#define GPIO_AF6_SPI2 ((uint8_t)0x06U) /* I2S2 Alternate Function mapping */ +#endif /* STM32F410Cx || STM32F410Rx */ +#define GPIO_AF6_SPI5 ((uint8_t)0x06U) /* SPI5/I2S5 Alternate Function mapping */ +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_I2C2 ((uint8_t)0x09U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF9_FMPI2C1 ((uint8_t)0x09U) /* FMPI2C1 Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F410Tx || STM32F410Cx || STM32F410Rx */ + +/*---------------------------------------- STM32F446xx -----------------------*/ +#if defined(STM32F446xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ +#define GPIO_AF3_CEC ((uint8_t)0x03U) /* CEC Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ +#define GPIO_AF4_FMPI2C1 ((uint8_t)0x04U) /* FMPI2C1 Alternate Function mapping */ +#define GPIO_AF4_CEC ((uint8_t)0x04U) /* CEC Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1/I2S1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI2 ((uint8_t)0x06U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_SPI4 ((uint8_t)0x06U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF6_SAI1 ((uint8_t)0x06U) /* SAI1 Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_UART5 ((uint8_t)0x07U) /* UART5 Alternate Function mapping */ +#define GPIO_AF7_SPI2 ((uint8_t)0x07U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF7_SPI3 ((uint8_t)0x07U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF7_SPDIFRX ((uint8_t)0x07U) /* SPDIFRX Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_SPDIFRX ((uint8_t)0x08U) /* SPDIFRX Alternate Function mapping */ +#define GPIO_AF8_SAI2 ((uint8_t)0x08U) /* SAI2 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_QSPI ((uint8_t)0x09U) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ +#define GPIO_AF10_SAI2 ((uint8_t)0x0AU) /* SAI2 Alternate Function mapping */ +#define GPIO_AF10_QSPI ((uint8_t)0x0AU) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ + +#endif /* STM32F446xx */ +/*----------------------------------------------------------------------------*/ + +/*-------------------------------- STM32F469xx/STM32F479xx--------------------*/ +#if defined(STM32F469xx) || defined(STM32F479xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_SPI5 ((uint8_t)0x05U) /* SPI5 Alternate Function mapping */ +#define GPIO_AF5_SPI6 ((uint8_t)0x05U) /* SPI6 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ +#define GPIO_AF6_SAI1 ((uint8_t)0x06U) /* SAI1 Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_UART7 ((uint8_t)0x08U) /* UART7 Alternate Function mapping */ +#define GPIO_AF8_UART8 ((uint8_t)0x08U) /* UART8 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_LTDC ((uint8_t)0x09U) /* LCD-TFT Alternate Function mapping */ +#define GPIO_AF9_QSPI ((uint8_t)0x09U) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ +#define GPIO_AF10_QSPI ((uint8_t)0x0AU) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ +#define GPIO_AF13_DSI ((uint8_t)0x0DU) /* DSI Alternate Function mapping */ + +/** + * @brief AF 14 selection + */ +#define GPIO_AF14_LTDC ((uint8_t)0x0EU) /* LCD-TFT Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ + +#endif /* STM32F469xx || STM32F479xx */ +/*----------------------------------------------------------------------------*/ +/** + * @} + */ + +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Macros GPIO Exported Macros + * @{ + */ +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Functions GPIO Exported Functions + * @{ + */ +/** + * @} + */ + +/* Private types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private constants ---------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Constants GPIO Private Constants + * @{ + */ +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Macros GPIO Private Macros + * @{ + */ +/** @defgroup GPIOEx_Get_Port_Index GPIO Get Port Index + * @{ + */ +#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx) +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U :\ + ((__GPIOx__) == (GPIOD))? 3U :\ + ((__GPIOx__) == (GPIOE))? 4U :\ + ((__GPIOx__) == (GPIOF))? 5U :\ + ((__GPIOx__) == (GPIOG))? 6U :\ + ((__GPIOx__) == (GPIOH))? 7U : 8U) +#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx */ + +#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx) ||\ + defined(STM32F469xx) || defined(STM32F479xx) +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U :\ + ((__GPIOx__) == (GPIOD))? 3U :\ + ((__GPIOx__) == (GPIOE))? 4U :\ + ((__GPIOx__) == (GPIOF))? 5U :\ + ((__GPIOx__) == (GPIOG))? 6U :\ + ((__GPIOx__) == (GPIOH))? 7U :\ + ((__GPIOx__) == (GPIOI))? 8U :\ + ((__GPIOx__) == (GPIOJ))? 9U : 10U) +#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx */ + +#if defined(STM32F410Tx) || defined(STM32F410Cx) || defined(STM32F410Rx) +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U : 7U) +#endif /* STM32F410Tx || STM32F410Cx || STM32F410Rx */ + +#if defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F411xE) +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U :\ + ((__GPIOx__) == (GPIOD))? 3U :\ + ((__GPIOx__) == (GPIOE))? 4U : 7U) +#endif /* STM32F401xC || STM32F401xE || STM32F411xE */ + +#if defined(STM32F446xx) || defined(STM32F412Zx) ||defined(STM32F412Vx) || defined(STM32F412Rx) || defined(STM32F412Cx) || defined(STM32F413xx) || defined(STM32F423xx) +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U :\ + ((__GPIOx__) == (GPIOD))? 3U :\ + ((__GPIOx__) == (GPIOE))? 4U :\ + ((__GPIOx__) == (GPIOF))? 5U :\ + ((__GPIOx__) == (GPIOG))? 6U : 7U) +#endif /* STM32F446xx || STM32F412Zx || STM32F412Vx || STM32F412Rx || STM32F412Cx || STM32F413xx || STM32F423xx */ + +/** + * @} + */ + +/** @defgroup GPIOEx_IS_Alternat_function_selection GPIO Check Alternate Function + * @{ + */ +/*------------------------- STM32F429xx/STM32F439xx---------------------------*/ +#if defined(STM32F429xx) || defined(STM32F439xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF15_EVENTOUT) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF5_SPI5) || ((AF) == GPIO_AF5_SPI6) || \ + ((AF) == GPIO_AF8_UART7) || ((AF) == GPIO_AF8_UART8) || \ + ((AF) == GPIO_AF12_FMC) || ((AF) == GPIO_AF6_SAI1) || \ + ((AF) == GPIO_AF14_LTDC)) + +#endif /* STM32F429xx || STM32F439xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F427xx/STM32F437xx------------------*/ +#if defined(STM32F427xx) || defined(STM32F437xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF15_EVENTOUT) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF5_SPI5) || ((AF) == GPIO_AF5_SPI6) || \ + ((AF) == GPIO_AF8_UART7) || ((AF) == GPIO_AF8_UART8) || \ + ((AF) == GPIO_AF12_FMC) || ((AF) == GPIO_AF6_SAI1)) + +#endif /* STM32F427xx || STM32F437xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F407xx/STM32F417xx------------------*/ +#if defined(STM32F407xx) || defined(STM32F417xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF12_FSMC) || ((AF) == GPIO_AF15_EVENTOUT)) + +#endif /* STM32F407xx || STM32F417xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F405xx/STM32F415xx------------------*/ +#if defined(STM32F405xx) || defined(STM32F415xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF12_OTG_HS_FS) || ((AF) == GPIO_AF12_SDIO) || \ + ((AF) == GPIO_AF12_FSMC) || ((AF) == GPIO_AF15_EVENTOUT)) + +#endif /* STM32F405xx || STM32F415xx */ + +/*----------------------------------------------------------------------------*/ + +/*---------------------------------------- STM32F401xx------------------------*/ +#if defined(STM32F401xC) || defined(STM32F401xE) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF4_I2C1) || \ + ((AF) == GPIO_AF4_I2C2) || ((AF) == GPIO_AF4_I2C3) || \ + ((AF) == GPIO_AF5_SPI1) || ((AF) == GPIO_AF5_SPI2) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF8_USART6) || ((AF) == GPIO_AF10_OTG_FS) || \ + ((AF) == GPIO_AF9_I2C2) || ((AF) == GPIO_AF9_I2C3) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF15_EVENTOUT)) + +#endif /* STM32F401xC || STM32F401xE */ +/*----------------------------------------------------------------------------*/ +/*---------------------------------------- STM32F410xx------------------------*/ +#if defined(STM32F410Tx) || defined(STM32F410Cx) || defined(STM32F410Rx) +#define IS_GPIO_AF(AF) (((AF) < 10U) || ((AF) == 15U)) +#endif /* STM32F410Tx || STM32F410Cx || STM32F410Rx */ + +/*---------------------------------------- STM32F411xx------------------------*/ +#if defined(STM32F411xE) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF4_I2C1) || \ + ((AF) == GPIO_AF4_I2C2) || ((AF) == GPIO_AF4_I2C3) || \ + ((AF) == GPIO_AF5_SPI1) || ((AF) == GPIO_AF5_SPI2) || \ + ((AF) == GPIO_AF5_SPI3) || ((AF) == GPIO_AF6_SPI4) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF6_SPI5) || ((AF) == GPIO_AF7_SPI3) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF8_USART6) || ((AF) == GPIO_AF10_OTG_FS) || \ + ((AF) == GPIO_AF9_I2C2) || ((AF) == GPIO_AF9_I2C3) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF15_EVENTOUT)) + +#endif /* STM32F411xE */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------- STM32F446xx ----------------*/ +#if defined(STM32F446xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF15_EVENTOUT) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF12_FMC) || ((AF) == GPIO_AF6_SAI1) || \ + ((AF) == GPIO_AF3_CEC) || ((AF) == GPIO_AF4_CEC) || \ + ((AF) == GPIO_AF5_SPI3) || ((AF) == GPIO_AF6_SPI2) || \ + ((AF) == GPIO_AF6_SPI4) || ((AF) == GPIO_AF7_UART5) || \ + ((AF) == GPIO_AF7_SPI2) || ((AF) == GPIO_AF7_SPI3) || \ + ((AF) == GPIO_AF7_SPDIFRX) || ((AF) == GPIO_AF8_SPDIFRX) || \ + ((AF) == GPIO_AF8_SAI2) || ((AF) == GPIO_AF9_QSPI) || \ + ((AF) == GPIO_AF10_SAI2) || ((AF) == GPIO_AF10_QSPI)) + +#endif /* STM32F446xx */ +/*----------------------------------------------------------------------------*/ + +/*------------------------------------------- STM32F469xx/STM32F479xx --------*/ +#if defined(STM32F469xx) || defined(STM32F479xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF15_EVENTOUT) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF5_SPI5) || ((AF) == GPIO_AF5_SPI6) || \ + ((AF) == GPIO_AF8_UART7) || ((AF) == GPIO_AF8_UART8) || \ + ((AF) == GPIO_AF12_FMC) || ((AF) == GPIO_AF6_SAI1) || \ + ((AF) == GPIO_AF14_LTDC) || ((AF) == GPIO_AF13_DSI) || \ + ((AF) == GPIO_AF9_QSPI) || ((AF) == GPIO_AF10_QSPI)) + +#endif /* STM32F469xx || STM32F479xx */ +/*----------------------------------------------------------------------------*/ + +/*------------------STM32F412Zx/STM32F412Vx/STM32F412Rx/STM32F412Cx-----------*/ +#if defined(STM32F412Zx) || defined(STM32F412Vx) || defined(STM32F412Rx) || defined(STM32F412Cx) +#define IS_GPIO_AF(AF) (((AF) < 16U) && ((AF) != 11U) && ((AF) != 14U) && ((AF) != 13U)) +#endif /* STM32F412Zx || STM32F412Vx || STM32F412Rx || STM32F412Cx */ +/*----------------------------------------------------------------------------*/ + +/*------------------STM32F413xx/STM32F423xx-----------------------------------*/ +#if defined(STM32F413xx) || defined(STM32F423xx) +#define IS_GPIO_AF(AF) (((AF) < 16U) && ((AF) != 13U)) +#endif /* STM32F413xx || STM32F423xx */ +/*----------------------------------------------------------------------------*/ + +/** + * @} + */ + +/** + * @} + */ + +/* Private functions ---------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Functions GPIO Private Functions + * @{ + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F4xx_HAL_GPIO_EX_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/inc/system_stm32f2xx.h b/board/inc/system_stm32f2xx.h new file mode 100644 index 0000000000..7b3fdb881d --- /dev/null +++ b/board/inc/system_stm32f2xx.h @@ -0,0 +1,122 @@ +/** + ****************************************************************************** + * @file system_stm32f2xx.h + * @author MCD Application Team + * @version V2.1.2 + * @date 29-June-2016 + * @brief CMSIS Cortex-M3 Device System Source File for STM32F2xx devices. +****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * 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. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f2xx_system + * @{ + */ + +/** + * @brief Define to prevent recursive inclusion + */ +#ifndef __SYSTEM_STM32F2XX_H +#define __SYSTEM_STM32F2XX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** @addtogroup STM32F2xx_System_Includes + * @{ + */ + +/** + * @} + */ + + +/** @addtogroup STM32F2xx_System_Exported_types + * @{ + */ + /* This variable is updated in three ways: + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetSysClockFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency + Note: If you use this function to configure the system clock; then there + is no need to call the 2 first functions listed above, since SystemCoreClock + variable is updated automatically. + */ +extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ + + +/** + * @} + */ + +/** @addtogroup STM32F2xx_System_Exported_Constants + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F2xx_System_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F2xx_System_Exported_Functions + * @{ + */ + +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /*__SYSTEM_STM32F2XX_H */ + +/** + * @} + */ + +/** + * @} + */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/inc/system_stm32f4xx.h b/board/inc/system_stm32f4xx.h new file mode 100644 index 0000000000..369b605ddd --- /dev/null +++ b/board/inc/system_stm32f4xx.h @@ -0,0 +1,124 @@ +/** + ****************************************************************************** + * @file system_stm32f4xx.h + * @author MCD Application Team + * @version V2.6.0 + * @date 04-November-2016 + * @brief CMSIS Cortex-M4 Device System Source File for STM32F4xx devices. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * 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. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f4xx_system + * @{ + */ + +/** + * @brief Define to prevent recursive inclusion + */ +#ifndef __SYSTEM_STM32F4XX_H +#define __SYSTEM_STM32F4XX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** @addtogroup STM32F4xx_System_Includes + * @{ + */ + +/** + * @} + */ + + +/** @addtogroup STM32F4xx_System_Exported_types + * @{ + */ + /* This variable is updated in three ways: + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetSysClockFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency + Note: If you use this function to configure the system clock; then there + is no need to call the 2 first functions listed above, since SystemCoreClock + variable is updated automatically. + */ +extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ + +extern const uint8_t AHBPrescTable[16]; /*!< AHB prescalers table values */ +extern const uint8_t APBPrescTable[8]; /*!< APB prescalers table values */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Exported_Constants + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Exported_Functions + * @{ + */ + +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /*__SYSTEM_STM32F4XX_H */ + +/** + * @} + */ + +/** + * @} + */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/board/libc.h b/board/libc.h new file mode 100644 index 0000000000..564d29cd89 --- /dev/null +++ b/board/libc.h @@ -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(); + } +} + diff --git a/board/main.c b/board/main.c new file mode 100644 index 0000000000..e24df3818d --- /dev/null +++ b/board/main.c @@ -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; +} + diff --git a/board/obj/.placeholder b/board/obj/.placeholder new file mode 100644 index 0000000000..e69de29bb2 diff --git a/board/pedal/.gitignore b/board/pedal/.gitignore new file mode 100644 index 0000000000..94053f2925 --- /dev/null +++ b/board/pedal/.gitignore @@ -0,0 +1 @@ +obj/* diff --git a/board/pedal/Makefile b/board/pedal/Makefile new file mode 100644 index 0000000000..37b95f90fb --- /dev/null +++ b/board/pedal/Makefile @@ -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/* + diff --git a/board/pedal/README b/board/pedal/README new file mode 100644 index 0000000000..cf779db258 --- /dev/null +++ b/board/pedal/README @@ -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 + diff --git a/board/pedal/main.c b/board/pedal/main.c new file mode 100644 index 0000000000..4656db2831 --- /dev/null +++ b/board/pedal/main.c @@ -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; +} diff --git a/board/pedal/obj/.gitkeep b/board/pedal/obj/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/board/power_saving.h b/board/power_saving.h new file mode 100644 index 0000000000..986adf3ce2 --- /dev/null +++ b/board/power_saving.h @@ -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; + } +} diff --git a/board/provision.h b/board/provision.h new file mode 100644 index 0000000000..2fad513508 --- /dev/null +++ b/board/provision.h @@ -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); + } +} + diff --git a/board/safety.h b/board/safety.h new file mode 100644 index 0000000000..dd99e6db2e --- /dev/null +++ b/board/safety.h @@ -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; +} diff --git a/board/safety/safety_cadillac.h b/board/safety/safety_cadillac.h new file mode 100644 index 0000000000..ef114abfe9 --- /dev/null +++ b/board/safety/safety_cadillac.h @@ -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, +}; diff --git a/board/safety/safety_chrysler.h b/board/safety/safety_chrysler.h new file mode 100644 index 0000000000..19149b6b7a --- /dev/null +++ b/board/safety/safety_chrysler.h @@ -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, +}; diff --git a/board/safety/safety_defaults.h b/board/safety/safety_defaults.h new file mode 100644 index 0000000000..9dc23b54b7 --- /dev/null +++ b/board/safety/safety_defaults.h @@ -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, +}; diff --git a/board/safety/safety_elm327.h b/board/safety/safety_elm327.h new file mode 100644 index 0000000000..1f44e992a0 --- /dev/null +++ b/board/safety/safety_elm327.h @@ -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, +}; diff --git a/board/safety/safety_ford.h b/board/safety/safety_ford.h new file mode 100644 index 0000000000..21c9c54db1 --- /dev/null +++ b/board/safety/safety_ford.h @@ -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, +}; diff --git a/board/safety/safety_gm.h b/board/safety/safety_gm.h new file mode 100644 index 0000000000..949f5c7e73 --- /dev/null +++ b/board/safety/safety_gm.h @@ -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, +}; diff --git a/board/safety/safety_gm_ascm.h b/board/safety/safety_gm_ascm.h new file mode 100644 index 0000000000..d452818d69 --- /dev/null +++ b/board/safety/safety_gm_ascm.h @@ -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, +}; + diff --git a/board/safety/safety_honda.h b/board/safety/safety_honda.h new file mode 100644 index 0000000000..44a57ec978 --- /dev/null +++ b/board/safety/safety_honda.h @@ -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, +}; diff --git a/board/safety/safety_hyundai.h b/board/safety/safety_hyundai.h new file mode 100644 index 0000000000..c1b55359bd --- /dev/null +++ b/board/safety/safety_hyundai.h @@ -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, +}; diff --git a/board/safety/safety_subaru.h b/board/safety/safety_subaru.h new file mode 100644 index 0000000000..c7a8c20e52 --- /dev/null +++ b/board/safety/safety_subaru.h @@ -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, +}; diff --git a/board/safety/safety_tesla.h b/board/safety/safety_tesla.h new file mode 100644 index 0000000000..b58e6b2bbf --- /dev/null +++ b/board/safety/safety_tesla.h @@ -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, +}; diff --git a/board/safety/safety_toyota.h b/board/safety/safety_toyota.h new file mode 100644 index 0000000000..c4d5795631 --- /dev/null +++ b/board/safety/safety_toyota.h @@ -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, +}; diff --git a/board/safety/safety_toyota_ipas.h b/board/safety/safety_toyota_ipas.h new file mode 100644 index 0000000000..99e6dae058 --- /dev/null +++ b/board/safety/safety_toyota_ipas.h @@ -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, +}; diff --git a/board/safety_declarations.h b/board/safety_declarations.h new file mode 100644 index 0000000000..21e863869b --- /dev/null +++ b/board/safety_declarations.h @@ -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; diff --git a/board/spi_flasher.h b/board/spi_flasher.h new file mode 100644 index 0000000000..94d41c7e32 --- /dev/null +++ b/board/spi_flasher.h @@ -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); + } +} + diff --git a/board/startup_stm32f205xx.s b/board/startup_stm32f205xx.s new file mode 100644 index 0000000000..f4b6c6cb74 --- /dev/null +++ b/board/startup_stm32f205xx.s @@ -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 + * + *

© COPYRIGHT 2016 STMicroelectronics

+ * + * 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****/ diff --git a/board/startup_stm32f413xx.s b/board/startup_stm32f413xx.s new file mode 100644 index 0000000000..00b645d11b --- /dev/null +++ b/board/startup_stm32f413xx.s @@ -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 + * + *

© COPYRIGHT 2016 STMicroelectronics

+ * + * 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****/ diff --git a/board/stm32_flash.ld b/board/stm32_flash.ld new file mode 100644 index 0000000000..500319377e --- /dev/null +++ b/board/stm32_flash.ld @@ -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) } +} diff --git a/board/tests/test_rsa.c b/board/tests/test_rsa.c new file mode 100644 index 0000000000..f4a7d6be0f --- /dev/null +++ b/board/tests/test_rsa.c @@ -0,0 +1,35 @@ +/* +gcc -DTEST_RSA test_rsa.c ../crypto/rsa.c ../crypto/sha.c && ./a.out +*/ + +#include +#include + +#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; +} + diff --git a/board/tools/dfu-util-aarch64 b/board/tools/dfu-util-aarch64 new file mode 100755 index 0000000000..250c592ada Binary files /dev/null and b/board/tools/dfu-util-aarch64 differ diff --git a/board/tools/dfu-util-aarch64-linux b/board/tools/dfu-util-aarch64-linux new file mode 100755 index 0000000000..b366f869ac Binary files /dev/null and b/board/tools/dfu-util-aarch64-linux differ diff --git a/board/tools/dfu-util-x86_64-linux b/board/tools/dfu-util-x86_64-linux new file mode 100755 index 0000000000..7be3dc3a7e Binary files /dev/null and b/board/tools/dfu-util-x86_64-linux differ diff --git a/board/tools/enter_download_mode.py b/board/tools/enter_download_mode.py new file mode 100755 index 0000000000..ff3cf84ca9 --- /dev/null +++ b/board/tools/enter_download_mode.py @@ -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) diff --git a/boardesp/.gitignore b/boardesp/.gitignore new file mode 100644 index 0000000000..75255a5851 --- /dev/null +++ b/boardesp/.gitignore @@ -0,0 +1,8 @@ +proxy +*.bin +esp-open-sdk +a.out +cert.h +gitversion.h +esp-open-sdk.dmg +obj/* diff --git a/boardesp/ELM327.md b/boardesp/ELM327.md new file mode 100644 index 0000000000..6453b35ca4 --- /dev/null +++ b/boardesp/ELM327.md @@ -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 +``` diff --git a/boardesp/Makefile b/boardesp/Makefile new file mode 100644 index 0000000000..0b8fe32b69 --- /dev/null +++ b/boardesp/Makefile @@ -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/* diff --git a/boardesp/README.md b/boardesp/README.md new file mode 100644 index 0000000000..5a8fab1477 --- /dev/null +++ b/boardesp/README.md @@ -0,0 +1,22 @@ + +Dependencies +----- + +**Debian / Ubuntu** + +``` +./get_sdk.sh +``` + +**Mac** + +``` +./get_sdk_mac.sh +``` + +Programming +----- + +``` +make +``` diff --git a/boardesp/elm327.c b/boardesp/elm327.c new file mode 100644 index 0000000000..58ac4c8638 --- /dev/null +++ b/boardesp/elm327.c @@ -0,0 +1,1576 @@ +#include "ets_sys.h" +#include "osapi.h" +#include "gpio.h" +#include "os_type.h" +#include "user_interface.h" +#include "espconn.h" +#include "mem.h" + +#include "driver/uart.h" + +//#define ELM_DEBUG + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +int ICACHE_FLASH_ATTR spi_comm(char *dat, int len, uint32_t *recvData, int recvDataLen); + +#define ELM_PORT 35000 + +//Version 1.5 is an invalid version used by many pirate clones +//that only partially support 1.0. +#define IDENT_MSG "ELM327 v1.5\r\r" +#define DEVICE_DESC "Panda\n\n" + +#define SHOW_CONNECTION(msg, p_conn) os_printf("%s %p, proto %p, %d.%d.%d.%d:%d disconnect\r\n", \ + msg, p_conn, (p_conn)->proto.tcp, (p_conn)->proto.tcp->remote_ip[0], \ + (p_conn)->proto.tcp->remote_ip[1], (p_conn)->proto.tcp->remote_ip[2], \ + (p_conn)->proto.tcp->remote_ip[3], (p_conn)->proto.tcp->remote_port) + +const static char hex_lookup[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + +typedef struct __attribute__((packed)) { + bool tx : 1; + bool : 1; + bool ext : 1; + uint32_t addr : 29; + + uint8_t len : 4; + uint8_t bus : 8; + uint8_t : 4; //unused + uint16_t ts : 16; + uint8_t data[8]; +} panda_can_msg_t; + +//TODO: Masking is likely unnecessary for these bit fields. Check. +#define panda_get_can_addr(recv) (((recv)->ext) ? ((recv)->addr & 0x1FFFFFFF) :\ + (((recv)->addr >> 18) & 0x7FF)) + +#define PANDA_CAN_FLAG_TRANSMIT 1 +#define PANDA_CAN_FLAG_EXTENDED 4 + +#define PANDA_USB_CAN_WRITE_BUS_NUM 3 +#define PANDA_USB_LIN_WRITE_BUS_NUM 2 + +typedef struct _elm_tcp_conn { + struct espconn *conn; + struct _elm_tcp_conn *next; +} elm_tcp_conn_t; + +typedef struct __attribute__((packed)) { + uint8_t len; + uint8_t dat[7]; //mode and data +} elm_can_obd_msg; + +typedef struct __attribute__((packed)) { + uint8_t priority; + uint8_t receiver; + uint8_t sender; + + uint8_t dat[8]; //mode, data, and checksum +} elm_lin_obd_msg; + +typedef struct __attribute__((packed)) { + uint16_t usb_ep_num; + uint16_t payload_len; + + uint8_t serial_port; + //uint8_t msg[8+3]; + elm_lin_obd_msg msg; +} elm_lin_usb_msg; + +static struct espconn elm_conn; +static esp_tcp elm_proto; +static elm_tcp_conn_t *connection_list = NULL; + +static char stripped_msg[0x100]; +static uint16 stripped_msg_len = 0; + +static char in_msg[0x100]; +static uint16 in_msg_len = 0; + +static char rsp_buff[536]; //TCP min MTU +static uint16 rsp_buff_len = 0; + +static uint8_t pandaSendData[0x14] = {0}; +static uint32_t pandaRecvData[0x40] = {0}; +static uint32_t pandaRecvDataDummy[0x40] = {0}; // Used for CAN write operations (no received data) + +#define ELM_MODE_SELECTED_PROTOCOL_DEFAULT 6 +#define ELM_MODE_TIMEOUT_DEFAULT 20; +#define ELM_MODE_KEEPALIVE_PERIOD_DEFAULT (0x92*20) + +static bool elm_mode_echo = true; +static bool elm_mode_linefeed = false; +static bool elm_mode_additional_headers = false; +static bool elm_mode_auto_protocol = true; +static uint8_t elm_selected_protocol = ELM_MODE_SELECTED_PROTOCOL_DEFAULT; +static bool elm_mode_print_spaces = true; +static uint8_t elm_mode_adaptive_timing = 1; +static bool elm_mode_allow_long = false; +static uint16_t elm_mode_timeout = ELM_MODE_TIMEOUT_DEFAULT; +static uint16_t elm_mode_keepalive_period = ELM_MODE_KEEPALIVE_PERIOD_DEFAULT; + +bool lin_bus_initialized = false; + +/*********************************************** + *** ELM CLI response functions *** + *** (for sending data back to the terminal) *** + ***********************************************/ + +// All ELM operations are global, so send data out to all connections +void ICACHE_FLASH_ATTR elm_tcp_tx_flush() { + if(!rsp_buff_len) return; // Was causing small error messages + + for(elm_tcp_conn_t *iter = connection_list; iter != NULL; iter = iter->next){ + int8_t err = espconn_send(iter->conn, rsp_buff, rsp_buff_len); + if(err){ + os_printf(" Wifi %p TX error code %d\n", iter->conn, err); + if(err == ESPCONN_ARG) { + if(iter == connection_list) { + connection_list = iter->next; + } else { + for(elm_tcp_conn_t *iter2 = connection_list; iter2 != NULL; iter2 = iter2->next) + if(iter2->next == iter) { + iter2->next = iter->next; + break; + } + } + os_printf(" deleting orphaned connection. iter: %p; conn: %p\n", iter, iter->conn); + os_free(iter); + } + } + } + rsp_buff_len = 0; +} + +static void ICACHE_FLASH_ATTR elm_append_rsp(const char *data, uint16_t len) { + uint16_t overflow_len = 0; + if(rsp_buff_len + len > sizeof(rsp_buff)) { + overflow_len = rsp_buff_len + len - sizeof(rsp_buff); + len = sizeof(rsp_buff) - rsp_buff_len; + } + if(!elm_mode_linefeed) { + memcpy(rsp_buff + rsp_buff_len, data, len); + rsp_buff_len += len; + } else { + for(int i=0; i < len && rsp_buff_len < sizeof(rsp_buff); i++){ + rsp_buff[rsp_buff_len++] = data[i]; + if(data[i] == '\r' && rsp_buff_len < sizeof(rsp_buff)) + rsp_buff[rsp_buff_len++] = '\n'; + } + } + if(overflow_len) { + os_printf("Packet full, sending\n"); + elm_tcp_tx_flush(); + elm_append_rsp(data + len, overflow_len); + } +} + +#define elm_append_rsp_const(str) elm_append_rsp(str, sizeof(str)-1) + +static void ICACHE_FLASH_ATTR elm_append_rsp_hex_byte(uint8_t num) { + elm_append_rsp(&hex_lookup[num >> 4], 1); + elm_append_rsp(&hex_lookup[num & 0xF], 1); + if(elm_mode_print_spaces) elm_append_rsp_const(" "); +} + +void ICACHE_FLASH_ATTR elm_append_rsp_can_msg_addr(const panda_can_msg_t *recv) { + //Show address + uint32_t addr = panda_get_can_addr(recv); + if(recv->ext){ + elm_append_rsp_hex_byte(addr>>24); + elm_append_rsp_hex_byte(addr>>16); + elm_append_rsp_hex_byte(addr>>8); + elm_append_rsp_hex_byte(addr); + } else { + elm_append_rsp(&hex_lookup[addr>>8], 1); + elm_append_rsp_hex_byte(addr); + } +} + +/*************************************** + *** Panda communication functions *** + *** (for controlling the Panda MCU) *** + ***************************************/ + +static int ICACHE_FLASH_ATTR panda_usbemu_ctrl_write(uint8_t request_type, uint8_t request, + uint16_t value, uint16_t index, uint16_t length) { + //self.sock.send(struct.pack("HHBBHHH", 0, 0, request_type, request, value, index, length)); + *(uint16_t*)(pandaSendData) = 0; + *(uint16_t*)(pandaSendData+2) = 0; + pandaSendData[4] = request_type; + pandaSendData[5] = request; + *(uint16_t*)(pandaSendData+6) = value; + *(uint16_t*)(pandaSendData+8) = index; + *(uint16_t*)(pandaSendData+10) = length; + + int returned_count = spi_comm(pandaSendData, 0x10, pandaRecvData, 0x40); + if(returned_count > 0x40 || returned_count < 0) + return -1; + return returned_count; +} + +#define panda_set_can0_cbaud(cbps) panda_usbemu_ctrl_write(0x40, 0xde, 0, cbps, 0) +#define panda_set_can0_kbaud(kbps) panda_usbemu_ctrl_write(0x40, 0xde, 0, kbps*10, 0) +#define panda_set_safety_mode(mode) panda_usbemu_ctrl_write(0x40, 0xdc, mode, 0, 0) +#define panda_kline_wakeup_pulse() panda_usbemu_ctrl_write(0x40, 0xf0, 0, 0, 0) +#define panda_clear_can_rx() panda_usbemu_ctrl_write(0x40, 0xf1, 0xFFFF, 0, 0) +#define panda_clear_lin_txrx() panda_usbemu_ctrl_write(0x40, 0xf2, 2, 0, 0) + +static int ICACHE_FLASH_ATTR panda_usbemu_can_read(panda_can_msg_t** can_msgs) { + int returned_count = spi_comm((uint8_t *)((const uint16 []){1,0}), 4, pandaRecvData, 0x40); + if(returned_count > 0x40 || returned_count < 0){ + os_printf("CAN read got invalid length\n"); + return -1; + } + *can_msgs = (panda_can_msg_t*)(pandaRecvData+1); + return returned_count/sizeof(panda_can_msg_t); +} + +static int ICACHE_FLASH_ATTR panda_usbemu_can_write(bool ext, uint32_t addr, + char *candata, uint8_t canlen) { + uint32_t rir; + + if(canlen > 8) return 0; + + if(ext || addr >= 0x800){ + rir = (addr << 3) | PANDA_CAN_FLAG_TRANSMIT | PANDA_CAN_FLAG_EXTENDED; + }else{ + rir = (addr << 21) | PANDA_CAN_FLAG_TRANSMIT; + } + + #define MAX_CAN_LEN 8 + + //Wifi USB Wrapper + *(uint16_t*)(pandaSendData) = PANDA_USB_CAN_WRITE_BUS_NUM; //USB Bulk Endpoint ID. + *(uint16_t*)(pandaSendData+2) = MAX_CAN_LEN; + //BULK MESSAGE + *(uint32_t*)(pandaSendData+4) = rir; + *(uint32_t*)(pandaSendData+8) = MAX_CAN_LEN | (0 << 4); //0 is CAN bus number. + //CAN DATA + memcpy(pandaSendData+12, candata, canlen); + memset(pandaSendData+12+canlen, 0, MAX_CAN_LEN-canlen); + for(int i = 12+canlen; i < 20; i++) pandaSendData[i] = 0; //Zero the rest + + /* spi_comm will erase data in the recv buffer even if you are only + * interested in sending data that gets no response (like writing + * can data). This behavior becomes problematic when trying to send + * a can message while processsing received can messages. A dummy + * recv buffer is used here so received data is not overwritten. */ + int returned_count = spi_comm(pandaSendData, 0x14, pandaRecvDataDummy, 0x40); + if(returned_count) + os_printf("ELM Can send expected 0 bytes back from panda. Got %d bytes instead\n", returned_count); + if(returned_count > 0x40) return 0; + return returned_count; +} + +elm_lin_obd_msg lin_last_sent_msg; +uint16_t lin_last_sent_msg_len = 0; +bool lin_await_msg_echo = false; + +static int ICACHE_FLASH_ATTR panda_usbemu_kline_read(uint16_t len) { + int returned_count = panda_usbemu_ctrl_write(0xC0, 0xE0, 2, 0, len); + if(returned_count > len || returned_count < 0){ + os_printf("LIN read got invalid length\n"); + return -1; + } + + #ifdef ELM_DEBUG + if(returned_count) { + os_printf("LIN Received %d bytes\n", returned_count); + os_printf(" Data: "); + for(int i = 0; i < returned_count; i++) + os_printf("%02x ", ((char*)(pandaRecvData+1))[i]); + os_printf("\n"); + } + #endif + return returned_count; +} + +static int ICACHE_FLASH_ATTR panda_usbemu_kline_write(elm_lin_obd_msg *msg) { + elm_lin_usb_msg usb_msg = {}; + + usb_msg.usb_ep_num = PANDA_USB_LIN_WRITE_BUS_NUM; //USB Bulk Endpoint ID. + usb_msg.payload_len = (msg->priority & 0x07) + 4 + 1; //The +1 is for serial_port + usb_msg.serial_port = 2; + memcpy(&usb_msg.msg, msg, sizeof(elm_lin_obd_msg)); + + /* spi_comm will erase data in the recv buffer even if you are only + * interested in sending data that gets no response (like writing + * can data). This behavior becomes problematic when trying to send + * a can message while processsing received can messages. A dummy + * recv buffer is used here so received data is not overwritten. */ + int returned_count = spi_comm((char*)&usb_msg, sizeof(elm_lin_usb_msg), pandaRecvDataDummy, 0x40); + + if(returned_count) + os_printf("ELM LIN send expected 0 bytes back from panda. Got %d bytes instead\n", returned_count); + if(returned_count > 0x40) return 0; + + return returned_count; +} + +/**************************************** + *** Ringbuffer *** + ****************************************/ + +//LIN data is delivered in chunks of arbitrary size. Using a +//ringbuffer to handle it. +uint8_t lin_ringbuff[0x20]; +uint8_t lin_ringbuff_start = 0; +uint8_t lin_ringbuff_end = 0; +#define lin_ringbuff_len \ + (((sizeof(lin_ringbuff) + lin_ringbuff_end) - lin_ringbuff_start)% sizeof(lin_ringbuff)) +#define lin_ringbuff_get(index) (lin_ringbuff[(lin_ringbuff_start + index) % sizeof(lin_ringbuff)]) +#define lin_ringbuff_consume(len) lin_ringbuff_start = ((lin_ringbuff_start + len) % sizeof(lin_ringbuff)) +#define lin_ringbuff_clear()\ + {lin_ringbuff_start = 0; \ + lin_ringbuff_end = 0;} + +int ICACHE_FLASH_ATTR elm_LIN_ringbuff_memcmp(uint8_t *data, uint16_t len) { + if(len > lin_ringbuff_len) return 1; + for(int i = 0; i < len; i++) + if(lin_ringbuff_get(i) != data[i]) return 1; + return 0; // Going with memcpy ret format where 0 means 'equal' +} + +uint16_t ICACHE_FLASH_ATTR elm_LIN_read_into_ringbuff() { + int bytelen = panda_usbemu_kline_read((sizeof(lin_ringbuff) - lin_ringbuff_len) - 1); + if(bytelen < 0) return 0; + for(int j = 0; j < bytelen; j++) { + lin_ringbuff[lin_ringbuff_end % sizeof(lin_ringbuff)] = ((char*)(pandaRecvData+1))[j]; + lin_ringbuff_end = (lin_ringbuff_end + 1) % sizeof(lin_ringbuff); + if(lin_ringbuff_start == lin_ringbuff_end) lin_ringbuff_start++; + } + + #ifdef ELM_DEBUG + if(bytelen){ + os_printf(" RB Data (%d %d %d): ", lin_ringbuff_start, lin_ringbuff_end, lin_ringbuff_len); + for(int i = 0; i < sizeof(lin_ringbuff); i++) + os_printf("%02x ", lin_ringbuff[i]); + os_printf("\n"); + } + #endif + + return bytelen; +} + +/**************************************** + *** String parsing utility functions *** + ****************************************/ + +static int8_t ICACHE_FLASH_ATTR elm_decode_hex_char(char b){ + if(b >= '0' && b <= '9') return b - '0'; + if(b >= 'A' && b <= 'F') return (b - 'A') + 10; + if(b >= 'a' && b <= 'f') return (b - 'a') + 10; + return -1; +} + +static uint8_t ICACHE_FLASH_ATTR elm_decode_hex_byte(const char* data) { + return (elm_decode_hex_char(data[0]) << 4) | elm_decode_hex_char(data[1]); +} + +static bool ICACHE_FLASH_ATTR elm_check_valid_hex_chars(const char* data, uint8_t len) { + for(int i = 0; i < len; i++){ + char b = data[i]; + if(!((b >= '0' && b <= '9') || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f'))) + return 0; + } + return 1; +} + +static uint16_t ICACHE_FLASH_ATTR elm_strip(const char *data, uint16_t lenin, + char *outbuff, uint16_t outbufflen) { + uint16_t count = 0; + for(uint16_t i = 0; i < lenin; i++) { + if(count >= outbufflen) break; + if(data[i] == ' ') continue; + if(data[i] >= 'a' && data[i] <= 'z'){ + outbuff[count++] = data[i] - ('a' - 'A'); + } else { + outbuff[count++] = data[i]; + } + if(data[i] == '\r') break; + } + return count; +} + +static int ICACHE_FLASH_ATTR elm_msg_find_cr_or_eos(char *data, uint16_t len){ + uint16_t i; + for(i = 0; i < len; i++) + if(data[i] == '\r') { + i++; + break; + } + return i; +} + +/***************************************************** + *** ELM protocol specification and implementation *** + *****************************************************/ + +typedef enum { + AUTO, LIN, CAN11, CAN29, NA +} elm_proto_type_t; + +typedef struct elm_protocol { + bool supported; + elm_proto_type_t type; + uint16_t cbaud; //Centibaud (cbaud * 10 = kbaud) + void (*process_obd)(const struct elm_protocol*, const char*, uint16_t); + //init is used to init and de-init a protocol. Init functions should + //not do things that would leave a new protocol in an invalid state + //after the new protocol's init is called (e.g. No arming timers). + void (*init)(const struct elm_protocol*); + char* name; +} elm_protocol_t; + +static const elm_protocol_t* ICACHE_FLASH_ATTR elm_current_proto(); +void ICACHE_FLASH_ATTR elm_reset_aux_timer(); +static void ICACHE_FLASH_ATTR elm_autodetect_cb(bool); + +static const elm_protocol_t elm_protocols[]; +//(sizeof(elm_protocols)/sizeof(elm_protocol_t)) +#define ELM_PROTOCOL_COUNT 13 + +#define LOOPCOUNT_FULL 4 +static int loopcount = 0; +static volatile os_timer_t elm_timeout; +static volatile os_timer_t elm_proto_aux_timeout; + +static bool is_auto_detecting = false; + +// Used only by elm_timer_cb, so not volatile +static bool did_multimessage = false; +static bool got_msg_this_run = false; +static bool can_tx_worked = false; +static uint8_t elm_msg_mode_ret_filter; +static uint8_t elm_msg_pid_ret_filter; + +/***************************************************** + *** ELM protocol specification and implementation *** + *** -> SAE J1850 implementation (Unsupported) *** + *****************************************************/ + +static void ICACHE_FLASH_ATTR elm_process_obd_cmd_J1850(const elm_protocol_t* proto, + const char *cmd, uint16_t len) { + elm_append_rsp_const("NO DATA\r\r>"); +} + +/***************************************************** + *** ELM protocol specification and implementation *** + *** -> ISO 14230-4 implementation *** + *****************************************************/ + +const char *lin_cmd_backup = NULL; //Holds msg while bus init is done +uint16_t lin_cmd_backup_len = 0; +bool lin_waiting_keepalive_echo = false; + +static void ICACHE_FLASH_ATTR elm_process_obd_cmd_LIN5baud(const elm_protocol_t* proto, + const char *cmd, uint16_t len) { + elm_append_rsp_const("BUS INIT: ...ERROR\r\r>"); +} + +bool ICACHE_FLASH_ATTR elm_lin_keepalive_echo() { + if(lin_waiting_keepalive_echo) { + for(int pass = 0; pass < 4 && lin_ringbuff_len < 5; pass++) { + elm_LIN_read_into_ringbuff(); + } + + lin_waiting_keepalive_echo = false; + //keepalive Echo should always come before other message echo. + if(lin_ringbuff_len >= 5 && !elm_LIN_ringbuff_memcmp("\xc1\x33\xf1\x3e\x23", 5)){ + lin_ringbuff_consume(5); + return true; + } else { + os_printf("Keep alive echo failed\n"); + return false; + } + } + return true; +} + +void ICACHE_FLASH_ATTR elm_LINFast_keepalive_timer_cb(void *arg) { + if(!lin_bus_initialized) { + os_printf("WARNING! Elm LIN keepalive timer running while bus is not initialized\n"); + return; + } + if(loopcount) { + os_printf("WARNING! Elm LIN keepalive timer during a tx/rx loop!\n"); + return; + } + if(lin_ringbuff_len) { + os_printf("WARNING! lin_ringbuff_len should be 0 when a keepalive echo is processed.\n"); + return; + } + + if(!elm_lin_keepalive_echo()) { + lin_bus_initialized = false; + return; + } + + elm_lin_obd_msg msg = {}; + + msg.priority = 0xC0 | 1; + msg.receiver = 0x33; + msg.sender = 0xF1; + msg.dat[0] = 0x3E; + msg.dat[1] = msg.dat[0] + msg.priority + msg.receiver + msg.sender; // checksum + + #ifdef ELM_DEBUG + os_printf("Sending LIN KEEPALIVE: Priority: %02x; RecvAddr: %02x; SendAddr: %02x; (%02x); ", + msg.priority, msg.receiver, msg.sender, 1); + for(int i = 0; i < 2; i++) os_printf("%02x ", msg.dat[i]); + os_printf("\n"); + #endif + + lin_waiting_keepalive_echo = true; + + panda_usbemu_kline_write(&msg); + elm_reset_aux_timer(); +} + +static void ICACHE_FLASH_ATTR elm_init_LINFast(const elm_protocol_t* proto){ + os_timer_disarm(&elm_proto_aux_timeout); + os_timer_setfn(&elm_proto_aux_timeout, (os_timer_func_t *)elm_LINFast_keepalive_timer_cb, proto); + + lin_bus_initialized = false; + lin_await_msg_echo = false; + lin_waiting_keepalive_echo = false; + + lin_cmd_backup = NULL; + lin_cmd_backup_len = 0; + + lin_ringbuff_clear(); + panda_clear_lin_txrx(); +} + +int ICACHE_FLASH_ATTR elm_LINFast_process_echo() { + if(!elm_lin_keepalive_echo()) { + os_printf("Keepalive echo not detected.\n"); + lin_ringbuff_clear(); + return -1; + } + + if(!lin_await_msg_echo) { + os_printf("Echo abort. Nothing waiting echo\n"); + return 1; + } + + for(int i = 0; i < 4; i++){ + if(lin_ringbuff_len < lin_last_sent_msg_len) elm_LIN_read_into_ringbuff(); + + if(lin_ringbuff_len >= lin_last_sent_msg_len){ + #ifdef ELM_DEBUG + os_printf("Got enough data %d\n", lin_last_sent_msg_len); + #endif + if(!elm_LIN_ringbuff_memcmp((uint8_t*)&lin_last_sent_msg, lin_last_sent_msg_len)) { + #ifdef ELM_DEBUG + os_printf("LIN data was sent successfully.\n"); + #endif + lin_ringbuff_consume(lin_last_sent_msg_len); + lin_await_msg_echo = false; + return 1; + } else { + #ifdef ELM_DEBUG + os_printf("Echo not correct.\n"); + os_printf(" RB Data (%d %d %d): ", lin_ringbuff_start, lin_ringbuff_end, lin_ringbuff_len); + for(int i = 0; i < sizeof(lin_ringbuff); i++) + os_printf("%02x ", lin_ringbuff[i]); + os_printf("\n"); + os_printf(" MSG Data (%d): ", lin_last_sent_msg_len); + for(int i = 0; i < lin_last_sent_msg_len; i++) + os_printf("%02x ", ((uint8_t*)&lin_last_sent_msg)[i]); + os_printf("\n"); + #endif + + if(lin_bus_initialized || loopcount == 0 && i == 4) { + lin_ringbuff_clear(); + return -1; + } else { + os_printf("Lin init echo misaligned? Consuming byte (%02x). Retry.\n", lin_ringbuff_get(0)); + lin_ringbuff_consume(1); + continue; + } + } + } + } + + return !lin_await_msg_echo; //true if echo handled +} + +void ICACHE_FLASH_ATTR elm_LINFast_timer_cb(void *arg){ + const elm_protocol_t* proto = (const elm_protocol_t*) arg; + loopcount--; + #ifdef ELM_DEBUG + os_printf("LIN CB call\n"); + #endif + + if(!lin_bus_initialized) { + os_printf("WARNING: LIN CB called without bus initialized!"); + return; // TODO: shoulnd't ever happen. Handle? + } + + int echo_result = elm_LINFast_process_echo(); + + if(echo_result == -1 || (echo_result == 0 && loopcount == 0)) { + if(!is_auto_detecting){ + elm_append_rsp_const("BUS ERROR\r\r>"); + elm_tcp_tx_flush(); + } + loopcount = 0; + lin_bus_initialized = false; + return; + } + + if(echo_result == 0) { + #ifdef ELM_DEBUG + os_printf("Not ready to process\n"); + #endif + os_timer_arm(&elm_timeout, 30, 0); + return; // Not ready to go on + } + + #ifdef ELM_DEBUG + os_printf("Processing ELM %d\n", lin_ringbuff_len); + #endif + + if(loopcount>0) { + for(int pass = 0; pass < 16 && loopcount; pass++){ + elm_LIN_read_into_ringbuff(); + + while(lin_ringbuff_len > 0){ + //if(lin_ringbuff_len > 0){ + if(lin_ringbuff_get(0) & 0x80 != 0x80){ + os_printf("Resetting LIN bus due to bad first byte.\n"); + loopcount = 0; + lin_bus_initialized = false; + lin_ringbuff_clear(); + + if(!is_auto_detecting){ + elm_append_rsp_const("ERROR\r\r>"); + elm_tcp_tx_flush(); + } + return; + } + + uint8_t newmsg_len = 4 + (lin_ringbuff_get(0) & 0x7); + if(lin_ringbuff_len >= newmsg_len) { + #ifdef ELM_DEBUG + os_printf("Processing LIN MSG. BuffLen %d; expect %d. Dat: ", lin_ringbuff_len, newmsg_len); + for(int i = 0; i < newmsg_len; i++) os_printf("%02x ", lin_ringbuff_get(i)); + os_printf("\n"); + #endif + got_msg_this_run = true; + loopcount = LOOPCOUNT_FULL; + + if(!is_auto_detecting){ + if(elm_mode_additional_headers){ + for(int i = 0; i < newmsg_len; i++) elm_append_rsp_hex_byte(lin_ringbuff_get(i)); + } else { + for(int i = 3; i < newmsg_len - 1; i++) elm_append_rsp_hex_byte(lin_ringbuff_get(i)); + } + elm_append_rsp_const("\r"); + } + + lin_ringbuff_consume(newmsg_len); + //elm_reset_aux_timer(); + } else { + break; //Stop consuming data if there is not enough data for the next msg. + } + } + } + os_timer_arm(&elm_timeout, 50, 0); + } else { + bool got_msg_this_run_backup = got_msg_this_run; + if(!got_msg_this_run) { + #ifdef ELM_DEBUG + os_printf(" No data collected\n"); + #endif + if(!is_auto_detecting) { + elm_append_rsp_const("NO DATA\r"); + } + } + got_msg_this_run = false; + + if(!is_auto_detecting) { + elm_append_rsp_const("\r>"); + elm_tcp_tx_flush(); + } else { + elm_autodetect_cb(got_msg_this_run_backup); + } + + //TX RX over, resume Keepalive timer + elm_reset_aux_timer(); + } +} + +void ICACHE_FLASH_ATTR elm_LINFast_businit_timer_cb(void *arg){ + const elm_protocol_t* proto = (const elm_protocol_t*) arg; + loopcount--; + #ifdef ELM_DEBUG + os_printf("LIN INIT CB call\n"); + #endif + + int echo_result = elm_LINFast_process_echo(); + + if(echo_result == -1 || (echo_result == 0 && loopcount == 0)) { + #ifdef ELM_DEBUG + os_printf("Init failed with echo test\n"); + #endif + + loopcount = 0; + lin_bus_initialized = 0; + + if(!is_auto_detecting){ + if(echo_result == -1) + elm_append_rsp_const("BUS ERROR\r\r>"); + else + elm_append_rsp_const("ERROR\r\r>"); + elm_tcp_tx_flush(); + } else { + elm_autodetect_cb(false); + } + return; + } + + if(echo_result == 0) { + #ifdef ELM_DEBUG + os_printf("Not ready to process\n"); + #endif + os_timer_arm(&elm_timeout, elm_mode_timeout, 0); + return; // Not ready to go on + } + + #ifdef ELM_DEBUG + os_printf("Bus init ready to process %d bytes\n", lin_ringbuff_len); + #endif + + if(lin_bus_initialized) return; // TODO: shoulnd't ever happen. Handle? + + if(loopcount>0) { + //Keep waiting for response + for(int i = 0; i < 4; i++){ + elm_LIN_read_into_ringbuff(); + + if(lin_ringbuff_len > 0){ + if(lin_ringbuff_get(0) & 0x80 != 0x80){ + os_printf("Resetting LIN bus due to bad first byte.\n"); + loopcount = 0; + lin_ringbuff_clear(); + + if(!is_auto_detecting){ + elm_append_rsp_const("ERROR\r\r>"); + elm_tcp_tx_flush(); + } else { + elm_autodetect_cb(false); + } + return; + } + + uint8_t newmsg_len = 4 + (lin_ringbuff_get(0) & 0x7); + if(lin_ringbuff_len < newmsg_len) { + os_printf("Resetting LIN because returned init data was wrong.\n"); + loopcount = 0; + lin_ringbuff_clear(); + + if(!is_auto_detecting){ + elm_append_rsp_const("ERROR\r\r>"); + elm_tcp_tx_flush(); + } else { + elm_autodetect_cb(false); + } + return; + } + + if(!elm_LIN_ringbuff_memcmp("\x83\xF1\x10\xC1\x8F\xE9\xBD", 7)) { + lin_ringbuff_consume(7); + lin_bus_initialized = true; + //lin_ringbuff_clear(); + + os_printf("BUS INITIALIZED\n"); + + elm_reset_aux_timer(); + + if(!is_auto_detecting) { + elm_append_rsp_const("OK\r"); + + //Do the send that was delayed + if(lin_cmd_backup_len) { + elm_tcp_tx_flush(); + proto->process_obd(proto, lin_cmd_backup, lin_cmd_backup_len); + } else { + elm_append_rsp_const("\r>"); + elm_tcp_tx_flush(); + } + } else { + #ifdef ELM_DEBUG + os_printf("LIN success. Silent because in autodetect.\n"); + #endif + elm_autodetect_cb(true); + // TODO: Since bus init is good, is it ok to skip sending the '0100' msg? + } + return; + } + } + } + os_timer_arm(&elm_timeout, elm_mode_timeout, 0); + } else { + #ifdef ELM_DEBUG + os_printf("Fall through on bus init\n"); + #endif + if(!is_auto_detecting){ + elm_append_rsp_const("ERROR\r\r>"); + elm_tcp_tx_flush(); + } else { + elm_autodetect_cb(false); + } + elm_reset_aux_timer(); + } +} + +static void ICACHE_FLASH_ATTR elm_process_obd_cmd_LINFast(const elm_protocol_t* proto, + const char *cmd, uint16_t len) { + elm_lin_obd_msg msg = {}; + uint8_t bytelen = (len-1)/2; + if((bytelen > 7 && !elm_mode_allow_long) || bytelen > 8) { + elm_append_rsp_const("?\r\r>"); + return; + } + + os_timer_disarm(&elm_proto_aux_timeout); + + if(!lin_bus_initialized) { + panda_clear_lin_txrx(); + + if(!is_auto_detecting) + elm_append_rsp_const("BUS INIT: "); + + lin_cmd_backup = cmd; + lin_cmd_backup_len = len; + + bytelen = 1; + msg.dat[0] = 0x81; + msg.dat[1] = 0x81; // checksum + + panda_kline_wakeup_pulse(); + } else { + bytelen = MIN(bytelen, 7); + for(int i = 0; i < bytelen; i++){ + msg.dat[i] = elm_decode_hex_byte(&cmd[i*2]); + msg.dat[bytelen] += msg.dat[i]; + } + + elm_msg_mode_ret_filter = msg.dat[0]; + elm_msg_pid_ret_filter = msg.dat[1]; + } + + msg.priority = 0xC0 | bytelen; + msg.receiver = 0x33; + msg.sender = 0xF1; + msg.dat[bytelen] += msg.priority + msg.receiver + msg.sender; // checksum + + #ifdef ELM_DEBUG + os_printf("Sending LIN OBD: Priority: %02x; RecvAddr: %02x; SendAddr: %02x; (%02x); ", + msg.priority, msg.receiver, msg.sender, bytelen); + for(int i = 0; i < 8; i++) os_printf("%02x ", msg.dat[i]); + os_printf("\n"); + #endif + + lin_last_sent_msg_len = (msg.priority & 0x07) + 4; + memcpy(&lin_last_sent_msg, &msg, lin_last_sent_msg_len); + lin_await_msg_echo = true; + panda_usbemu_kline_write(&msg); + + loopcount = LOOPCOUNT_FULL + 1; + os_timer_disarm(&elm_timeout); + + if(lin_bus_initialized) { + os_timer_setfn(&elm_timeout, (os_timer_func_t *)elm_LINFast_timer_cb, proto); + elm_LINFast_timer_cb((void*)proto); + } else { + os_timer_setfn(&elm_timeout, (os_timer_func_t *)elm_LINFast_businit_timer_cb, proto); + elm_LINFast_businit_timer_cb((void*)proto); + } +} + +/***************************************************** + *** ELM protocol specification and implementation *** + *** -> ISO 15765-4 implementation *** + *****************************************************/ + +void ICACHE_FLASH_ATTR elm_ISO15765_timer_cb(void *arg){ + const elm_protocol_t* proto = (const elm_protocol_t*) arg; + loopcount--; + if(loopcount>0) { + for(int pass = 0; pass < 16 && loopcount; pass++){ + panda_can_msg_t *can_msgs; + int num_can_msgs = panda_usbemu_can_read(&can_msgs); + + #ifdef ELM_DEBUG + if(num_can_msgs) os_printf(" Received %d can messages\n", num_can_msgs); + #endif + + if(num_can_msgs < 0) continue; + if(!num_can_msgs) break; + + for(int i = 0; i < num_can_msgs; i++){ + + panda_can_msg_t *recv = &can_msgs[i]; + + #ifdef ELM_DEBUG + os_printf(" RECV: Bus: %d; Addr: %08x; ext: %d; tx: %d; Len: %d; ", + recv->bus, panda_get_can_addr(recv), recv->ext, recv->tx, recv->len); + for(int j = 0; j < recv->len; j++) os_printf("%02x ", recv->data[j]); + os_printf("Ts: %d\n", recv->ts); + #endif + + if (recv->bus==0 && recv->len == 8 && + ( + (proto->type == CAN11 && !recv->ext && (panda_get_can_addr(recv) & 0x7F8) == 0x7E8) || + (proto->type == CAN29 && recv->ext && (panda_get_can_addr(recv) & 0x1FFFFF00) == 0x18DAF100) + ) + ) { + if(recv->data[0] <= 7 && + recv->data[1] == (0x40|elm_msg_mode_ret_filter) && + recv->data[2] == elm_msg_pid_ret_filter) { + got_msg_this_run = true; + loopcount = LOOPCOUNT_FULL; + + #ifdef ELM_DEBUG + os_printf(" CAN msg response, index: %d\n", i); + #endif + + if(!is_auto_detecting){ + if(elm_mode_additional_headers){ + elm_append_rsp_can_msg_addr(recv); + for(int j = 0; j < recv->data[0]+1; j++) elm_append_rsp_hex_byte(recv->data[j]); + } else { + for(int j = 1; j < recv->data[0]+1; j++) elm_append_rsp_hex_byte(recv->data[j]); + } + + elm_append_rsp_const("\r"); + elm_tcp_tx_flush(); + } + + } else if((recv->data[0] & 0xF0) == 0x10 && + recv->data[2] == (0x40|elm_msg_mode_ret_filter) && + recv->data[3] == elm_msg_pid_ret_filter) { + got_msg_this_run = true; + loopcount = LOOPCOUNT_FULL; + panda_usbemu_can_write(0, + (proto->type==CAN11) ? + 0x7E0 | (panda_get_can_addr(recv)&0x7) : + (0x18DA00F1 | (((panda_get_can_addr(recv))&0xFF)<<8)), + "\x30\x00\x00", 3); + + did_multimessage = true; + + #ifdef ELM_DEBUG + os_printf(" CAN multimsg start response, index: %d, len %d\n", i, + ((recv->data[0]&0xF)<<8) | recv->data[1]); + #endif + + if(!is_auto_detecting){ + if(!elm_mode_additional_headers) { + elm_append_rsp(&hex_lookup[recv->data[0]&0xF], 1); + elm_append_rsp_hex_byte(recv->data[1]); + elm_append_rsp_const("\r0:"); + if(elm_mode_print_spaces) elm_append_rsp_const(" "); + for(int j = 2; j < 8; j++) elm_append_rsp_hex_byte(recv->data[j]); + } else { + elm_append_rsp_can_msg_addr(recv); + for(int j = 0; j < 8; j++) elm_append_rsp_hex_byte(recv->data[j]); + } + + elm_append_rsp_const("\r"); + elm_tcp_tx_flush(); + } + + } else if (did_multimessage && (recv->data[0] & 0xF0) == 0x20) { + got_msg_this_run = true; + loopcount = LOOPCOUNT_FULL; + #ifdef ELM_DEBUG + os_printf(" CAN multimsg data response, index: %d\n", i); + #endif + + if(!is_auto_detecting){ + if(!elm_mode_additional_headers) { + elm_append_rsp(&hex_lookup[recv->data[0] & 0xF], 1); + elm_append_rsp_const(":"); + if(elm_mode_print_spaces) elm_append_rsp_const(" "); + for(int j = 1; j < 8; j++) elm_append_rsp_hex_byte(recv->data[j]); + } else { + elm_append_rsp_can_msg_addr(recv); + for(int j = 0; j < 8; j++) elm_append_rsp_hex_byte(recv->data[j]); + } + elm_append_rsp_const("\r"); + } + } + } else if (recv->bus == 0x80 && recv->len == 8 && + (panda_get_can_addr(recv) == ((proto->type==CAN11) ? 0x7DF : 0x18DB33F1)) + ) { + //Can send receipt + #ifdef ELM_DEBUG + os_printf(" Got CAN tx receipt\n"); + #endif + can_tx_worked = true; + } + } + } + os_timer_arm(&elm_timeout, elm_mode_timeout, 0); + } else { + bool got_msg_this_run_backup = got_msg_this_run; + if(did_multimessage) { + os_printf(" End of multi message\n"); + } else if(!got_msg_this_run) { + os_printf(" No data collected\n"); + if(!is_auto_detecting) { + if(can_tx_worked) { + elm_append_rsp_const("NO DATA\r"); + } else { + elm_append_rsp_const("CAN ERROR\r"); + } + } + } + did_multimessage = false; + got_msg_this_run = false; + can_tx_worked = false; + + if(!is_auto_detecting) { + elm_append_rsp_const("\r>"); + elm_tcp_tx_flush(); + } else { + elm_autodetect_cb(got_msg_this_run_backup); + } + } +} + +static void ICACHE_FLASH_ATTR elm_init_ISO15765(const elm_protocol_t* proto){ + panda_set_can0_cbaud(proto->cbaud); +} + +static void ICACHE_FLASH_ATTR elm_process_obd_cmd_ISO15765(const elm_protocol_t* proto, + const char *cmd, uint16_t len) { + elm_can_obd_msg msg = {}; + msg.len = (len-1)/2; + if((msg.len > 7 && !elm_mode_allow_long) || msg.len > 8) { + elm_append_rsp_const("?\r\r>"); + return; + } + + msg.len = MIN(msg.len, 7); + + for(int i = 0; i < msg.len; i++) + msg.dat[i] = elm_decode_hex_byte(&cmd[i*2]); + + elm_msg_mode_ret_filter = msg.dat[0]; + elm_msg_pid_ret_filter = msg.dat[1]; + + #ifdef ELM_DEBUG + os_printf("Sending CAN OBD: %02x; ", msg.len); + for(int i = 0; i < 7; i++) + os_printf("%02x ", msg.dat[i]); + os_printf("\n"); + #endif + + panda_clear_can_rx(); + + panda_usbemu_can_write(0, (proto->type==CAN11) ? 0x7DF : 0x18DB33F1, + (uint8_t*)&msg, msg.len+1); + + #ifdef ELM_DEBUG + os_printf("Starting up timer\n"); + #endif + + loopcount = LOOPCOUNT_FULL; + os_timer_disarm(&elm_timeout); + os_timer_setfn(&elm_timeout, (os_timer_func_t *)elm_ISO15765_timer_cb, proto); + os_timer_arm(&elm_timeout, elm_mode_timeout, 0); +} + +/***************************************************** + *** ELM protocol specification and implementation *** + *** -> Stuf for unsupported CAN protocols *** + *****************************************************/ + +static void ICACHE_FLASH_ATTR elm_process_obd_cmd_CANGen(const elm_protocol_t* proto, + const char *cmd, uint16_t len) { + elm_append_rsp_const("NO DATA\r\r>"); +} + +/***************************************************** + *** ELM protocol specification and implementation *** + *** -> AUTO Detect implementation *** + *****************************************************/ + +static int elm_autodetect_proto_iter; +static uint16_t elm_staged_auto_msg_len; +static const char* elm_staged_auto_msg; + +static void ICACHE_FLASH_ATTR elm_autodetect_cb(bool proto_worked){ + if(proto_worked) { + os_printf("Autodetect proto success\n"); + is_auto_detecting = false; + elm_selected_protocol = elm_autodetect_proto_iter; + elm_current_proto()->process_obd(elm_current_proto(), + elm_staged_auto_msg, elm_staged_auto_msg_len); + } else { + for(elm_autodetect_proto_iter++; elm_autodetect_proto_iter < ELM_PROTOCOL_COUNT; + elm_autodetect_proto_iter++){ + const elm_protocol_t *proto = &elm_protocols[elm_autodetect_proto_iter]; + if(proto->supported && proto->type != AUTO) { + os_printf("*** AUTO trying '%s'\n", proto->name); + proto->init(proto); + proto->process_obd(proto, "0100\r", 5); // Try sending on the bus + return; + } + } + + //if(elm_autodetect_main()) return; + is_auto_detecting = false; + os_printf("Autodetect failed\n"); + elm_append_rsp_const("UNABLE TO CONNECT\r\r>"); + elm_tcp_tx_flush(); + } +} + +static void ICACHE_FLASH_ATTR elm_process_obd_cmd_AUTO(const elm_protocol_t* proto, + const char *cmd, uint16_t len) { + elm_append_rsp_const("SEARCHING...\r"); + elm_staged_auto_msg_len = len; + elm_staged_auto_msg = cmd; + is_auto_detecting = true; + + elm_autodetect_proto_iter = 0; + elm_autodetect_cb(false); +} + +/***************************************************** + *** ELM protocol specification and implementation *** + *** -> Protocol Registry and related functions. *** + *****************************************************/ + +static const elm_protocol_t elm_protocols[] = { + {true, AUTO, 0, elm_process_obd_cmd_AUTO, NULL, "AUTO", }, + {false, NA, 416, elm_process_obd_cmd_J1850, NULL, "SAE J1850 PWM", }, + {false, NA, 104, elm_process_obd_cmd_J1850, NULL, "SAE J1850 VPW", }, + {false, LIN, 104, elm_process_obd_cmd_LIN5baud, NULL, "ISO 9141-2", }, + {false, LIN, 104, elm_process_obd_cmd_LIN5baud, NULL, "ISO 14230-4 (KWP 5BAUD)", }, + {true, LIN, 104, elm_process_obd_cmd_LINFast, NULL, "ISO 14230-4 (KWP FAST)", }, + {true, CAN11, 5000, elm_process_obd_cmd_ISO15765, elm_init_ISO15765, "ISO 15765-4 (CAN 11/500)",}, + {true, CAN29, 5000, elm_process_obd_cmd_ISO15765, elm_init_ISO15765, "ISO 15765-4 (CAN 29/500)",}, + {true, CAN11, 2500, elm_process_obd_cmd_ISO15765, elm_init_ISO15765, "ISO 15765-4 (CAN 11/250)",}, + {true, CAN29, 2500, elm_process_obd_cmd_ISO15765, elm_init_ISO15765, "ISO 15765-4 (CAN 29/250)",}, + {false, CAN29, 2500, elm_process_obd_cmd_CANGen, NULL, "SAE J1939 (CAN 29/250)", }, + {false, CAN11, 1250, elm_process_obd_cmd_CANGen, NULL, "USER1 (CAN 11/125)", }, + {false, CAN11, 500, elm_process_obd_cmd_CANGen, NULL, "USER2 (CAN 11/50)", }, +}; + +static const elm_protocol_t* ICACHE_FLASH_ATTR elm_current_proto() { + return &elm_protocols[elm_selected_protocol]; +} + +void ICACHE_FLASH_ATTR elm_reset_aux_timer() { + os_timer_disarm(&elm_proto_aux_timeout); + if(elm_mode_keepalive_period) + os_timer_arm(&elm_proto_aux_timeout, elm_mode_keepalive_period, 0); +} + +void ICACHE_FLASH_ATTR elm_proto_reinit(const elm_protocol_t *proto) { + if(proto->init) proto->init(proto); +} + +/******************************************* + *** ELM AT command parsing and handling *** + *******************************************/ + +enum at_cmd_ids_t { // FULL ELM 1.0 list + AT_INVALID, //Fake + + AT_AMP1, + AT_AL, + AT_AT0, AT_AT1, AT_AT2, // Added ELM 1.2, expected by Torque + AT_BD, + AT_BI, + AT_CAF0, AT_CAF1, + AT_CF_8, AT_CF_3, + AT_CFC0, AT_CFC1, + AT_CM_8, AT_CM_3, + AT_CP, + AT_CS, + AT_CV, + AT_D, + AT_DP, AT_DPN, + AT_E0, AT_E1, + AT_H0, AT_H1, + AT_I, + AT_IB10, + AT_IB96, + AT_L0, AT_L1, + AT_M0, AT_M1, AT_MA, + AT_MR, + AT_MT, + AT_NL, + AT_PC, + AT_R0, AT_R1, + AT_RV, + AT_S0, AT_S1, // Added ELM 1.3, expected by Torque + AT_SH_6, AT_SH_3, + AT_SPA, AT_SP, + AT_ST, + AT_SW, + AT_TPA, AT_TP, + AT_WM_XYZA, AT_WM_XYZAB, AT_WM_XYZABC, + AT_WS, + AT_Z, +}; + +typedef struct { + char* name; + uint8_t name_len; + uint8_t cmd_len; + enum at_cmd_ids_t id; +} at_cmd_reg_t; + +static const at_cmd_reg_t at_cmd_reg[] = { + {"@1", 2, 2, AT_AMP1}, + {"AL", 2, 2, AT_AL}, + {"AT0", 3, 3, AT_AT0}, // Added ELM 1.2, expected by Torque + {"AT1", 3, 3, AT_AT1}, // Added ELM 1.2, expected by Torque + {"AT2", 3, 3, AT_AT2}, // Added ELM 1.2, expected by Torque + {"DP", 2, 2, AT_DP}, + {"DPN", 3, 3, AT_DPN}, + {"E0", 2, 2, AT_E0}, + {"E1", 2, 2, AT_E1}, + {"H0", 2, 2, AT_H0}, + {"H1", 2, 2, AT_H1}, + {"I", 1, 1, AT_I}, + {"L0", 2, 2, AT_L0}, + {"L1", 2, 2, AT_L1}, + {"M0", 2, 2, AT_M0}, + //{"M1", 2, 2, AT_M1}, + {"NL", 2, 2, AT_NL}, + {"PC", 2, 2, AT_PC}, + {"S0", 2, 2, AT_S0}, // Added ELM 1.3, expected by Torque + {"S1", 2, 2, AT_S1}, // Added ELM 1.3, expected by Torque + {"SP", 2, 3, AT_SP}, + {"SPA", 3, 4, AT_SPA}, + {"ST", 2, 4, AT_ST}, + {"SW", 2, 4, AT_SW}, + {"Z", 1, 1, AT_Z}, +}; +#define AT_CMD_REG_LEN (sizeof(at_cmd_reg)/sizeof(at_cmd_reg_t)) + +static enum at_cmd_ids_t ICACHE_FLASH_ATTR elm_parse_at_cmd(char *cmd, uint16_t len){ + int i; + for(i=0; i7 BYTES) + elm_mode_allow_long = true; + break; + case AT_AT0: //DISABLE ADAPTIVE TIMING + elm_mode_adaptive_timing = 0; + break; + case AT_AT1: //SET ADAPTIVE TIMING TO AUTO1 + elm_mode_adaptive_timing = 1; + break; + case AT_AT2: //SET ADAPTIVE TIMING TO AUTO2 + elm_mode_adaptive_timing = 2; + break; + case AT_DP: //DESCRIBE THE PROTOCOL BY NAME + if(elm_mode_auto_protocol && elm_selected_protocol != 0) + elm_append_rsp_const("AUTO, "); + elm_append_rsp(elm_current_proto()->name, + strlen(elm_current_proto()->name)); + elm_append_rsp_const("\r\r"); + return; + case AT_DPN: //DESCRIBE THE PROTOCOL BY NUMBER + //TODO: Required. Report currently selected protocol + if(elm_mode_auto_protocol) + elm_append_rsp_const("A"); + elm_append_rsp(&hex_lookup[elm_selected_protocol], 1); + elm_append_rsp_const("\r\r"); + return; // Don't display 'OK' + case AT_E0: //ECHO OFF + elm_mode_echo = false; + break; + case AT_E1: //ECHO ON + elm_mode_echo = true; + break; + case AT_H0: //SHOW FULL CAN HEADERS OFF + elm_mode_additional_headers = false; + break; + case AT_H1: //SHOW FULL CAN HEADERS ON + elm_mode_additional_headers = true; + break; + case AT_I: //IDENTIFY SELF + elm_append_rsp_const(IDENT_MSG); + return; + case AT_L0: //LINEFEED OFF + elm_mode_linefeed = false; + break; + case AT_L1: //LINEFEED ON + elm_mode_linefeed = true; + break; + case AT_M0: //DISABLE NONVOLATILE STORAGE + //Memory storage is likely unnecessary + break; + case AT_NL: //DISABLE LONG MESSAGE SUPPORT (>7 BYTES) + elm_mode_allow_long = false; + break; + case AT_PC: //PROTOCOL CANCEL (Stop timers and stuff) + { + //Init functions should idenpotently prepare the protocol to be used. + //Thus, the init function can be used as a protocol cancel function + elm_proto_reinit(elm_current_proto()); + break; + } + case AT_S0: //DISABLE PRINTING SPACES IN ECU RESPONSES + elm_mode_print_spaces = false; + break; + case AT_S1: //ENABLE PRINTING SPACES IN ECU RESPONSES + elm_mode_print_spaces = true; + break; + case AT_SP: //SET PROTOCOL + tmp = elm_decode_hex_char(cmd[2]); + if(tmp == -1 || tmp >= ELM_PROTOCOL_COUNT) { + elm_append_rsp_const("?\r\r"); + return; + } + + //De-Init previous protocol + elm_proto_reinit(elm_current_proto()); + + elm_selected_protocol = tmp; + elm_mode_auto_protocol = (tmp == 0); + + //Init new protocol + elm_proto_reinit(elm_current_proto()); + break; + case AT_SPA: //SET PROTOCOL WITH AUTO FALLBACK + tmp = elm_decode_hex_char(cmd[3]); + if(tmp == -1 || tmp >= ELM_PROTOCOL_COUNT) { + elm_append_rsp_const("?\r\r"); + return; + } + + //De-Init previous protocol + elm_proto_reinit(elm_current_proto()); + + elm_selected_protocol = tmp; + elm_mode_auto_protocol = true; + + //Init new protocol + elm_proto_reinit(elm_current_proto()); + break; + case AT_ST: //SET TIMEOUT + if(!elm_check_valid_hex_chars(&cmd[2], 2)) { + elm_append_rsp_const("?\r\r"); + return; + } + + tmp = elm_decode_hex_byte(&cmd[2]); + //20 for CAN, 4 for LIN + elm_mode_timeout = tmp ? tmp*20 : ELM_MODE_TIMEOUT_DEFAULT; + break; + case AT_SW: //SET WAKEUP TIME INTERVAL + if(!elm_check_valid_hex_chars(&cmd[2], 2)) { + elm_append_rsp_const("?\r\r"); + return; + } + + tmp = elm_decode_hex_byte(&cmd[2]); + elm_mode_keepalive_period = tmp ? MAX(tmp, 0x20) * 20 : 0; + + if(lin_bus_initialized){ + os_timer_disarm(&elm_proto_aux_timeout); + if(elm_mode_keepalive_period) + os_timer_arm(&elm_proto_aux_timeout, elm_mode_keepalive_period, 0); + } + break; + case AT_Z: //RESET + elm_mode_echo = true; + elm_mode_linefeed = false; + elm_mode_additional_headers = false; + elm_mode_auto_protocol = true; + elm_selected_protocol = ELM_MODE_SELECTED_PROTOCOL_DEFAULT; + elm_mode_print_spaces = true; + elm_mode_adaptive_timing = 1; + elm_mode_allow_long = false; + elm_mode_timeout = ELM_MODE_TIMEOUT_DEFAULT; + elm_mode_keepalive_period = ELM_MODE_KEEPALIVE_PERIOD_DEFAULT; + + elm_append_rsp_const("\r\r"); + elm_append_rsp_const(IDENT_MSG); + panda_set_safety_mode(0xE327); + + elm_proto_reinit(elm_current_proto()); + return; + default: + elm_append_rsp_const("?\r\r"); + return; + } + + elm_append_rsp_const("OK\r\r"); +} + +/************************************* + *** Connection and cli management *** + *************************************/ + +static void ICACHE_FLASH_ATTR elm_append_in_msg(char *data, uint16_t len) { + if(in_msg_len + len > sizeof(in_msg)) + len = sizeof(in_msg) - in_msg_len; + memcpy(in_msg + in_msg_len, data, len); + in_msg_len += len; +} + +static int ICACHE_FLASH_ATTR elm_msg_is_at_cmd(char *data, uint16_t len){ + return len >= 4 && data[0] == 'A' && data[1] == 'T'; +} + +static void ICACHE_FLASH_ATTR elm_rx_cb(void *arg, char *data, uint16_t len) { + #ifdef ELM_DEBUG + //os_printf("\nGot ELM Data In: '%s'\n", data); + #endif + + rsp_buff_len = 0; + len = elm_msg_find_cr_or_eos(data, len); + + if(loopcount){ + os_timer_disarm(&elm_timeout); + loopcount = 0; + got_msg_this_run = false; + can_tx_worked = false; + did_multimessage = false; + + os_printf("Interrupting operation, stopping timer. msg len: %d\n", len); + elm_append_rsp_const("STOPPED\r\r>"); + if(len == 1 && data[0] == '\r') { + os_printf("Empty msg source of interrupt.\n"); + elm_tcp_tx_flush(); + return; + } + } + + if(!(len == 1 && data[0] == '\r') && in_msg_len && in_msg[in_msg_len-1] == '\r'){ + in_msg_len = 0; + } + + if(!(len == 1 && data[0] == '\r' && in_msg_len && in_msg[in_msg_len-1] == '\r')) { + // Not Repeating last message + elm_append_in_msg(data, len); //Aim to remove this memcpy + } + if(elm_mode_echo) + elm_append_rsp(in_msg, in_msg_len); + + if(in_msg_len > 0 && in_msg[in_msg_len-1] == '\r') { //Got a full line + stripped_msg_len = elm_strip(in_msg, in_msg_len, stripped_msg, sizeof(stripped_msg)); + + if(elm_msg_is_at_cmd(stripped_msg, stripped_msg_len)) { + elm_process_at_cmd(stripped_msg+2, stripped_msg_len-2); + elm_append_rsp_const(">"); + } else if(elm_check_valid_hex_chars(stripped_msg, stripped_msg_len - 1)) { + elm_current_proto()->process_obd(elm_current_proto(), stripped_msg, stripped_msg_len); + } else { + elm_append_rsp_const("?\r\r>"); + } + } + + elm_tcp_tx_flush(); + + //Just clear the buffer if full with no termination + if(in_msg_len == sizeof(in_msg) && in_msg[in_msg_len-1] != '\r') + in_msg_len = 0; +} + +void ICACHE_FLASH_ATTR elm_tcp_disconnect_cb(void *arg){ + struct espconn *pesp_conn = (struct espconn *)arg; + + elm_tcp_conn_t * prev = NULL; + for(elm_tcp_conn_t *iter = connection_list; iter != NULL; iter=iter->next){ + struct espconn *conn = iter->conn; + //SHOW_CONNECTION("Considering Disconnecting", conn); + if(!memcmp(pesp_conn->proto.tcp->remote_ip, conn->proto.tcp->remote_ip, 4) && + pesp_conn->proto.tcp->remote_port == conn->proto.tcp->remote_port){ + os_printf("Deleting ELM Connection!\n"); + if(prev){ + prev->next = iter->next; + } else { + connection_list = iter->next; + } + os_free(iter); + break; + } + + prev = iter; + } + + if(connection_list == NULL) { + //If all clients are disconnected, reset the protocol (cancels + //keep alive timers). This will not detect inproperly killed + //connections. In this case, periodic events associated with the + //current protocol will continue until a new client attaches, a + //command is sent generating a response (ELM will try to responde + //to the dead connection, and remove it upon error), and finally, + //the new client disconnects. OFC a power cycle is also an option. + elm_proto_reinit(elm_current_proto()); + } +} + +void ICACHE_FLASH_ATTR elm_tcp_connect_cb(void *arg) { + struct espconn *pesp_conn = (struct espconn *)arg; + //SHOW_CONNECTION("New connection", pesp_conn); + espconn_set_opt(&elm_conn, ESPCONN_NODELAY); + espconn_regist_recvcb(pesp_conn, elm_rx_cb); + //Allow several sends to be queued at a time. + espconn_tcp_set_buf_count(pesp_conn, 3); + + bool connection_address_already_there = false; + for(elm_tcp_conn_t *iter2 = connection_list; iter2 != NULL; iter2 = iter2->next) + if(iter2->conn == pesp_conn){connection_address_already_there = true; break;} + + if(connection_address_already_there) { + os_printf("ELM WIFI: Memory reuse of recently killed connection\n"); + } else { + os_printf("ELM WIFI: Adding connection\n"); + elm_tcp_conn_t *newconn = os_malloc(sizeof(elm_tcp_conn_t)); + if(!newconn) { + os_printf("Failed to allocate place for connection\n"); + } else { + newconn->next = connection_list; + newconn->conn = pesp_conn; + connection_list = newconn; + } + } +} + +void ICACHE_FLASH_ATTR elm327_init() { + // control listener + elm_proto.local_port = ELM_PORT; + elm_conn.type = ESPCONN_TCP; + elm_conn.state = ESPCONN_NONE; + elm_conn.proto.tcp = &elm_proto; + espconn_regist_connectcb(&elm_conn, elm_tcp_connect_cb); + espconn_regist_disconcb(&elm_conn, elm_tcp_disconnect_cb); + espconn_accept(&elm_conn); + espconn_regist_time(&elm_conn, 0, 0); // 60s timeout for all connections +} diff --git a/boardesp/get_sdk.sh b/boardesp/get_sdk.sh new file mode 100755 index 0000000000..adccf678f9 --- /dev/null +++ b/boardesp/get_sdk.sh @@ -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 + diff --git a/boardesp/get_sdk_ci.sh b/boardesp/get_sdk_ci.sh new file mode 100755 index 0000000000..b11cb099a6 --- /dev/null +++ b/boardesp/get_sdk_ci.sh @@ -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 diff --git a/boardesp/get_sdk_mac.sh b/boardesp/get_sdk_mac.sh new file mode 100755 index 0000000000..a8c2d709d4 --- /dev/null +++ b/boardesp/get_sdk_mac.sh @@ -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 + diff --git a/boardesp/include/espmissingincludes.h b/boardesp/include/espmissingincludes.h new file mode 100644 index 0000000000..51672d3f50 --- /dev/null +++ b/boardesp/include/espmissingincludes.h @@ -0,0 +1,78 @@ +#ifndef ESPMISSINGINCLUDES_H +#define ESPMISSINGINCLUDES_H + +#include +#include +#include + + +int strcasecmp(const char *a, const char *b); +#ifndef FREERTOS +#include +#include +//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< _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); +} + diff --git a/boardesp/user_config.h b/boardesp/user_config.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/boardesp/webserver.c b/boardesp/webserver.c new file mode 100644 index 0000000000..f855f88c91 --- /dev/null +++ b/boardesp/webserver.c @@ -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" +"\n" +"\n" +"\n" +"Panda\n" +"\n" +"\n" +"
This is your comma.ai panda\n\n"
+"It's open source. Find the code here\n"
+"Designed to work with our dashcam, chffr\n";
+
+char pagefooter[] = "
\n" +"\n" +"\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;isecure it"); + } + + ets_strcat(resp,"\nSet USB Mode:" + "" + "" + "\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); +} + diff --git a/buy.png b/buy.png new file mode 100644 index 0000000000..a4a9e0fd40 Binary files /dev/null and b/buy.png differ diff --git a/certs/debug b/certs/debug new file mode 100644 index 0000000000..39864b6b1a --- /dev/null +++ b/certs/debug @@ -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----- diff --git a/certs/debug.pub b/certs/debug.pub new file mode 100644 index 0000000000..00e219d7bb --- /dev/null +++ b/certs/debug.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC948lnRo4x44Rd7Y8bQAML4aKDC4XRx958fHV8K6+FbCaP1Z42U2kX0yygak0LjoDutpgObmGHZA+Iz3HeUD6VGjr/teN24vPk+A95cRsjt8rgmGQ96HNjaNgjR+gl1F9XxFimMzir82Xpl1ekTueJNXa7ia5HVH1nFdiksOKHGQ== batman@y840 diff --git a/certs/debugesp b/certs/debugesp new file mode 100644 index 0000000000..789beaac19 --- /dev/null +++ b/certs/debugesp @@ -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----- diff --git a/certs/debugesp.pub b/certs/debugesp.pub new file mode 100644 index 0000000000..3afcf3988e --- /dev/null +++ b/certs/debugesp.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCjIHvrSCWN0Nec6ozbImYik30PIF7JSWgdwDKTxSJ05RM3pj5ELQEGt3qcaVrTokO68tpt5Gu1p6ZsNqWg7iVTW9M7Qj7IH45YDzQP/PSRjgSosQA66f5Gokba5QrW38myqimvj+0p+YH+CNGCBRlTUQGCO8uLCspMZneRSLPW9Q== batman@y840 diff --git a/certs/release.pub b/certs/release.pub new file mode 100644 index 0000000000..19066e29a7 --- /dev/null +++ b/certs/release.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDGN9GU2nOc0kKq6vdZI5qUMzHt234ngqofrgCFFxL0D2Whex0zACp9gar0HZp+bvtpoSgU/Ev8wexNKr+A9QTradljiuxi5ctrOra9k+wxqNj63Wrcu4+wU5UnJEVf/buV4jCOFffMT8z3PO4imt8LzHuEIC/m/ASKVYyvuvBRQQ== batman@y840 diff --git a/certs/releaseesp.pub b/certs/releaseesp.pub new file mode 100644 index 0000000000..1d1d54bb7e --- /dev/null +++ b/certs/releaseesp.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDN4pVyGuJJSde1l3Fjay8qPxog09DsAJZtYPk+armoYO1L6YKReUTcMNyHQYZZMZFmhCdgjCgTIF2QYWMoP4KSe8l6JF04YPP51dIgefc6UXjtlSI8Pyutr0v9xXjSfsVm3RAJxDSHgzs9AoMsluKCL+LhAR1nd7cuHXITJ80O4w== batman@y840 diff --git a/common/version.mk b/common/version.mk new file mode 100644 index 0000000000..cc66efaa6d --- /dev/null +++ b/common/version.mk @@ -0,0 +1,20 @@ +ifeq ($(RELEASE),1) + BUILD_TYPE = "RELEASE" +else + BUILD_TYPE = "DEBUG" +endif + +SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) + +ifneq ($(wildcard $(SELF_DIR)/../.git/HEAD),) +obj/gitversion.h: $(SELF_DIR)/../VERSION $(SELF_DIR)/../.git/HEAD $(SELF_DIR)/../.git/index + echo "const uint8_t gitversion[] = \"$(shell cat $(SELF_DIR)/../VERSION)-$(BUILDER)-$(shell git rev-parse --short=8 HEAD)-$(BUILD_TYPE)\";" > $@ +else +ifneq ($(wildcard $(SELF_DIR)/../../.git/modules/panda/HEAD),) +obj/gitversion.h: $(SELF_DIR)/../VERSION $(SELF_DIR)/../../.git/modules/panda/HEAD $(SELF_DIR)/../../.git/modules/panda/index + echo "const uint8_t gitversion[] = \"$(shell cat $(SELF_DIR)/../VERSION)-$(BUILDER)-$(shell git rev-parse --short=8 HEAD)-$(BUILD_TYPE)\";" > $@ +else +obj/gitversion.h: $(SELF_DIR)/../VERSION + echo "const uint8_t gitversion[] = \"$(shell cat $(SELF_DIR)/../VERSION)-$(BUILDER)-unknown-$(BUILD_TYPE)\";" > $@ +endif +endif diff --git a/crypto/getcertheader.py b/crypto/getcertheader.py new file mode 100755 index 0000000000..75d04e977a --- /dev/null +++ b/crypto/getcertheader.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +import sys +import struct +from Crypto.PublicKey import RSA + +def egcd(a, b): + if a == 0: + return (b, 0, 1) + else: + g, y, x = egcd(b % a, a) + return (g, x - (b // a) * y, y) + +def modinv(a, m): + g, x, y = egcd(a, m) + if g != 1: + raise Exception('modular inverse does not exist') + else: + return x % m + +def to_c_string(x): + mod = (hex(x)[2:-1].rjust(0x100, '0')) + hh = ''.join('\\x'+mod[i:i+2] for i in range(0, 0x100, 2)) + return hh + +def to_c_uint32(x): + nums = [] + for i in range(0x20): + nums.append(x%(2**32)) + x /= (2**32) + return "{"+'U,'.join(map(str, nums))+"U}" + +for fn in sys.argv[1:]: + rsa = RSA.importKey(open(fn).read()) + rr = pow(2**1024, 2, rsa.n) + n0inv = 2**32 - modinv(rsa.n, 2**32) + + cname = fn.split("/")[-1].split(".")[0] + "_rsa_key" + + print 'RSAPublicKey '+cname+' = {.len = 0x20,' + print ' .n0inv = %dU,' % n0inv + print ' .n = %s,' % to_c_uint32(rsa.n) + print ' .rr = %s,' % to_c_uint32(rr) + print ' .exponent = %d,' % rsa.e + print '};' + + diff --git a/crypto/hash-internal.h b/crypto/hash-internal.h new file mode 100644 index 0000000000..05ec3ec9fd --- /dev/null +++ b/crypto/hash-internal.h @@ -0,0 +1,63 @@ +/* + * Copyright 2007 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 Google Inc. ``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 Google Inc. 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. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ + +#include "stdint.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +struct HASH_CTX; // forward decl + +typedef struct HASH_VTAB { + void (* const init)(struct HASH_CTX*); + void (* const update)(struct HASH_CTX*, const void*, int); + const uint8_t* (* const final)(struct HASH_CTX*); + const uint8_t* (* const hash)(const void*, int, uint8_t*); + int size; +} HASH_VTAB; + +typedef struct HASH_CTX { + const HASH_VTAB * f; + uint64_t count; + uint8_t buf[64]; + uint32_t state[8]; // upto SHA2 +} HASH_CTX; + +#define HASH_init(ctx) (ctx)->f->init(ctx) +#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len) +#define HASH_final(ctx) (ctx)->f->final(ctx) +#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest) +#define HASH_size(ctx) (ctx)->f->size + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ diff --git a/crypto/rsa.c b/crypto/rsa.c new file mode 100644 index 0000000000..24171e8790 --- /dev/null +++ b/crypto/rsa.c @@ -0,0 +1,294 @@ +/* rsa.c +** +** Copyright 2012, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * 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. +** * Neither the name of Google Inc. 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 Google Inc. ``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 Google Inc. 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. +*/ + +#include "rsa.h" +#include "sha.h" + +// a[] -= mod +static void subM(const RSAPublicKey* key, + uint32_t* a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +// return a[] >= mod +static int geM(const RSAPublicKey* key, + const uint32_t* a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; // equal +} + +// montgomery c[] += a * b[] / R % mod +static void montMulAdd(const RSAPublicKey* key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +// montgomery c[] = a[] * b[] / R % mod +static void montMul(const RSAPublicKey* key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +// In-place public exponentiation. +// Input and output big-endian byte array in inout. +static void modpow(const RSAPublicKey* key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t* aaa = 0; + int i; + + // Convert from big endian byte array to little endian word array. + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + if (key->exponent == 65537) { + aaa = aaR; // Re-use location. + montMul(key, aR, a, key->rr); // aR = a * RR / R mod M + for (i = 0; i < 16; i += 2) { + montMul(key, aaR, aR, aR); // aaR = aR * aR / R mod M + montMul(key, aR, aaR, aaR); // aR = aaR * aaR / R mod M + } + montMul(key, aaa, aR, a); // aaa = aR * a / R mod M + } else if (key->exponent == 3) { + aaa = aR; // Re-use location. + montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ + montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ + montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ + } + + // Make sure aaa < mod; aaa is at most 1x mod too large. + if (geM(key, aaa)) { + subM(key, aaa); + } + + // Convert to bigendian byte array + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +// other flavor which omits the optional parameter entirely). This code does not +// accept signatures without the optional parameter. + +/* +static const uint8_t sha_padding[RSANUMBYTES] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x21, 0x30, + 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, + 0x05, 0x00, 0x04, 0x14, + + // 20 bytes of hash go here. + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; +*/ + +static const uint8_t sha_padding_1024[RSANUMBYTES] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, + + // 20 bytes of hash go here. + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +// SHA-1 of PKCS1.5 signature sha_padding for 2048 bit, as above. +// At the location of the bytes of the hash all 00 are hashed. +/*static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = { + 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e, + 0x6e, 0xfc, 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68, + 0x7c, 0xfb, 0xf1, 0x67 +};*/ + +// Verify a 2048-bit RSA PKCS1.5 signature against an expected hash. +// Both e=3 and e=65537 are supported. hash_len may be +// SHA_DIGEST_SIZE (== 20) to indicate a SHA-1 hash, or +// SHA256_DIGEST_SIZE (== 32) to indicate a SHA-256 hash. No other +// values are supported. +// +// Returns 1 on successful verification, 0 on failure. +int RSA_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *hash, + const int hash_len) { + uint8_t buf[RSANUMBYTES]; + int i; + //const uint8_t* padding_hash; + + if (key->len != RSANUMWORDS) { + return 0; // Wrong key passed in. + } + + if (len != sizeof(buf)) { + return 0; // Wrong input length. + } + + if (hash_len != SHA_DIGEST_SIZE) { + return 0; // Unsupported hash. + } + + if (key->exponent != 3 && key->exponent != 65537) { + return 0; // Unsupported exponent. + } + + for (i = 0; i < len; ++i) { // Copy input to local workspace. + buf[i] = signature[i]; + } + + modpow(key, buf); // In-place exponentiation. + +#ifdef TEST_RSA + printf("sig\n"); + for (i=0;i> (32 - (bits)))) + +static void SHA1_Transform(SHA_CTX* ctx) { + uint32_t W[80]; + uint32_t A, B, C, D, E; + uint8_t* p = ctx->buf; + int t; + + for(t = 0; t < 16; ++t) { + uint32_t tmp = *p++ << 24; + tmp |= *p++ << 16; + tmp |= *p++ << 8; + tmp |= *p++; + W[t] = tmp; + } + + for(; t < 80; t++) { + W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + + for(t = 0; t < 80; t++) { + uint32_t tmp = rol(5,A) + E + W[t]; + + if (t < 20) + tmp += (D^(B&(C^D))) + 0x5A827999; + else if ( t < 40) + tmp += (B^C^D) + 0x6ED9EBA1; + else if ( t < 60) + tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC; + else + tmp += (B^C^D) + 0xCA62C1D6; + + E = D; + D = C; + C = rol(30,B); + B = A; + A = tmp; + } + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +static const HASH_VTAB SHA_VTAB = { + SHA_init, + SHA_update, + SHA_final, + SHA_hash, + SHA_DIGEST_SIZE +}; + +void SHA_init(SHA_CTX* ctx) { + ctx->f = &SHA_VTAB; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; + ctx->count = 0; +} + + +void SHA_update(SHA_CTX* ctx, const void* data, int len) { + int i = (int) (ctx->count & 63); + const uint8_t* p = (const uint8_t*)data; + + ctx->count += len; + + while (len--) { + ctx->buf[i++] = *p++; + if (i == 64) { + SHA1_Transform(ctx); + i = 0; + } + } +} + + +const uint8_t* SHA_final(SHA_CTX* ctx) { + uint8_t *p = ctx->buf; + uint64_t cnt = ctx->count * 8; + int i; + + SHA_update(ctx, (uint8_t*)"\x80", 1); + while ((ctx->count & 63) != 56) { + SHA_update(ctx, (uint8_t*)"\0", 1); + } + + /* Hack - right shift operator with non const argument requires + * libgcc.a which is missing in EON + * thus expanding for loop from + + for (i = 0; i < 8; ++i) { + uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8)); + SHA_update(ctx, &tmp, 1); + } + + to + */ + + uint8_t tmp = 0; + tmp = (uint8_t) (cnt >> ((7 - 0) * 8)); + SHA_update(ctx, &tmp, 1); + tmp = (uint8_t) (cnt >> ((7 - 1) * 8)); + SHA_update(ctx, &tmp, 1); + tmp = (uint8_t) (cnt >> ((7 - 2) * 8)); + SHA_update(ctx, &tmp, 1); + tmp = (uint8_t) (cnt >> ((7 - 3) * 8)); + SHA_update(ctx, &tmp, 1); + tmp = (uint8_t) (cnt >> ((7 - 4) * 8)); + SHA_update(ctx, &tmp, 1); + tmp = (uint8_t) (cnt >> ((7 - 5) * 8)); + SHA_update(ctx, &tmp, 1); + tmp = (uint8_t) (cnt >> ((7 - 6) * 8)); + SHA_update(ctx, &tmp, 1); + tmp = (uint8_t) (cnt >> ((7 - 7) * 8)); + SHA_update(ctx, &tmp, 1); + + for (i = 0; i < 5; i++) { + uint32_t tmp = ctx->state[i]; + *p++ = tmp >> 24; + *p++ = tmp >> 16; + *p++ = tmp >> 8; + *p++ = tmp >> 0; + } + + return ctx->buf; +} + +/* Convenience function */ +const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest) { + SHA_CTX ctx; + SHA_init(&ctx); + SHA_update(&ctx, data, len); + memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE); + return digest; +} diff --git a/crypto/sha.h b/crypto/sha.h new file mode 100644 index 0000000000..4b51a531bf --- /dev/null +++ b/crypto/sha.h @@ -0,0 +1,51 @@ +/* + * Copyright 2005 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 Google Inc. ``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 Google Inc. 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. + */ +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ + +#include "hash-internal.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef HASH_CTX SHA_CTX; + +void SHA_init(SHA_CTX* ctx); +void SHA_update(SHA_CTX* ctx, const void* data, int len); +const uint8_t* SHA_final(SHA_CTX* ctx); + +// Convenience method. Returns digest address. +// NOTE: *digest needs to hold SHA_DIGEST_SIZE bytes. +const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest); + +#define SHA_DIGEST_SIZE 20 + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ diff --git a/crypto/sign.py b/crypto/sign.py new file mode 100755 index 0000000000..159299271e --- /dev/null +++ b/crypto/sign.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +import os +import sys +import struct +import hashlib +from Crypto.PublicKey import RSA + +rsa = RSA.importKey(open(sys.argv[3]).read()) + +with open(sys.argv[1]) as f: + dat = f.read() + +print "signing", len(dat), "bytes" + +with open(sys.argv[2], "wb") as f: + if os.getenv("SETLEN") is not None: + x = struct.pack("I", len(dat)) + dat[4:] + # mock signature of dat[4:] + dd = hashlib.sha1(dat[4:]).digest() + else: + x = dat + dd = hashlib.sha1(dat).digest() + print "hash:",dd.encode("hex") + dd = "\x00\x01" + "\xff"*0x69 + "\x00" + dd + rsa_out = pow(int(dd.encode("hex"), 16), rsa.d, rsa.n) + sig = (hex(rsa_out)[2:-1].rjust(0x100, '0')).decode("hex") + x += sig + f.write(x) + diff --git a/crypto/stdint.h b/crypto/stdint.h new file mode 100644 index 0000000000..67ac93ed75 --- /dev/null +++ b/crypto/stdint.h @@ -0,0 +1,4 @@ +#define uint8_t unsigned char +#define uint32_t unsigned int +#define int64_t long long +#define uint64_t unsigned long long diff --git a/docs/guide.pdf b/docs/guide.pdf new file mode 100644 index 0000000000..5dbc95680a Binary files /dev/null and b/docs/guide.pdf differ diff --git a/drivers/linux/.gitignore b/drivers/linux/.gitignore new file mode 100644 index 0000000000..93a6cde763 --- /dev/null +++ b/drivers/linux/.gitignore @@ -0,0 +1,6 @@ +.*.cmd +*.ko +.tmp_versions +Module.symvers +modules.order +*.mod.c diff --git a/drivers/linux/Makefile b/drivers/linux/Makefile new file mode 100644 index 0000000000..e5b1ec4219 --- /dev/null +++ b/drivers/linux/Makefile @@ -0,0 +1,18 @@ +VERSION=0.0.1 +obj-m+=panda.o + +link: + sudo dkms add `pwd` + +build: + sudo dkms build panda/$(VERSION) + +install: + sudo dkms install panda/$(VERSION) + +all: build install + +uninstall: + sudo dkms uninstall panda/$(VERSION) + sudo dkms remove panda/$(VERSION) --all + diff --git a/drivers/linux/README.md b/drivers/linux/README.md new file mode 100644 index 0000000000..81e95523ac --- /dev/null +++ b/drivers/linux/README.md @@ -0,0 +1,19 @@ +Installs the panda linux kernel driver using DKMS. + +This will allow the panda to work with tools such as `can-utils` + +prerequisites: + - `apt-get install dkms gcc linux-headers-$(uname -r) make sudo` + +installation: + - `make link` (only needed the first time. It will report an error on subsequent attempts to link) + - `make all` + - `make install` + +uninstall: + - `make uninstall` + +usage: + +You will need to bring it up using `sudo ifconfig can0 up` or +`sudo ip link set dev can0 up`, depending on your platform. diff --git a/drivers/linux/dkms.conf b/drivers/linux/dkms.conf new file mode 100644 index 0000000000..da9cba04a2 --- /dev/null +++ b/drivers/linux/dkms.conf @@ -0,0 +1,6 @@ +PACKAGE_NAME="panda" +PACKAGE_VERSION="0.0.1" +BUILT_MODULE_NAME[0]="panda" +DEST_MODULE_LOCATION[0]="/kernel/drivers/net/panda/" +AUTOINSTALL="yes" + diff --git a/drivers/linux/panda.c b/drivers/linux/panda.c new file mode 100644 index 0000000000..4c5980a9d2 --- /dev/null +++ b/drivers/linux/panda.c @@ -0,0 +1,613 @@ +/** + * @file panda.c + * @author Jessy Diamond Exum + * @date 16 June 2017 + * @version 0.1 + * @brief Driver for the Comma.ai Panda CAN adapter to allow it to be controlled via + * the Linux SocketCAN interface. + * @see https://github.com/commaai/panda for the full project. + * @see Inspired by net/can/usb/mcba_usb.c from Linux Kernel 4.12-rc4. + */ + +#include +#include +#include +#include // Macros used to mark up functions e.g., __init __exit +#include // Contains types, macros, functions for the kernel +#include // Core header for loading LKMs into the kernel +#include +#include + +/* vendor and product id */ +#define PANDA_MODULE_NAME "panda" +#define PANDA_VENDOR_ID 0XBBAA +#define PANDA_PRODUCT_ID 0XDDCC + +#define PANDA_MAX_TX_URBS 20 +#define PANDA_CTX_FREE PANDA_MAX_TX_URBS + +#define PANDA_USB_RX_BUFF_SIZE 0x40 +#define PANDA_USB_TX_BUFF_SIZE (sizeof(struct panda_usb_can_msg)) + +#define PANDA_NUM_CAN_INTERFACES 3 + +#define PANDA_CAN_TRANSMIT 1 +#define PANDA_CAN_EXTENDED 4 + +#define PANDA_BITRATE 500000 + +#define PANDA_DLC_MASK 0x0F + +struct panda_usb_ctx { + struct panda_inf_priv *priv; + u32 ndx; + u8 dlc; +}; + +struct panda_dev_priv; + +struct panda_inf_priv { + struct can_priv can; + struct panda_usb_ctx tx_context[PANDA_MAX_TX_URBS]; + struct net_device *netdev; + struct usb_anchor tx_submitted; + atomic_t free_ctx_cnt; + u8 interface_num; + u8 mcu_can_ifnum; + struct panda_dev_priv *priv_dev; +}; + +struct panda_dev_priv { + struct usb_device *udev; + struct device *dev; + struct usb_anchor rx_submitted; + struct panda_inf_priv *interfaces[PANDA_NUM_CAN_INTERFACES]; +}; + +struct __packed panda_usb_can_msg { + u32 rir; + u32 bus_dat_len; + u8 data[8]; +}; + +static const struct usb_device_id panda_usb_table[] = { + { USB_DEVICE(PANDA_VENDOR_ID, PANDA_PRODUCT_ID) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, panda_usb_table); + + +// panda: CAN1 = 0 CAN2 = 1 CAN3 = 4 +const int can_numbering[] = {0,1,4}; + +struct panda_inf_priv * +panda_get_inf_from_bus_id(struct panda_dev_priv *priv_dev, int bus_id){ + int inf_num; + for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++) + if(can_numbering[inf_num] == bus_id) + return priv_dev->interfaces[inf_num]; + return NULL; +} + +// CTX handling shamlessly ripped from mcba_usb.c linux driver +static inline void panda_init_ctx(struct panda_inf_priv *priv) +{ + int i = 0; + + for (i = 0; i < PANDA_MAX_TX_URBS; i++) { + priv->tx_context[i].ndx = PANDA_CTX_FREE; + priv->tx_context[i].priv = priv; + } + + atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context)); +} + +static inline struct panda_usb_ctx *panda_usb_get_free_ctx(struct panda_inf_priv *priv, + struct can_frame *cf) +{ + int i = 0; + struct panda_usb_ctx *ctx = NULL; + + for (i = 0; i < PANDA_MAX_TX_URBS; i++) { + if (priv->tx_context[i].ndx == PANDA_CTX_FREE) { + ctx = &priv->tx_context[i]; + ctx->ndx = i; + ctx->dlc = cf->can_dlc; + + atomic_dec(&priv->free_ctx_cnt); + break; + } + } + + printk("CTX num %d\n", atomic_read(&priv->free_ctx_cnt)); + if (!atomic_read(&priv->free_ctx_cnt)){ + /* That was the last free ctx. Slow down tx path */ + printk("SENDING TOO FAST\n"); + netif_stop_queue(priv->netdev); + } + + return ctx; +} + +/* panda_usb_free_ctx and panda_usb_get_free_ctx are executed by different + * threads. The order of execution in below function is important. + */ +static inline void panda_usb_free_ctx(struct panda_usb_ctx *ctx) +{ + /* Increase number of free ctxs before freeing ctx */ + atomic_inc(&ctx->priv->free_ctx_cnt); + + ctx->ndx = PANDA_CTX_FREE; + + /* Wake up the queue once ctx is marked free */ + netif_wake_queue(ctx->priv->netdev); +} + + + +static void panda_urb_unlink(struct panda_inf_priv *priv) +{ + usb_kill_anchored_urbs(&priv->priv_dev->rx_submitted); + usb_kill_anchored_urbs(&priv->tx_submitted); +} + +static int panda_set_output_enable(struct panda_inf_priv* priv, bool enable){ + return usb_control_msg(priv->priv_dev->udev, + usb_sndctrlpipe(priv->priv_dev->udev, 0), + 0xDC, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + enable ? 0x1337 : 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); +} + +static void panda_usb_write_bulk_callback(struct urb *urb) +{ + struct panda_usb_ctx *ctx = urb->context; + struct net_device *netdev; + + WARN_ON(!ctx); + + netdev = ctx->priv->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + if (!netif_device_present(netdev)) + return; + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += ctx->dlc; + + can_get_echo_skb(netdev, ctx->ndx); + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + + /* Release the context */ + panda_usb_free_ctx(ctx); +} + + +static netdev_tx_t panda_usb_xmit(struct panda_inf_priv *priv, + struct panda_usb_can_msg *usb_msg, + struct panda_usb_ctx *ctx) +{ + struct urb *urb; + u8 *buf; + int err; + + /* create a URB, and a buffer for it, and copy the data to the URB */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + buf = usb_alloc_coherent(priv->priv_dev->udev, + PANDA_USB_TX_BUFF_SIZE, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) { + err = -ENOMEM; + goto nomembuf; + } + + memcpy(buf, usb_msg, PANDA_USB_TX_BUFF_SIZE); + + usb_fill_bulk_urb(urb, priv->priv_dev->udev, + usb_sndbulkpipe(priv->priv_dev->udev, 3), buf, + PANDA_USB_TX_BUFF_SIZE, panda_usb_write_bulk_callback, + ctx); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) + goto failed; + + /* Release our reference to this URB, the USB core will eventually free it entirely. */ + usb_free_urb(urb); + + return 0; + + failed: + usb_unanchor_urb(urb); + usb_free_coherent(priv->priv_dev->udev, PANDA_USB_TX_BUFF_SIZE, buf, urb->transfer_dma); + + if (err == -ENODEV) + netif_device_detach(priv->netdev); + else + netdev_warn(priv->netdev, "failed tx_urb %d\n", err); + + nomembuf: + usb_free_urb(urb); + + return err; +} + +static void panda_usb_process_can_rx(struct panda_dev_priv *priv_dev, + struct panda_usb_can_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + int bus_num; + struct panda_inf_priv *priv_inf; + struct net_device_stats *stats; + + bus_num = (msg->bus_dat_len >> 4) & 0xf; + priv_inf = panda_get_inf_from_bus_id(priv_dev, bus_num); + if(!priv_inf){ + printk("Got something on an unused interface %d\n", bus_num); + return; + } + printk("Recv bus %d\n", bus_num); + + stats = &priv_inf->netdev->stats; + //u16 sid; + + if (!netif_device_present(priv_inf->netdev)) + return; + + skb = alloc_can_skb(priv_inf->netdev, &cf); + if (!skb) + return; + + if(msg->rir & PANDA_CAN_EXTENDED){ + cf->can_id = (msg->rir >> 3) | CAN_EFF_FLAG; + }else{ + cf->can_id = (msg->rir >> 21); + } + + // TODO: Handle Remote Frames + //if (msg->dlc & MCBA_DLC_RTR_MASK) + // cf->can_id |= CAN_RTR_FLAG; + + cf->can_dlc = get_can_dlc(msg->bus_dat_len & PANDA_DLC_MASK); + + memcpy(cf->data, msg->data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + netif_rx(skb); +} + +static void panda_usb_read_int_callback(struct urb *urb) +{ + struct panda_dev_priv *priv_dev = urb->context; + int retval; + int pos = 0; + int inf_num; + + switch (urb->status) { + case 0: /* success */ + break; + case -ENOENT: + case -ESHUTDOWN: + return; + default: + dev_info(priv_dev->dev, "Rx URB aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + while (pos < urb->actual_length) { + struct panda_usb_can_msg *msg; + + if (pos + sizeof(struct panda_usb_can_msg) > urb->actual_length) { + dev_err(priv_dev->dev, "format error\n"); + break; + } + + msg = (struct panda_usb_can_msg *)(urb->transfer_buffer + pos); + + panda_usb_process_can_rx(priv_dev, msg); + + pos += sizeof(struct panda_usb_can_msg); + } + + resubmit_urb: + usb_fill_int_urb(urb, priv_dev->udev, + usb_rcvintpipe(priv_dev->udev, 1), + urb->transfer_buffer, PANDA_USB_RX_BUFF_SIZE, + panda_usb_read_int_callback, priv_dev, 5); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV){ + for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++) + if(priv_dev->interfaces[inf_num]) + netif_device_detach(priv_dev->interfaces[inf_num]->netdev); + }else if (retval) + dev_err(priv_dev->dev, "failed resubmitting read bulk urb: %d\n", retval); +} + + +static int panda_usb_start(struct panda_dev_priv *priv_dev) +{ + int err; + struct urb *urb = NULL; + u8 *buf; + int inf_num; + + for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++) + panda_init_ctx(priv_dev->interfaces[inf_num]); + + err = usb_set_interface(priv_dev->udev, 0, 1); + if (err) { + dev_err(priv_dev->dev, "Can not set alternate setting to 1, error: %i", err); + return err; + } + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + return -ENOMEM; + } + + buf = usb_alloc_coherent(priv_dev->udev, PANDA_USB_RX_BUFF_SIZE, + GFP_KERNEL, &urb->transfer_dma); + if (!buf) { + dev_err(priv_dev->dev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + usb_fill_int_urb(urb, priv_dev->udev, + usb_rcvintpipe(priv_dev->udev, 1), + buf, PANDA_USB_RX_BUFF_SIZE, + panda_usb_read_int_callback, priv_dev, 5); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &priv_dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(priv_dev->udev, PANDA_USB_RX_BUFF_SIZE, + buf, urb->transfer_dma); + usb_free_urb(urb); + dev_err(priv_dev->dev, "Failed in start, while submitting urb.\n"); + return err; + } + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + + + return 0; +} + +/* Open USB device */ +static int panda_usb_open(struct net_device *netdev) +{ + struct panda_inf_priv *priv = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + //priv->can_speed_check = true; + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + netif_start_queue(netdev); + + return 0; +} + +/* Close USB device */ +static int panda_usb_close(struct net_device *netdev) +{ + struct panda_inf_priv *priv = netdev_priv(netdev); + + priv->can.state = CAN_STATE_STOPPED; + + netif_stop_queue(netdev); + + /* Stop polling */ + panda_urb_unlink(priv); + + close_candev(netdev); + + return 0; +} + +static netdev_tx_t panda_usb_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct panda_inf_priv *priv_inf = netdev_priv(netdev); + struct can_frame *cf = (struct can_frame *)skb->data; + struct panda_usb_ctx *ctx = NULL; + struct net_device_stats *stats = &priv_inf->netdev->stats; + int err; + struct panda_usb_can_msg usb_msg = {}; + int bus = priv_inf->mcu_can_ifnum; + + if (can_dropped_invalid_skb(netdev, skb)){ + printk("Invalid CAN packet"); + return NETDEV_TX_OK; + } + + ctx = panda_usb_get_free_ctx(priv_inf, cf); + + //Warning: cargo cult. Can't tell what this is for, but it is + //everywhere and encouraged in the documentation. + can_put_echo_skb(skb, priv_inf->netdev, ctx->ndx); + + if(cf->can_id & CAN_EFF_FLAG){ + usb_msg.rir = cpu_to_le32(((cf->can_id & 0x1FFFFFFF) << 3) | + PANDA_CAN_TRANSMIT | PANDA_CAN_EXTENDED); + }else{ + usb_msg.rir = cpu_to_le32(((cf->can_id & 0x7FF) << 21) | PANDA_CAN_TRANSMIT); + } + usb_msg.bus_dat_len = cpu_to_le32((cf->can_dlc & 0x0F) | (bus << 4)); + + memcpy(usb_msg.data, cf->data, cf->can_dlc); + + //TODO Handle Remote Frames + //if (cf->can_id & CAN_RTR_FLAG) + // usb_msg.dlc |= PANDA_DLC_RTR_MASK; + + netdev_err(netdev, "Received data from socket. canid: %x; len: %d\n", cf->can_id, cf->can_dlc); + + err = panda_usb_xmit(priv_inf, &usb_msg, ctx); + if (err) + goto xmit_failed; + + return NETDEV_TX_OK; + + xmit_failed: + can_free_echo_skb(priv_inf->netdev, ctx->ndx); + panda_usb_free_ctx(ctx); + dev_kfree_skb(skb); + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +static const struct net_device_ops panda_netdev_ops = { + .ndo_open = panda_usb_open, + .ndo_stop = panda_usb_close, + .ndo_start_xmit = panda_usb_start_xmit, +}; + +static int panda_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct panda_inf_priv *priv_inf; + int err = -ENOMEM; + int inf_num; + struct panda_dev_priv *priv_dev; + struct usb_device *usbdev = interface_to_usbdev(intf); + + priv_dev = kzalloc(sizeof(struct panda_dev_priv), GFP_KERNEL); + if (!priv_dev) { + dev_err(&intf->dev, "Couldn't alloc priv_dev\n"); + return -ENOMEM; + } + priv_dev->udev = usbdev; + priv_dev->dev = &intf->dev; + usb_set_intfdata(intf, priv_dev); + + ////// Interface privs + for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++){ + netdev = alloc_candev(sizeof(struct panda_inf_priv), PANDA_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't alloc candev\n"); + goto cleanup_candev; + } + netdev->netdev_ops = &panda_netdev_ops; + netdev->flags |= IFF_ECHO; /* we support local echo */ + + priv_inf = netdev_priv(netdev); + priv_inf->netdev = netdev; + priv_inf->priv_dev = priv_dev; + priv_inf->interface_num = inf_num; + priv_inf->mcu_can_ifnum = can_numbering[inf_num]; + + init_usb_anchor(&priv_dev->rx_submitted); + init_usb_anchor(&priv_inf->tx_submitted); + + /* Init CAN device */ + priv_inf->can.state = CAN_STATE_STOPPED; + priv_inf->can.bittiming.bitrate = PANDA_BITRATE; + + SET_NETDEV_DEV(netdev, &intf->dev); + + err = register_candev(netdev); + if (err) { + netdev_err(netdev, "couldn't register PANDA CAN device: %d\n", err); + free_candev(priv_inf->netdev); + goto cleanup_candev; + } + + priv_dev->interfaces[inf_num] = priv_inf; + } + + err = panda_usb_start(priv_dev); + if (err) { + dev_err(&intf->dev, "Failed to initialize Comma.ai Panda CAN controller\n"); + goto cleanup_candev; + } + + err = panda_set_output_enable(priv_inf, true); + if (err) { + dev_info(&intf->dev, "Failed to initialize send enable message to Panda.\n"); + goto cleanup_candev; + } + + dev_info(&intf->dev, "Comma.ai Panda CAN controller connected\n"); + + return 0; + + cleanup_candev: + for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++){ + priv_inf = priv_dev->interfaces[inf_num]; + if(priv_inf){ + unregister_candev(priv_inf->netdev); + free_candev(priv_inf->netdev); + }else + break; + } + + kfree(priv_dev); + + return err; +} + +/* Called by the usb core when driver is unloaded or device is removed */ +static void panda_usb_disconnect(struct usb_interface *intf) +{ + struct panda_dev_priv *priv_dev = usb_get_intfdata(intf); + struct panda_inf_priv *priv_inf; + int inf_num; + + usb_set_intfdata(intf, NULL); + + for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++){ + priv_inf = priv_dev->interfaces[inf_num]; + if(priv_inf){ + netdev_info(priv_inf->netdev, "device disconnected\n"); + unregister_candev(priv_inf->netdev); + free_candev(priv_inf->netdev); + }else + break; + } + + panda_urb_unlink(priv_inf); + kfree(priv_dev); +} + +static struct usb_driver panda_usb_driver = { + .name = PANDA_MODULE_NAME, + .probe = panda_usb_probe, + .disconnect = panda_usb_disconnect, + .id_table = panda_usb_table, +}; + +module_usb_driver(panda_usb_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jessy Diamond Exum "); +MODULE_DESCRIPTION("SocketCAN driver for Comma.ai's Panda Adapter."); +MODULE_VERSION("0.1"); diff --git a/drivers/linux/test/Makefile b/drivers/linux/test/Makefile new file mode 100644 index 0000000000..c73945e4cd --- /dev/null +++ b/drivers/linux/test/Makefile @@ -0,0 +1,2 @@ +all: + gcc main.c -o cantest -pthread -lpthread diff --git a/drivers/linux/test/main.c b/drivers/linux/test/main.c new file mode 100644 index 0000000000..1f44efc76e --- /dev/null +++ b/drivers/linux/test/main.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +const char *ifname = "can0"; + +static unsigned char payload[] = {0xAA, 0xAA, 0xAA, 0xAA, 0x07, 0x00, 0x00, 0x00}; +int packet_len = 8; +int dir = 0; + +void *write_thread( void *dat ){ + int nbytes; + struct can_frame frame; + int s = *((int*) dat); + + while(1){ + for(int i = 0; i < 1; i ++){ + if(packet_len % 2){ + frame.can_id = 0x8AA | CAN_EFF_FLAG; + }else{ + frame.can_id = 0xAA; + } + + frame.can_dlc = packet_len; + memcpy(frame.data, payload, frame.can_dlc); + + nbytes = write(s, &frame, sizeof(struct can_frame)); + + printf("Wrote %d bytes; addr: %lx; datlen: %d\n", nbytes, frame.can_id, frame.can_dlc); + + if(dir){ + packet_len++; + if(packet_len >= 8) + dir = 0; + }else{ + packet_len--; + if(packet_len <= 0) + dir = 1; + } + } + sleep(2); + } +} + + +int main(void) +{ + pthread_t sndthread; + int err, s, nbytes; + struct sockaddr_can addr; + struct can_frame frame; + struct ifreq ifr; + + if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + perror("Error while opening socket"); + return -1; + } + + strcpy(ifr.ifr_name, ifname); + ioctl(s, SIOCGIFINDEX, &ifr); + + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + + printf("%s at index %d\n", ifname, ifr.ifr_ifindex); + + if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Error in socket bind"); + return -2; + } + + /////// Create Write Thread + + err = pthread_create( &sndthread, NULL, write_thread, (void*) &s); + if(err){ + fprintf(stderr,"Error - pthread_create() return code: %d\n", err); + exit(EXIT_FAILURE); + } + + /////// Listen to socket + while (1) { + struct can_frame framein; + + // Read in a CAN frame + int numBytes = read(s, &framein, CANFD_MTU); + switch (numBytes) { + case CAN_MTU: + if(framein.can_id & 0x80000000) + printf("Received %u byte payload; canid 0x%lx (EXT)\n", + framein.can_dlc, framein.can_id & 0x7FFFFFFF); + else + printf("Received %u byte payload; canid 0x%lx\n", framein.can_dlc, framein.can_id); + break; + case CANFD_MTU: + // TODO: Should make an example for CAN FD + break; + case -1: + // Check the signal value on interrupt + //if (EINTR == errno) + // continue; + + // Delay before continuing + sleep(1); + default: + continue; + } + } + + return 0; +} diff --git a/drivers/linux/test/run.sh b/drivers/linux/test/run.sh new file mode 100755 index 0000000000..5301719b49 --- /dev/null +++ b/drivers/linux/test/run.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +sudo ifconfig can0 up +make +./cantest diff --git a/drivers/windows/.gitignore b/drivers/windows/.gitignore new file mode 100644 index 0000000000..dbe7ad5a91 --- /dev/null +++ b/drivers/windows/.gitignore @@ -0,0 +1,306 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +Debug_x86/ +Debug_x64/ +Release_x86/ +Release_x64/ +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# installer +*.exe diff --git a/drivers/windows/ECUsim CLI/ECUsim CLI.cpp b/drivers/windows/ECUsim CLI/ECUsim CLI.cpp new file mode 100644 index 0000000000..6c8a469acc --- /dev/null +++ b/drivers/windows/ECUsim CLI/ECUsim CLI.cpp @@ -0,0 +1,38 @@ +// ECUsim CLI.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include "ECUsim DLL\ECUsim.h" + +std::unique_ptr sim; + +BOOL CtrlHandler(DWORD fdwCtrlType) +{ + if (fdwCtrlType != CTRL_C_EVENT) return FALSE; + + sim->stop(); + sim->join(); + + return(TRUE); +} + +int main(int argc, // Number of strings in array argv + char *argv[], // Array of command-line argument strings + char *envp[]) // Array of environment variable strings +{ + + int count; + + // Display each command-line argument. + std::cout << "\nCommand-line arguments:\n"; + for (count = 0; count < argc; count++) + std::cout << " argv[" << count << "] " << argv[count] << "\n"; + + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); + + sim.reset(new ECUsim("", 500000)); + sim->join(); + + return 0; +} + diff --git a/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj b/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj new file mode 100644 index 0000000000..4b9de8c442 --- /dev/null +++ b/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D99E2FCD-21A4-4065-949A-31E34E0E69D1} + Win32Proj + ECUsimCLI + 10.0.16299.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib + + + + + Use + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib + + + + + + + + + + + Create + Create + Create + Create + + + + + {96e0e646-ee76-444d-9a77-a0cd7f781deb} + + + + + + \ No newline at end of file diff --git a/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj.filters b/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj.filters new file mode 100644 index 0000000000..ea223e30b2 --- /dev/null +++ b/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/drivers/windows/ECUsim CLI/stdafx.cpp b/drivers/windows/ECUsim CLI/stdafx.cpp new file mode 100644 index 0000000000..d4a23c3cf7 --- /dev/null +++ b/drivers/windows/ECUsim CLI/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// ECUsim CLI.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/ECUsim CLI/stdafx.h b/drivers/windows/ECUsim CLI/stdafx.h new file mode 100644 index 0000000000..b005a839de --- /dev/null +++ b/drivers/windows/ECUsim CLI/stdafx.h @@ -0,0 +1,15 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: reference additional headers your program requires here diff --git a/drivers/windows/ECUsim CLI/targetver.h b/drivers/windows/ECUsim CLI/targetver.h new file mode 100644 index 0000000000..87c0086de7 --- /dev/null +++ b/drivers/windows/ECUsim CLI/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj b/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj new file mode 100644 index 0000000000..93d75c14be --- /dev/null +++ b/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj @@ -0,0 +1,197 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {96E0E646-EE76-444D-9A77-A0CD7F781DEB} + Win32Proj + ECUsimDLL + 10.0.16299.0 + + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + ecusim + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + true + ecusim + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + ecusim + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + ecusim + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + Use + Level3 + Disabled + _DEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + + + + + + + false + + + false + + + false + + + false + + + + + + Create + Create + Create + Create + + + + + {5528aefb-638d-49af-b9d4-965154e7d531} + + + + + + \ No newline at end of file diff --git a/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj.filters b/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj.filters new file mode 100644 index 0000000000..299d654451 --- /dev/null +++ b/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/drivers/windows/ECUsim DLL/ECUsim.cpp b/drivers/windows/ECUsim DLL/ECUsim.cpp new file mode 100644 index 0000000000..19f2bf360e --- /dev/null +++ b/drivers/windows/ECUsim DLL/ECUsim.cpp @@ -0,0 +1,261 @@ +#include "stdafx.h" +#include "ECUsim.h" + +ECUsim::ECUsim(std::string sn, unsigned long can_baud, bool ext_addr) : + doloop(TRUE), verbose(TRUE), can11b_enabled(TRUE), can29b_enabled(TRUE), ext_addr(ext_addr){ + this->panda = panda::Panda::openPanda(sn); + this->panda->set_can_speed_cbps(panda::PANDA_CAN1, can_baud / 100); //Don't pass in baud where baud%100 != 0 + this->panda->set_safety_mode(panda::SAFETY_ALLOUTPUT); + this->panda->set_can_loopback(FALSE); + this->panda->can_clear(panda::PANDA_CAN_RX); + + DWORD threadid; + this->thread_can = CreateThread(NULL, 0, _canthreadBootstrap, (LPVOID)this, 0, &threadid); +} + +ECUsim::~ECUsim() { + this->stop(); + this->join(); +} + +void ECUsim::stop() { + this->doloop = FALSE; +} + +void ECUsim::join() { + WaitForSingleObject(this->thread_can, INFINITE); +} + +DWORD WINAPI ECUsim::_canthreadBootstrap(LPVOID This) { + return ((ECUsim*)This)->can_recv_thread_function(); +} + +DWORD ECUsim::can_recv_thread_function() { + while (this->doloop) { + auto msgs = this->panda->can_recv(); + for (auto& msg : msgs) { + if (msg.is_receipt) continue; + if (msg.bus == 0 && !msg.is_receipt /*&& msg.len == 8*/ && msg.dat[0] >= 2) { + if (this->verbose) { + printf("Processing message (bus: %d; addr: %X; 29b: %d):\n ", msg.bus, msg.addr, msg.addr_29b); + for (int i = 0; i < msg.len; i++) printf("%02X ", msg.dat[i]); + printf("\n"); + } + this->_CAN_process_msg(msg); + } else { + if (this->verbose) { + printf("Rejecting message (bus: %d; addr: %X; 29b: %d):\n ", msg.bus, msg.addr, msg.addr_29b); + for (int i = 0; i < msg.len; i++) printf("%02X ", msg.dat[i]); + printf("\n"); + } + } + } + } + + return 0; +} + +BOOL ECUsim::_can_addr_matches(panda::PANDA_CAN_MSG& msg) { + if (this->can11b_enabled && !msg.addr_29b && (msg.addr == 0x7DF || (msg.addr & 0x7F8) == 0x7E0)) { + if (!this->ext_addr) { + return TRUE; + } else { + return msg.len >= 1 && msg.dat[0] == 0x13;//13 is an arbitrary address picked to test ext addresses + } + } + if (this->can29b_enabled && msg.addr_29b && ((msg.addr & 0x1FFF00FF) == 0x18DB00F1 || (msg.addr & 0x1FFF00FF) == 0x18da00f1)) { + if (!this->ext_addr) { + return TRUE; + } else { + return msg.len >= 1 && msg.dat[0] == 0x13;//13 is an arbitrary address picked to test ext addresses + } + } + return FALSE; +} + +void ECUsim::_CAN_process_msg(panda::PANDA_CAN_MSG& msg) { + std::string outmsg; + uint32_t outaddr; + uint8_t formatted_msg_buff[8]; + bool doreply = FALSE; + + if (this->_can_addr_matches(msg)) {// && msg.len == 8) { + uint8_t *dat = (this->ext_addr) ? &msg.dat[1] : &msg.dat[0]; + if ((dat[0] & 0xF0) == 0x10) { + printf("Got a multiframe write request\n"); + outaddr = (msg.addr_29b) ? 0x18DAF1EF : 0x7E8; + this->panda->can_send(outaddr, msg.addr_29b, (const uint8_t*)"\x30\x00\x00", 3, panda::PANDA_CAN1); + return; + } + + /////////// Check if Flow Control Msg + if ((dat[0] & 0xF0) == 0x30 && msg.len >= 3 && this->can_multipart_data.size() > 0) { + if (this->verbose) printf("More data requested\n"); + uint8_t block_size = dat[1], sep_time_min = dat[2]; + outaddr = (msg.addr == 0x7DF || msg.addr == 0x7E0) ? 0x7E8 : 0x18DAF1EF; //ext addr 5th byte is just always 0x13 for simplicity + + unsigned int msgnum = 1; + while (this->can_multipart_data.size()) { + unsigned int datalen = this->ext_addr ? + min(6, this->can_multipart_data.size()): //EXT ADDR VALUE + min(7, this->can_multipart_data.size()); //NORMAL ADDR VALUE + + unsigned int idx = 0; + if (this->ext_addr) + formatted_msg_buff[idx++] = 0x13; //EXT ADDR + formatted_msg_buff[idx++] = 0x20 | msgnum; + for (int i = 0; i < datalen; i++) { + formatted_msg_buff[i + idx] = this->can_multipart_data.front(); + this->can_multipart_data.pop(); + } + for (int i = datalen + idx; i < sizeof(formatted_msg_buff); i++) + formatted_msg_buff[i] = 0; + + if (this->verbose) { + printf("Multipart reply to %X.\n ", outaddr); + for (int i = 0; i < datalen + idx; i++) printf("%02X ", formatted_msg_buff[i]); + printf("\n"); + } + + this->panda->can_send(outaddr, msg.addr_29b, formatted_msg_buff, datalen + idx, panda::PANDA_CAN1); + msgnum = (msgnum + 1) % 0x10; + Sleep(10); + } + return; + } + + /////////// Normal message in + outmsg = this->process_obd_msg(dat[1], dat[2], doreply); + if (doreply) { + outaddr = (msg.addr_29b) ? 0x18DAF1EF : 0x7E8; + + if (outmsg.size() <= (this->ext_addr ? 4 : 5)) { + unsigned int idx = 0; + if(this->ext_addr) + formatted_msg_buff[idx++] = 0x13; //EXT ADDR + formatted_msg_buff[idx++] = outmsg.size() + 2; + formatted_msg_buff[idx++] = 0x40 | dat[1]; + formatted_msg_buff[idx++] = dat[2]; //PID + memcpy_s(&formatted_msg_buff[idx], sizeof(formatted_msg_buff) - idx, outmsg.c_str(), outmsg.size()); + for (int i = idx + outmsg.size(); i < 8; i++) + formatted_msg_buff[i] = 0; + + if (this->verbose) { + printf("Replying to %X.\n ", outaddr); + for (int i = 0; i < 8; i++) printf("%02X ", formatted_msg_buff[i]); + printf("\n"); + } + + this->panda->can_send(outaddr, msg.addr_29b, formatted_msg_buff, 8, panda::PANDA_CAN1); //outmsg.size() + 3 + } else { + uint8_t first_msg_len = this->ext_addr ? + min(2, outmsg.size() % 7) : //EXT ADDR VALUES + min(3, outmsg.size() % 7); //NORMAL ADDR VALUES + uint8_t payload_len = outmsg.size() + 3; + + unsigned int idx = 0; + if (this->ext_addr) + formatted_msg_buff[idx++] = 0x13; //EXT ADDR + formatted_msg_buff[idx++] = 0x10 | ((payload_len >> 8) & 0xF); + formatted_msg_buff[idx++] = payload_len & 0xFF; + formatted_msg_buff[idx++] = 0x40 | dat[1]; + formatted_msg_buff[idx++] = dat[2]; //PID + formatted_msg_buff[idx++] = 1; + memcpy_s(&formatted_msg_buff[idx], sizeof(formatted_msg_buff) - idx, outmsg.c_str(), first_msg_len); + + if (this->verbose) { + printf("Replying FIRST FRAME to %X.\n ", outaddr); + for (int i = 0; i < 8; i++) printf("%02X ", formatted_msg_buff[i]); + printf("\n"); + } + + this->panda->can_send(outaddr, msg.addr_29b, formatted_msg_buff, 8, panda::PANDA_CAN1); + for (int i = first_msg_len; i < outmsg.size(); i++) + this->can_multipart_data.push(outmsg[i]); + } + } + } +} + +std::string ECUsim::process_obd_msg(UCHAR mode, UCHAR pid, bool& return_data) { + std::string tmp; + return_data = TRUE; + + switch (mode) { + case 0x01: // Mode : Show current data + switch (pid) { + case 0x00: //List supported things + return "\xff\xff\xff\xfe"; //b"\xBE\x1F\xB8\x10" #Bitfield, random features + case 0x01: // Monitor Status since DTC cleared + return std::string("\x00\x00\x00\x00", 4); //Bitfield, random features + case 0x04: // Calculated engine load + return "\x2f"; + case 0x05: // Engine coolant temperature + return "\x3c"; + case 0x0B: // Intake manifold absolute pressure + return "\x90"; + case 0x0C: // Engine RPM + return "\x1A\xF8"; + case 0x0D: // Vehicle Speed + return "\x53"; + case 0x10: // MAF air flow rate + return "\x01\xA0"; + case 0x11: // Throttle Position + return "\x90"; + case 0x33: // Absolute Barometric Pressure + return "\x90"; + default: + return_data = FALSE; + return ""; + } + case 0x09: // Mode : Request vehicle information + switch (pid) { + case 0x02: // Show VIN + return "1D4GP00R55B123456"; + case 0xFC: // test long multi message.Ligned up for LIN responses + for (int i = 0; i < 80; i++) { + tmp += "\xAA\xAA"; + } + return tmp;//">BBH", 0xAA, 0xAA, num + 1) + case 0xFD: // test long multi message + for (int i = 0; i < 80; i++) { + tmp += "\xAA\xAA\xAA"; + tmp.push_back(i >> 24); + tmp.push_back((i >> 16) & 0xFF); + tmp.push_back((i >> 8) & 0xFF); + tmp.push_back(i & 0xFF); + } + return "\xAA\xAA\xAA" + tmp; + case 0xFE: // test very long multi message + tmp = "\xAA\xAA\xAA"; + for (int i = 0; i < 584; i++) { + tmp += "\xAA\xAA\xAA"; + tmp.push_back(i >> 24); + tmp.push_back((i >> 16) & 0xFF); + tmp.push_back((i >> 8) & 0xFF); + tmp.push_back(i & 0xFF); + } + return tmp + "\xAA"; + case 0xFF: + for (int i = 0; i < 584; i++) { + tmp += "\xAA\xAA\xAA\xAA\xAA"; + tmp.push_back(((i + 1) >> 8) & 0xFF); + tmp.push_back((i + 1) & 0xFF); + } + return std::string("\xAA\x00\x00", 3) + tmp; + default: + return_data = FALSE; + return ""; + } + case 0x3E: + if (pid == 0) { + return_data = TRUE; + return ""; + } + return_data = FALSE; + return ""; + default: + return_data = FALSE; + return ""; + } +} diff --git a/drivers/windows/ECUsim DLL/ECUsim.h b/drivers/windows/ECUsim DLL/ECUsim.h new file mode 100644 index 0000000000..2f5fe0f7ad --- /dev/null +++ b/drivers/windows/ECUsim DLL/ECUsim.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include "panda_shared/panda.h" +#include + +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the ECUSIMDLL_EXPORTS +// symbol defined on the command line. This symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// ECUSIMDLL_API functions as being imported from a DLL, whereas this DLL sees symbols +// defined with this macro as being exported. +#ifdef ECUSIMDLL_EXPORTS +#define ECUSIMDLL_API __declspec(dllexport) +#else +#define ECUSIMDLL_API __declspec(dllimport) +#endif + +// This class is exported from the ECUsim DLL.dll +class ECUSIMDLL_API ECUsim { +public: + ECUsim(std::string sn, unsigned long can_baud, bool ext_addr = FALSE); + ECUsim(panda::Panda && p, unsigned long can_baud, bool ext_addr = FALSE); + ~ECUsim(); + + void stop(); + void join(); + + // Flag determines if verbose output is enabled + volatile bool verbose; + BOOL ext_addr; +private: + std::unique_ptr panda; + + static DWORD WINAPI _canthreadBootstrap(LPVOID This); + DWORD can_recv_thread_function(); + + BOOL _can_addr_matches(panda::PANDA_CAN_MSG & msg); + + void _CAN_process_msg(panda::PANDA_CAN_MSG & msg); + + std::string process_obd_msg(UCHAR mode, UCHAR pid, bool& return_data); + + HANDLE thread_can; + volatile bool doloop; + std::queue can_multipart_data; + + BOOL can11b_enabled; + BOOL can29b_enabled; +}; diff --git a/drivers/windows/ECUsim DLL/dllmain.cpp b/drivers/windows/ECUsim DLL/dllmain.cpp new file mode 100644 index 0000000000..69b58914b3 --- /dev/null +++ b/drivers/windows/ECUsim DLL/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/drivers/windows/ECUsim DLL/stdafx.cpp b/drivers/windows/ECUsim DLL/stdafx.cpp new file mode 100644 index 0000000000..b4056ecd06 --- /dev/null +++ b/drivers/windows/ECUsim DLL/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// ECUsim DLL.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/ECUsim DLL/stdafx.h b/drivers/windows/ECUsim DLL/stdafx.h new file mode 100644 index 0000000000..f3a07375c7 --- /dev/null +++ b/drivers/windows/ECUsim DLL/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + + +// TODO: reference additional headers your program requires here diff --git a/drivers/windows/ECUsim DLL/targetver.h b/drivers/windows/ECUsim DLL/targetver.h new file mode 100644 index 0000000000..87c0086de7 --- /dev/null +++ b/drivers/windows/ECUsim DLL/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/drivers/windows/README.md b/drivers/windows/README.md new file mode 100644 index 0000000000..06c7a51101 --- /dev/null +++ b/drivers/windows/README.md @@ -0,0 +1,144 @@ +``` + ;" ^; ;' ", +______/\\\\\\\\\\\____/\\\\\\\\\_______/\\\\\\\\\\\\\\\______/\\\\\\\\\\_____________/\\\____ ; s$$$$$$$s ; + _____\/////\\\///___/\\\///////\\\____\/\\\///////////_____/\\\///////\\\__________/\\\\\____ , ss$$$$$$$$$$s ,' + _________\/\\\_____\///______\//\\\___\/\\\_______________\///______/\\\_________/\\\/\\\____ ;s$$$$$$$$$$$$$$$ + _________\/\\\_______________/\\\/____\/\\\\\\\\\\\\_____________/\\\//________/\\\/\/\\\____ $$$$$$$$$$$$$$$$$$ + _________\/\\\____________/\\\//______\////////////\\\__________\////\\\_____/\\\/__\/\\\____ $$$$P""Y$$$Y""W$$$$$ + _________\/\\\_________/\\\//____________________\//\\\____________\//\\\__/\\\\\\\\\\\\\\\\_ $$$$ p"$$$"q $$$$$ + __/\\\___\/\\\_______/\\\/____________/\\\________\/\\\___/\\\______/\\\__\///////////\\\//__ $$$$ .$$$$$. $$$$ + _\//\\\\\\\\\_______/\\\\\\\\\\\\\\\_\//\\\\\\\\\\\\\/___\///\\\\\\\\\/_____________\/\\\____ _ $$$$$$$$$$$$$$$$ + __\/////////_______\///////////////___\/////////////_______\/////////_______________\///_____| | "Y$$$"*"$$$Y" + _ __ __ _ _ __ __| | __ _"$b.$$" + | '_ \ / _` | '_ \ / _` |/ _` | + | |_) | (_| | | | | (_| | (_| | + | .__/ \__,_|_| |_|\__,_|\__,_| + | | A comma.ai product. + |_| (Code by Jessy Diamond Exum) +``` + + +# Installing J2534 driver: + +[Download](https://github.com/commaai/panda/files/1742802/panda.J2534.driver.install.zip) + +Depending on what version of windows you are on, you may need to separately install the WinUSB driver (see next section). + +# Installing WinUSB driver: + +Installation automatically happens for Windows 8 and Windows 10 because the panda +firmware contains the USB descriptors necessary to auto-install the WinUSB driver. + +Windows 7 will not auto-install the WinUSB driver. You can use Zadig to install +the WinUSB driver. This software is not tested on anything before 7. + +More details here: +[WinUSB (Winusb.sys) Installation](https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-installation) +[WCID Devices](https://github.com/pbatard/libwdi/wiki/WCID-Devices) +[Zadig for installing libusb compatible driver](https://github.com/pbatard/libwdi/wiki/Zadig) + +# Using J2534: + +After installing the J2534 drivers for the panda, you can do... nothing. +You first need to get a J2534 client that can load the drivers and talk to +the panda for you. + +A simple tool for testing J2534 drivers is DrewTech's 'J2534-1 Bus Analysis +Tool' available in the 'Other Support Applications' section of their +[Download Page](http://www.drewtech.com/downloads/). + +# What is J2534? + +J2534 is an API that tries to provide a consistent way to send/receive +messages over the many different protocols supported by the OBD II +port. The place this is perhaps most obvious, is sending data over +different protocols (each using unique packetizing methods) using the +same data format. + +For each PassThru Device that should be used with J2534 (in this case, +the panda), a 'driver' has to be written that can be loaded by a +client application wanting to send/receive data. + +A lot of J2534 has good ideas behind it, but the standard has some odd choices: + +* Platform Locked: Requires using the Windows Registry to find installed J2534 libraries/drivers. Drivers have to be DLLs. +* Architecture Locked: So far there is only support for x86. +* No device autodetect, and poor support for selecting from multiple devices. +* Constant vague language about important behavior (small differences between vendors). +* Most common differences become standard in later revisions. + +# Why use J2534 with the panda? + +J2534 is the only interface supported by most professional grade +vehicle diagnostics systems (such as HDS). These tools are useful for +diagnosing vehicles, as well as reverse engineering some lesser known +features. + +# What parts are supported with panda? + +- [ ] **J1850VPW** *(Outdated, and not physically supported by the panda)* +- [ ] **J1850PWM** *(Outdated, and not physically supported by the panda)* +- [X] **CAN** +- [X] **ISO15765** +- [ ] **ISO9141** *(This protocol could be implemented if 5 BAUD init support is added to the panda.)* +- [ ] **ISO14230/KWP2000** *(Could be supported with FAST init, 5baud init if panda adds support for 5bps serial)* + +# Building the Project: + +This project is developed with Visual Studio 2017, the Windows SDK, +and the Windows Driver Kit (WDK). + +The WDK is only required for creating the signed WinUSB inf file. The +WDK may also provide the headers for WinUSB. + +To build all the projects required for the installer, in Visual +Studio, select **Build->Batch Build.** In the project list select: + +- **"panda"** *Release|x86* +- **"panda"** *Release|x64* +- **"panda Driver Package"** Debug|x86 (Note this inf file works with x86/amd64). +- **"pandaJ2534DLL"** *Release|x86* + +The installer is generated with [NullSoft NSIS](http://nsis.sourceforge.net/Main_Page). +Use NSIS to run panda_install.nsi after building all the required projects. + +Before generating the installer, you must go to copy vscruntimeinfo.nsh.sample to +vscruntimeinfo.nsh and follow the instructions to bundle in the Visual Studio C +Runtime required by your version of Visual Studio. Without this runtime, the panda +code will not work, so without this file, the installer will refuse to build. + +# Developing: + +- Edit and merge pandaJ2534DLL\J2534register_x64.reg to register your development J2534 DLL. +- Add your output directory (panda\drivers\windows\Debug_x86) to your system PATH to avoid insanity. + +# ToDo Items: + +- Apply a style-guide and consistent naming convention for Classes/Functions/Variables. +- Send multiple messages (each with a different address) from a given connection at the same time. +- Implement ISO14230/KWP2000 FAST (LIN communication is already supported with the raw panda USB driver). +- Find more documentation about SW_CAN_PS (Single Wire CAN, aka GMLAN). +- Find example of client using a _PS version of a protocol (PS is pin select, and may support using different CAN buses). + + +# Known Issues: + +- ISO15765 Multi-frame TX: Hardware delays make transmission overshoot + STMIN by several milliseconds. This does not violate the requirements + of STMIN, it just means it is a little slower than it could be. + +- All Tx messages from a single Connection are serialized. This can be + relaxed to allow serialization of messages based on their address + (making multiple queues, effectively one queue per address). + +# Troubleshooting: +troubleshooting: +1. Install DrewTech J2534-1 Bus Analysis Tool +http://www.drewtech.com/downloads/tools/Drew%20Technologies%20Tool%20for%20J2534-1%20API%20v1.07.msi +2. Open DrewTech tool and make sure it shows "panda" as a device listed (this means registry settings are correct) +3. When DrewTech tool attempts to load the driver it will show an error if it fails +4. To figure out why the driver fails to load install Process Monitor and filter by the appropriate process name +https://docs.microsoft.com/en-us/sysinternals/downloads/procmon + +# Other: +Panda head ASCII art by dcau \ No newline at end of file diff --git a/drivers/windows/docs/Message_Size.png b/drivers/windows/docs/Message_Size.png new file mode 100644 index 0000000000..3a20a2ef39 Binary files /dev/null and b/drivers/windows/docs/Message_Size.png differ diff --git a/drivers/windows/docs/RxBits_defs.jpg b/drivers/windows/docs/RxBits_defs.jpg new file mode 100644 index 0000000000..db01f62c95 Binary files /dev/null and b/drivers/windows/docs/RxBits_defs.jpg differ diff --git a/drivers/windows/docs/RxBits_valid.png b/drivers/windows/docs/RxBits_valid.png new file mode 100644 index 0000000000..6f7c4c326d Binary files /dev/null and b/drivers/windows/docs/RxBits_valid.png differ diff --git a/drivers/windows/docs/bus_init_signla.png b/drivers/windows/docs/bus_init_signla.png new file mode 100644 index 0000000000..9aa71faa27 Binary files /dev/null and b/drivers/windows/docs/bus_init_signla.png differ diff --git a/drivers/windows/docs/connection_flags.png b/drivers/windows/docs/connection_flags.png new file mode 100644 index 0000000000..dd578a2cb9 Binary files /dev/null and b/drivers/windows/docs/connection_flags.png differ diff --git a/drivers/windows/docs/iso15765_ioctls.png b/drivers/windows/docs/iso15765_ioctls.png new file mode 100644 index 0000000000..7de4a118e0 Binary files /dev/null and b/drivers/windows/docs/iso15765_ioctls.png differ diff --git a/drivers/windows/docs/message_send.png b/drivers/windows/docs/message_send.png new file mode 100644 index 0000000000..112aab14cb Binary files /dev/null and b/drivers/windows/docs/message_send.png differ diff --git a/drivers/windows/docs/msg_filter_passfail.png b/drivers/windows/docs/msg_filter_passfail.png new file mode 100644 index 0000000000..4a91facb45 Binary files /dev/null and b/drivers/windows/docs/msg_filter_passfail.png differ diff --git a/drivers/windows/docs/other notes.txt b/drivers/windows/docs/other notes.txt new file mode 100644 index 0000000000..a066d26465 --- /dev/null +++ b/drivers/windows/docs/other notes.txt @@ -0,0 +1,347 @@ +When using the ISO 15765-4 protocol, only SingleFrame messages can be transmitted without a matching +flow control filter. Also, PCI bytes are transparently added by the API. See PassThruStartMsgFilter +and Appendix A for a discussion of flow control filters. + + + +PassThruReadMsgs +This function reads messages and indications from the receive buffer. All messages and indications shall +be read in the order that they occurred on the bus. If a transmit message generated a loopback message +and TxDone indication, the TxDone indication shall always be queued first. Except for loopback messages +and indications, no messages shall be queued for reception without matching a PASS_FILTER +(for non-ISO 15765) or FLOW_CONTROL filter (for ISO 15765). On ISO 15765, PCI bytes are transparently +removed by the API. If the function is successful, a value of STATUS_NOERROR is returned. + + +PassThruWriteMsgs +Write timeout (in milliseconds). When a value of 0 is specified, the function queues as +many of the specified messages as possible and returns immediately. When a value +greater than 0 is specified, the function will block until the Timeout has expired, an error +has occurred, or the desired number of messages have been transmitted on the vehicle +network. Even if the device can buffer only one packet at a time, this function shall be +able to send an arbitrary number of packets if a Timeout value is supplied. Since the +function returns early if all the messages have been sent, there is normally no penalty for +having a large timeout (several seconds). If the number of messages requested have +been written, the function shall not return ERR_TIMEOUT, even if the timeout value is +zero. + +When an ERR_TIMEOUT is returned, only the number of messages that were sent on +the vehicle network is known. The number of messages queued is unknown. Application +writers should avoid this ambiguity by using a Timeout value large enough to work on +slow devices and networks with arbitration delays. + + + +PassThruStartPeriodicMsg +This function will immediately queue the specified message for transmission, and repeat at the specified +interval. Periodic messages are limited in length to a single frame message of 12 bytes or less, including +header or CAN ID. Periodic messages shall have priority over messages queued with +PassThruWriteMsgs, but periodic messages must not violate bus idle timing parameters (e.g. P3_MIN). +Periodic messages shall generate TxDone indications (ISO 15765) and loopback messages (on any +protocol, if enabled). On ISO 15765, periodic messages can be sent during a multi-frame transmission or +reception. If the function is successful, a value of STATUS_NOERROR is returned. The Pass-Thru +device must support a minimum of ten periodic messages. + +PassThruDisconnect shall delete all periodic messages on that channel. PassThruClose shall delete all +periodic messages on all channels for the device. All periodic messages will be stopped on a +PassThruDisconnect for the associated protocol or a PassThruClose for the device. + + + +PASSTHRUSTARTMSGFILTER +This function starts filtering of incoming messages. If the function is successful, a value of +STATUS_NOERROR is returned. A minimum of ten message filters shall be supported by the interface +for each supported protocol. PassThruDisconnect shall delete all message filters on that channel. + +PassThruClose shall delete all filters on all channels for the device. Pattern and Mask messages shall +follow the protocol formats specified in Section 8. However, only the first twelve (12) bytes, including +header or CAN ID, are used by the filter. ERR_INVALID_MSG shall be returned if the filter length +exceeds 12. Note that this function does not clear any messages that may have been received and +queued before the filter was set. Users are cautioned to consider performing a CLEAR_RX_BUFFER +after starting a message filter to be sure that unwanted frames are purged from any receive buffers. + + + + + + + + +FILTER RELATED STUFF +For all protocols except ISO 15765: +• PASS_FILTERs and BLOCK_FILTERs will be applied to all received messages. They shall not be +applied to indications or loopback messages + +• FLOW_CONTROL_FILTERs must not be used and shall cause the interface to return +ERR_INVALID_FILTER_ID + +• Both pMaskMsg and pPatternMsg must have the same DataSize and TxFlags. Otherwise, the +interface shall return ERR_INVALID_MSG + +• The default filter behavior after PassThruConnect is to block all messages, which means no messages +will be placed in the receive queue until a PASS_FILTER has been set. Messages that match a +PASS_FILTER can still be blocked by a BLOCK_FILTER + +• Figure 16 and Figure 17 show how the message filtering mechanism operates + +For ISO 15765: +• PASS_FILTERs and BLOCK_FILTERs must not be used and shall cause the interface to return +ERR_INVALID_FILTER_ID + +• Filters shall not be applied to indications or loopback messages. When loopback is on, the original +message shall be copied to the receive queue upon the last segment being transmitted on the bus. + +• Non-segmented messages do not need to match a FLOW_CONTROL_FILTER. + +• No segmented messages can be transmitted without matching an appropriate FLOW_CONTROL_FILTER. +An appropriate filter is one in which the pFlowControlMsg CAN ID matches the messages to be +transmitted. Also, the ISO 15765_ADDR_TYPE (reference TxFlags in Section 8.7.3) bits must match. +If that bit is set, the first byte after the CAN IDs (the extended address) +must match too. + +• No message (segmented or unsegmented) shall be received without matching an appropriate +FLOW_CONTROL_FILTER. An appropriate filter is one in which the pPatternMsg CAN ID matches +the incoming message ID. If the ISO 15765_ADDR_TYPE (reference TxFlags in Section 8.7.3) bit is +set in the filter, the first byte after the CAN IDs (the extended address) must match too. + +• All 3 message pointers must have the same DataSize and TxFlags. Otherwise, the interface shall +return ERR_INVALID_MSG. + +• Both the pFlowControlMsg ID and the pPatternMsg ID must be unique (not match any IDs in any other +filters). The only exception is that pPatternMsg can equal pFlowControlMsg to allow for receiving +functionally addressed messages. In this case, only non-segmented messages can be received. + +• See Appendix A for a detailed description of flow control filter usage. + + + + +8.4 Format Checks for Messages Passed to the API +The vendor DLL shall validate all PASSTHRU_MSG structures, and return an ERR_INVALID_MSG in the following cases: +• DataSize violates Min Tx or Max Tx columns in Figure 42 + +• Source address (Data[3]) is different from the Node ID (Ioctl SET_CONFIG, Parameter NODE_ADDRESS) on J1850PWM + +• The header length field is incorrect for the number of bytes in the message on ISO14230 + +• The CAN_29_BIT flag of the message does not match the CAN_29_BIT flag passed to +PassThruConnect, unless the CAN_ID_BOTH bit was set on connect + +The vendor DLL shall return ERR_MSG_PROTOCOL_ID when the ProtocolID field in the message does +not match the Protocol ID specified when opening the channel. + + + +8.5 Conventions for Returning Messages from the API +When returning a message in PassThruReadMsg: +– DataSize shall tell the application how many bytes in the Data array are valid. ExtraDataIndex will be +the (non-zero) index of the last byte of the message. If ExtraDataIndex is not equal to DataSize there +are extra data bytes after the message. If loopback is on, RxStatus must be consulted to tell if the +message came via loopback. + +– DataSize will be in the range shown in the Min Rx and Max Rx columns of Figure 42. If the device +receives a message from the vehicle bus that is too long or too short, the message shall be discarded +with no error. + +– For received messages, ExtraDataIndex shall be equal to DataSize, except when the interface is +returning SAE J1850 PWM IFR bytes. In no case shall ExtraDataIndex be larger than DataSize. + +– When receiving a message on an SAE J1850 PWM channel, the message shall have any IFR bytes +appended. In this case, ExtraDataIndex shall be the index of the first IFR byte, and DataSize shall be +the total length of the original message plus all IFR bytes. For example, if there are two IFR bytes, +DataSize will be incremented by two, and ExtraDataIndex will be DataSize - 2. When loopback is on, +the loopback message shall contain any IFR bytes. + + + +8.6 Conventions for Retuning Indications from the API +When returning an indication in PassThruReadMsg: +– ExtraDataIndex must be zero + +– DataSize shall tell the application how many bytes in the Data array are valid + +– RxStatus must be consulted to determine the indication type (See Section 8.4). + +– A TxDone indication (ISO 15765 only) is generated by the DLL after a SingleFrame message is sent, +or the last frame of a multi-segment transmission is sent. DataSize shall be 4 (or 5 when the message +was using Extended Addressing). Data shall contain the CAN ID (and possible Extended Address) of +the message just sent. If loopback is on, the TxDone indication shall precede the loopback message in +the receive queue. + +– An RxBreak indication (SAE J2610/SCI and SAE J1850VPW only) is generated by the DLL if a break +is received. + +– An RxStart indication is generated by the DLL when starting to receive a message on ISO9141 or +ISO14230, or when receiving the FirstFrame signal of a multi-segment ISO 15765 message. + + + +9.1 Naming of Files +Each vendor will provide a different name implementation of the API DLL and a number of these +implementations could simultaneously reside on the same PC. No vendor shall name its implementation +“J2534.DLL”. All implementations shall have the string “32” suffixed to end of the name of the API DLL to +indicate 32-bit. For example, if the company name is “Vendor X” the name could be VENDRX32.DLL. + +For simplicity, an API DLL shall be named in accordance with the file allocation table (FAT) file system +naming convention (which allows up to eight characters for the file name and three characters for the +extension with no spaces anywhere). Note that, given this criteria, the major name of an API DLL can be +no greater than six characters. The OEM application can determine the name of the appropriate vendor’s +DLL using the Win32 Registry mechanism described in this section. + + + + +A.1 Flow Control Overview +ISO 15765-2 was designed to send blocks of up to 4095 bytes on top of the limited 8-byte payload of raw +CAN frames. If the data is small enough, it can fit in a single frame and be transmitted like a raw CAN +message with additional headers. Otherwise, the block is broken up into segments and becomes a +segmented transmission, generating CAN frames in both directions. For flexibility, the receiver of the +segments can control the rate at which the segments are sent. + +Each transmission is actually part of a conversation between two nodes. There is no discovery +mechanism for conversation partners. Therefore, each desired conversation must be pre-defined on each +side before the conversation can start. Conversations are symmetric, meaning that either side can send a +block of data to the other. A conversation can only have one transfer (in one direction) in progress at a +time. One transfer must complete before the next transfer (in the same or in a different direction) can +start. The device must support multiple transfers at once, as long as each one is part of a different +conversation. Raw CAN frames are not allowed when using ISO15765-2. + +A key feature of a conversation is that each side has a unique CAN ID, and each side uses their unique +CAN ID for all transmissions during the conversation. No other CAN IDs are part of the conversation. +Even though the useful data is only flowing in one direction, both sides are transmitting. One side is +sending the flow control message to pace the segments of data coming from the other side. + +For example, during OBD communication, a pass-thru device and an ECU might have a conversation. +The pass-thru device will use the "Tester1" physical CAN ID ($241), and the first ECU will use the +"ECU1" physical CAN ID ($641). During a multi-segment transfer, both sides will be transmitting using +only their respective IDs. It does not matter if the data is being sent by the ECU or by the Tester, the IDs +remain the same. + +It is important to understand the difference between OBD Requests/Responses and ISO 15765-2 +transfers. The OBD Request is transmitted from the Tester to the ECU using functional addressing. +Because segmented transfer is not possible on functional addresses, the message must fit in a single +frame. The OBD Response is a message from the ECU to the Tester using physical addressing. Unlike +other protocols, the responses are not sequential. In fact, the responses can overlap, as if each ECU +were having a private conversation with the Tester. Some of the responses may fit in a single frame, +while others will require a segmented transfer from the ECU to the tester. + + +A.2 Transmitting a Segmented Message +When PassThruWrite is called, the API will search the list of flow control filters, looking for a +pFlowControlMsg that matches the CAN ID (and possible extended address) of the message being sent. +Upon matching a filter, the pass-thru device will: + +• Start the ISO 15765 transfer by sending a FirstFrame on the bus. The CAN ID of this segment was +specified in both the message and the matching pFlowControlMsg. In our example, this is $241. + +• Wait for a FlowControl frame from the conversation partner. The CAN ID to look for is specified in the +corresponding pPatternMsg. In our example, this is $641. + +• Transmit the message data in ConsecutiveFrames according to the FlowControl frame’s instructions +for BS (BlockSize) and STmin (SeparationTime minimum). Again, the pass-thru device transmits using +CAN ID specified in pFlowControlMsg. In our example, this is $241. + +• Repeat the previous two steps as required. + +• When finished, the pass-thru device will place a TxDone indication in the API receive queue. The data +will contain the CAN ID specified in pFlowControlMsg. In our example, this is $241. + +• If loopback is on, the entire message sent will appear in the API receive queue with the +TX_MSG_TYPE bit set to 1 in RxStatus. The loopback shall not precede the TxDone indication. + +Before any multi-segment transfer can take place, the conversation must be set up on both sides. It’s +assumed that the ECU is already setup. The application is responsible for setting up the pass-thru device. +This setup must be done once (and only once) per conversation. The setup involves a single call to +PassThruStartMsgFilter, with the following parameters: + +A.2.2 Data Transmission +Once the conversation is set up, any number of messages (to the conversation partner) can be +transmitted using PassThruWriteMsg. The interface shall handle all aspects of the transfer, including +pacing (slowing) the transmission to the requirements of the receiver. + +When there are multiple conversations setup, the pass-thru device will search all of the flow control filters +for a matching pFlowControlMsg. If there is no match, the message cannot be sent because the pass- +thru device doesn’t know which partner will be pacing the conversation. + +When doing blocking writes, it is important to pick a timeout long enough to cover entire transfer, even if +the ECU is pacing things slowly. Otherwise PassThruWriteMsg will return with a timeout, even though the +transmission is proceeding normally. + + +A.3 Transmitting an Unsegmented Message +As a special case, transfers that fit in a single frame can be transmitted without setting up a conversation. +This is useful during an OBD Request, which is a functionally addressed message that is broadcast to all +ECUs. This message must be small enough to fit into a single frame (including headers) because it is not +possible to do one segmented transfer to multiple ECUs. + +When using functional addressing for an OBD Request, it is important to remember that there can be no +direct reply. Instead, each ECU will send their OBD Response using physical addressing to their +conversation partner (e.g. ECU1 to Tester1, ECU2 to Tester2) as defined by ISO 15765-4. The OBD +Response may be a segmented transfer, or it may be a single frame. + +In this case, no conversation setup is necessary. The call to PassThruWriteMsg is the same as above, +except that the DataSize must be 7 bytes or less (6 bytes or less if extended addressing is turned on). +The pass-thru device will automatically insert a PCI byte before transmission. + + +A.4 Receiving a Segmented Message +Message reception is asynchronous to the application. When a FirstFrame is seen on the bus, the pass- +thru device will search the list of flow control filters, looking for a pPatternMsg message with the same +CAN ID (and possible extended address) as the FirstFrame. Upon matching a filter, the pass-thru device will: + +• Place an RxStart indication in the API receive queue. This indication has the START_OF_MESSAGE +bit set in RxFlags. The message data will contain the CAN ID of the sender. In our example, this is +$641. DataSize will be 4 bytes (5 with extended addressing), and ExtraDataIndex will be zero. + +• Send a FlowControl frame to the conversation partner. The FlowStatus field shall be set to +ontinueToSend. The CAN ID of this segment comes from the filter’s corresponding +pFlowControlMsg. In our example, this CAN ID is $241. The BS (BlockSize) and STmin +(SeparationTime minimum) parameters default to zero, but can be changed with the SET_CONFIGIoctl. + +• Wait for the conversation partner to send C +onsecutiveFrames containing the actual data. The +partner’s CAN ID is specified in pPatternMsg. In our example, this CAN ID is $641. + +• Repeat as necessary until the entire block has been received. When finished, the pass-thru device will +put the assembled message into the API receive queue. The CAN ID of the assembled message will +be the CAN ID of the sender. In our example, this CAN ID is $641. + +If the FirstFrame does not match any flow control filters, then the message must be ignored by the +device. + +Segmented messages cause the API to generate an RxStart indication. This lets the application know +that the device has started message reception. It may take a while before message reception is +complete, especially if the application has increased BS and STmin. + +Once the transfer is complete, the entire message can be read like on any other protocol. Usually, +applications will call PassThruReadMsg again immediately after getting an RxStart indication. Application +writers should not assume that the complete message will always follow the RxStart indication. If multiple +conversations are setup, indications and messages from other conversations can be received in between +the RxStart indication and the actual message. The parameters for PassThruReadMsg are exactly the +same as in the previous section. The only difference is that the DataSize will be larger and +ExtraDataIndex will be non-zero. + + + +A.5 Receiving an Unsegmented Message +No messages can be received until a conversation is setup. Each conversation setup will receive +messages from exactly one CAN ID (and extended address if present). Because setup is bi-directional, +the same PassThruStartMsgFilter call used for transmission will allow for message reception. + +When a SingleFrame is seen on the bus, the pass-thru device will search the list of flow control filters, +looking for a pPatternMsg message with the same C +AN ID (and possible extended address) as the +SingleFrame. Upon matching a filter, the pass-thru device will strip the PCI byte and queue the packet for +reception. If the SingleFrame does not match a flow control filter, it must be discarded. + +The only difference between the previous cases is that single-frame messages do not generate an +RxStart indication. + + + + + + + + + diff --git a/drivers/windows/docs/read_msg_flags.png b/drivers/windows/docs/read_msg_flags.png new file mode 100644 index 0000000000..6d0a1f8742 Binary files /dev/null and b/drivers/windows/docs/read_msg_flags.png differ diff --git a/drivers/windows/docs/reginfo.txt b/drivers/windows/docs/reginfo.txt new file mode 100644 index 0000000000..03182832b2 --- /dev/null +++ b/drivers/windows/docs/reginfo.txt @@ -0,0 +1,2 @@ +#32 bit: HKEY_LOCAL_MACHINE\SOFTWARE\PassThruSupport +#64 bit: HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\PassThruSupport diff --git a/drivers/windows/docs/start_msg_filter.png b/drivers/windows/docs/start_msg_filter.png new file mode 100644 index 0000000000..7cb1a7a4c8 Binary files /dev/null and b/drivers/windows/docs/start_msg_filter.png differ diff --git a/drivers/windows/docs/start_msg_filter2.png b/drivers/windows/docs/start_msg_filter2.png new file mode 100644 index 0000000000..54063a5074 Binary files /dev/null and b/drivers/windows/docs/start_msg_filter2.png differ diff --git a/drivers/windows/docs/start_msg_filter3.png b/drivers/windows/docs/start_msg_filter3.png new file mode 100644 index 0000000000..ffc8e34820 Binary files /dev/null and b/drivers/windows/docs/start_msg_filter3.png differ diff --git a/drivers/windows/docs/start_msg_filter4.png b/drivers/windows/docs/start_msg_filter4.png new file mode 100644 index 0000000000..9c21fb014d Binary files /dev/null and b/drivers/windows/docs/start_msg_filter4.png differ diff --git a/drivers/windows/docs/timeout_info.txt b/drivers/windows/docs/timeout_info.txt new file mode 100644 index 0000000000..22cfa2c5d5 --- /dev/null +++ b/drivers/windows/docs/timeout_info.txt @@ -0,0 +1,42 @@ +From focum information on NI hardware: https://forums.ni.com/t5/Automotive-and-Embedded-Networks/15765-2-with-NI-products/td-p/1454256 + +///////////////////////////////////////////////////////////////////// +Timeout Diag Command is the timeout in milliseconds the master +waits for the response to a diagnostic request message. The default is +1000 ms. + +Timeout FC (Bs) is the timeout in milliseconds the master waits +for a Flow Control frame after sending a First Frame or the last +Consecutive Frame of a block. The default is 250 ms. + +Timeout CF (Cr) is the timeout in milliseconds the master waits +for a Consecutive Frame in a multiframe response. The default is +250 ms. + +Receive Block Size (BS) is the number of Consecutive Frames the +slave sends in one block before waiting for the next Flow Control +frame. A value of 0 (default) means all Consecutive Frames are sent +in one run without interruption. + +Wait Time CF (STmin) defines the minimum time for the slave to +wait between sending two Consecutive Frames of a block. Values +from 0 to 127 are wait times in milliseconds. Values 241 to 249 +(Hex F1 to F9) mean wait times of 100 μs to 900 μs, respectively. +All other values are reserved. The default is 5 ms. + +Max Wait Frames (N_WFTmax) is the maximum number of WAIT +frames the master accepts before terminating the connection. The +default is 10. + + +There are no defined lower limits for these values; you can specify any +value down to 0. However, as you correctly pointed out, the timing is +done by Windows, and will be subject to the jitter introduced by the OS +which can easily be in the order of 10s of milliseconds. It is however +hard to give more accurate numbers as the actual jitter is dependent on +the workload of the computer +///////////////////////////////////////////////////////////////////// + +J2534 04.04 does not appear to have default adjustable parameters for +the timeout related fields. For now, these default values shall be used +in the Panda J2534 implementation. diff --git a/drivers/windows/panda Driver Package/panda Driver Package.vcxproj b/drivers/windows/panda Driver Package/panda Driver Package.vcxproj new file mode 100644 index 0000000000..5b448e96a8 --- /dev/null +++ b/drivers/windows/panda Driver Package/panda Driver Package.vcxproj @@ -0,0 +1,99 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {BD34DB24-F5DC-4992-A74F-05FAF731ABED} + {a1357fe7-03e0-4d61-85f4-09c7ed38c0c1} + v4.5 + 12.0 + $driverCurrentWindowsConfigurationName$ Debug + Win32 + panda_Driver_Package + $(LatestTargetPlatformVersion) + + + + Windows10 + true + WindowsKernelModeDriver10.0 + Utility + Package + true + + + Windows10 + false + WindowsKernelModeDriver10.0 + Utility + Package + true + + + + + + + + + + + + DbgengRemoteDebugger + + + + False + False + True + + 133563 + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + DbgengRemoteDebugger + + + + False + False + True + + 133563 + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + + + + + + + + + + + $(KMDF_VERSION_MAJOR).$(KMDF_VERSION_MINOR) + + + + + + + $(KMDF_VERSION_MAJOR).$(KMDF_VERSION_MINOR) + + + + + + \ No newline at end of file diff --git a/drivers/windows/panda Driver Package/panda Driver Package.vcxproj.filters b/drivers/windows/panda Driver Package/panda Driver Package.vcxproj.filters new file mode 100644 index 0000000000..b4cf077981 --- /dev/null +++ b/drivers/windows/panda Driver Package/panda Driver Package.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + {8E41214B-6785-4CFE-B992-037D68949A14} + inf;inv;inx;mof;mc; + + + + + Driver Files + + + \ No newline at end of file diff --git a/drivers/windows/panda Driver Package/panda.inf b/drivers/windows/panda Driver Package/panda.inf new file mode 100644 index 0000000000..69390dcbc0 Binary files /dev/null and b/drivers/windows/panda Driver Package/panda.inf differ diff --git a/drivers/windows/panda.ico b/drivers/windows/panda.ico new file mode 100644 index 0000000000..593a5cd33c Binary files /dev/null and b/drivers/windows/panda.ico differ diff --git a/drivers/windows/panda.sln b/drivers/windows/panda.sln new file mode 100644 index 0000000000..39c8a63e28 --- /dev/null +++ b/drivers/windows/panda.sln @@ -0,0 +1,92 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pandaJ2534DLL", "pandaJ2534DLL\pandaJ2534DLL.vcxproj", "{A2BB18A5-F26B-48D6-BBB5-B83D64473C77}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "panda", "panda\panda.vcxproj", "{5528AEFB-638D-49AF-B9D4-965154E7D531}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "panda_playground", "panda_playground\panda_playground.vcxproj", "{691DB635-C272-4B98-897E-0505B970DCA9}" + ProjectSection(ProjectDependencies) = postProject + {5528AEFB-638D-49AF-B9D4-965154E7D531} = {5528AEFB-638D-49AF-B9D4-965154E7D531} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "panda Driver Package", "panda Driver Package\panda Driver Package.vcxproj", "{BD34DB24-F5DC-4992-A74F-05FAF731ABED}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tests", "pandaJ2534DLL Test\pandaJ2534DLL Test.vcxproj", "{7912F978-B48C-4C5D-8BFD-5D1E22158E47}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ECUsim DLL", "ECUsim DLL\ECUsim DLL.vcxproj", "{96E0E646-EE76-444D-9A77-A0CD7F781DEB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ECUsim CLI", "ECUsim CLI\ECUsim CLI.vcxproj", "{D99E2FCD-21A4-4065-949A-31E34E0E69D1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "panda_shared", "panda_shared\panda_shared.vcxitems", "{0C843279-68C7-4679-AE51-9BC463D50D1C}" +EndProject +Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + panda_shared\panda_shared.vcxitems*{0c843279-68c7-4679-ae51-9bc463d50d1c}*SharedItemsImports = 9 + panda_shared\panda_shared.vcxitems*{5528aefb-638d-49af-b9d4-965154e7d531}*SharedItemsImports = 4 + panda_shared\panda_shared.vcxitems*{a2bb18a5-f26b-48d6-bbb5-b83d64473c77}*SharedItemsImports = 4 + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Debug|x64.ActiveCfg = Debug|Win32 + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Debug|x86.ActiveCfg = Debug|Win32 + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Debug|x86.Build.0 = Debug|Win32 + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Release|x64.ActiveCfg = Release|Win32 + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Release|x86.ActiveCfg = Release|Win32 + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Release|x86.Build.0 = Release|Win32 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x64.ActiveCfg = Debug|x64 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x64.Build.0 = Debug|x64 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x86.ActiveCfg = Debug|Win32 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x86.Build.0 = Debug|Win32 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x64.ActiveCfg = Release|x64 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x64.Build.0 = Release|x64 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x86.ActiveCfg = Release|Win32 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x86.Build.0 = Release|Win32 + {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x64.ActiveCfg = Debug|x64 + {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x64.Build.0 = Debug|x64 + {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x86.ActiveCfg = Debug|Win32 + {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x86.Build.0 = Debug|Win32 + {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x64.ActiveCfg = Release|x64 + {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x64.Build.0 = Release|x64 + {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x86.ActiveCfg = Release|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Debug|x64.ActiveCfg = Debug|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Debug|x86.ActiveCfg = Debug|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Release|x64.ActiveCfg = Release|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Release|x86.ActiveCfg = Release|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Debug|x64.ActiveCfg = Debug|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Debug|x86.ActiveCfg = Debug|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Debug|x86.Build.0 = Debug|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Release|x64.ActiveCfg = Release|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Release|x86.ActiveCfg = Release|Win32 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x64.ActiveCfg = Debug|x64 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x64.Build.0 = Debug|x64 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x86.ActiveCfg = Debug|Win32 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x86.Build.0 = Debug|Win32 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x64.ActiveCfg = Release|x64 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x64.Build.0 = Release|x64 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x86.ActiveCfg = Release|Win32 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x64.ActiveCfg = Debug|x64 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x64.Build.0 = Debug|x64 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x86.ActiveCfg = Debug|Win32 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x86.Build.0 = Debug|Win32 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x64.ActiveCfg = Release|x64 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x64.Build.0 = Release|x64 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x86.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8AF3826E-406A-4F1C-BA80-B4D7FD4B52E1} + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection +EndGlobal diff --git a/drivers/windows/panda/dllmain.cpp b/drivers/windows/panda/dllmain.cpp new file mode 100644 index 0000000000..69b58914b3 --- /dev/null +++ b/drivers/windows/panda/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/drivers/windows/panda/main.cpp b/drivers/windows/panda/main.cpp new file mode 100644 index 0000000000..621c600b82 --- /dev/null +++ b/drivers/windows/panda/main.cpp @@ -0,0 +1,79 @@ +#include "stdafx.h" + +#include + +LONG __cdecl +_tmain( + LONG Argc, + LPTSTR * Argv + ) +/*++ + +Routine description: + + Sample program that communicates with a USB device using WinUSB + +--*/ +{ + DEVICE_DATA deviceData; + HRESULT hr; + USB_DEVICE_DESCRIPTOR deviceDesc; + BOOL bResult; + BOOL noDevice; + ULONG lengthReceived; + + UNREFERENCED_PARAMETER(Argc); + UNREFERENCED_PARAMETER(Argv); + + // + // Find a device connected to the system that has WinUSB installed using our + // INF + // + hr = OpenDevice(&deviceData, &noDevice); + + if (FAILED(hr)) { + + if (noDevice) { + + printf(_T("Device not connected or driver not installed\n")); + + } else { + + printf(_T("Failed looking for device, HRESULT 0x%x\n"), hr); + } + + return 0; + } + + // + // Get device descriptor + // + bResult = WinUsb_GetDescriptor(deviceData.WinusbHandle, + USB_DEVICE_DESCRIPTOR_TYPE, + 0, + 0, + (PBYTE) &deviceDesc, + sizeof(deviceDesc), + &lengthReceived); + + if (FALSE == bResult || lengthReceived != sizeof(deviceDesc)) { + + printf(_T("Error among LastError %d or lengthReceived %d\n"), + FALSE == bResult ? GetLastError() : 0, + lengthReceived); + CloseDevice(&deviceData); + return 0; + } + + // + // Print a few parts of the device descriptor + // + printf(_T("Device found: VID_%04X&PID_%04X; bcdUsb %04X; path: %s\n"), + deviceDesc.idVendor, + deviceDesc.idProduct, + deviceDesc.bcdUSB, + deviceData.DevicePath); + + CloseDevice(&deviceData); + return 0; +} diff --git a/drivers/windows/panda/panda.ico b/drivers/windows/panda/panda.ico new file mode 100644 index 0000000000..ff0e071f54 Binary files /dev/null and b/drivers/windows/panda/panda.ico differ diff --git a/drivers/windows/panda/panda.rc b/drivers/windows/panda/panda.rc new file mode 100644 index 0000000000..88cf9f7678 Binary files /dev/null and b/drivers/windows/panda/panda.rc differ diff --git a/drivers/windows/panda/panda.vcxproj b/drivers/windows/panda/panda.vcxproj new file mode 100644 index 0000000000..22879c7cae --- /dev/null +++ b/drivers/windows/panda/panda.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {5528AEFB-638D-49AF-B9D4-965154E7D531} + Win32Proj + panda + 10.0.16299.0 + + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions) + true + false + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib + + + + + Use + Level3 + Disabled + _DEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions) + true + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib + + + + + + + + + false + + + false + + + false + + + false + + + + + Create + Create + Create + Create + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/windows/panda/panda.vcxproj.filters b/drivers/windows/panda/panda.vcxproj.filters new file mode 100644 index 0000000000..afddad6e8b --- /dev/null +++ b/drivers/windows/panda/panda.vcxproj.filters @@ -0,0 +1,43 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/drivers/windows/panda/resource.h b/drivers/windows/panda/resource.h new file mode 100644 index 0000000000..bf006ff4b3 Binary files /dev/null and b/drivers/windows/panda/resource.h differ diff --git a/drivers/windows/panda/stdafx.cpp b/drivers/windows/panda/stdafx.cpp new file mode 100644 index 0000000000..8793e09b40 --- /dev/null +++ b/drivers/windows/panda/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// panda.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/panda/stdafx.h b/drivers/windows/panda/stdafx.h new file mode 100644 index 0000000000..cc3d3b9667 --- /dev/null +++ b/drivers/windows/panda/stdafx.h @@ -0,0 +1,19 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#endif +// Windows Header Files: +#include + +#include +#include +#include +#include diff --git a/drivers/windows/pandaJ2534DLL Test/ECUsim_tests.cpp b/drivers/windows/pandaJ2534DLL Test/ECUsim_tests.cpp new file mode 100644 index 0000000000..8a9161475c --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/ECUsim_tests.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" +#include "Loader4.h" +#include "pandaJ2534DLL/J2534_v0404.h" +#include "panda_shared/panda.h" +#include "Timer.h" +#include "ECUsim DLL\ECUsim.h" +#include "TestHelpers.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace pandaWCUsimTest +{ + + TEST_CLASS(ECUsimTests) + { + public: + + TEST_METHOD(ECUsim_ISO15765_SingleFrameTx_29bStandardAddrPad500k) + { + ECUsim sim("", 500000); + auto p = getPanda(500); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x02\x01\x00", 3, panda::PANDA_CAN1); + auto msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x02\x01\x00", 3), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x06\x41\x00\xff\xff\xff\xfe\x00", 8), LINE_INFO()); + } + + TEST_METHOD(ECUsim_ISO15765_SingleFrameTx_29bStandardAddrPad250k) + { + ECUsim sim("", 250000); + auto p = getPanda(250); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x02\x01\x00", 3, panda::PANDA_CAN1); + auto msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x02\x01\x00", 3), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x06""\x41\x00""\xff\xff\xff\xfe""\x00", 8), LINE_INFO()); + } + + TEST_METHOD(ECUsim_ISO15765_SingleFrameTx_29bExtAddrPad500k) + { + ECUsim sim("", 500000, TRUE); + auto p = getPanda(500); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x13""\x02\x01\x00", 4, panda::PANDA_CAN1); + auto msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x13""\x02\x01\x00", 4), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x06""\x41\x00""\xff\xff\xff\xfe", 8), LINE_INFO()); + } + + TEST_METHOD(ECUsim_ISO15765_MultiFrameTx_29bStandardAddrPad500k) + { + ECUsim sim("", 500000); + auto p = getPanda(500); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x02\x09\x02", 3, panda::PANDA_CAN1); + auto msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x02\x09\x02", 3), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x10\x14""\x49\x02\x01""1D4", 8), LINE_INFO()); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x30\x00\x00", 3, panda::PANDA_CAN1); + msg_recv = panda_recv_loop(p, 3); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x30\x0\x0", 3), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x21""GP00R55", 8), LINE_INFO()); + check_panda_can_msg(msg_recv[2], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x22""B123456", 8), LINE_INFO()); + } + + TEST_METHOD(ECUsim_ISO15765_MultiFrameTx_29bExtAddrPad500k) + { + ECUsim sim("", 500000, TRUE); + auto p = getPanda(500); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x13""\x02\x09\x02", 4, panda::PANDA_CAN1); + auto msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x13""\x02\x09\x02", 4), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x10\x14""\x49\x02\x01""1D", 8), LINE_INFO()); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x13""\x30\x00\x00", 4, panda::PANDA_CAN1); + msg_recv = panda_recv_loop(p, 4); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x13""\x30\x0\x0", 4), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x21""4GP00R", 8), LINE_INFO()); + check_panda_can_msg(msg_recv[2], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x22""55B123", 8), LINE_INFO()); + check_panda_can_msg(msg_recv[3], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x23""456", 5), LINE_INFO()); + } + }; + +} \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/Loader4.cpp b/drivers/windows/pandaJ2534DLL Test/Loader4.cpp new file mode 100644 index 0000000000..f4a0b70fa5 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/Loader4.cpp @@ -0,0 +1,240 @@ +// Loader4.cpp +// (c) 2005 National Control Systems, Inc. +// Portions (c) 2004 Drew Technologies, Inc. +// Dynamic J2534 v04.04 dll loader for VB + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to: +// the Free Software Foundation, Inc. +// 51 Franklin Street, Fifth Floor +// Boston, MA 02110-1301, USA + +// National Control Systems, Inc. +// 10737 Hamburg Rd +// Hamburg, MI 48139 +// 810-231-2901 + +// Drew Technologies, Inc. +// 7012 E.M -36, Suite 3B +// Whitmore Lake, MI 48189 +// 810-231-3171 + +#define STRICT +#include "stdafx.h" +#include +#include "Loader4.h" + +PTOPEN LocalOpen; +PTCLOSE LocalClose; +PTCONNECT LocalConnect; +PTDISCONNECT LocalDisconnect; +PTREADMSGS LocalReadMsgs; +PTWRITEMSGS LocalWriteMsgs; +PTSTARTPERIODICMSG LocalStartPeriodicMsg; +PTSTOPPERIODICMSG LocalStopPeriodicMsg; +PTSTARTMSGFILTER LocalStartMsgFilter; +PTSTOPMSGFILTER LocalStopMsgFilter; +PTSETPROGRAMMINGVOLTAGE LocalSetProgrammingVoltage; +PTREADVERSION LocalReadVersion; +PTGETLASTERROR LocalGetLastError; +PTIOCTL LocalIoctl; + +HINSTANCE hDLL = NULL; +//BOOL bIsCorrectVersion = FALSE; + +BOOL WINAPI DllMain(HINSTANCE hInstA, DWORD dwReason, LPVOID lpvReserved) +{ + switch (dwReason) { + case DLL_PROCESS_ATTACH: + // The DLL is being mapped into the process's address space + + case DLL_THREAD_ATTACH: + // A thread is being created + break; + + case DLL_THREAD_DETACH: + // A thread is exiting cleanly + break; + + case DLL_PROCESS_DETACH: + // The DLL is being unmapped from the process's address space + break; + } + + return TRUE; +} + + +long WINAPI LoadJ2534Dll(char *sLib) +{ + long lFuncList = 0; + + if (hDLL != NULL) UnloadJ2534Dll(); + hDLL = LoadLibraryA (sLib); + if (hDLL == NULL) return ERR_NO_DLL; + + LocalOpen = (PTOPEN)(GetProcAddress(hDLL, "PassThruOpen")); + if (LocalOpen == NULL) lFuncList = lFuncList | ERR_NO_PTOPEN; + + LocalClose = (PTCLOSE)(GetProcAddress(hDLL, "PassThruClose")); + if (LocalClose == NULL) lFuncList = lFuncList | ERR_NO_PTCLOSE; + + LocalConnect = (PTCONNECT)(GetProcAddress(hDLL,"PassThruConnect")); + if (LocalConnect == NULL) lFuncList = lFuncList | ERR_NO_PTCONNECT; + + LocalDisconnect = (PTDISCONNECT)(GetProcAddress(hDLL,"PassThruDisconnect")); + if (LocalDisconnect == NULL) lFuncList = lFuncList | ERR_NO_PTDISCONNECT; + + LocalReadMsgs = (PTREADMSGS)(GetProcAddress(hDLL,"PassThruReadMsgs")); + if (LocalReadMsgs == NULL) lFuncList = lFuncList | ERR_NO_PTREADMSGS; + + LocalWriteMsgs = (PTWRITEMSGS)(GetProcAddress(hDLL,"PassThruWriteMsgs")); + if (LocalWriteMsgs == NULL) lFuncList = lFuncList | ERR_NO_PTWRITEMSGS; + + LocalStartPeriodicMsg = (PTSTARTPERIODICMSG)(GetProcAddress(hDLL,"PassThruStartPeriodicMsg")); + if (LocalStartPeriodicMsg == NULL) lFuncList = lFuncList | ERR_NO_PTSTARTPERIODICMSG; + + LocalStopPeriodicMsg = (PTSTOPPERIODICMSG)(GetProcAddress(hDLL,"PassThruStopPeriodicMsg")); + if (LocalStopPeriodicMsg == NULL) lFuncList = lFuncList | ERR_NO_PTSTOPPERIODICMSG; + + LocalStartMsgFilter = (PTSTARTMSGFILTER)(GetProcAddress(hDLL,"PassThruStartMsgFilter")); + if (LocalStartPeriodicMsg == NULL) lFuncList = lFuncList | ERR_NO_PTSTARTMSGFILTER; + + LocalStopMsgFilter = (PTSTOPMSGFILTER)(GetProcAddress(hDLL,"PassThruStopMsgFilter")); + if (LocalStopMsgFilter == NULL) lFuncList = lFuncList | ERR_NO_PTSTOPMSGFILTER; + + LocalSetProgrammingVoltage = (PTSETPROGRAMMINGVOLTAGE)(GetProcAddress(hDLL,"PassThruSetProgrammingVoltage")); + if (LocalSetProgrammingVoltage == NULL) lFuncList = lFuncList | ERR_NO_PTSETPROGRAMMINGVOLTAGE; + + LocalReadVersion = (PTREADVERSION)(GetProcAddress(hDLL,"PassThruReadVersion")); + if (LocalReadVersion == NULL) lFuncList = lFuncList | ERR_NO_PTREADVERSION; + + LocalGetLastError = (PTGETLASTERROR)(GetProcAddress(hDLL,"PassThruGetLastError")); + if (LocalGetLastError == NULL) lFuncList = lFuncList | ERR_NO_PTGETLASTERROR; + + LocalIoctl = (PTIOCTL)(GetProcAddress(hDLL,"PassThruIoctl")); + if (LocalIoctl == NULL) lFuncList = lFuncList | ERR_NO_PTIOCTL; + + if (lFuncList == ERR_NO_FUNCTIONS) return ERR_WRONG_DLL_VER; + + return lFuncList; +} + +long WINAPI UnloadJ2534Dll() +{ + if (FreeLibrary(hDLL)) + { + hDLL = NULL; + LocalOpen = NULL; + LocalClose = NULL; + LocalConnect = NULL; + LocalDisconnect = NULL; + LocalReadMsgs = NULL; + LocalWriteMsgs = NULL; + LocalStartPeriodicMsg = NULL; + LocalStopPeriodicMsg = NULL; + LocalStartMsgFilter = NULL; + LocalStopMsgFilter = NULL; + LocalSetProgrammingVoltage = NULL; + LocalReadVersion = NULL; + LocalGetLastError = NULL; + LocalIoctl = NULL; + return 0; + } + return ERR_NO_DLL; +} + +long WINAPI PassThruOpen(void *pName, unsigned long *pDeviceID) +{ + if (LocalOpen == NULL) return ERR_FUNC_MISSING; + return LocalOpen(pName, pDeviceID); +} + +long WINAPI PassThruClose(unsigned long DeviceID) +{ + if (LocalOpen == NULL) return ERR_FUNC_MISSING; + return LocalClose(DeviceID); +} + +long WINAPI PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long Baudrate, unsigned long *pChannelID) +{ + if (LocalConnect == NULL) return ERR_FUNC_MISSING; + return LocalConnect(DeviceID, ProtocolID, Flags, Baudrate, pChannelID); +} + +long WINAPI PassThruDisconnect(unsigned long ChannelID) +{ + if (LocalDisconnect == NULL) return ERR_FUNC_MISSING; + return LocalDisconnect(ChannelID); +} + +long WINAPI PassThruReadMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) +{ + if (LocalReadMsgs == NULL) return ERR_FUNC_MISSING; + return LocalReadMsgs(ChannelID, pMsg, pNumMsgs, Timeout); +} + +long WINAPI PassThruWriteMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) +{ + if (LocalWriteMsgs == NULL) return ERR_FUNC_MISSING; + return LocalWriteMsgs(ChannelID, pMsg, pNumMsgs, Timeout); +} + +long WINAPI PassThruStartPeriodicMsg(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval) +{ + if (LocalStartPeriodicMsg == NULL) return ERR_FUNC_MISSING; + return LocalStartPeriodicMsg(ChannelID, pMsg, pMsgID, TimeInterval); +} + +long WINAPI PassThruStopPeriodicMsg(unsigned long ChannelID, unsigned long MsgID) +{ + if (LocalStopPeriodicMsg == NULL) return ERR_FUNC_MISSING; + return LocalStopPeriodicMsg(ChannelID, MsgID); +} + +long WINAPI PassThruStartMsgFilter(unsigned long ChannelID, unsigned long FilterType, + PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) +{ + if (LocalStartMsgFilter == NULL) return ERR_FUNC_MISSING; + return LocalStartMsgFilter(ChannelID, FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID); +} + +long WINAPI PassThruStopMsgFilter(unsigned long ChannelID, unsigned long FilterID) +{ + if (LocalStopMsgFilter == NULL) return ERR_FUNC_MISSING; + return LocalStopMsgFilter(ChannelID, FilterID); +} + +long WINAPI PassThruSetProgrammingVoltage(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage) +{ + if (LocalSetProgrammingVoltage == NULL) return ERR_FUNC_MISSING; + return LocalSetProgrammingVoltage(DeviceID, PinNumber, Voltage); +} + +long WINAPI PassThruReadVersion(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion) +{ + if (LocalReadVersion == NULL) return ERR_FUNC_MISSING; + return LocalReadVersion(DeviceID, pFirmwareVersion, pDllVersion, pApiVersion); +} + +long WINAPI PassThruGetLastError(char *pErrorDescription) +{ + if (LocalGetLastError == NULL) return ERR_FUNC_MISSING; + return LocalGetLastError(pErrorDescription); +} + +long WINAPI PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput) +{ + if (LocalIoctl == NULL) return ERR_FUNC_MISSING; + return LocalIoctl(ChannelID, IoctlID, pInput, pOutput); +} \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/Loader4.h b/drivers/windows/pandaJ2534DLL Test/Loader4.h new file mode 100644 index 0000000000..9710144141 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/Loader4.h @@ -0,0 +1,55 @@ +// Loader4.h +// (c) 2005 National Control Systems, Inc. +// Portions (c) 2004 Drew Technologies, Inc. + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to: +// the Free Software Foundation, Inc. +// 51 Franklin Street, Fifth Floor +// Boston, MA 02110-1301, USA + +// National Control Systems, Inc. +// 10737 Hamburg Rd +// Hamburg, MI 48139 +// 810-231-2901 + +// Drew Technologies, Inc. +// 7012 E.M -36, Suite 3B +// Whitmore Lake, MI 48189 +// 810-231-3171 + +#include "pandaJ2534DLL/J2534_v0404.h" + +//Other Functions +long WINAPI LoadJ2534Dll(char *); +long WINAPI UnloadJ2534Dll(); + +// NCS Returns of any functions not found +#define ERR_NO_PTOPEN 0x0001 +#define ERR_NO_PTCLOSE 0x0002 +#define ERR_NO_PTCONNECT 0x0004 +#define ERR_NO_PTDISCONNECT 0x0008 +#define ERR_NO_PTREADMSGS 0x0010 +#define ERR_NO_PTWRITEMSGS 0x0020 +#define ERR_NO_PTSTARTPERIODICMSG 0x0040 +#define ERR_NO_PTSTOPPERIODICMSG 0x0080 +#define ERR_NO_PTSTARTMSGFILTER 0x0100 +#define ERR_NO_PTSTOPMSGFILTER 0x0200 +#define ERR_NO_PTSETPROGRAMMINGVOLTAGE 0x0400 +#define ERR_NO_PTREADVERSION 0x0800 +#define ERR_NO_PTGETLASTERROR 0x1000 +#define ERR_NO_PTIOCTL 0x2000 +#define ERR_NO_FUNCTIONS 0x3fff +#define ERR_NO_DLL -1 +#define ERR_WRONG_DLL_VER -2 +#define ERR_FUNC_MISSING -3 diff --git a/drivers/windows/pandaJ2534DLL Test/TestHelpers.cpp b/drivers/windows/pandaJ2534DLL Test/TestHelpers.cpp new file mode 100644 index 0000000000..1281eb9d61 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/TestHelpers.cpp @@ -0,0 +1,254 @@ +#include "stdafx.h" +#include "TestHelpers.h" +#include "Loader4.h" +#include "pandaJ2534DLL/J2534_v0404.h" +#include "panda_shared/panda.h" +#include "Timer.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +void write_ioctl(unsigned int chanid, unsigned int param, unsigned int val, const __LineInfo* pLineInfo) { + SCONFIG config = { param, val }; + SCONFIG_LIST inconfig = { 1, &config }; + + Assert::AreEqual(STATUS_NOERROR, PassThruIoctl(chanid, SET_CONFIG, &inconfig, NULL), _T("Failed to set IOCTL."), pLineInfo); +} + +std::vector panda_recv_loop_loose(std::unique_ptr& p, unsigned int min_num, unsigned long timeout_ms) { + std::vector ret_messages; + Timer t = Timer(); + + while (t.getTimePassed() < timeout_ms) { + Sleep(10); + std::vectormsg_recv = p->can_recv(); + if (msg_recv.size() > 0) { + ret_messages.insert(std::end(ret_messages), std::begin(msg_recv), std::end(msg_recv)); + } + } + + Assert::IsTrue(min_num <= ret_messages.size(), _T("Received too few messages.")); + return ret_messages; +} + +std::vector panda_recv_loop(std::unique_ptr& p, unsigned int num_expected, unsigned long timeout_ms) { + std::vector ret_messages; + Timer t = Timer(); + + while (t.getTimePassed() < timeout_ms) { + Sleep(10); + std::vectormsg_recv = p->can_recv(); + if (msg_recv.size() > 0) { + ret_messages.insert(std::end(ret_messages), std::begin(msg_recv), std::end(msg_recv)); + } + if (ret_messages.size() >= num_expected) break; + } + + std::ostringstream stringStream; + + stringStream << "j2534_recv_loop Broke at " << t.getTimePassed() << " ms size is " << ret_messages.size() << std::endl; + + if (num_expected != ret_messages.size()) { + stringStream << "Incorrect number of messages received. Displaying the messages:" << std::endl; + for (auto msg : ret_messages) { + stringStream << " TS: " << msg.recv_time << "; Dat: "; + for (int i = 0; i < msg.len; i++) stringStream << std::hex << std::setw(2) << std::setfill('0') << int(msg.dat[i] & 0xFF) << " "; + stringStream << std::endl; + } + } + + Logger::WriteMessage(stringStream.str().c_str()); + + Assert::AreEqual(num_expected, ret_messages.size(), _T("Received wrong number of messages.")); + return ret_messages; +} + +void check_panda_can_msg(panda::PANDA_CAN_MSG& msgin, uint8_t bus, unsigned long addr, bool addr_29b, + bool is_receipt, std::string dat, const __LineInfo* pLineInfo) { + Assert::AreEqual(bus, msgin.bus, _T("Wrong msg bus"), pLineInfo); + Assert::AreEqual(addr, msgin.addr, _T("Wrong msg addr"), pLineInfo); + Assert::AreEqual(addr_29b, msgin.addr_29b, _T("Wrong msg 29b flag"), pLineInfo); + Assert::AreEqual(is_receipt, msgin.is_receipt, _T("Wrong msg receipt flag"), pLineInfo); + + std::ostringstream logmsg; + logmsg << "Expected Hex ("; + for (int i = 0; i < dat.size(); i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(dat[i] & 0xFF) << " "; + logmsg << "); Actual Hex ("; + for (int i = 0; i < msgin.len; i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(((char*)msgin.dat)[i] & 0xFF) << " "; + logmsg << ")"; + Logger::WriteMessage(logmsg.str().c_str()); + + Assert::AreEqual(dat.size(), msgin.len, _T("Wrong msg len"), pLineInfo); + Assert::AreEqual(dat, std::string((char*)msgin.dat, msgin.len), _T("Wrong msg payload"), pLineInfo); +} + +unsigned long J2534_start_periodic_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize, + unsigned long ExtraDataIndex, const char * Data, unsigned long TimeInterval, const __LineInfo * pLineInfo) { + PASSTHRU_MSG msg = { ProtocolID, 0, TxFlags, 0, DataSize, ExtraDataIndex }; + memcpy_s(msg.Data, 4128, Data, DataSize); + unsigned long msgID; + Assert::AreEqual(STATUS_NOERROR, J2534_start_periodic_msg(chanid, ProtocolID, TxFlags, DataSize, + ExtraDataIndex, Data, TimeInterval, &msgID, pLineInfo), _T("Failed to start Periodic Message."), pLineInfo); + return msgID; +} + +unsigned long J2534_start_periodic_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize, + unsigned long ExtraDataIndex, const char * Data, unsigned long TimeInterval, unsigned long* msgID, const __LineInfo * pLineInfo) { + PASSTHRU_MSG msg = { ProtocolID, 0, TxFlags, 0, DataSize, ExtraDataIndex }; + memcpy_s(msg.Data, 4128, Data, DataSize); + return PassThruStartPeriodicMsg(chanid, &msg, msgID, TimeInterval); +} + +void J2534_send_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo) { + + PASSTHRU_MSG msg = { ProtocolID, RxStatus, TxFlags, Timestamp, DataSize, ExtraDataIndex }; + memcpy_s(msg.Data, 4128, Data, DataSize); + unsigned long msgcount = 1; + Assert::AreEqual(STATUS_NOERROR, PassThruWriteMsgs(chanid, &msg, &msgcount, 0), _T("Failed to write message."), pLineInfo); + Assert::AreEqual(1, msgcount, _T("Wrong message count after tx."), pLineInfo); +} + +long J2534_send_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data) { + + PASSTHRU_MSG msg = { ProtocolID, RxStatus, TxFlags, Timestamp, DataSize, ExtraDataIndex }; + memcpy_s(msg.Data, 4128, Data, DataSize); + unsigned long msgcount = 1; + return PassThruWriteMsgs(chanid, &msg, &msgcount, 0); +} + +//Allow more messages to come in than the min. +std::vector j2534_recv_loop_loose(unsigned int chanid, unsigned int min_num, unsigned long timeout_ms) { + std::vector ret_messages; + PASSTHRU_MSG recvbuff[4] = {}; + Timer t = Timer(); + + while (t.getTimePassed() < timeout_ms) { + unsigned long msgcount = 4; + unsigned int res = PassThruReadMsgs(chanid, recvbuff, &msgcount, 0); + if (res == ERR_BUFFER_EMPTY) continue; + Assert::IsFalse(msgcount > 4, _T("PassThruReadMsgs returned more data than the buffer could hold.")); + Assert::AreEqual(STATUS_NOERROR, res, _T("Failed to read message.")); + if (msgcount > 0) { + for (unsigned int i = 0; i < msgcount; i++) { + ret_messages.push_back(recvbuff[i]); + } + } + } + + Assert::IsTrue(min_num <= ret_messages.size(), _T("Received too few messages.")); + return ret_messages; +} + +std::vector j2534_recv_loop(unsigned int chanid, unsigned int num_expected, unsigned long timeout_ms) { + std::vector ret_messages; + PASSTHRU_MSG recvbuff[4] = {}; + Timer t = Timer(); + + while (t.getTimePassed() < timeout_ms) { + unsigned long msgcount = 4; + unsigned int res = PassThruReadMsgs(chanid, recvbuff, &msgcount, 0); + if (res == ERR_BUFFER_EMPTY) continue; + Assert::IsFalse(msgcount > 4, _T("PassThruReadMsgs returned more data than the buffer could hold.")); + Assert::AreEqual(STATUS_NOERROR, res, _T("Failed to read message.")); + if (msgcount > 0) { + for (unsigned int i = 0; i < msgcount; i++) { + ret_messages.push_back(recvbuff[i]); + } + } + if (ret_messages.size() >= num_expected) break; + } + + std::ostringstream stringStream; + stringStream << "j2534_recv_loop Broke at " << t.getTimePassed() << " ms size is " << ret_messages.size() << std::endl; + + if (num_expected != ret_messages.size()) { + stringStream << "Incorrect number of messages received. Displaying the messages:" << std::endl; + for (auto msg : ret_messages) { + stringStream << " TS: " << msg.Timestamp << "; Dat: "; + for (int i = 0; i < msg.DataSize; i++) stringStream << std::hex << std::setw(2) << std::setfill('0') << int(msg.Data[i] & 0xFF) << " "; + stringStream << std::endl; + } + } + + Logger::WriteMessage(stringStream.str().c_str()); + + Assert::AreEqual(num_expected, ret_messages.size(), _T("Received wrong number of messages.")); + return ret_messages; +} + +void check_J2534_can_msg(PASSTHRU_MSG& msgin, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo) { + Assert::AreEqual(DataSize, msgin.DataSize, _T("Wrong msg len"), pLineInfo); + + std::ostringstream logmsg; + logmsg << "Expected Hex ("; + for (int i = 0; i < DataSize; i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(Data[i] & 0xFF) << " "; + logmsg << "); Actual Hex ("; + for (int i = 0; i < msgin.DataSize; i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(((char*)msgin.Data)[i] & 0xFF) << " "; + logmsg << ")"; + Logger::WriteMessage(logmsg.str().c_str()); + Assert::AreEqual(std::string(Data, DataSize), std::string((char*)msgin.Data, msgin.DataSize), _T("Wrong msg payload"), pLineInfo); + + Assert::AreEqual(ProtocolID, msgin.ProtocolID, _T("Wrong msg protocol"), pLineInfo); + Assert::AreEqual(RxStatus, msgin.RxStatus, _T("Wrong msg receipt rxstatus"), pLineInfo); + Assert::AreEqual(TxFlags, msgin.TxFlags, _T("Wrong msg receipt txflag"), pLineInfo); + Assert::AreEqual(ExtraDataIndex, msgin.ExtraDataIndex, _T("Wrong msg ExtraDataIndex"), pLineInfo); +} + +unsigned long J2534_set_PASS_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx, + unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo) { + unsigned long filterid; + PASSTHRU_MSG mask_msg = { ProtocolID, 0, tx, 0, len, 0, 0 }; + PASSTHRU_MSG pattern_msg = { ProtocolID, 0, tx, 0, len, 0, 0 }; + memcpy(mask_msg.Data, mask, len); + memcpy(pattern_msg.Data, pattern, len); + Assert::AreEqual(STATUS_NOERROR, PassThruStartMsgFilter(chanid, PASS_FILTER, &mask_msg, &pattern_msg, NULL, &filterid), + _T("Failed to create filter."), pLineInfo); + return filterid; +} + +unsigned long J2534_set_BLOCK_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx, + unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo) { + unsigned long filterid; + PASSTHRU_MSG mask_msg = { ProtocolID, 0, tx, 0, len, 0, 0 }; + PASSTHRU_MSG pattern_msg = { ProtocolID, 0, tx, 0, len, 0, 0 }; + memcpy(mask_msg.Data, mask, len); + memcpy(pattern_msg.Data, pattern, len); + Assert::AreEqual(STATUS_NOERROR, PassThruStartMsgFilter(chanid, BLOCK_FILTER, &mask_msg, &pattern_msg, NULL, &filterid), + _T("Failed to create filter."), pLineInfo); + return filterid; +} + +unsigned long J2534_set_flowctrl_filter(unsigned long chanid, unsigned long tx, + unsigned long len, char* mask, char* pattern, char* flow, const __LineInfo* pLineInfo) { + unsigned long filterid; + PASSTHRU_MSG mask_msg = { ISO15765, 0, tx, 0, len, 0, 0 }; + PASSTHRU_MSG pattern_msg = { ISO15765, 0, tx, 0, len, 0, 0 }; + PASSTHRU_MSG flow_msg = { ISO15765, 0, tx, 0, len, 0, 0 }; + memcpy(mask_msg.Data, mask, len); + memcpy(pattern_msg.Data, pattern, len); + memcpy(flow_msg.Data, flow, len); + Assert::AreEqual(STATUS_NOERROR, PassThruStartMsgFilter(chanid, FLOW_CONTROL_FILTER, &mask_msg, &pattern_msg, &flow_msg, &filterid), + _T("Failed to create filter."), pLineInfo); + return filterid; +} + +std::unique_ptr getPanda(unsigned long kbaud, BOOL loopback) { + auto p = panda::Panda::openPanda(""); + Assert::IsTrue(p != nullptr, _T("Could not open raw panda device to test communication.")); + p->set_can_speed_kbps(panda::PANDA_CAN1, kbaud); + p->set_safety_mode(panda::SAFETY_ALLOUTPUT); + p->set_can_loopback(loopback); + p->can_clear(panda::PANDA_CAN_RX); + return p; +} + +std::vector checked_panda_send(std::unique_ptr& p, uint32_t addr, bool is_29b, + char* msg, uint8_t len, unsigned int num_expected, const __LineInfo* pLineInfo, unsigned long timeout_ms) { + Assert::IsTrue(p->can_send(addr, is_29b, (const uint8_t*)msg, len, panda::PANDA_CAN1), _T("Panda send says it failed."), pLineInfo); + auto panda_msg_recv = panda_recv_loop(p, 1 + num_expected, timeout_ms); + check_panda_can_msg(panda_msg_recv[0], 0, addr, is_29b, TRUE, std::string(msg, len), pLineInfo); + panda_msg_recv.erase(panda_msg_recv.begin()); + return panda_msg_recv; +} diff --git a/drivers/windows/pandaJ2534DLL Test/TestHelpers.h b/drivers/windows/pandaJ2534DLL Test/TestHelpers.h new file mode 100644 index 0000000000..9df59d7b5e --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/TestHelpers.h @@ -0,0 +1,48 @@ +#pragma once +#include "stdafx.h" +#include "pandaJ2534DLL/J2534_v0404.h" +#include "panda_shared/panda.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +extern void write_ioctl(unsigned int chanid, unsigned int param, unsigned int val, const __LineInfo* pLineInfo = NULL); + +extern std::vector panda_recv_loop_loose(std::unique_ptr& p, unsigned int min_num, unsigned long timeout_ms = 100); + +extern std::vector panda_recv_loop(std::unique_ptr& p, unsigned int num_expected, unsigned long timeout_ms = 100); + +extern void check_panda_can_msg(panda::PANDA_CAN_MSG& msgin, uint8_t bus, unsigned long addr, bool addr_29b, + bool is_receipt, std::string dat, const __LineInfo* pLineInfo = NULL); + +extern unsigned long J2534_start_periodic_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize, + unsigned long ExtraDataIndex, const char * Data, unsigned long TimeInterval, const __LineInfo * pLineInfo); + +extern unsigned long J2534_start_periodic_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize, + unsigned long ExtraDataIndex, const char* Data, unsigned long TimeInterval, unsigned long* msgID, const __LineInfo* pLineInfo = NULL); + +extern void J2534_send_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo = NULL); + +extern long J2534_send_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data); + +extern std::vector j2534_recv_loop_loose(unsigned int chanid, unsigned int min_num, unsigned long timeout_ms = 100); + +extern std::vector j2534_recv_loop(unsigned int chanid, unsigned int num_expected, unsigned long timeout_ms = 100); + +extern void check_J2534_can_msg(PASSTHRU_MSG& msgin, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo = NULL); + +extern unsigned long J2534_set_PASS_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx, + unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo = NULL); + +extern unsigned long J2534_set_BLOCK_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx, + unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo = NULL); + +extern unsigned long J2534_set_flowctrl_filter(unsigned long chanid, unsigned long tx, + unsigned long len, char* mask, char* pattern, char* flow, const __LineInfo* pLineInfo = NULL); + +extern std::unique_ptr getPanda(unsigned long kbaud = 500, BOOL loopback = FALSE); + +extern std::vector checked_panda_send(std::unique_ptr& p, uint32_t addr, bool is_29b, + char* msg, uint8_t len, unsigned int num_expected=0, const __LineInfo* pLineInfo = NULL, unsigned long timeout_ms = 100); diff --git a/drivers/windows/pandaJ2534DLL Test/Timer.cpp b/drivers/windows/pandaJ2534DLL Test/Timer.cpp new file mode 100644 index 0000000000..33d029e844 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/Timer.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "Timer.h" + + +Timer::Timer() +{ + reset(); +} + +// gets the time elapsed from construction. +unsigned long long /*milliseconds*/ Timer::getTimePassed(){ + // get the new time + auto end = std::chrono::time_point_cast(clock::now()); + + // return the difference of the times + return (end - start).count(); +} + +void Timer::reset() { + start = std::chrono::time_point_cast(clock::now()); +} \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/Timer.h b/drivers/windows/pandaJ2534DLL Test/Timer.h new file mode 100644 index 0000000000..cbf5579a5c --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/Timer.h @@ -0,0 +1,20 @@ +#pragma once +#include + +//Copied from https://stackoverflow.com/a/31488113 + +class Timer +{ + using clock = std::chrono::steady_clock; + using time_point_type = std::chrono::time_point < clock, std::chrono::milliseconds >; +public: + Timer(); + + // gets the time elapsed from construction. + unsigned long long /*milliseconds*/ getTimePassed(); + + void reset(); + +private: + time_point_type start; +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/j2534_tests.cpp b/drivers/windows/pandaJ2534DLL Test/j2534_tests.cpp new file mode 100644 index 0000000000..674569accc --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/j2534_tests.cpp @@ -0,0 +1,1602 @@ +#include "stdafx.h" +#include "Loader4.h" +#include "pandaJ2534DLL/J2534_v0404.h" +#include "panda_shared/panda.h" +#include "Timer.h" +#include "ECUsim DLL\ECUsim.h" +#include "TestHelpers.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace pandaJ2534DLLTest +{ + TEST_CLASS(J2534DLLInitialization) + { + public: + + TEST_CLASS_CLEANUP(deinit) { + UnloadJ2534Dll(); + } + + TEST_METHOD(J2534_Driver_Init) + { + long err = LoadJ2534Dll("pandaJ2534_0404_32.dll"); + Assert::IsTrue(err == 0, _T("Library failed to load properly. Check the export names and library location.")); + } + + }; + + TEST_CLASS(J2534DeviceInitialization) + { + public: + + TEST_METHOD_INITIALIZE(init) { + LoadJ2534Dll("pandaJ2534_0404_32.dll"); + } + + TEST_METHOD_CLEANUP(deinit) { + if (didopen) { + PassThruClose(devid); + didopen = FALSE; + } + UnloadJ2534Dll(); + } + + TEST_METHOD(J2534_Device_OpenDevice__Empty) + { + Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO()); + } + + TEST_METHOD(J2534_Device_OpenDevice__J2534_2) + { + Assert::AreEqual(STATUS_NOERROR, open_dev("J2534-2:"), _T("Failed to open device."), LINE_INFO()); + } + + TEST_METHOD(J2534_Device_OpenDevice__SN) + { + auto pandas_available = panda::Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas detected.")); + + Assert::AreEqual(STATUS_NOERROR, open_dev(pandas_available[0].c_str()), _T("Failed to open device."), LINE_INFO()); + + auto pandas_available_2 = panda::Panda::listAvailablePandas(); + for (auto panda_sn : pandas_available_2) + Assert::AreNotEqual(panda_sn, pandas_available[0]); + } + + TEST_METHOD(J2534_Device_CloseDevice) + { + Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO()); + Assert::AreEqual(STATUS_NOERROR, close_dev(devid), _T("Failed to close device."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_DEVICE_ID, PassThruClose(devid), _T("The 2nd close should have failed with ERR_INVALID_DEVICE_ID."), LINE_INFO()); + } + + TEST_METHOD(J2534_Device_ConnectDisconnect) + { + unsigned long chanid; + Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO()); + Assert::AreEqual(STATUS_NOERROR, PassThruConnect(devid, CAN, 0, 500000, &chanid), _T("Failed to open channel."), LINE_INFO()); + + Assert::AreEqual(STATUS_NOERROR, PassThruDisconnect(chanid), _T("Failed to close channel."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_CHANNEL_ID, PassThruDisconnect(chanid), _T("The 2nd disconnect should have failed with ERR_INVALID_CHANNEL_ID."), LINE_INFO()); + } + + TEST_METHOD(J2534_Device_ConnectInvalidProtocol) + { + unsigned long chanid; + Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_PROTOCOL_ID, PassThruConnect(devid, 999, 0, 500000, &chanid), + _T("Did not report ERR_INVALID_PROTOCOL_ID."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_CHANNEL_ID, PassThruDisconnect(chanid), _T("The channel should not have been created."), LINE_INFO()); + } + + bool didopen = FALSE; + unsigned long devid; + + unsigned long open_dev(const char* name, long assert_err = STATUS_NOERROR, TCHAR* failmsg = _T("Failed to open device.")) { + unsigned int res = PassThruOpen((void*)name, &devid); + if (res == STATUS_NOERROR) didopen = TRUE; + return res; + } + + unsigned long close_dev(unsigned long devid) { + unsigned long res = PassThruClose(devid); + if (res == STATUS_NOERROR) didopen = FALSE; + return res; + } + + }; + + TEST_CLASS(J2534DeviceCAN) + { + public: + + TEST_METHOD_INITIALIZE(init) { + LoadJ2534Dll("pandaJ2534_0404_32.dll"); + } + + TEST_METHOD_CLEANUP(deinit) { + if (didopen) { + PassThruClose(devid); + didopen = FALSE; + } + UnloadJ2534Dll(); + } + + //Test that the BAUD rate of a CAN connection can be changed. + TEST_METHOD(J2534_CAN_SetBaud) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK + auto p = getPanda(250); + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + j2534_recv_loop(chanid, 0); + panda_recv_loop(p, 0); + + write_ioctl(chanid, DATA_RATE, 250000, LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], CAN, TX_MSG_TYPE, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_11b_Tx) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI", LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + + j2534_recv_loop(chanid, 0, 50); // Check no message is returned (since loopback is off) + } + + TEST_METHOD(J2534_CAN_29b_Tx) + { + auto chanid = J2534_open_and_connect("", CAN, CAN_29BIT_ID, 500000, LINE_INFO()); + auto p = getPanda(500); + + Assert::AreEqual(ERR_INVALID_MSG, J2534_send_msg(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI"), _T("11b address should fail to tx."), LINE_INFO()); + J2534_send_msg_checked(chanid, CAN, 0, CAN_29BIT_ID, 0, 6, 6, "\x0\x0\x3\xAB""YO", LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, TRUE, FALSE, "YO", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_11b29b_Tx) + { + auto chanid = J2534_open_and_connect("", CAN, CAN_ID_BOTH, 500000, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI", LINE_INFO()); + J2534_send_msg_checked(chanid, CAN, 0, CAN_29BIT_ID, 0, 6, 6, "\x0\x0\x3\xAB""YO", LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x3AB, TRUE, FALSE, "YO", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_TxEcho) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 9, 9, "\x0\x0\x3\xAB""HIDOG", LINE_INFO()); + + auto msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HIDOG", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 0); + + ///////////////////////////////// + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 7, 7, "\x0\x0\x3\xAB""SUP", LINE_INFO()); + + msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "SUP", LINE_INFO()); + + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], CAN, TX_MSG_TYPE, 0, 3 + 4, 0, "\x0\x0\x3\xAB""SUP", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_RxAndPassAllFilters) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO()); + auto p = getPanda(500); + + p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1); + p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x2\xAC""HIJKL", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_RxAndLimitedPassFilter) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + J2534_set_PASS_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO()); + auto p = getPanda(500); + + p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1); + p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x2\xAC""HIJKL", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_RxAndPassBlockFilter) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO()); + J2534_set_BLOCK_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO()); + auto p = getPanda(500); + + p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1); + p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); + p->can_send(0x3FA, FALSE, (const uint8_t*)"MNOPQ", 5, panda::PANDA_CAN1); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2, 1000); + check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x3\xFA""MNOPQ", LINE_INFO()); + } + + //Check that the order of the pass and block filter do not matter + TEST_METHOD(J2534_CAN_RxAndFilterBlockPass) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + J2534_set_BLOCK_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO()); + J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO()); + auto p = getPanda(500); + + p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1); + p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); // Should not pass filter + p->can_send(0x3FA, FALSE, (const uint8_t*)"MNOPQ", 5, panda::PANDA_CAN1); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2, 2000); + check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x3\xFA""MNOPQ", LINE_INFO()); + } + + //Check that the order of the pass and block filter do not matter + TEST_METHOD(J2534_CAN_RxAndFilterRemoval) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto filterid0 = J2534_set_BLOCK_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO()); + auto filterid1 = J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO()); + + Assert::AreEqual(STATUS_NOERROR, PassThruStopMsgFilter(chanid, filterid0), _T("Failed to delete filter."), LINE_INFO()); + + auto p = getPanda(500); + + p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1); + p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); + p->can_send(0x3FA, FALSE, (const uint8_t*)"MNOPQ", 5, panda::PANDA_CAN1); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 3, 1000); + check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x2\xAC""HIJKL", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[2], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x3\xFA""MNOPQ", LINE_INFO()); + } + + //Check that the order of the pass and block filter do not matter + TEST_METHOD(J2534_CAN_RxWithTimeout) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO()); + auto p = getPanda(500); + + PASSTHRU_MSG recvbuff; + unsigned long msgcount = 1; + unsigned int res = PassThruReadMsgs(chanid, &recvbuff, &msgcount, 100); // Here is where we test the timeout + Assert::AreEqual(ERR_BUFFER_EMPTY, res, _T("No message should be found"), LINE_INFO()); + Assert::AreEqual(0, msgcount, _T("Received wrong number of messages.")); + + //TODO Test that the timings work right instead of just testing it doesn't crash. + } + + TEST_METHOD(J2534_CAN_Baud) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 250000, LINE_INFO()); + auto p = getPanda(250); + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI", LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_PeriodicMessageStartStop) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto p = getPanda(500); + + auto msgid = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 3, 250); + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO()); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[2], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + + auto timediff_1_0 = msg_recv[1].recv_time - msg_recv[0].recv_time; + auto timediff_2_1 = msg_recv[2].recv_time - msg_recv[1].recv_time; + + std::ostringstream stringStream1; + stringStream1 << "times1: " << timediff_1_0 << ", " << timediff_2_1 << std::endl; + Logger::WriteMessage(stringStream1.str().c_str()); + + Assert::IsTrue(timediff_1_0 > 90000); + Assert::IsTrue(timediff_1_0 < 110000); + Assert::IsTrue(timediff_2_1 > 90000); + Assert::IsTrue(timediff_2_1 < 110000); + + msg_recv = panda_recv_loop(p, 0, 300); + } + + TEST_METHOD(J2534_CAN_PeriodicMessageMultipleStartStop) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto p = getPanda(500); + + auto msgid0 = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO()); + auto msgid1 = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x1\x23""YO", 80, LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 9, 370); + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid0), _T("Failed to delete filter."), LINE_INFO()); + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid1), _T("Failed to delete filter."), LINE_INFO()); + //time diagram. 10 ms per character. * is send event. : is termination of periodic messages. + //*---------*---------*---------*-----:----* HI + //*-------*-------*-------*-------*---:----* YO + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO()); + check_panda_can_msg(msg_recv[2], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO()); + check_panda_can_msg(msg_recv[3], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[4], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO()); + check_panda_can_msg(msg_recv[5], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[6], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO()); + check_panda_can_msg(msg_recv[7], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[8], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO()); + + auto timediff_HI_3_0 = msg_recv[3].recv_time - msg_recv[0].recv_time; + auto timediff_HI_5_3 = msg_recv[5].recv_time - msg_recv[3].recv_time; + auto timediff_HI_7_5 = msg_recv[7].recv_time - msg_recv[5].recv_time; + + auto timediff_YO_2_1 = msg_recv[2].recv_time - msg_recv[1].recv_time; + auto timediff_YO_4_2 = msg_recv[4].recv_time - msg_recv[2].recv_time; + auto timediff_YO_6_4 = msg_recv[6].recv_time - msg_recv[4].recv_time; + auto timediff_YO_8_6 = msg_recv[8].recv_time - msg_recv[6].recv_time; + + std::ostringstream stringStreamHi; + stringStreamHi << "HiTimes: " << timediff_HI_3_0 << ", " << timediff_HI_5_3 << ", " << timediff_HI_7_5 << std::endl; + Logger::WriteMessage(stringStreamHi.str().c_str()); + + std::ostringstream stringStreamYo; + stringStreamYo << "HiTimes: " << timediff_YO_2_1 << ", " << timediff_YO_4_2 << ", " << timediff_YO_6_4 << ", " << timediff_YO_8_6 << std::endl; + Logger::WriteMessage(stringStreamYo.str().c_str()); + + Assert::IsTrue(timediff_HI_3_0 > 90000); + Assert::IsTrue(timediff_HI_3_0 < 110000); + Assert::IsTrue(timediff_HI_5_3 > 90000); + Assert::IsTrue(timediff_HI_5_3 < 110000); + Assert::IsTrue(timediff_HI_7_5 > 90000); + Assert::IsTrue(timediff_HI_7_5 < 110000); + + Assert::IsTrue(timediff_YO_2_1 > 80000-10000); + Assert::IsTrue(timediff_YO_2_1 < 80000+1000); + Assert::IsTrue(timediff_YO_4_2 > 80000 - 10000); + Assert::IsTrue(timediff_YO_4_2 < 80000 + 10000); + Assert::IsTrue(timediff_YO_6_4 > 80000 - 10000); + Assert::IsTrue(timediff_YO_6_4 < 80000 + 10000); + Assert::IsTrue(timediff_YO_8_6 > 80000 - 10000); + Assert::IsTrue(timediff_YO_8_6 < 80000 + 10000); + + msg_recv = panda_recv_loop(p, 0, 300); + } + + TEST_METHOD(J2534_CAN_PeriodicMessageStartStop_Loopback) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK + auto p = getPanda(500); + auto msgid = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 3, 250); + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO()); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[2], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 3); + check_J2534_can_msg(j2534_msg_recv[0], CAN, TX_MSG_TYPE, 0, 6, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], CAN, TX_MSG_TYPE, 0, 6, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[2], CAN, TX_MSG_TYPE, 0, 6, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + + auto timediff_1_0 = j2534_msg_recv[1].Timestamp - j2534_msg_recv[0].Timestamp; + auto timediff_2_1 = j2534_msg_recv[2].Timestamp - j2534_msg_recv[1].Timestamp; + + std::ostringstream stringStream1; + stringStream1 << "times1: " << timediff_1_0 << ", " << timediff_2_1 << std::endl; + Logger::WriteMessage(stringStream1.str().c_str()); + + Assert::IsTrue(timediff_1_0 > 90000); + Assert::IsTrue(timediff_1_0 < 110000); + Assert::IsTrue(timediff_2_1 > 90000); + Assert::IsTrue(timediff_2_1 < 110000); + + msg_recv = panda_recv_loop(p, 0, 300); + } + + TEST_METHOD(J2534_CAN_PeriodicMessageWithTx) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto p = getPanda(500); + auto msgid = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO()); + + J2534_send_msg(chanid, CAN, 0, 0, 0, 7, 0, "\x0\x0\x3\xAB""LOL"); + + std::vector msg_recv = panda_recv_loop(p, 4, 250); + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO()); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x3AB, FALSE, FALSE, "LOL", LINE_INFO());//Staggered write inbetween multiple scheduled TXs + check_panda_can_msg(msg_recv[2], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[3], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + + auto timediff_2_0 = msg_recv[2].recv_time - msg_recv[0].recv_time; + auto timediff_3_2 = msg_recv[3].recv_time - msg_recv[2].recv_time; + + std::ostringstream stringStream1; + stringStream1 << "times1: " << timediff_2_0 << ", " << timediff_3_2 << std::endl; + Logger::WriteMessage(stringStream1.str().c_str()); + + Assert::IsTrue(timediff_2_0 > 90000); + Assert::IsTrue(timediff_2_0 < 110000); + Assert::IsTrue(timediff_3_2 > 90000); + Assert::IsTrue(timediff_3_2 < 110000); + + msg_recv = panda_recv_loop(p, 0, 300); + } + + TEST_METHOD(J2534_CAN_BaudInvalid) + { + unsigned long chanid; + Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_BAUDRATE, PassThruConnect(devid, CAN, 0, 6000000, &chanid), _T("Baudrate should have been invalid."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_BAUDRATE, PassThruConnect(devid, CAN, 0, 200, &chanid), _T("Baudrate should have been invalid."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_BAUDRATE, PassThruConnect(devid, CAN, 0, 250010, &chanid), _T("Baudrate should have been invalid."), LINE_INFO()); + } + + bool didopen = FALSE; + unsigned long devid; + + unsigned long open_dev(const char* name, long assert_err = STATUS_NOERROR, TCHAR* failmsg = _T("Failed to open device.")) { + unsigned int res = PassThruOpen((void*)name, &devid); + if (res == STATUS_NOERROR) didopen = TRUE; + return res; + } + + unsigned long J2534_open_and_connect(const char* name, unsigned long ProtocolID, unsigned long Flags, unsigned long bps, const __LineInfo* pLineInfo = NULL) { + unsigned long chanid; + Assert::AreEqual(STATUS_NOERROR, open_dev(name), _T("Failed to open device."), pLineInfo); + Assert::AreEqual(STATUS_NOERROR, PassThruConnect(devid, ProtocolID, Flags, bps, &chanid), _T("Failed to open channel."), pLineInfo); + write_ioctl(chanid, LOOPBACK, FALSE, LINE_INFO()); // DISABLE J2534 ECHO/LOOPBACK + return chanid; + } + + }; + + TEST_CLASS(J2534DeviceISO15765) + { + public: + + TEST_METHOD_INITIALIZE(init) { + LoadJ2534Dll("pandaJ2534_0404_32.dll"); + } + + TEST_METHOD_CLEANUP(deinit) { + if (didopen) { + PassThruClose(devid); + didopen = FALSE; + } + UnloadJ2534Dll(); + } + + //Test that the BAUD rate of a ISO15765 connection can be changed. + TEST_METHOD(J2534_ISO15765_SetBaud) + { + auto chanid = J2534_open_and_connect("", ISO15765, 0, 500000, LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK + auto p = getPanda(250); + + J2534_send_msg_checked(chanid, ISO15765, 0, 0, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + j2534_recv_loop(chanid, 0); + panda_recv_loop(p, 0); + + write_ioctl(chanid, DATA_RATE, 250000, LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, TX_INDICATION, 0, 4, 0, "\x0\x0\x3\xAB", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, TX_MSG_TYPE, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x3AB, FALSE, FALSE, "\x2""HI", LINE_INFO()); + } + + ///////////////////// Tests checking things don't send/receive ///////////////////// + + //Check tx PASSES and rx FAIL WITHOUT a filter. 29 bit. NO Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_PassTxFailRx_29b_NoFilter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + auto p = getPanda(500); + + //TX: works because all single frame writes should work (with or without a flow contorl filter) + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO()); + + //RX: Reads require a flow control filter, and should fail without one. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x06\x41\x00\xff\xff\xff\xfe", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + } + + //Check tx and rx FAIL WITHOUT a filter. 29 bit. NO Filter. NoPadding. STD address. First Frame. + TEST_METHOD(J2534_ISO15765_FailTxRx_29b_NoFilter_NoPad_STD_FF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + auto p = getPanda(500); + + //TX + Assert::AreEqual(ERR_NO_FLOW_CONTROL, J2534_send_msg(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 12, 0, "\x18\xda\xef\xf1\xA1\xB2\xC3\xD4\xE5\xF6\x09\x1A"), + _T("Should fail to tx without a filter."), LINE_INFO()); + j2534_recv_loop(chanid, 0); + panda_recv_loop(p, 0); + + //RX; Send full response and check didn't receive flow control from J2534 device + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x14\x49\x02\x01""1D4", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GP00R55", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""B123456", 8, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0);//Check a full message is not accepted. + } + + //Check tx PASSES and rx FAIL with a MISMATCHED filter. 29 bit. Mismatch Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_PassTxFailRx_29b_MismatchFilter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //TX: works because all single frame writes should work (with or without a flow contorl filter) + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 6, 0, "\x18\xda\xe0\xf1""\x11\x22", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xe0\xf1", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAE0F1, TRUE, FALSE, "\x02""\x11\x22", LINE_INFO()); + + //RX. Send ISO15765 single frame to device. Address still doesn't match filter, so should not be received. + checked_panda_send(p, 0x18DAF1E0, TRUE, "\x06\x41\x00\xff\xff\xff\xfe", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + } + + //Check tx and rx FAIL with a MISMATCHED filter. 29 bit. Mismatch Filter. NoPadding. STD address. First Frame. + TEST_METHOD(J2534_ISO15765_FailTxRx_29b_MismatchFilter_NoPad_STD_FF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //TX + Assert::AreEqual(ERR_NO_FLOW_CONTROL, J2534_send_msg(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 12, 0, "\x18\xda\xe0\xf1""USELESS STUFF"), + _T("Should fail to tx without a filter."), LINE_INFO()); + j2534_recv_loop(chanid, 0); + panda_recv_loop(p, 0); + + //RX; Send a full response and check didn't receive flow control from J2534 device + checked_panda_send(p, 0x18DAF1E0, TRUE, "\x10\x14\x49\x02\x01""1D4", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1E0, TRUE, "\x21""GP00R55", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1E0, TRUE, "\x22""B123456", 8, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0);//Check a full message is not accepted. + } + + //Check tx FAILS with a MISMATCHED filter 29bit flag. 29 bit. Mismatch Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_FailTxRx_29b_MismatchFilterFlag29b_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x0\x0\x1\xab", "\x0\x0\x1\xcd", LINE_INFO()); + auto p = getPanda(500); + + //TX + Assert::AreEqual(ERR_INVALID_MSG, J2534_send_msg(chanid, ISO15765, 0, 0, 0, 6, 0, "\x0/x0/x1/xcd\x01\x00"), + _T("mismatched address should fail to tx."), LINE_INFO()); + j2534_recv_loop(chanid, 0); + panda_recv_loop(p, 0); + + //RX. Send ISO15765 single frame to device. Address still doesn't match filter, so should not be received. + checked_panda_send(p, 0x1ab, FALSE, "\x06\x41\x00\xff\xff\xff\xfe", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + } + + ///////////////////// Tests checking things actually send/receive. Standard Addressing ///////////////////// + + //Check rx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x07""ABCD123", 8, 0, LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 11, 11, "\x18\xda\xf1\xef""ABCD123", LINE_INFO()); + } + + //Check tx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO()); + } + + //Check tx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Single Frame. Loopback. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SF_LOOPBACK) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO()); + } + + //Check rx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_STD_FFCF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""en byte", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s here", 7, 0, LINE_INFO()); + + //Check J2534 constructed the whole message + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 4 + 0x13, 4 + 0x13, "\x18\xda\xf1\xef""nineteen bytes here", LINE_INFO()); + } + + //Check multi frame tx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_FFCF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 14, 0, "\x18\xda\xef\xf1""\xAA\xBB\xCC\xDD\xEE\xFF\x11\x22\x33\x44", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 0); // No TxDone msg until after the final tx frame is sent + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x0A""\xAA\xBB\xCC\xDD\xEE\xFF", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""\x11\x22\x33\x44", LINE_INFO()); + + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + } + + //Check rx passes with filter. 11 bit. Good Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessRx_11b_Filter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, 0, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, 0, 4, "\xff\xff\xff\xff", "\x0\x0\x1\xab", "\x0\x0\x1\xcd", LINE_INFO()); + auto p = getPanda(500); + + checked_panda_send(p, 0x1ab, FALSE, "\x07""ABCD123", 8, 0, LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, 0, 0, 11, 11, "\x0\x0\x1\xab""ABCD123", LINE_INFO()); + } + + //Check tx passes with filter. 11 bit. Good Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_11b_Filter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, 0, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, 0, 4, "\xff\xff\xff\xff", "\x0\x0\x1\xab", "\x0\x0\x1\xcd", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, 0, 0, 11, 0, "\x0\x0\x1\xcd""TX_TEST", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, TX_INDICATION, 0, 4, 0, "\x0\x0\x1\xcd", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x1CD, FALSE, FALSE, "\x07""TX_TEST", LINE_INFO()); + } + + //Check tx passes with filter multiple times. 29 bit. Good Filter. NoPadding. STD address. Multiple Single Frames. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MultipleSF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 4); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[2], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[3], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO()); + } + + //Check that receiver's flow control block size requests are respected. 29 bit. Good Filter. NoPadding. STD address. Multiple Frames with multiple flow control. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_FLOWCONTROLBlockSize) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 52, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x34""AABBCC", LINE_INFO()); + + // [flow_status, block_size, st_min] + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x01\x00", 3, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x00", 3, 2, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x01\x00", 3, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x00\x00", 3, 3, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + } + + //Check that receiver's flow control separation time requests are respected. 29 bit. Good Filter. NoPadding. STD address. Multiple Frames with multiple flow control. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_FLOWCONTROLSTMinMultiFc) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 52, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x34""AABBCC", LINE_INFO()); + + // [flow_status, block_size, st_min] + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x03\x0A", 3, 3, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + auto timediff0_1_0 = panda_msg_recv[1].recv_time - panda_msg_recv[0].recv_time; + auto timediff0_2_1 = panda_msg_recv[2].recv_time - panda_msg_recv[1].recv_time; + + std::ostringstream stringStream0; + stringStream0 << "times0: " << timediff0_1_0 << ", " << timediff0_2_1 << std::endl; + Logger::WriteMessage(stringStream0.str().c_str()); + + Assert::IsTrue(timediff0_1_0 > 10000); + Assert::IsTrue(timediff0_1_0 < 32000);//Flexible, but trying to make sure things don't just all lag for a second or something + Assert::IsTrue(timediff0_2_1 > 10000); + Assert::IsTrue(timediff0_2_1 < 32000); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x04\x20", 3, 4, LINE_INFO(), 500); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ", LINE_INFO()); + auto timediff1_1_0 = panda_msg_recv[1].recv_time - panda_msg_recv[0].recv_time; + auto timediff1_2_1 = panda_msg_recv[2].recv_time - panda_msg_recv[1].recv_time; + auto timediff1_3_2 = panda_msg_recv[3].recv_time - panda_msg_recv[2].recv_time; + + std::ostringstream stringStream1; + stringStream1 << "times1: " << timediff1_1_0 << ", " << timediff1_2_1 << ", " << timediff1_3_2 << std::endl; + Logger::WriteMessage(stringStream1.str().c_str()); + + Assert::IsTrue(timediff1_1_0 > 32000); + Assert::IsTrue(timediff1_2_1 > 32000); + Assert::IsTrue(timediff1_3_2 > 32000); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + } + + //Check that receiver's flow control separation time requests are respected 2. 29 bit. Good Filter. NoPadding. STD address. Multiple Frames with one flow control. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_FLOWCONTROLSTMinSingleFc) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 52, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x34""AABBCC", LINE_INFO()); + + // [flow_status, block_size, st_min] + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x07\x0A", 3, 7, LINE_INFO(), 500); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[4], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[5], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[6], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ", LINE_INFO()); + + auto timediff_1_0 = panda_msg_recv[1].recv_time - panda_msg_recv[0].recv_time; + auto timediff_2_1 = panda_msg_recv[2].recv_time - panda_msg_recv[1].recv_time; + auto timediff_3_2 = panda_msg_recv[3].recv_time - panda_msg_recv[2].recv_time; + auto timediff_4_3 = panda_msg_recv[4].recv_time - panda_msg_recv[3].recv_time; + auto timediff_5_4 = panda_msg_recv[5].recv_time - panda_msg_recv[4].recv_time; + auto timediff_6_5 = panda_msg_recv[6].recv_time - panda_msg_recv[5].recv_time; + + std::ostringstream stringStream1; + stringStream1 << "times1: " << timediff_1_0 << ", " << timediff_2_1 << ", " << timediff_3_2 << + ", " << timediff_4_3 << ", " << timediff_5_4 << ", " << timediff_6_5 << std::endl; + Logger::WriteMessage(stringStream1.str().c_str()); + + Assert::IsTrue(timediff_1_0 > 10000); + Assert::IsTrue(timediff_2_1 > 10000); + Assert::IsTrue(timediff_3_2 > 10000); + Assert::IsTrue(timediff_4_3 > 10000); + Assert::IsTrue(timediff_5_4 > 10000); + Assert::IsTrue(timediff_6_5 > 10000); + } + + //Check that tx works for messages with more than 16 frames. 29 bit. Good Filter. NoPadding. STD address. Large multiframe message. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_LotsOfFrames) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 125, 0, + "\x18\xda\xef\xf1" + "AABBCC""DDEEFFG""GHHIIJJ""KKLLMMN""NOOPPQQ""RRSSTTU""UVVWWXX""YYZZ112""2334455""6677889" + "900abcd""efghijk""lmnopqr""stuvwxy""z!@#$%^""&*()_+-""=`~ABCD""EFGHIJK", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x7D""AABBCC", LINE_INFO()); + + // [flow_status, block_size, st_min] + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x00\x00", 3, 17, LINE_INFO(), 1000); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[4], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[5], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[6], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ112", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[7], 0, 0x18DAEFF1, TRUE, FALSE, "\x28""2334455", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[8], 0, 0x18DAEFF1, TRUE, FALSE, "\x29""6677889", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[9], 0, 0x18DAEFF1, TRUE, FALSE, "\x2A""900abcd", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[10], 0, 0x18DAEFF1, TRUE, FALSE, "\x2B""efghijk", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[11], 0, 0x18DAEFF1, TRUE, FALSE, "\x2C""lmnopqr", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[12], 0, 0x18DAEFF1, TRUE, FALSE, "\x2D""stuvwxy", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[13], 0, 0x18DAEFF1, TRUE, FALSE, "\x2E""z!@#$%^", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[14], 0, 0x18DAEFF1, TRUE, FALSE, "\x2F""&*()_+-", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[15], 0, 0x18DAEFF1, TRUE, FALSE, "\x20""=`~ABCD", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[16], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""EFGHIJK", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + } + + //Check tx passes with filter multiple times. 29 bit. Good Filter. NoPadding. STD address. Multiple Single Frames. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MultipleMFSF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO()); + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x17""Long d", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x00\x00", 3, 4, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""ata bec", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""ause I ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""can", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 4); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[2], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[3], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 5, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + } + + //Check tx passes after message timeout. 29 bit. Good Filter. NoPadding. STD address. Multiple Frame timeout then Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFTimeoutSFSuccess) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO()); + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 2, 1000); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x17""Long d", LINE_INFO()); //First Frame. Not replying so it needs to time out. + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO()); //Reply to the next message. + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 5, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + } + + //Check tx passes after mid-message timeout. 29 bit. Good Filter. NoPadding. STD address. Multiple Frame mid-timeout then Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFMidTimeoutSFSuccess) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO()); + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x17""Long d", LINE_INFO()); //First Frame. Not replying so it needs to time out. + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x01\x00", 3, 2, LINE_INFO(), 1000);//Start a conversation + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""ata bec", LINE_INFO());//Check passthru device sent more data, but don't reply to it + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO()); //Reply to the next message. + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 5, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + } + + //Check slow tx passes without hitting FC timeout. 29 bit. Good Filter. NoPadding. STD address. Long STmin, catches if FC timeout applies before needed. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SLOWMF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x06\x7F", 3, 6, LINE_INFO(), 3000);//Start a conversation... but slow. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());//Check this convo doesn't trigger that timeout. + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[4], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());//Some of these should fail to recv if there is an issue. + check_panda_can_msg(panda_msg_recv[5], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + } + + //Check MF tx can be sent along side of a periodic message. 29 bit. Good Filter. NoPadding. STD address. Long STmin, checks that MF tx and periodic TX don't break each other. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SLOWMF_WithPeriodicMsg) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + //Timing diagram of this test. + //* is a periodic msg transfer; F is first frame, L is Flow control, C is Consecutive Frame. + // *~~~~~~~*~~~~~~~*~~~~~~~* (The alignment here is unimportant. The exact order is not checked. + //F C----C----C----C----C----C (100 ms between Cs) + // L + + auto msgid = J2534_start_periodic_msg_checked(chanid, ISO15765, CAN_29BIT_ID, 6, 0, "\x18\xda\xef\xf1""HI", 130, LINE_INFO()); + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x02""HI", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + Assert::IsTrue(p->can_send(0x18DAF1EF, TRUE, (const uint8_t*)"\x30\x06\x64", 3, panda::PANDA_CAN1), _T("Panda send says it failed."), LINE_INFO()); + + Timer t_permsg = Timer(); + Timer t_MFmsg = Timer(); + unsigned int MFframesReceived = 0; + unsigned int PeriodicMsgReceived = 1; //Because of the first panda_recv_loop above. + std::array const mfMsgExpectedParts{ "\x21""DDEEFFG", "\x22""GHHIIJJ", "\x23""KKLLMMN", "\x24""NOOPPQQ", "\x25""RRSSTTU", "\x26""UVVWWXX" }; + + while (TRUE) { + std::vectormsg_recv = p->can_recv(); + for (auto msg : msg_recv) { + if (msg.is_receipt) continue; + if ((msg.dat[0] & 0xf0) == 0x20) { + Assert::AreEqual(mfMsgExpectedParts[MFframesReceived], std::string((const char*)msg.dat, msg.len), _T("Got wrong part of MF msg."), LINE_INFO()); + MFframesReceived++; + t_MFmsg.reset(); + } else if (std::string((const char*)msg.dat, msg.len) == "\x02HI") { + PeriodicMsgReceived++; + t_permsg.reset(); + } else { + Assert::IsTrue(FALSE, _T("Got impossible message. Something is very wrong. Check other tests."), LINE_INFO()); + } + } + + if (MFframesReceived >= 6) break; + Assert::IsTrue(300 > t_permsg.getTimePassed(), _T("Timed out waiting for periodic msessage frame."), LINE_INFO()); + Assert::IsTrue(300 > t_MFmsg.getTimePassed(), _T("Timed out waiting for multiframe msessage frame."), LINE_INFO()); + + if (msg_recv.size() == 0) + Sleep(10); + } + + //Stop the periodic message and grab any data it may have sent since we last checked. + //Not sure if this is needed. + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO()); + auto extra_panda_msg = panda_recv_loop_loose(p, 0, 200); + for (auto msg : extra_panda_msg) { + if (std::string((const char*)msg.dat, msg.len) == "\x02HI") { + PeriodicMsgReceived++; + Logger::WriteMessage("Received extra periodic message."); + } else { + Assert::IsTrue(FALSE, _T("Got impossible message. Something is very wrong. Check other tests."), LINE_INFO()); + } + } + + Assert::IsTrue(PeriodicMsgReceived > 3, _T("Did not receive enough periodic messages. Likely canceled or delayed."), LINE_INFO()); + + std::ostringstream stringStream; + stringStream << "PeriodicMsgReceived = " << PeriodicMsgReceived << std::endl; + Logger::WriteMessage(stringStream.str().c_str()); + + unsigned int periodicTxIndicationCount = 0; + unsigned int TxIndicationCount = 0; + auto j2534_msg_recv = j2534_recv_loop(chanid, 2 + (PeriodicMsgReceived * 2)); + for (int i = 0; i < PeriodicMsgReceived + 1; i++) { + check_J2534_can_msg(j2534_msg_recv[(i * 2) + 0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + switch (j2534_msg_recv[(i * 2) + 1].DataSize) { + case 4 + 2: + check_J2534_can_msg(j2534_msg_recv[(i * 2) + 1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 2, 0, "\x18\xda\xef\xf1""HI", LINE_INFO()); + break; + case 4 + 48: + check_J2534_can_msg(j2534_msg_recv[(i * 2) + 1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + break; + default: + Assert::IsTrue(FALSE, _T("Got unexpected data!"), LINE_INFO()); + } + } + + Assert::AreNotEqual(PeriodicMsgReceived, periodicTxIndicationCount, _T("Wrong number of periodic msgs reported by passthru device."), LINE_INFO()); + Assert::AreNotEqual(1, TxIndicationCount, _T("Wrong number of TX msgs reported by passthru device."), LINE_INFO()); + } + + ///////////////////// Tests checking things break or recover during send/receive ///////////////////// + + //Check rx FAILS when frame is dropped. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_FailRx_29b_Filter_NoPad_STD_FFCF_DropFrame) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the rest of the message + //Missing the 2nd frame "\x21""en byte" + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s here", 7, 0, LINE_INFO()); + + //Check J2534 DOES NOT construct the incomplete message + j2534_recv_loop(chanid, 0); + } + + //Check rx ignores frames that arrive out of order. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_FFCF_FrameNumSkip) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""XXXXXX", 7, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x23""ZZZZZZ", 7, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO()); + + //Check J2534 constructa the complete message from the correctly numbered frames. + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 4 + 0x13, 4 + 0x13, "\x18\xda\xf1\xef""ABCDEFGHIJKLMNOPQRS", LINE_INFO()); + } + + //Check Single Frame rx RESETS ongoing multiframe transmission. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_SFRxResetsMFRx) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the next part of the message multi message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO()); + + //ABORTING MESSAGE + //Send a NEW single frame message and check the J2534 device gets it (but not the original message it was receiving. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x06""ABC123", 7, 0, LINE_INFO()); + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 10, 10, "\x18\xda\xf1\xef""ABC123", LINE_INFO()); + + //Resume sending the old message, and check th eJ2534 device didn't get a message. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + } + + //The documentation says that a s ingle channel can not send and receive messages trhough a + //single conversation (flow control filter) at the same time. However, the required behavior + //when this is detected is not described. This test was my best understanding of how it was + //wanted, but I no longer see the point. For now I am disabling it. + /*//Check Single Frame tx RESETS ongoing multiframe rx transmission. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_SFTxResetsMFRx) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the next part of the message multi message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + + //ABORTING MESSAGE + //Send a NEW single frame message and check the J2534 device gets it (but not the original message it was receiving. + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + + panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO()); + /////////////////////////// + + //Resume sending the old message, and check th eJ2534 device didn't get a message. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + }*/ + + //TODO check rx is cleared by tx (multi). Or not.... read above note. + + //Check multiframe rx RESETS ongoing multiframe transmission. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_FFCF_MFRxResetsMFRx) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the next part of the multi message A + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO()); + + //ABORTING MESSAGE A + //Send a NEW multi frame message (B) and check the J2534 device gets it (but not the original message it was receiving. + //Send first frame, then check we get a flow control frame + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""en byte", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s here", 7, 0, LINE_INFO()); + + //Check J2534 constructed the whole message + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 4 + 0x13, 4 + 0x13, "\x18\xda\xf1\xef""nineteen bytes here", LINE_INFO()); + //////////////////////// End sending B + + //Resume sending the multi message A, and check th eJ2534 device didn't get a message. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + } + + //Check rx fails gracefully if final CF of MF rx is too short. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_FailRxFinalCFTooShort_29b_Filter_NoPad_STD_FFCF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""en byte", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s her", 6, 0, LINE_INFO()); //The transaction should reset here because more data could have been sent. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x23""e", 2, 0, LINE_INFO()); + + //Check J2534 constructed the whole message + j2534_msg_recv = j2534_recv_loop(chanid, 0); + } + + //Check rx fails gracefully if first frame is too short. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_FailRxFFTooShort_29b_Filter_NoPad_STD_FFCF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame. The transaction should reset immediately because more data could have been sent in this frame. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninet", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""een byt", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""es here", 8, 0, LINE_INFO()); + + //Check J2534 constructed the whole message + j2534_recv_loop(chanid, 0); + } + + //Check MF tx will stop upon receiving a flow control ABORT. 29 bit. Good Filter. NoPadding. STD address. Large STmin, then abort, then send SF. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD__MF_FCAbort_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x20", 3, 2, LINE_INFO());//Start a conversation. FC timeout is 32 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x32\x0\x0", 3, 0, LINE_INFO());//Abort the conversation + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + } + + //Check MF tx will stop upon receiving a flow control ABORT during valid blocksize. 29 bit. Good Filter. NoPadding. STD address. Large STmin, then mid tx abort, then send SF. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD__MF_FCMixTXAbort_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x06\x7F", 3, 1, LINE_INFO(), 200);//Start a conversation. FC timeout is 127 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x32\x0\x0", 3, 0, LINE_INFO());//Abort the conversation + panda_recv_loop(p, 0, 200); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + } + + //Check slow tx can be stalled past timeout with CF WAIT frames. 29 bit. Good Filter. NoPadding. STD address. MF tx that would timeout without WAIT frames. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFWithWaitFrames) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + write_ioctl(chanid, ISO15765_WFT_MAX, 10, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x40", 3, 2, LINE_INFO(), 3000);//Start a conversation. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 4, LINE_INFO(), 3000);//Start a conversation. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());//Some of these should fail to recv if there is an issue. + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + } + + //Check slow tx can be stalled past timeout with CF WAIT frames during normal TX. 29 bit. Good Filter. NoPadding. STD address. Stalling working MF tx. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFWithMidTXWaitFrames) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + write_ioctl(chanid, ISO15765_WFT_MAX, 10, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x06\x64", 3, 2, LINE_INFO(), 120);//Start a conversation. STmin 100. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 4, LINE_INFO(), 3000);//Start a conversation. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());//Some of these should fail to recv if there is an issue. + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + } + + //Check that too many WAIT frames will abort the transfer. 29 bit. Good Filter. NoPadding. STD address. Too much stalling causes abort. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFTooManyWaitFrames) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + write_ioctl(chanid, ISO15765_WFT_MAX, 2, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x64", 3, 2, LINE_INFO(), 120);//Start a conversation. STmin 100. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x64", 3, 2, LINE_INFO(), 120);//Resume the conversation. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + + //Should not resume because the conversation has been delayed too long. + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 0, LINE_INFO(), 300); + + //Send a SF message to check the tubes are not clogged. + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + } + + ///////////////////// Tests checking things actually send/receive. Ext 5 byte Addressing ///////////////////// + + //Check rx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_EXT_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO()); + auto p = getPanda(500); + + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x06""ABC123", 8, 0, LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 0, 11, 11, "\x18\xda\xf1\xef\x13""ABC123", LINE_INFO()); + } + + //Check tx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_EXT_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 0, 11, 0, "\x18\xda\xef\xf1\x13""DERP!!", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION | ISO15765_ADDR_TYPE, 0, 5, 0, "\x18\xda\xef\xf1\x13", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x13""\x06""DERP!!", LINE_INFO()); + } + + //Check rx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Multi Frame. + TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_EXT_FFCF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + Assert::IsTrue(p->can_send(0x18DAF1EF, TRUE, (const uint8_t*)"\x13""\x10\x13""ninet", 8, panda::PANDA_CAN1), _T("Panda send says it failed."), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE | ISO15765_ADDR_TYPE, 0, 5, 0, "\x18\xda\xf1\xef\x13", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAF1EF, TRUE, TRUE, std::string("\x13""\x10\x13""ninet", 8), LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x13""\x30\x00\x00", 4), LINE_INFO()); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x21""een by", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x22""tes he", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x23""re", 8, 0, LINE_INFO()); + + //Check J2534 constructed the whole message + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 0, 5 + 0x13, 5 + 0x13, "\x18\xda\xf1\xef\x13""nineteen bytes here", LINE_INFO()); + } + + //Check tx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Multi Frame. + /*TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_EXT_FFCF) + { //TODO when TX works with flow control}*/ + + ///////////////////// Tests checking things break or recover during send/receive. Ext 5 byte Addressing ///////////////////// + + //Check rx FAILS when frame is dropped. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_FailRx_29b_Filter_NoPad_EXT_FFCF_DropFrame) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13\x10\x13""ninet", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x13\x30\x00\x00", 4), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE | ISO15765_ADDR_TYPE, 0, 5, 0, "\x18\xda\xf1\xef\x13", LINE_INFO()); + + //Send the rest of the message + //Missing the 2nd frame "\x13""\x21""een by" + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x22""tes he", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x23""re", 8, 0, LINE_INFO()); + + //Check J2534 DOES NOT construct the incomplete message + j2534_recv_loop(chanid, 0); + } + + bool didopen = FALSE; + unsigned long devid; + + unsigned long open_dev(const char* name, long assert_err = STATUS_NOERROR, TCHAR* failmsg = _T("Failed to open device.")) { + unsigned int res = PassThruOpen((void*)name, &devid); + if (res == STATUS_NOERROR) didopen = TRUE; + return res; + } + + unsigned long J2534_open_and_connect(const char* name, unsigned long ProtocolID, unsigned long Flags, unsigned long bps, const __LineInfo* pLineInfo = NULL) { + unsigned long chanid; + Assert::AreEqual(STATUS_NOERROR, open_dev(name), _T("Failed to open device."), pLineInfo); + Assert::AreEqual(STATUS_NOERROR, PassThruConnect(devid, ProtocolID, Flags, bps, &chanid), _T("Failed to open channel."), pLineInfo); + write_ioctl(chanid, LOOPBACK, FALSE, LINE_INFO()); // DISABLE J2534 ECHO/LOOPBACK + return chanid; + } + + }; +} diff --git a/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj b/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj new file mode 100644 index 0000000000..f19c743461 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj @@ -0,0 +1,122 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {7912F978-B48C-4C5D-8BFD-5D1E22158E47} + Win32Proj + pandaJ2534DLLTest + 10.0.16299.0 + Tests + + + + DynamicLibrary + true + v141 + Unicode + false + + + DynamicLibrary + false + v141 + true + Unicode + false + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + Use + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories);$(SolutionDir) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + Level3 + Use + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories);$(SolutionDir) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + {96e0e646-ee76-444d-9a77-a0cd7f781deb} + + + {a2bb18a5-f26b-48d6-bbb5-b83d64473c77} + + + + + + \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj.filters b/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj.filters new file mode 100644 index 0000000000..476ce458d3 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/panda_tests.cpp b/drivers/windows/pandaJ2534DLL Test/panda_tests.cpp new file mode 100644 index 0000000000..51b0bfcf2f --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/panda_tests.cpp @@ -0,0 +1,187 @@ +#include "stdafx.h" +#include "panda_shared/panda.h" +#include "TestHelpers.h" + +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +using namespace panda; + +namespace pandaTestNative +{ + TEST_CLASS(DeviceDiscovery) + { + public: + + TEST_METHOD(Panda_DevDiscover_ListDevices) + { + auto pandas_available = Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found.")); + for (auto sn : pandas_available) { + Assert::IsTrue(sn.size() == 24, _T("panda Serial Number not 24 characters long.")); + } + } + + TEST_METHOD(Panda_DevDiscover_OpenFirstDevice) + { + auto pandas_available = Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found.")); + + auto p1 = Panda::openPanda(pandas_available[0]); + Assert::IsFalse(p1 == nullptr, _T("Could not open panda.")); + } + + TEST_METHOD(Panda_DevDiscover_OpenDeviceNoName) + { + auto pandas_available = Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found.")); + + auto p1 = Panda::openPanda(""); + Assert::IsFalse(p1 == nullptr, _T("Could not open panda.")); + Assert::IsTrue(p1->get_usb_sn() == pandas_available[0], _T("Could not open panda.")); + } + + TEST_METHOD(Panda_DevDiscover_OpenDeviceUnavailable) + { + auto p1 = Panda::openPanda("ZZZZZZZZZZZZZZZZZZZZZZZZ"); + Assert::IsTrue(p1 == nullptr, _T("Invalid sn still worked.")); + } + + TEST_METHOD(Panda_DevDiscover_WillNotOpenAlreadyOpenedDevice) + { + auto pandas_available = Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found.")); + + auto p1 = Panda::openPanda(pandas_available[0]); + Assert::IsFalse(p1 == nullptr, _T("Could not open panda.")); + + auto p2 = Panda::openPanda(pandas_available[0]); + Assert::IsTrue(p2 == nullptr, _T("Opened an already open panda.")); + } + + TEST_METHOD(Panda_DevDiscover_OpenedDeviceNotListed) + { + auto pandas_available = Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found.")); + + auto p1 = Panda::openPanda(pandas_available[0]); + Assert::IsFalse(p1 == nullptr, _T("Could not open panda.")); + + auto pandas_available2 = Panda::listAvailablePandas(); + for (auto sn : pandas_available2) { + Assert::IsFalse(p1->get_usb_sn() == sn, _T("Opened panda appears in list of available pandas.")); + } + + } + }; + + TEST_CLASS(CANOperations) + { + public: + + TEST_METHOD(Panda_CAN_Echo) + { + auto p0 = getPanda(500, TRUE); + + uint32_t addr = 0xAA; + bool is_29b = FALSE; + uint8_t candata[8]; + + for (auto canbus : { PANDA_CAN1, PANDA_CAN2, PANDA_CAN3 }) { + uint8_t len = (rand() % 8) + 1; + for (size_t i = 0; i < len; i++) + candata[i] = rand() % 256; + + p0->can_send(addr, is_29b, candata, len, canbus); + Sleep(10); + + auto can_msgs = p0->can_recv(); + + Assert::AreEqual(2, can_msgs.size(), _T("Received the wrong number of CAN messages."), LINE_INFO()); + + for (auto msg : can_msgs) { + Assert::IsTrue(msg.addr == addr, _T("Wrong addr.")); + Assert::IsTrue(msg.bus == canbus, _T("Wrong bus.")); + Assert::IsTrue(msg.len == len, _T("Wrong len.")); + Assert::AreEqual(memcmp(msg.dat, candata, msg.len), 0, _T("Received CAN data not equal")); + for (int i = msg.len; i < 8; i++) + Assert::IsTrue(msg.dat[i] == 0, _T("Received CAN data not trailed by 0s")); + } + + Assert::IsTrue(can_msgs[0].is_receipt, _T("Didn't get receipt.")); + Assert::IsFalse(can_msgs[1].is_receipt, _T("Didn't get echo.")); + } + } + + TEST_METHOD(Panda_CAN_ChangeBaud) + { + auto p0 = getPanda(250); + auto p1 = getPanda(500); + + p0->can_send(0xAA, FALSE, (const uint8_t*)"\x1\x2\x3\x4\x5\x6\x7\x8", 8, panda::PANDA_CAN1); + panda_recv_loop(p0, 0); + panda_recv_loop(p1, 0); + + p0->set_can_speed_kbps(panda::PANDA_CAN1, 500); + + auto panda_msg_recv = panda_recv_loop(p0, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0xAA, FALSE, TRUE, "\x1\x2\x3\x4\x5\x6\x7\x8", LINE_INFO()); + panda_msg_recv = panda_recv_loop(p1, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0xAA, FALSE, FALSE, "\x1\x2\x3\x4\x5\x6\x7\x8", LINE_INFO()); + + ////////////////// + + p0->set_can_speed_kbps(panda::PANDA_CAN1, 250); + p0->can_send(0xC4, FALSE, (const uint8_t*)"\xA\B\xC\xD\xE\xF\x10\x11", 8, panda::PANDA_CAN1); + panda_recv_loop(p0, 0); + panda_recv_loop(p1, 0); + + p1->set_can_speed_kbps(panda::PANDA_CAN1, 250); + + panda_msg_recv = panda_recv_loop(p0, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0xC4, FALSE, TRUE, "\xA\B\xC\xD\xE\xF\x10\x11", LINE_INFO()); + panda_msg_recv = panda_recv_loop(p1, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0xC4, FALSE, FALSE, "\xA\B\xC\xD\xE\xF\x10\x11", LINE_INFO()); + } + + TEST_METHOD(Panda_CAN_ClearClears) + { + auto p0 = getPanda(500, TRUE); + p0->can_send(0xAA, FALSE, (const uint8_t*)"\x0\x1\x2\x3\x4\x5\x6\x7", 8, panda::PANDA_CAN1); + Sleep(100); + p0->can_clear(PANDA_CAN_RX); + + auto can_msgs = p0->can_recv(); + Assert::IsTrue(can_msgs.size() == 0, _T("Received messages after a clear.")); + } + }; + + TEST_CLASS(SerialOperations) + { + public: + + TEST_METHOD(Panda_LIN_Echo) + { + auto p0 = getPanda(500); + + for (auto lin_port : { SERIAL_LIN1, SERIAL_LIN2 }) { + p0->serial_clear(lin_port); + + for (int i = 0; i < 10; i++) { + uint8_t len = (rand() % LIN_MSG_MAX_LEN) + 1; + std::string lindata; + lindata.reserve(len); + + for (size_t j = 0; j < len; j++) + lindata += (const char)(rand() % 256); + + p0->serial_write(lin_port, lindata.c_str(), len); + Sleep(10); + + auto retdata = p0->serial_read(lin_port); + Assert::AreEqual(retdata, lindata); + } + } + } + }; +} \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/stdafx.cpp b/drivers/windows/pandaJ2534DLL Test/stdafx.cpp new file mode 100644 index 0000000000..84a1f0aaf3 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pandaJ2534DLL Test.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/pandaJ2534DLL Test/stdafx.h b/drivers/windows/pandaJ2534DLL Test/stdafx.h new file mode 100644 index 0000000000..1ac8bd8dcc --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/stdafx.h @@ -0,0 +1,14 @@ +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include +#include + +// Headers for CppUnitTest +#include "CppUnitTest.h" +#include //Used for formatting in TestHelpers.cpp +#include +#include diff --git a/drivers/windows/pandaJ2534DLL Test/targetver.h b/drivers/windows/pandaJ2534DLL Test/targetver.h new file mode 100644 index 0000000000..87c0086de7 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/drivers/windows/pandaJ2534DLL/Action.h b/drivers/windows/pandaJ2534DLL/Action.h new file mode 100644 index 0000000000..e9721c2eda --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/Action.h @@ -0,0 +1,57 @@ +#pragma once +#include + +#include "J2534Frame.h" + +class J2534Connection; + +/** +An Action represents a unit of work that can be scheduled for execution at a later time. +Actions are not guaranteed to be run at their specified time, but a best effort is made. +An Action will never execute early, but can execute later depending on what is in the +queus. +Many different operations are based on this base class. Instead of making a thread, +consider if the work can be offloaded to the Task Queue. +*/ +class Action +{ +public: + Action( + std::weak_ptr connection, + std::chrono::microseconds delay + ) : connection(connection), delay(delay) { }; + + Action( + std::weak_ptr connection + ) : connection(connection), delay(std::chrono::microseconds(0)) { }; + + //The function called by the task runner when this action is to be invoked. + virtual void execute() = 0; + + //Reschedule this Action for now(). + void scheduleImmediate() { + expire = std::chrono::steady_clock::now(); + } + + //Reschedule this Action relative to its last expiration time. + void scheduleDelay() { + expire += this->delay; + } + + //Reschedule this action {delay} after now(). + void scheduleImmediateDelay() { + expire = std::chrono::steady_clock::now() + this->delay; + } + + //Reschedule this Action based on a specific base time. + void schedule(std::chrono::time_point starttine, BOOL adddelayed) { + this->expire = starttine; + if (adddelayed) + expire += this->delay; + } + + std::weak_ptr connection; + std::chrono::microseconds delay; + //The timestamp at which point this Action is ready to be executed. + std::chrono::time_point expire; +}; diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection.cpp b/drivers/windows/pandaJ2534DLL/J2534Connection.cpp new file mode 100644 index 0000000000..aa364b0f8a --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection.cpp @@ -0,0 +1,283 @@ +#include "stdafx.h" +#include "J2534Connection.h" +#include "Timer.h" + +J2534Connection::J2534Connection( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate +) : panda_dev(panda_dev), ProtocolID(ProtocolID), Flags(Flags), BaudRate(BaudRate), port(0) { } + +unsigned long J2534Connection::validateTxMsg(PASSTHRU_MSG* msg) { + if (msg->DataSize < this->getMinMsgLen() || msg->DataSize > this->getMaxMsgLen()) + return ERR_INVALID_MSG; + return STATUS_NOERROR; +} + +long J2534Connection::PassThruReadMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) { + //Timeout of 0 means return immediately. Non zero means WAIT for that time then return. Dafuk. + long err_code = STATUS_NOERROR; + Timer t = Timer(); + + unsigned long msgnum = 0; + while (msgnum < *pNumMsgs) { + if (Timeout > 0 && t.getTimePassed() >= Timeout) { + err_code = ERR_TIMEOUT; + break; + } + + //Synchronized won't work where we have to break out of a loop + messageRxBuff_mutex.lock(); + if (this->messageRxBuff.empty()) { + messageRxBuff_mutex.unlock(); + if (Timeout == 0) + break; + Sleep(2); + continue; + } + + auto msg_in = this->messageRxBuff.front(); + this->messageRxBuff.pop(); + messageRxBuff_mutex.unlock(); + + PASSTHRU_MSG *msg_out = &pMsg[msgnum++]; + msg_out->ProtocolID = this->ProtocolID; + msg_out->DataSize = msg_in.Data.size(); + memcpy(msg_out->Data, msg_in.Data.c_str(), msg_in.Data.size()); + msg_out->Timestamp = msg_in.Timestamp; + msg_out->RxStatus = msg_in.RxStatus; + msg_out->ExtraDataIndex = msg_in.ExtraDataIndex; + msg_out->TxFlags = 0; + if (msgnum == *pNumMsgs) break; + } + + if (msgnum == 0) + err_code = ERR_BUFFER_EMPTY; + *pNumMsgs = msgnum; + return err_code; +} + +long J2534Connection::PassThruWriteMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) { + //There doesn't seem to be much reason to implement the timeout here. + for (int msgnum = 0; msgnum < *pNumMsgs; msgnum++) { + PASSTHRU_MSG* msg = &pMsg[msgnum]; + if (msg->ProtocolID != this->ProtocolID) { + *pNumMsgs = msgnum; + return ERR_MSG_PROTOCOL_ID; + } + + auto retcode = this->validateTxMsg(msg); + if (retcode != STATUS_NOERROR) { + *pNumMsgs = msgnum; + return retcode; + } + + auto msgtx = this->parseMessageTx(*pMsg); + if (msgtx != nullptr) //Nullptr is supported for unimplemented connection types. + this->schedultMsgTx(std::dynamic_pointer_cast(msgtx)); + } + return STATUS_NOERROR; +} + +//The docs say that a device has to support 10 periodic messages, though more is ok. +//It is easier to store them on the connection, so 10 per connection it is. +long J2534Connection::PassThruStartPeriodicMsg(PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval) { + if (pMsg->DataSize < getMinMsgLen() || pMsg->DataSize > getMaxMsgSingleFrameLen()) return ERR_INVALID_MSG; + if (pMsg->ProtocolID != this->ProtocolID) return ERR_MSG_PROTOCOL_ID; + if (TimeInterval < 5 || TimeInterval > 65535) return ERR_INVALID_TIME_INTERVAL; + + for (int i = 0; i < this->periodicMessages.size(); i++) { + if (periodicMessages[i] != nullptr) continue; + + *pMsgID = i; + auto msgtx = this->parseMessageTx(*pMsg); + if (msgtx != nullptr) { + periodicMessages[i] = std::make_shared(std::chrono::microseconds(TimeInterval*1000), msgtx); + periodicMessages[i]->scheduleImmediate(); + if (auto panda_dev = this->getPandaDev()) { + panda_dev->insertActionIntoTaskList(periodicMessages[i]); + } + } + return STATUS_NOERROR; + } + return ERR_EXCEEDED_LIMIT; +} + +long J2534Connection::PassThruStopPeriodicMsg(unsigned long MsgID) { + if (MsgID >= this->periodicMessages.size() || this->periodicMessages[MsgID] == nullptr) + return ERR_INVALID_MSG_ID; + this->periodicMessages[MsgID]->cancel(); + this->periodicMessages[MsgID] = nullptr; + return STATUS_NOERROR; +} + +long J2534Connection::PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, + PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) { + for (int i = 0; i < this->filters.size(); i++) { + if (filters[i] == nullptr) { + try { + auto newfilter = std::make_shared(this, FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg); + for (int check_idx = 0; check_idx < filters.size(); check_idx++) { + if (filters[check_idx] == nullptr) continue; + if (filters[check_idx] == newfilter) { + filters[i] = nullptr; + return ERR_NOT_UNIQUE; + } + } + *pFilterID = i; + filters[i] = newfilter; + return STATUS_NOERROR; + } catch (int e) { + return e; + } + } + } + return ERR_EXCEEDED_LIMIT; +} + +long J2534Connection::PassThruStopMsgFilter(unsigned long FilterID) { + if (FilterID >= this->filters.size() || this->filters[FilterID] == nullptr) + return ERR_INVALID_FILTER_ID; + this->filters[FilterID] = nullptr; + return STATUS_NOERROR; +} + +long J2534Connection::PassThruIoctl(unsigned long IoctlID, void *pInput, void *pOutput) { + return STATUS_NOERROR; +} + +long J2534Connection::init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput) { return ERR_FAILED; } +long J2534Connection::initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput) { return ERR_FAILED; } +long J2534Connection::clearTXBuff() { + if (auto panda_ps = this->panda_dev.lock()) { + synchronized(staged_writes_lock) { + this->txbuff = {}; + panda_ps->panda->can_clear(panda::PANDA_CAN1_TX); + } + } + return STATUS_NOERROR; +} +long J2534Connection::clearRXBuff() { + if (auto panda_ps = this->panda_dev.lock()) { + synchronized(messageRxBuff_mutex) { + this->messageRxBuff = {}; + panda_ps->panda->can_clear(panda::PANDA_CAN_RX); + } + } + return STATUS_NOERROR; +} +long J2534Connection::clearPeriodicMsgs() { + for (int i = 0; i < this->periodicMessages.size(); i++) { + if (periodicMessages[i] == nullptr) continue; + this->periodicMessages[i]->cancel(); + this->periodicMessages[i] = nullptr; + } + + return STATUS_NOERROR; +} +long J2534Connection::clearMsgFilters() { + for (auto& filter : this->filters) filter = nullptr; + return STATUS_NOERROR; +} + +void J2534Connection::setBaud(unsigned long baud) { + this->BaudRate = baud; +} + +void J2534Connection::schedultMsgTx(std::shared_ptr msgout) { + if (auto panda_ps = this->panda_dev.lock()) { + synchronized(staged_writes_lock) { + this->txbuff.push(msgout); + panda_ps->registerConnectionTx(shared_from_this()); + } + } +} + +void J2534Connection::rescheduleExistingTxMsgs() { + if (auto panda_ps = this->panda_dev.lock()) { + synchronized(staged_writes_lock) { + panda_ps->unstallConnectionTx(shared_from_this()); + } + } +} + +//Works well as long as the protocol doesn't support flow control. +void J2534Connection::processMessage(const J2534Frame& msg) { + FILTER_RESULT filter_res = FILTER_RESULT_NEUTRAL; + + for (auto filter : this->filters) { + if (filter == nullptr) continue; + FILTER_RESULT current_check_res = filter->check(msg); + if (current_check_res == FILTER_RESULT_BLOCK) return; + if (current_check_res == FILTER_RESULT_PASS) filter_res = FILTER_RESULT_PASS; + } + + if (filter_res == FILTER_RESULT_PASS) { + addMsgToRxQueue(msg); + } +} + +void J2534Connection::processIOCTLSetConfig(unsigned long Parameter, unsigned long Value) { + switch (Parameter) { + case DATA_RATE: // 5-500000 + this->setBaud(Value); + break; + case LOOPBACK: // 0 (OFF), 1 (ON) [0] + this->loopback = (Value != 0); + break; + case ISO15765_WFT_MAX: + break; + case NODE_ADDRESS: // J1850PWM Related (Not supported by panda). HDS requires these to 'work'. + case NETWORK_LINE: + case P1_MIN: // A bunch of stuff relating to ISO9141 and ISO14230 that the panda + case P1_MAX: // currently doesn't support. Don't let HDS know we can't use these. + case P2_MIN: + case P2_MAX: + case P3_MIN: + case P3_MAX: + case P4_MIN: + case P4_MAX: + case W0: + case W1: + case W2: + case W3: + case W4: + case W5: + case TIDLE: + case TINIL: + case TWUP: + case PARITY: + case T1_MAX: // SCI related options. The panda does not appear to support this + case T2_MAX: + case T3_MAX: + case T4_MAX: + case T5_MAX: + break; // Just smile and nod. + default: + printf("Got unknown SET code %X\n", Parameter); + } + + // reserved parameters usually mean special equiptment is required + if (Parameter >= 0x20) { + throw ERR_NOT_SUPPORTED; + } +} + +unsigned long J2534Connection::processIOCTLGetConfig(unsigned long Parameter) { + switch (Parameter) { + case DATA_RATE: + return this->getBaud(); + case LOOPBACK: + return this->loopback; + break; + case BIT_SAMPLE_POINT: + return 80; + case SYNC_JUMP_WIDTH: + return 15; + default: + // HDS rarely reads off values through ioctl GET_CONFIG, but it often + // just wants the call to pass without erroring, so just don't do anything. + printf("Got unknown code %X\n", Parameter); + } +} diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection.h b/drivers/windows/pandaJ2534DLL/J2534Connection.h new file mode 100644 index 0000000000..70f25a1063 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection.h @@ -0,0 +1,141 @@ +#pragma once +#include "panda_shared/panda.h" +#include "J2534_v0404.h" +#include "synchronize.h" +#include "J2534Frame.h" +#include "PandaJ2534Device.h" +#include "J2534MessageFilter.h" +#include "MessagePeriodic.h" + +class J2534Frame; +class Action; +class PandaJ2534Device; +class J2534MessageFilter; + +#define check_bmask(num, mask)(((num) & mask) == mask) + +/** +Class representing a generic J2534 Connection created by PassThruConnect, +and is associated with a channelID given to the J2534 API user. +Subclasses implement specific J2534 supported protocols. +*/ +class J2534Connection : public std::enable_shared_from_this { + friend class PandaJ2534Device; + +public: + J2534Connection( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate + ); + virtual ~J2534Connection() {}; + + //J2534 API functions + + virtual long PassThruReadMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); + long PassThruWriteMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); + virtual long PassThruStartPeriodicMsg(PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval); + virtual long PassThruStopPeriodicMsg(unsigned long MsgID); + + virtual long PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, + PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID); + + virtual long PassThruStopMsgFilter(unsigned long FilterID); + virtual long PassThruIoctl(unsigned long IoctlID, void *pInput, void *pOutput); + + //Functions for parsing messages to be send with PassThruWriteMsgs. + + virtual unsigned long validateTxMsg(PASSTHRU_MSG* msg); + virtual std::shared_ptr parseMessageTx(PASSTHRU_MSG& msg) { return nullptr; }; + + //IOCTL functions + + long init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput); + long initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput); + long clearTXBuff(); + long clearRXBuff(); + long clearPeriodicMsgs(); + long clearMsgFilters(); + + virtual void setBaud(unsigned long baud); + + unsigned long getBaud() { + return this->BaudRate; + } + + unsigned long getProtocol() { + return this->ProtocolID; + }; + + virtual bool isProtoCan() { + return FALSE; + } + + //Port is used in a protocol specific way to differentiate tranceivers. + unsigned long getPort() { + return this->port; + } + + virtual void processIOCTLSetConfig(unsigned long Parameter, unsigned long Value); + + virtual unsigned long processIOCTLGetConfig(unsigned long Parameter); + + //Called when the passthru device has received a message for this connection + //Loopback messages are processed separately. + virtual void processMessage(const J2534Frame& msg); + + //Limitations on message size. Override in every subclass. + + virtual unsigned long getMinMsgLen() { + return 1; + } + + virtual unsigned long getMaxMsgLen() { + return 4128; + } + + virtual unsigned long getMaxMsgSingleFrameLen() { + return 12; + } + + //Add an Action to the Task Queue for future processing. + //The task should be set its expire time before being submitted. + void schedultMsgTx(std::shared_ptr msgout); + + void rescheduleExistingTxMsgs(); + + std::shared_ptr getPandaDev() { + if (auto panda_dev_sp = this->panda_dev.lock()) + return panda_dev_sp; + return nullptr; + } + + //Add a message to the queue read by PassThruReadMsgs(). + void addMsgToRxQueue(const J2534Frame& frame) { + synchronized(messageRxBuff_mutex) { + messageRxBuff.push(frame); + } + } + + bool loopback = FALSE; + +protected: + unsigned long ProtocolID; + unsigned long Flags; + unsigned long BaudRate; + unsigned long port; + + std::weak_ptr panda_dev; + + Mutex messageRxBuff_mutex; + std::queue messageRxBuff; + + std::array, 10> filters; + std::queue> txbuff; + + std::array, 10> periodicMessages; + +private: + Mutex staged_writes_lock; +}; diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.cpp b/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.cpp new file mode 100644 index 0000000000..342616900d --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "J2534Connection_CAN.h" +#include "MessageTx_CAN.h" +#include "Timer.h" + +J2534Connection_CAN::J2534Connection_CAN( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate + ) : J2534Connection(panda_dev, ProtocolID, Flags, BaudRate) { + this->port = 0; + + if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000) + throw ERR_INVALID_BAUDRATE; + + panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, BaudRate/100); +}; + +unsigned long J2534Connection_CAN::validateTxMsg(PASSTHRU_MSG* msg) { + if ((msg->DataSize < this->getMinMsgLen() || msg->DataSize > this->getMaxMsgLen() || + (val_is_29bit(msg->TxFlags) != this->_is_29bit() && !check_bmask(this->Flags, CAN_ID_BOTH)))) + return ERR_INVALID_MSG; + return STATUS_NOERROR; +} + +std::shared_ptr J2534Connection_CAN::parseMessageTx(PASSTHRU_MSG& msg) { + return std::dynamic_pointer_cast(std::make_shared(shared_from_this(), msg)); +} + +void J2534Connection_CAN::setBaud(unsigned long BaudRate) { + if (auto panda_dev = this->getPandaDev()) { + if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000) + throw ERR_NOT_SUPPORTED; + + panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100)); + return J2534Connection::setBaud(BaudRate); + } else { + throw ERR_DEVICE_NOT_CONNECTED; + } +} diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.h b/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.h new file mode 100644 index 0000000000..3971351eea --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.h @@ -0,0 +1,43 @@ +#pragma once + +#include "J2534Connection.h" +#include "panda_shared/panda.h" + +#define val_is_29bit(num) check_bmask(num, CAN_29BIT_ID) + +class J2534Connection_CAN : public J2534Connection { +public: + J2534Connection_CAN( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate + ); + + virtual unsigned long validateTxMsg(PASSTHRU_MSG* msg); + + virtual std::shared_ptr parseMessageTx(PASSTHRU_MSG& pMsg); + + virtual void setBaud(unsigned long baud); + + virtual unsigned long getMinMsgLen() { + return 4; + } + + virtual unsigned long getMaxMsgLen() { + return 12; + } + + virtual unsigned long getMaxMsgSingleFrameLen() { + return 12; + } + + virtual bool isProtoCan() { + return TRUE; + } + + bool _is_29bit() { + return (this->Flags & CAN_29BIT_ID) == CAN_29BIT_ID; + } + +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.cpp b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.cpp new file mode 100644 index 0000000000..a83f6f4331 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.cpp @@ -0,0 +1,232 @@ +#include "stdafx.h" +#include "J2534Connection_ISO15765.h" +#include "Timer.h" +#include "constants_ISO15765.h" +#include + +J2534Connection_ISO15765::J2534Connection_ISO15765( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate +) : J2534Connection(panda_dev, ProtocolID, Flags, BaudRate), wftMax(0) { + this->port = 0; + + if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000) + throw ERR_INVALID_BAUDRATE; + + panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100)); +} + +unsigned long J2534Connection_ISO15765::validateTxMsg(PASSTHRU_MSG* msg) { + if ((msg->DataSize < this->getMinMsgLen() + (msg_is_extaddr(msg) ? 1 : 0) || + msg->DataSize > this->getMaxMsgLen() + (msg_is_extaddr(msg) ? 1 : 0) || + (val_is_29bit(msg->TxFlags) != this->_is_29bit() && !check_bmask(this->Flags, CAN_ID_BOTH)))) + return ERR_INVALID_MSG; + + int fid = get_matching_out_fc_filter_id(std::string((const char*)msg->Data, msg->DataSize), msg->TxFlags, 0xFFFFFFFF); + if (msg->DataSize > getMaxMsgSingleFrameLen() && fid == -1) return ERR_NO_FLOW_CONTROL; //11 bytes (4 for CANid, 7 payload) is max length of input frame. + + return STATUS_NOERROR; +} + +std::shared_ptr J2534Connection_ISO15765::parseMessageTx(PASSTHRU_MSG& msg) { + int fid = get_matching_out_fc_filter_id(std::string((const char*)msg.Data, msg.DataSize), msg.TxFlags, 0xFFFFFFFF); + if (msg.DataSize > getMaxMsgSingleFrameLen() && fid == -1) 1; + + return std::dynamic_pointer_cast( + std::make_shared(shared_from_this(), msg, (fid == -1) ? nullptr : this->filters[fid]) + ); +} + +//https://happilyembedded.wordpress.com/2016/02/15/can-multiple-frame-transmission/ +void J2534Connection_ISO15765::processMessage(const J2534Frame& msg) { + if (msg.ProtocolID != CAN) return; + + int fid = get_matching_in_fc_filter_id(msg, this->Flags); + if (fid == -1) return; + + auto filter = this->filters[fid]; + bool is_ext_addr = check_bmask(filter->flags, ISO15765_ADDR_TYPE); + uint8_t addrlen = is_ext_addr ? 5 : 4; + + switch (msg_get_type(msg, addrlen)) { + case FRAME_FLOWCTRL: + { + if (this->txbuff.size() == 0) + return; + if (msg.Data.size() < addrlen + 3) return; + uint8_t flow_status = msg.Data[addrlen] & 0x0F; + uint8_t block_size = msg.Data[addrlen + 1]; + uint8_t st_min = msg.Data[addrlen + 2]; + + auto txConvo = std::static_pointer_cast(this->txbuff.front()); + switch (flow_status) { + case FLOWCTRL_CONTINUE: { + if (st_min > 0xF9) break; + if (st_min >= 0xf1 && st_min <= 0xf9) { + txConvo->flowControlContinue(block_size, std::chrono::microseconds((st_min & 0x0F) * 100)); + } else if(st_min <= 0x7f) { + txConvo->flowControlContinue(block_size, std::chrono::microseconds(st_min * 1000)); + } else { + break; + } + txConvo->scheduleImmediate(); + this->rescheduleExistingTxMsgs(); + break; + } + case FLOWCTRL_WAIT: + txConvo->flowControlWait(this->wftMax); + break; + case FLOWCTRL_ABORT: + txConvo->flowControlAbort(); + break; + } + break; + } + case FRAME_SINGLE: + { + this->rxConversations[fid] = nullptr; //Reset any current transaction. + + if (is_ext_addr) { + if ((msg.Data[5] & 0x0F) > 6) return; + } else { + if ((msg.Data[4] & 0x0F) > 7) return; + } + + J2534Frame outframe(ISO15765, msg.RxStatus, 0, msg.Timestamp); + if (msg.Data.size() != 8 && check_bmask(this->Flags, ISO15765_FRAME_PAD)) + outframe.RxStatus |= ISO15765_PADDING_ERROR; + if (is_ext_addr) + outframe.RxStatus |= ISO15765_ADDR_TYPE; + outframe.Data = msg.Data.substr(0, addrlen) + msg.Data.substr(addrlen + 1, msg.Data[addrlen]); + outframe.ExtraDataIndex = outframe.Data.size(); + + addMsgToRxQueue(outframe); + break; + } + case FRAME_FIRST: + { + if (msg.Data.size() < 12) { + //A frame was received that could have held more data. + //No examples of this protocol show that happening, so + //it will be assumed that it is grounds to reset rx. + this->rxConversations[fid] = nullptr; + return; + } + + J2534Frame outframe(ISO15765, msg.RxStatus | START_OF_MESSAGE, 0, msg.Timestamp); + if (is_ext_addr) + outframe.RxStatus |= ISO15765_ADDR_TYPE; + outframe.Data = msg.Data.substr(0, addrlen); + + addMsgToRxQueue(outframe); + + this->rxConversations[fid] = std::make_shared( + ((msg.Data[addrlen] & 0x0F) << 8) | msg.Data[addrlen + 1], + msg.Data.substr(addrlen + 2, 12 - (addrlen + 2)), + msg.RxStatus, filter); + + //TODO maybe the flow control should also be scheduled in the TX list. + //Doing it this way because the filter can be 5 bytes in ext address mode. + std::string flowfilter = filter->get_flowctrl(); + uint32_t flow_addr = (((uint8_t)flowfilter[0]) << 24) | ((uint8_t)(flowfilter[1]) << 16) | ((uint8_t)(flowfilter[2]) << 8) | ((uint8_t)flowfilter[3]); + + std::string flowstrlresp; + if (flowfilter.size() > 4) + flowstrlresp += flowfilter[4]; + flowstrlresp += std::string("\x30\x00\x00", 3); + if (check_bmask(filter->flags, ISO15765_FRAME_PAD)) { + flowstrlresp += std::string(8 - flowstrlresp.size(), '\x00'); + } + + if (auto panda_dev_sp = this->panda_dev.lock()) { + panda_dev_sp->panda->can_send(flow_addr, val_is_29bit(msg.RxStatus), (const uint8_t *)flowstrlresp.c_str(), (uint8_t)flowstrlresp.size(), panda::PANDA_CAN1); + } + break; + } + case FRAME_CONSEC: + { + auto& convo = this->rxConversations[fid]; + if (convo == nullptr) return; + + if (!convo->rx_add_frame(msg.Data[addrlen], (is_ext_addr ? 6 : 7), msg.Data.substr(addrlen + 1))) { + //Delete this conversation. + convo = nullptr; + return; + } + + std::string final_msg; + if (convo->flush_result(final_msg)) { + convo = nullptr; + J2534Frame outframe(ISO15765, msg.RxStatus, 0, msg.Timestamp); + if (is_ext_addr) + outframe.RxStatus |= ISO15765_ADDR_TYPE; + outframe.Data = msg.Data.substr(0, addrlen) + final_msg; + outframe.ExtraDataIndex = outframe.Data.size(); + + addMsgToRxQueue(outframe); + } + break; + } + } +} + +void J2534Connection_ISO15765::setBaud(unsigned long BaudRate) { + if (auto panda_dev = this->getPandaDev()) { + if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000) + throw ERR_NOT_SUPPORTED; + + panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100)); + return J2534Connection::setBaud(BaudRate); + } else { + throw ERR_DEVICE_NOT_CONNECTED; + } +} + +long J2534Connection_ISO15765::PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, + PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) { + + if (FilterType != FLOW_CONTROL_FILTER) return ERR_INVALID_FILTER_ID; + return J2534Connection::PassThruStartMsgFilter(FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID); +} + +int J2534Connection_ISO15765::get_matching_out_fc_filter_id(const std::string& msgdata, unsigned long flags, unsigned long flagmask) { + for (unsigned int i = 0; i < this->filters.size(); i++) { + if (this->filters[i] == nullptr) continue; + auto filter = this->filters[i]->get_flowctrl(); + if (filter == msgdata.substr(0, filter.size()) && + (this->filters[i]->flags & flagmask) == (flags & flagmask)) + return i; + } + return -1; +} + +int J2534Connection_ISO15765::get_matching_in_fc_filter_id(const J2534Frame& msg, unsigned long flagmask) { + for (unsigned int i = 0; i < this->filters.size(); i++) { + if (this->filters[i] == nullptr) continue; + if (this->filters[i]->check(msg) == FILTER_RESULT_MATCH && + (this->filters[i]->flags & flagmask) == (msg.RxStatus & flagmask)) + return i; + } + return -1; +} + +void J2534Connection_ISO15765::processIOCTLSetConfig(unsigned long Parameter, unsigned long Value) { + switch (Parameter) { + case ISO15765_WFT_MAX: + this->wftMax = Value; + break; + default: + J2534Connection::processIOCTLSetConfig(Parameter, Value); + } +} + +unsigned long J2534Connection_ISO15765::processIOCTLGetConfig(unsigned long Parameter) { + switch (Parameter) { + case ISO15765_WFT_MAX: + return this->wftMax; + default: + return J2534Connection::processIOCTLGetConfig(Parameter); + } +} diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.h b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.h new file mode 100644 index 0000000000..beb9f012e0 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.h @@ -0,0 +1,65 @@ +#pragma once +#include +#include "J2534Connection.h" +#include "J2534Connection_CAN.h" +#include "MessageTx_ISO15765.h" +#include "MessageRx.h" + +class MessageTx_ISO15765; + +typedef struct { + std::string dispatched_msg; + std::string remaining_payload; +} PRESTAGED_WRITE; + +class J2534Connection_ISO15765 : public J2534Connection { +public: + J2534Connection_ISO15765( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate + ); + + virtual long PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG * pMaskMsg, PASSTHRU_MSG * pPatternMsg, PASSTHRU_MSG * pFlowControlMsg, unsigned long * pFilterID); + + int get_matching_out_fc_filter_id(const std::string & msgdata, unsigned long flags, unsigned long flagmask); + + int get_matching_in_fc_filter_id(const J2534Frame& msg, unsigned long flagmask); + + virtual unsigned long validateTxMsg(PASSTHRU_MSG* msg); + + virtual std::shared_ptr parseMessageTx(PASSTHRU_MSG& msg); + + virtual void processMessage(const J2534Frame& msg); + + virtual void setBaud(unsigned long baud); + + virtual void processIOCTLSetConfig(unsigned long Parameter, unsigned long Value); + + virtual unsigned long processIOCTLGetConfig(unsigned long Parameter); + + virtual unsigned long getMinMsgLen() { + return 4; + } + + virtual unsigned long getMaxMsgLen() { + return 4099; + }; + + virtual unsigned long getMaxMsgSingleFrameLen() { + return 11; + } + + virtual bool _is_29bit() { + return (this->Flags & CAN_29BIT_ID) == CAN_29BIT_ID; + } + + virtual bool isProtoCan() { + return TRUE; + } + +private: + std::array, 10> rxConversations; + unsigned int wftMax; +}; diff --git a/drivers/windows/pandaJ2534DLL/J2534Frame.h b/drivers/windows/pandaJ2534DLL/J2534Frame.h new file mode 100644 index 0000000000..2549216b6f --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Frame.h @@ -0,0 +1,48 @@ +#pragma once +#include "J2534_v0404.h" +#include "panda_shared/panda.h" + +/*A move convenient container for J2534 Messages than the static buffer provided by default.*/ +class J2534Frame { +public: + J2534Frame(unsigned long ProtocolID, unsigned long RxStatus=0, unsigned long TxFlags=0, unsigned long Timestamp=0) : + ProtocolID(ProtocolID), RxStatus(RxStatus), TxFlags(TxFlags), Timestamp(Timestamp), ExtraDataIndex(0), Data("") { }; + + J2534Frame(const panda::PANDA_CAN_MSG& msg_in) { + ProtocolID = CAN; + ExtraDataIndex = msg_in.len + 4; + Data.reserve(msg_in.len + 4); + Data += msg_in.addr >> 24; + Data += (msg_in.addr >> 16) & 0xFF; + Data += (msg_in.addr >> 8) & 0xFF; + Data += msg_in.addr & 0xFF; + Data += std::string((char*)&msg_in.dat, msg_in.len); + Timestamp = msg_in.recv_time; + RxStatus = (msg_in.addr_29b ? CAN_29BIT_ID : 0) | + (msg_in.is_receipt ? TX_MSG_TYPE : 0); + } + + J2534Frame(const PASSTHRU_MSG& msg) { + this->ProtocolID = msg.ProtocolID; + this->RxStatus = msg.RxStatus; + this->TxFlags = msg.TxFlags; + this->Timestamp = msg.Timestamp; + this->ExtraDataIndex = msg.ExtraDataIndex; + this->Data = std::string((const char*)msg.Data, msg.DataSize); + } + + J2534Frame() { + this->ProtocolID = 0; + this->RxStatus = 0; + this->TxFlags = 0; + this->Timestamp = 0; + this->ExtraDataIndex = 0; + } + + unsigned long ProtocolID; + unsigned long RxStatus; + unsigned long TxFlags; + unsigned long Timestamp; + unsigned long ExtraDataIndex; + std::string Data; +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/J2534MessageFilter.cpp b/drivers/windows/pandaJ2534DLL/J2534MessageFilter.cpp new file mode 100644 index 0000000000..2d19e1f4e5 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534MessageFilter.cpp @@ -0,0 +1,104 @@ +#include "stdafx.h" +#include "J2534MessageFilter.h" +#include "J2534Frame.h" + +J2534MessageFilter::J2534MessageFilter( + J2534Connection *const conn, + unsigned int filtertype, + PASSTHRU_MSG *pMaskMsg, + PASSTHRU_MSG *pPatternMsg, + PASSTHRU_MSG *pFlowControlMsg +) : filtertype(filtertype), flags(0), conn(conn) { + switch (filtertype) { + case PASS_FILTER: + case BLOCK_FILTER: + if (pMaskMsg == NULL || pPatternMsg == NULL) + throw ERR_NULL_PARAMETER; + if (pFlowControlMsg != NULL) + throw ERR_INVALID_FILTER_ID; + if (pMaskMsg->DataSize != pPatternMsg->DataSize) + throw ERR_INVALID_MSG; + break; + case FLOW_CONTROL_FILTER: + if (conn->getProtocol() != ISO15765) throw ERR_MSG_PROTOCOL_ID; //CHECK + if (pFlowControlMsg == NULL || pMaskMsg == NULL || pPatternMsg == NULL) + throw ERR_NULL_PARAMETER; + break; + default: + throw ERR_INVALID_MSG; + } + + if (!(conn->getMinMsgLen() < pMaskMsg->DataSize || pMaskMsg->DataSize < conn->getMaxMsgLen())) + throw ERR_INVALID_MSG; + if (conn->getProtocol() != pMaskMsg->ProtocolID) + throw ERR_MSG_PROTOCOL_ID; + this->maskMsg = std::string((char*)pMaskMsg->Data, pMaskMsg->DataSize); + + if (!(conn->getMinMsgLen() < pPatternMsg->DataSize || pPatternMsg->DataSize < conn->getMaxMsgLen())) + throw ERR_INVALID_MSG; + if (conn->getProtocol() != pPatternMsg->ProtocolID) + throw ERR_MSG_PROTOCOL_ID; + this->patternMsg = std::string((char*)pPatternMsg->Data, pPatternMsg->DataSize); + if (this->maskMsg.size() != this->patternMsg.size()) + throw ERR_INVALID_MSG; + + if (pFlowControlMsg) { + if (!(conn->getMinMsgLen() < pFlowControlMsg->DataSize || pFlowControlMsg->DataSize < conn->getMaxMsgLen())) + throw ERR_INVALID_MSG; + if (conn->getProtocol() != pFlowControlMsg->ProtocolID) + throw ERR_MSG_PROTOCOL_ID; + if (pMaskMsg->TxFlags != pPatternMsg->TxFlags || pMaskMsg->TxFlags != pFlowControlMsg->TxFlags) + throw ERR_INVALID_MSG; + if(pFlowControlMsg->TxFlags & ~(ISO15765_FRAME_PAD | CAN_29BIT_ID | ISO15765_ADDR_TYPE)) + throw ERR_INVALID_MSG; + if ((pFlowControlMsg->TxFlags & ISO15765_ADDR_TYPE) == ISO15765_ADDR_TYPE) { + if(pFlowControlMsg->DataSize != 5) + throw ERR_INVALID_MSG; + } else { + if (pFlowControlMsg->DataSize != 4) + throw ERR_INVALID_MSG; + } + this->flowCtrlMsg = std::string((char*)pFlowControlMsg->Data, pFlowControlMsg->DataSize); + if (this->flowCtrlMsg.size() != this->patternMsg.size()) + throw ERR_INVALID_MSG; + this->flags = pFlowControlMsg->TxFlags; + } +} + +bool J2534MessageFilter::operator ==(const J2534MessageFilter &b) const { + if (this->filtertype != b.filtertype) return FALSE; + if (this->maskMsg != b.maskMsg) return FALSE; + if (this->patternMsg != b.patternMsg) return FALSE; + if (this->flowCtrlMsg != b.flowCtrlMsg) return FALSE; + if (this->flags != b.flags) return FALSE; + return TRUE; +} + +FILTER_RESULT J2534MessageFilter::check(const J2534Frame& msg) { + bool matches = TRUE; + if (msg.Data.size() < this->maskMsg.size()) { + matches = FALSE; + } else { + for (int i = 0; i < this->maskMsg.size(); i++) { + if (this->patternMsg[i] != (msg.Data[i] & this->maskMsg[i])) { + matches = FALSE; + break; + } + } + } + + switch (this->filtertype) { + case PASS_FILTER: + return matches ? FILTER_RESULT_PASS : FILTER_RESULT_NEUTRAL; + case BLOCK_FILTER: + return matches ? FILTER_RESULT_BLOCK: FILTER_RESULT_NEUTRAL; + case FLOW_CONTROL_FILTER: + return matches ? FILTER_RESULT_MATCH : FILTER_RESULT_NOMATCH; + default: + throw std::out_of_range("Filtertype should not be able to be anything but PASS, BLOCK, or FLOW_CONTROL"); + } +} + +std::string J2534MessageFilter::get_flowctrl() { + return std::string(this->flowCtrlMsg); +} diff --git a/drivers/windows/pandaJ2534DLL/J2534MessageFilter.h b/drivers/windows/pandaJ2534DLL/J2534MessageFilter.h new file mode 100644 index 0000000000..c5e9a68390 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534MessageFilter.h @@ -0,0 +1,47 @@ +#pragma once +#include "J2534_v0404.h" +#include "J2534Connection.h" +#include "J2534Frame.h" + +typedef enum { + FILTER_RESULT_BLOCK, + FILTER_RESULT_NEUTRAL, + FILTER_RESULT_PASS, + FILTER_RESULT_NOMATCH = FILTER_RESULT_BLOCK, + FILTER_RESULT_MATCH = FILTER_RESULT_PASS, +} FILTER_RESULT; + +//Forward declare +class J2534Connection; + +/* Represents a J2534 Message Filter created by PassThruStartMsgFilter. + +J2534 uses filters to sort out messages in a simple and sane way. Except for +flow control filters. J2534 v04.04 uses filters to manage 'conversations' in +protocols that support flow control like ISO15765. The whole solution is a +hack, and J2534 v05.00 greatly simplifies this concept. But we are using +v04.04 so, here we are. +*/ +class J2534MessageFilter { +public: + J2534MessageFilter( + J2534Connection *const conn, + unsigned int filtertype, + PASSTHRU_MSG *pMaskMsg, + PASSTHRU_MSG *pPatternMsg, + PASSTHRU_MSG *pFlowControlMsg + ); + + bool J2534MessageFilter::operator ==(const J2534MessageFilter &b) const; + + FILTER_RESULT check(const J2534Frame& msg); + std::string get_flowctrl(); + + unsigned long flags; + J2534Connection *const conn; +private: + unsigned int filtertype; + std::string maskMsg; + std::string patternMsg; + std::string flowCtrlMsg; +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/J2534_v0404.h b/drivers/windows/pandaJ2534DLL/J2534_v0404.h new file mode 100644 index 0000000000..7cccf6b429 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534_v0404.h @@ -0,0 +1,428 @@ +// +// Copyright (c) 2015-2016 DashLogic, Inc. +// All Rights Reserved. +// +// http://www.dashlogic.com +// sales@dashlogic.com +// +// Redistribution and use in source and binary forms, with or without +// modification, including use for commercial purposes, 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 the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// 4. Redistributions of any form whatsoever must retain the following +// acknowledgment: 'This product includes software developed by +// "DashLogic, Inc." (http://www.dashlogic.com/).' +// +// 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. +// + + +// +// Formatting: +// Indents: Use tabs only (1 tab per indent) +// Tab Size: 4 spaces +// +// File Revision: +// $Rev: 5216 $ +// $Date: 2016-03-15 09:32:34 -0600 (Tue, 15 Mar 2016) $ +// + +#pragma once + +#ifdef PANDAJ2534DLL_EXPORTS +#define PANDAJ2534DLL_API extern "C" __declspec(dllexport) +#else +#define PANDAJ2534DLL_API +//__declspec(dllimport) +#endif + +// +// Platform-specific Defines: +// +// PTAPI: Define this yourself if you want a specific calling +// convention or other modifiers on the Pass-Thru API +// functions. Typically, on Windows, PTAPI will be defined +// as WINAPI, which enables the __stdcall convention. +// +#define PTAPI __stdcall //WINAPI + +// +// J2534-1 v04.04 ProtocolID Values +// +#define J1850VPW 0x01 +#define J1850PWM 0x02 +#define ISO9141 0x03 +#define ISO14230 0x04 +#define CAN 0x05 +#define ISO15765 0x06 +#define SCI_A_ENGINE 0x07 +#define SCI_A_TRANS 0x08 +#define SCI_B_ENGINE 0x09 +#define SCI_B_TRANS 0x0A + + +// +// J2534-2 ProtocolID Values +// +#define J1850VPW_PS 0x00008000 +#define J1850PWM_PS 0x00008001 +#define ISO9141_PS 0x00008002 +#define ISO14230_PS 0x00008003 +#define CAN_PS 0x00008004 +#define ISO15765_PS 0x00008005 +#define J2610_PS 0x00008006 +#define SW_ISO15765_PS 0x00008007 +#define SW_CAN_PS 0x00008008 +#define GM_UART_PS 0x00008009 +#define CAN_CH1 0x00009000 +#define CAN_CH2 (CAN_CH1 + 1) +#define CAN_CH128 (CAN_CH1 + 127) +#define J1850VPW_CH1 0x00009080 +#define J1850VPW_CH2 (J1850VPW_CH1 + 1) +#define J1850VPW_CH128 (J1850VPW_CH1 + 127) +#define J1850PWM_CH1 0x00009160 +#define J1850PWM_CH2 (J1850PWM_CH1 + 1) +#define J1850PWM_CH128 (J1850PWM_CH1 + 127) +#define ISO9141_CH1 0x00009240 +#define ISO9141_CH2 (ISO9141_CH1 + 1) +#define ISO9141_CH128 (ISO9141_CH1 + 127) +#define ISO14230_CH1 0x00009320 +#define ISO14230_CH2 (ISO14230_CH1 + 1) +#define ISO14230_CH128 (ISO14230_CH1 + 127) +#define ISO15765_CH1 0x00009400 +#define ISO15765_CH2 (ISO15765_CH1 + 1) +#define ISO15765_CH128 (ISO15765_CH1 + 127) +#define SW_CAN_CAN_CH1 0x00009480 +#define SW_CAN_CAN_CH2 (SW_CAN_CAN_CH1 + 1) +#define SW_CAN_CAN_CH128 (SW_CAN_CAN_CH1 + 127) +#define SW_CAN_ISO15765_CH1 0x00009560 +#define SW_CAN_ISO15765_CH2 (SW_CAN_ISO15765_CH1 + 1) +#define SW_CAN_ISO15765_CH128 (SW_CAN_ISO15765_CH1 + 127) +#define J2610_CH1 0x00009640 +#define J2610_CH2 (J2610_CH1 + 1) +#define J2610_CH128 (J2610_CH1 + 127) +#define ANALOG_IN_CH1 0x0000C000 +#define ANALOG_IN_CH2 0x0000C001 +#define ANALOG_IN_CH32 0x0000C01F + + +// +// J2534-1 v04.04 Error Values +// +#define STATUS_NOERROR 0x00 // Function call successful. +#define ERR_NOT_SUPPORTED 0x01 // Device cannot support requested functionality mandated in J2534. Device is not fully SAE J2534 compliant. +#define ERR_INVALID_CHANNEL_ID 0x02 // Invalid ChannelID value. +#define ERR_INVALID_PROTOCOL_ID 0x03 // Invalid or unsupported ProtocolID, or there is a resource conflict (i.e. trying to connect to multiple mutually exclusive protocols such as J1850PWM and J1850VPW, or CAN and SCI, etc.). +#define ERR_NULL_PARAMETER 0x04 // NULL pointer supplied where a valid pointer is required. +#define ERR_INVALID_IOCTL_VALUE 0x05 // Invalid value for Ioctl parameter. +#define ERR_INVALID_FLAGS 0x06 // Invalid flag values. +#define ERR_FAILED 0x07 // Undefined error, use PassThruGetLastError() for text description. +#define ERR_DEVICE_NOT_CONNECTED 0x08 // Unable to communicate with device. +#define ERR_TIMEOUT 0x09 // Read or write timeout: + // PassThruReadMsgs() - No message available to read or could not read the specified number of messages. The actual number of messages read is placed in . + // PassThruWriteMsgs() - Device could not write the specified number of messages. The actual number of messages sent on the vehicle network is placed in . +#define ERR_INVALID_MSG 0x0A // Invalid message structure pointed to by pMsg. +#define ERR_INVALID_TIME_INTERVAL 0x0B // Invalid TimeInterval value. +#define ERR_EXCEEDED_LIMIT 0x0C // Exceeded maximum number of message IDs or allocated space. +#define ERR_INVALID_MSG_ID 0x0D // Invalid MsgID value. +#define ERR_DEVICE_IN_USE 0x0E // Device is currently open. +#define ERR_INVALID_IOCTL_ID 0x0F // Invalid IoctlID value. +#define ERR_BUFFER_EMPTY 0x10 // Protocol message buffer empty, no messages available to read. +#define ERR_BUFFER_FULL 0x11 // Protocol message buffer full. All the messages specified may not have been transmitted. +#define ERR_BUFFER_OVERFLOW 0x12 // Indicates a buffer overflow occurred and messages were lost. +#define ERR_PIN_INVALID 0x13 // Invalid pin number, pin number already in use, or voltage already applied to a different pin. +#define ERR_CHANNEL_IN_USE 0x14 // Channel number is currently connected. +#define ERR_MSG_PROTOCOL_ID 0x15 // Protocol type in the message does not match the protocol associated with the Channel ID +#define ERR_INVALID_FILTER_ID 0x16 // Invalid Filter ID value. +#define ERR_NO_FLOW_CONTROL 0x17 // No flow control filter set or matched (for ProtocolID ISO15765 only). +#define ERR_NOT_UNIQUE 0x18 // A CAN ID in pPatternMsg or pFlowControlMsg matches either ID in an existing FLOW_CONTROL_FILTER +#define ERR_INVALID_BAUDRATE 0x19 // The desired baud rate cannot be achieved within the tolerance specified in SAE J2534-1 Section 6.5 +#define ERR_INVALID_DEVICE_ID 0x1A // Device ID invalid. + + +// +// J2534-1 v04.04 Connect Flags +// +#define CAN_29BIT_ID 0x0100 +#define ISO9141_NO_CHECKSUM 0x0200 +#define CAN_ID_BOTH 0x0800 +#define ISO9141_K_LINE_ONLY 0x1000 + + +// +// J2534-1 v04.04 Filter Type Values +// +#define PASS_FILTER 0x00000001 +#define BLOCK_FILTER 0x00000002 +#define FLOW_CONTROL_FILTER 0x00000003 + + +// +// J2534-1 v04.04 Programming Voltage Pin Numbers +// +#define AUXILIARY_OUTPUT_PIN 0 +#define SAE_J1962_CONNECTOR_PIN_6 6 +#define SAE_J1962_CONNECTOR_PIN_9 9 +#define SAE_J1962_CONNECTOR_PIN_11 11 +#define SAE_J1962_CONNECTOR_PIN_12 12 +#define SAE_J1962_CONNECTOR_PIN_13 13 +#define SAE_J1962_CONNECTOR_PIN_14 14 +#define SAE_J1962_CONNECTOR_PIN_15 15 // Short to ground only + + +// +// J2534-1 v04.04 Programming Voltage Values +// +#define SHORT_TO_GROUND 0xFFFFFFFE +#define VOLTAGE_OFF 0xFFFFFFFF + + +// +// J2534-1 v04.04 API Version Values +// +#define J2534_APIVER_FEBRUARY_2002 "02.02" +#define J2534_APIVER_NOVEMBER_2004 "04.04" + + +// +// J2534-1 v04.04 IOCTL ID Values +// +#define GET_CONFIG 0x01 // pInput = SCONFIG_LIST, pOutput = NULL +#define SET_CONFIG 0x02 // pInput = SCONFIG_LIST, pOutput = NULL +#define READ_VBATT 0x03 // pInput = NULL, pOutput = unsigned long +#define FIVE_BAUD_INIT 0x04 // pInput = SBYTE_ARRAY, pOutput = SBYTE_ARRAY +#define FAST_INIT 0x05 // pInput = PASSTHRU_MSG, pOutput = PASSTHRU_MSG +#define CLEAR_TX_BUFFER 0x07 // pInput = NULL, pOutput = NULL +#define CLEAR_RX_BUFFER 0x08 // pInput = NULL, pOutput = NULL +#define CLEAR_PERIODIC_MSGS 0x09 // pInput = NULL, pOutput = NULL +#define CLEAR_MSG_FILTERS 0x0A // pInput = NULL, pOutput = NULL +#define CLEAR_FUNCT_MSG_LOOKUP_TABLE 0x0B // pInput = NULL, pOutput = NULL +#define ADD_TO_FUNCT_MSG_LOOKUP_TABLE 0x0C // pInput = SBYTE_ARRAY, pOutput = NULL +#define DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE 0x0D // pInput = SBYTE_ARRAY, pOutput = NULL +#define READ_PROG_VOLTAGE 0x0E // pInput = NULL, pOutput = unsigned long + + +// +// J2534-2 IOCTL ID Values +// +#define SW_CAN_HS 0x00008000 // pInput = NULL, pOutput = NULL +#define SW_CAN_NS 0x00008001 // pInput = NULL, pOutput = NULL +#define SET_POLL_RESPONSE 0x00008002 // pInput = SBYTE_ARRAY, pOutput = NULL +#define BECOME_MASTER 0x00008003 // pInput = unsigned char, pOutput = NULL + + +// +// J2534-1 v04.04 Configuration Parameter Values +// Default value is enclosed in square brackets "[" and "]" +// +#define DATA_RATE 0x01 // 5-500000 +#define LOOPBACK 0x03 // 0 (OFF), 1 (ON) [0] +#define NODE_ADDRESS 0x04 // J1850PWM: 0x00-0xFF +#define NETWORK_LINE 0x05 // J1850PWM: 0 (BUS_NORMAL), 1 (BUS_PLUS), 2 (BUS_MINUS) [0] +#define P1_MIN 0x06 // ISO9141 or ISO14230: Not used by interface +#define P1_MAX 0x07 // ISO9141 or ISO14230: 0x1-0xFFFF (.5 ms per bit) [40 (20ms)] +#define P2_MIN 0x08 // ISO9141 or ISO14230: Not used by interface +#define P2_MAX 0x09 // ISO9141 or ISO14230: Not used by interface +#define P3_MIN 0x0A // ISO9141 or ISO14230: 0x0-0xFFFF (.5 ms per bit) [110 (55ms)] +#define P3_MAX 0x0B // ISO9141 or ISO14230: Not used by interface +#define P4_MIN 0x0C // ISO9141 or ISO14230: 0x0-0xFFFF (.5 ms per bit) [10 (5ms)] +#define P4_MAX 0x0D // ISO9141 or ISO14230: Not used by interface +#define W0 0x19 // ISO9141: 0x0-0xFFFF (1 ms per bit) [300] +#define W1 0x0E // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [300] +#define W2 0x0F // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [20] +#define W3 0x10 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [20] +#define W4 0x11 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [50] +#define W5 0x12 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [300] +#define TIDLE 0x13 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [300] +#define TINIL 0x14 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [25] +#define TWUP 0x15 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [50] +#define PARITY 0x16 // ISO9141 or ISO14230: 0 (NO_PARITY), 1 (ODD_PARITY), 2 (EVEN_PARITY) [0] +#define BIT_SAMPLE_POINT 0x17 // CAN: 0-100 (1% per bit) [80] +#define SYNC_JUMP_WIDTH 0x18 // CAN: 0-100 (1% per bit) [15] +#define T1_MAX 0x1A // SCI: 0x0-0xFFFF (1 ms per bit) [20] +#define T2_MAX 0x1B // SCI: 0x0-0xFFFF (1 ms per bit) [100] +#define T3_MAX 0x24 // SCI: 0x0-0xFFFF (1 ms per bit) [50] +#define T4_MAX 0x1C // SCI: 0x0-0xFFFF (1 ms per bit) [20] +#define T5_MAX 0x1D // SCI: 0x0-0xFFFF (1 ms per bit) [100] +#define ISO15765_BS 0x1E // ISO15765: 0x0-0xFF [0] +#define ISO15765_STMIN 0x1F // ISO15765: 0x0-0xFF [0] +#define ISO15765_BS_TX 0x22 // ISO15765: 0x0-0xFF,0xFFFF [0xFFFF] +#define ISO15765_STMIN_TX 0x23 // ISO15765: 0x0-0xFF,0xFFFF [0xFFFF] +#define DATA_BITS 0x20 // ISO9141 or ISO14230: 0 (8 data bits), 1 (7 data bits) [0] +#define FIVE_BAUD_MOD 0x21 // ISO9141 or ISO14230: 0 (ISO 9141-2/14230-4), 1 (Inv KB2), 2 (Inv Addr), 3 (ISO 9141) [0] +#define ISO15765_WFT_MAX 0x25 // ISO15765: 0x0-0xFF [0] + + +// +// J2534-2 Configuration Parameter Values +// Default value is enclosed in square brackets "[" and "]" +// +#define CAN_MIXED_FORMAT 0x00008000 // See #defines below. [0] +#define J1962_PINS 0x00008001 // 0xPPSS PP: 0x00-0x10 SS: 0x00-0x10 PP!=SS, except 0x0000. Exclude pins 4, 5, and 16. [0] +#define SW_CAN_HS_DATA_RATE 0x00008010 // SWCAN: 5-500000 [83333] +#define SW_CAN_SPEEDCHANGE_ENABLE 0x00008011 // SWCAN: 0 (DISABLE_SPDCHANGE), 1 (ENABLE_SPDCHANGE) [0] +#define SW_CAN_RES_SWITCH 0x00008012 // SWCAN: 0 (DISCONNECT_RESISTOR), 1 (CONNECT_RESISTOR), 2 (AUTO_ RESISTOR) [0] +#define ACTIVE_CHANNELS 0x00008020 // ANALOG: 0-0xFFFFFFFF +#define SAMPLE_RATE 0x00008021 // ANALOG: 0-0xFFFFFFFF [0] (high bit changes meaning from samples/sec to seconds/sample) +#define SAMPLES_PER_READING 0x00008022 // ANALOG: 1-0xFFFFFFFF [1] +#define READINGS_PER_MSG 0x00008023 // ANALOG: 1-0x00000408 (1 - 1032) [1] +#define AVERAGING_METHOD 0x00008024 // ANALOG: 0-0xFFFFFFFF [0] +#define SAMPLE_RESOLUTION 0x00008025 // ANALOG READ-ONLY: 0x1-0x20 (1 - 32) +#define INPUT_RANGE_LOW 0x00008026 // ANALOG READ-ONLY: 0x80000000-0x7FFFFFFF (-2147483648-2147483647) +#define INPUT_RANGE_HIGH 0x00008027 // ANALOG READ-ONLY: 0x80000000-0x7FFFFFFF (-2147483648-2147483647) + + +// +// J2534-2 Mixed-Mode/Format CAN Definitions +// +#define CAN_MIXED_FORMAT_OFF 0 // Messages will be treated as ISO 15765 ONLY. +#define CAN_MIXED_FORMAT_ON 1 // Messages will be treated as either ISO 15765 or an unformatted CAN frame. +#define CAN_MIXED_FORMAT_ALL_FRAMES 2 // Messages will be treated as ISO 15765, an unformatted CAN frame, or both. + + +// +// J2534-2 Analog Channel Averaging Method Definitions +// +#define SIMPLE_AVERAGE 0x00000000 // Simple arithmetic mean +#define MAX_LIMIT_AVERAGE 0x00000001 // Choose the biggest value +#define MIN_LIMIT_AVERAGE 0x00000002 // Choose the lowest value +#define MEDIAN_AVERAGE 0x00000003 // Choose arithmetic median + + +// +// J2534-1 v04.04 RxStatus Definitions +// +#define TX_MSG_TYPE 0x0001 +#define START_OF_MESSAGE 0x0002 +#define RX_BREAK 0x0004 +#define TX_INDICATION 0x0008 +#define ISO15765_PADDING_ERROR 0x0010 +#define ISO15765_ADDR_TYPE 0x0080 +//#define CAN_29BIT_ID 0x0100 // Defined above + + +// +// J2534-2 RxStatus Definitions +// +#define SW_CAN_HV_RX 0x00010000 // SWCAN Channels Only +#define SW_CAN_HS_RX 0x00020000 // SWCAN Channels Only +#define SW_CAN_NS_RX 0x00040000 // SWCAN Channels Only +#define OVERFLOW_ 0x00010000 // Analog Input Channels Only + + +// +// J2534-1 v04.04 TxFlags Definitions +// +#define ISO15765_FRAME_PAD 0x0040 +//#define ISO15765_ADDR_TYPE 0x0080 // Defined above +//#define CAN_29BIT_ID 0x0100 // Defined above +#define WAIT_P3_MIN_ONLY 0x0200 +#define SCI_MODE 0x400000 +#define SCI_TX_VOLTAGE 0x800000 + + +// +// J2534-2 TxFlags Definitions +// +#define SW_CAN_HV_TX 0x00000400 + + +// +// J2534-1 v04.04 Structure Definitions +// +typedef struct +{ + unsigned long Parameter; // Name of parameter + unsigned long Value; // Value of the parameter +} SCONFIG; + + +typedef struct +{ + unsigned long NumOfParams; // Number of SCONFIG elements + SCONFIG* ConfigPtr; // Array of SCONFIG +} SCONFIG_LIST; + + +typedef struct +{ + unsigned long NumOfBytes; // Number of bytes in the array + unsigned char* BytePtr; // Array of bytes +} SBYTE_ARRAY; + + +typedef struct +{ + unsigned long ProtocolID; + unsigned long RxStatus; + unsigned long TxFlags; + unsigned long Timestamp; + unsigned long DataSize; + unsigned long ExtraDataIndex; + unsigned char Data[4128]; +} PASSTHRU_MSG; + +// +// J2534-1 v04.04 Function Prototypes +// +PANDAJ2534DLL_API long PTAPI PassThruOpen(void *pName, unsigned long *pDeviceID); +PANDAJ2534DLL_API long PTAPI PassThruClose(unsigned long DeviceID); +PANDAJ2534DLL_API long PTAPI PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long BaudRate, unsigned long *pChannelID); +PANDAJ2534DLL_API long PTAPI PassThruDisconnect(unsigned long ChannelID); +PANDAJ2534DLL_API long PTAPI PassThruReadMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); +PANDAJ2534DLL_API long PTAPI PassThruWriteMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); +PANDAJ2534DLL_API long PTAPI PassThruStartPeriodicMsg(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval); +PANDAJ2534DLL_API long PTAPI PassThruStopPeriodicMsg(unsigned long ChannelID, unsigned long MsgID); +PANDAJ2534DLL_API long PTAPI PassThruStartMsgFilter(unsigned long ChannelID, unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID); +PANDAJ2534DLL_API long PTAPI PassThruStopMsgFilter(unsigned long ChannelID, unsigned long FilterID); +PANDAJ2534DLL_API long PTAPI PassThruSetProgrammingVoltage(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage); +PANDAJ2534DLL_API long PTAPI PassThruReadVersion(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion); +PANDAJ2534DLL_API long PTAPI PassThruGetLastError(char *pErrorDescription); +PANDAJ2534DLL_API long PTAPI PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput); + + +// +// J2534-1 v04.04 Function Typedefs +// These function typedefs allow simpler use of the J2534 API by +// allowing you to do things like this: +// PTCONNECT pPassThruConnectFunc = GetProcAddress(hModule, "PassThruConnect"); +// if (pPassThruConnectFunc == NULL) +// return FALSE; +// pPassThruConnectFunc(DeviceID, CAN, CAN_29BIT_ID, 500000, &ChannelID); +// +typedef long (PTAPI *PTOPEN)(void *pName, unsigned long *pDeviceID); +typedef long (PTAPI *PTCLOSE)(unsigned long DeviceID); +typedef long (PTAPI *PTCONNECT)(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long BaudRate, unsigned long *pChannelID); +typedef long (PTAPI *PTDISCONNECT)(unsigned long ChannelID); +typedef long (PTAPI *PTREADMSGS)(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); +typedef long (PTAPI *PTWRITEMSGS)(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); +typedef long (PTAPI *PTSTARTPERIODICMSG)(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval); +typedef long (PTAPI *PTSTOPPERIODICMSG)(unsigned long ChannelID, unsigned long MsgID); +typedef long (PTAPI *PTSTARTMSGFILTER)(unsigned long ChannelID, unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID); +typedef long (PTAPI *PTSTOPMSGFILTER)(unsigned long ChannelID, unsigned long FilterID); +typedef long (PTAPI *PTSETPROGRAMMINGVOLTAGE)(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage); +typedef long (PTAPI *PTREADVERSION)(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion); +typedef long (PTAPI *PTGETLASTERROR)(char *pErrorDescription); +typedef long (PTAPI *PTIOCTL)(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput); diff --git a/drivers/windows/pandaJ2534DLL/J2534register_x64.reg b/drivers/windows/pandaJ2534DLL/J2534register_x64.reg new file mode 100644 index 0000000000..120ab391c7 Binary files /dev/null and b/drivers/windows/pandaJ2534DLL/J2534register_x64.reg differ diff --git a/drivers/windows/pandaJ2534DLL/MessagePeriodic.cpp b/drivers/windows/pandaJ2534DLL/MessagePeriodic.cpp new file mode 100644 index 0000000000..0c3416e0da --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessagePeriodic.cpp @@ -0,0 +1,30 @@ +#include "stdafx.h" +#include "MessagePeriodic.h" +#include "J2534Connection.h" + +MessagePeriodic::MessagePeriodic( + std::chrono::microseconds delay, + std::shared_ptr msg +) : Action(msg->connection, delay), msg(msg), runyet(FALSE), active(TRUE) { }; + +void MessagePeriodic::execute() { + if (!this->active) return; + if (this->runyet) { + if (msg->isFinished()) { + msg->reset(); + msg->execute(); + } + } else { + this->runyet = TRUE; + msg->execute(); + } + + if (auto conn_sp = this->connection.lock()) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + //Scheduling must be relative to now incase there was a long stall that + //would case it to be super far behind and try to catch up forever. + this->scheduleImmediateDelay(); + panda_dev_sp->insertActionIntoTaskList(shared_from_this()); + } + } +} diff --git a/drivers/windows/pandaJ2534DLL/MessagePeriodic.h b/drivers/windows/pandaJ2534DLL/MessagePeriodic.h new file mode 100644 index 0000000000..40132565c1 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessagePeriodic.h @@ -0,0 +1,33 @@ +#pragma once +#include "Action.h" +#include "MessageTx.h" + +class J2534Connection; + +/* A message that is resent on a given period. Created with calls to PassThruStartPeriodicMessage. + +Instead of making each J2534 protocol implementation have to implement periodic message +functionality, this class takes a message to be sent, and passes along the execute call +to the message, then reschedules itself. +*/ +class MessagePeriodic : public Action, public std::enable_shared_from_this +{ +public: + MessagePeriodic( + std::chrono::microseconds delay, + std::shared_ptr msg + ); + + virtual void execute(); + + void cancel() { + this->active = FALSE; + } + +protected: + std::shared_ptr msg; + +private: + BOOL runyet; + BOOL active; +}; diff --git a/drivers/windows/pandaJ2534DLL/MessageRx.h b/drivers/windows/pandaJ2534DLL/MessageRx.h new file mode 100644 index 0000000000..2af24364ff --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageRx.h @@ -0,0 +1,61 @@ +#pragma once + +class MessageRx +{ +public: + MessageRx( + unsigned long size, + std::string piece, + unsigned long rxFlags, + std::shared_ptr filter + ) : expected_size(size & 0xFFF), flags(rxFlags) { + msg.reserve(expected_size); + msg = piece; + next_part = 1; + }; + + bool rx_add_frame(uint8_t pci_byte, unsigned int max_packet_size, const std::string piece) { + if ((pci_byte & 0x0F) != this->next_part) { + //TODO: Maybe this should instantly fail the transaction. + return TRUE; + } + + this->next_part = (this->next_part + 1) % 0x10; + unsigned int payload_len = MIN(expected_size - msg.size(), max_packet_size); + if (piece.size() < payload_len) { + //A frame was received that could have held more data. + //No examples of this protocol show that happening, so + //it will be assumed that it is grounds to reset rx. + return FALSE; + } + msg += piece.substr(0, payload_len); + + return TRUE; + } + + unsigned int bytes_remaining() { + return this->expected_size - this->msg.size(); + } + + bool is_ready() { + return this->msg.size() == this->expected_size; + } + + bool flush_result(std::string& final_msg) { + if (this->msg.size() == this->expected_size) { + final_msg = this->msg; + return TRUE; + } + return FALSE; + } + + uint8_t getNextConsecutiveFrameId() { + return this->next_part++; + } + + std::weak_ptr filter; + unsigned long flags; + unsigned long expected_size; + std::string msg; + unsigned char next_part; +}; diff --git a/drivers/windows/pandaJ2534DLL/MessageTx.h b/drivers/windows/pandaJ2534DLL/MessageTx.h new file mode 100644 index 0000000000..5315fa058f --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx.h @@ -0,0 +1,25 @@ +#pragma once +#include "Action.h" +#include "J2534Frame.h" + +class J2534Connection; + +class MessageTx : public Action, public std::enable_shared_from_this +{ +public: + MessageTx( + std::weak_ptr connection_in, + PASSTHRU_MSG& to_send + ) : Action(connection_in), fullmsg(to_send) { }; + + virtual BOOL checkTxReceipt(J2534Frame frame) = 0; + + virtual BOOL isFinished() = 0; + + virtual BOOL txReady() = 0; + + virtual void reset() = 0; + +protected: + J2534Frame fullmsg; +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/MessageTxTimeout.cpp b/drivers/windows/pandaJ2534DLL/MessageTxTimeout.cpp new file mode 100644 index 0000000000..2e21ba3ce1 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTxTimeout.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" +#include "J2534Connection.h" +#include "MessageTxTimeout.h" + +MessageTxTimeoutable::MessageTxTimeoutable( + std::weak_ptr connection, + PASSTHRU_MSG& to_send +) : MessageTx(connection, to_send), recvCount(0) { }; + +void MessageTxTimeoutable::scheduleTimeout(std::chrono::microseconds timeoutus) { + if (auto conn_sp = this->connection.lock()) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + auto timeoutobj = std::make_shared(std::static_pointer_cast(shared_from_this()), timeoutus); + panda_dev_sp->scheduleAction(std::static_pointer_cast(timeoutobj), TRUE); + } + } +} + +void MessageTxTimeoutable::scheduleTimeout(unsigned long timeoutus) { + scheduleTimeout(std::chrono::microseconds(timeoutus)); +} + + + +MessageTxTimeout::MessageTxTimeout( + std::shared_ptr msg, + std::chrono::microseconds timeout +) : Action(msg->connection), msg(msg), lastRecvCount(msg->getRecvCount()) { + delay = timeout; +}; + +MessageTxTimeout::MessageTxTimeout( + std::shared_ptr msg, + unsigned long timeout +) : MessageTxTimeout(msg, std::chrono::microseconds(timeout * 1000)) { }; + +void MessageTxTimeout::execute() { + if (auto msg_sp = this->msg.lock()) { + if (msg_sp->getRecvCount() == this->lastRecvCount) { + msg_sp->onTimeout(); + } + } +} diff --git a/drivers/windows/pandaJ2534DLL/MessageTxTimeout.h b/drivers/windows/pandaJ2534DLL/MessageTxTimeout.h new file mode 100644 index 0000000000..a9c878468c --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTxTimeout.h @@ -0,0 +1,52 @@ +#pragma once +#include "Action.h" +#include "MessageTx.h" + +class MessageTxTimeout; + +/* A special type of MessageTx for multipart messages that supports being canceled with a timeout.*/ +class MessageTxTimeoutable : public MessageTx +{ +public: + MessageTxTimeoutable( + std::weak_ptr connection, + PASSTHRU_MSG& to_send + ); + + unsigned long getRecvCount() { + return recvCount; + } + + virtual void onTimeout() = 0; + +protected: + unsigned long recvCount; + + void scheduleTimeout(std::chrono::microseconds timeoutus); + + void scheduleTimeout(unsigned long timeoutus); +}; + + +/* An Action that cancels MessageTxTimeoutableif the Timeout Actoin executes +before the MessageTxTimeoutableif renews its timeout. +*/ +class MessageTxTimeout : public Action +{ +public: + MessageTxTimeout( + std::shared_ptr msg, + std::chrono::microseconds timeout + ); + + MessageTxTimeout( + std::shared_ptr msg, + unsigned long timeout + ); + + virtual void execute(); + +private: + std::weak_ptr msg; + unsigned long lastRecvCount; +}; diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_CAN.cpp b/drivers/windows/pandaJ2534DLL/MessageTx_CAN.cpp new file mode 100644 index 0000000000..8217ce539a --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx_CAN.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" +#include "MessageTx_CAN.h" +#include "J2534Connection_CAN.h" + +MessageTx_CAN::MessageTx_CAN( + std::shared_ptr connection_in, + PASSTHRU_MSG& to_send +) : MessageTx(connection_in, to_send), sentyet(FALSE), txInFlight(FALSE) {}; + +void MessageTx_CAN::execute() { + uint32_t addr = ((uint8_t)fullmsg.Data[0]) << 24 | ((uint8_t)fullmsg.Data[1]) << 16 | + ((uint8_t)fullmsg.Data[2]) << 8 | ((uint8_t)fullmsg.Data[3]); + + if (auto conn_sp = std::static_pointer_cast(this->connection.lock())) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + auto payload = fullmsg.Data.substr(4); + if (panda_dev_sp->panda->can_send(addr, check_bmask(this->fullmsg.TxFlags, CAN_29BIT_ID), + (const uint8_t*)payload.c_str(), (uint8_t)payload.size(), panda::PANDA_CAN1) == FALSE) { + return; + } + this->txInFlight = TRUE; + this->sentyet = TRUE; + panda_dev_sp->txMsgsAwaitingEcho.push(shared_from_this()); + } + } +} + +//Returns TRUE if receipt is consumed by the msg, FALSE otherwise. +BOOL MessageTx_CAN::checkTxReceipt(J2534Frame frame) { + if (txReady()) return FALSE; + if (frame.Data == fullmsg.Data && ((this->fullmsg.TxFlags & CAN_29BIT_ID) == (frame.RxStatus & CAN_29BIT_ID))) { + txInFlight = FALSE; + if (auto conn_sp = std::static_pointer_cast(this->connection.lock())) + if (conn_sp->loopback) + conn_sp->addMsgToRxQueue(frame); + return TRUE; + } + return FALSE; +} + +void MessageTx_CAN::reset() { + sentyet = FALSE; + txInFlight = FALSE; +} diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_CAN.h b/drivers/windows/pandaJ2534DLL/MessageTx_CAN.h new file mode 100644 index 0000000000..afac75ef81 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx_CAN.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include "MessageTx.h" + +class J2534Connection; + +class MessageTx_CAN : public MessageTx +{ +public: + MessageTx_CAN( + std::shared_ptr connection_in, + PASSTHRU_MSG& to_send + ); + + virtual void execute(); + + //Returns TRUE if receipt is consumed by the msg, FALSE otherwise. + virtual BOOL checkTxReceipt(J2534Frame frame); + + virtual BOOL isFinished() { + return !txInFlight && sentyet; + }; + + virtual BOOL txReady() { + return !sentyet; + }; + + virtual void reset(); + +private: + BOOL sentyet; + BOOL txInFlight; +}; diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.cpp b/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.cpp new file mode 100644 index 0000000000..023088d3c6 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.cpp @@ -0,0 +1,180 @@ +#include "stdafx.h" +#include "MessageTx_ISO15765.h" +#include "constants_ISO15765.h" + +//in microseconsa +#define TIMEOUT_FC 250000 //Flow Control +#define TIMEOUT_CF 250000 //Consecutive Frames + +MessageTx_ISO15765::MessageTx_ISO15765( + std::shared_ptr connection_in, + PASSTHRU_MSG& to_send, + std::shared_ptr filter +) : MessageTxTimeoutable(connection_in, to_send), filter(filter), frames_sent(0), +consumed_count(0), txInFlight(FALSE), sendAll(FALSE), block_size(0), numWaitFrames(0), didtimeout(FALSE), issuspended(FALSE){ + + CANid = ((uint8_t)fullmsg.Data[0]) << 24 | ((uint8_t)fullmsg.Data[1]) << 16 | + ((uint8_t)fullmsg.Data[2]) << 8 | ((uint8_t)fullmsg.Data[3]); + + payload = fullmsg.Data.substr(addressLength()); + + if (check_bmask(fullmsg.TxFlags, ISO15765_ADDR_TYPE)) + data_prefix = fullmsg.Data[4]; + + if (payload.size() <= (7 - data_prefix.size())) { + isMultipart = FALSE; + auto framepayload = data_prefix + std::string(1, (char)payload.size()) + payload; + if (check_bmask(this->fullmsg.TxFlags, ISO15765_FRAME_PAD)) + framepayload += std::string(8 - framepayload.size(), '\x00'); + framePayloads.push_back(framepayload); + } else { + isMultipart = TRUE; + unsigned long first_payload_len = 6 - data_prefix.size(); // 5 or 6 + std::string framepayload = data_prefix + + (char)(0x10 | ((payload.size() >> 8) & 0xF)) + + (char)(payload.size() & 0xFF) + + payload.substr(0, first_payload_len); + framePayloads.push_back(framepayload); + + unsigned int pktnum = 1; + uint8_t CFDatSize = 7 - data_prefix.size(); + while (TRUE) { + framepayload = data_prefix + (char)(0x20 | (pktnum % 0x10)) + + payload.substr(first_payload_len + (CFDatSize * (pktnum-1)), CFDatSize); + + if (check_bmask(this->fullmsg.TxFlags, ISO15765_FRAME_PAD)) + framepayload += std::string(8 - framepayload.size(), '\x00'); + framePayloads.push_back(framepayload); + if (first_payload_len + (CFDatSize * pktnum) >= payload.size()) break; + pktnum++; + } + + } +}; + +unsigned int MessageTx_ISO15765::addressLength() { + return check_bmask(fullmsg.TxFlags, ISO15765_ADDR_TYPE) ? 5 : 4; +} + +void MessageTx_ISO15765::execute() { + if (didtimeout || issuspended) return; + if (this->frames_sent >= this->framePayloads.size()) return; + if (block_size == 0 && !sendAll && this->frames_sent > 0) return; + if (block_size > 0 && !sendAll) block_size--; + + if (auto conn_sp = this->connection.lock()) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + auto& outFramePayload = this->framePayloads[this->frames_sent]; + if (panda_dev_sp->panda->can_send(this->CANid, check_bmask(this->fullmsg.TxFlags, CAN_29BIT_ID), + (const uint8_t*)outFramePayload.c_str(), (uint8_t)outFramePayload.size(), panda::PANDA_CAN1) == FALSE) { + return; + } + + this->txInFlight = TRUE; + this->frames_sent++; + panda_dev_sp->txMsgsAwaitingEcho.push(shared_from_this()); + } + } +} + +//Returns TRUE if receipt is consumed by the msg, FALSE otherwise. +BOOL MessageTx_ISO15765::checkTxReceipt(J2534Frame frame) { + if (!txInFlight) return FALSE; + if (frame.Data.size() >= addressLength() + 1 && (frame.Data[addressLength()] & 0xF0) == FRAME_FLOWCTRL) return FALSE; + + if (frame.Data == fullmsg.Data.substr(0, 4) + framePayloads[frames_sent - 1] && + ((this->fullmsg.TxFlags & CAN_29BIT_ID) == (frame.RxStatus & CAN_29BIT_ID))) { //Check receipt is expected + txInFlight = FALSE; //Received the expected receipt. Allow another msg to be sent. + + if (this->recvCount == 0 && this->framePayloads.size() > 1) + scheduleTimeout(TIMEOUT_FC); + + if (frames_sent == framePayloads.size()) { //Check message done + if (auto conn_sp = std::static_pointer_cast(this->connection.lock())) { + unsigned long flags = (filter == nullptr) ? fullmsg.TxFlags : this->filter->flags; + + J2534Frame outframe(ISO15765); + outframe.Timestamp = frame.Timestamp; + outframe.RxStatus = TX_MSG_TYPE | TX_INDICATION | (flags & (ISO15765_ADDR_TYPE | CAN_29BIT_ID)); + outframe.Data = frame.Data.substr(0, addressLength()); + conn_sp->addMsgToRxQueue(outframe); + + if (conn_sp->loopback) { + J2534Frame outframe(ISO15765); + outframe.Timestamp = frame.Timestamp; + outframe.RxStatus = TX_MSG_TYPE | (flags & (ISO15765_ADDR_TYPE | CAN_29BIT_ID)); + outframe.Data = this->fullmsg.Data; + conn_sp->addMsgToRxQueue(outframe); + } + + } //TODO what if fails + } else { + //Restart timeout if we are waiting for a flow control frame. + //FC frames are required when we are not sending all, the + //current block_size batch has not been sent, a FC message has + //already been received (differentiating from first frame), the + //message is not finished, and there is more than one frame in + //the message. + if (block_size == 0 && recvCount != 0 && !sendAll && !this->isFinished() && this->framePayloads.size() > 1) + scheduleTimeout(TIMEOUT_CF); + } + return TRUE; + } + return FALSE; +} + +BOOL MessageTx_ISO15765::isFinished() { + return this->frames_sent == this->framePayloads.size() && !txInFlight; +} + +BOOL MessageTx_ISO15765::txReady() { + return block_size > 0 || sendAll || this->frames_sent == 0; +} + +void MessageTx_ISO15765::reset() { + frames_sent = 0; + consumed_count = 0; + block_size = 0; + txInFlight = FALSE; + sendAll = FALSE; + numWaitFrames = 0; + didtimeout = FALSE; +} + +void MessageTx_ISO15765::onTimeout() { + didtimeout = TRUE; + if (auto conn_sp = std::static_pointer_cast(this->connection.lock())) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + panda_dev_sp->removeConnectionTopAction(conn_sp, shared_from_this()); + } + } +} + +void MessageTx_ISO15765::flowControlContinue(uint8_t block_size, std::chrono::microseconds separation_time) { + this->issuspended = FALSE; + this->block_size = block_size; + this->delay = separation_time; + this->sendAll = block_size == 0; + this->recvCount++; +} + +void MessageTx_ISO15765::flowControlWait(unsigned long N_WFTmax) { + this->issuspended = TRUE; + this->recvCount++; + this->numWaitFrames++; + this->sendAll = FALSE; + this->block_size = block_size; + this->delay = std::chrono::microseconds(0); + //Docs are vague on if 0 means NO WAITS ALLOWED or NO LIMIT TO WAITS. + //It is less likely to cause issue if NO LIMIT is assumed. + if (N_WFTmax > 0 && this->numWaitFrames > N_WFTmax) { + this->onTimeout(); //Trigger self destruction of message. + } else { + scheduleTimeout(TIMEOUT_FC); + } +} + +void MessageTx_ISO15765::flowControlAbort() { + this->recvCount++; //Invalidate future timeout actions. + this->onTimeout(); //Trigger self destruction of message. +} diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.h b/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.h new file mode 100644 index 0000000000..0113edb8f9 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.h @@ -0,0 +1,54 @@ +#pragma once +#include "MessageTxTimeout.h" +#include "J2534Connection_ISO15765.h" + +class J2534Connection_ISO15765; + +/** +A specialized message type that can handle J2534 single and multi +frame (with flow control) writes. +*/ +class MessageTx_ISO15765 : public MessageTxTimeoutable +{ +public: + MessageTx_ISO15765( + std::shared_ptr connection, + PASSTHRU_MSG& to_send, + std::shared_ptr filter + ); + + unsigned int addressLength(); + + virtual void execute(); + + virtual BOOL checkTxReceipt(J2534Frame frame); + + virtual BOOL isFinished(); + + virtual BOOL txReady(); + + virtual void reset(); + + virtual void onTimeout(); + + //Functions for ISO15765 flow control + + void MessageTx_ISO15765::flowControlContinue(uint8_t block_size, std::chrono::microseconds separation_time); + void MessageTx_ISO15765::flowControlWait(unsigned long N_WFTmax); + void MessageTx_ISO15765::flowControlAbort(); + + std::shared_ptr filter; + unsigned long frames_sent; + unsigned long consumed_count; + uint8_t block_size; + unsigned long CANid; + std::string data_prefix; + std::string payload; + BOOL isMultipart; + std::vector framePayloads; + BOOL txInFlight; + BOOL sendAll; + unsigned int numWaitFrames; + BOOL didtimeout; + BOOL issuspended; +}; diff --git a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp new file mode 100644 index 0000000000..4cda1fa2e2 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp @@ -0,0 +1,238 @@ +#include "stdafx.h" +#include "PandaJ2534Device.h" +#include "J2534Frame.h" + +PandaJ2534Device::PandaJ2534Device(std::unique_ptr new_panda) : txInProgress(FALSE) { + this->panda = std::move(new_panda); + + this->panda->set_esp_power(FALSE); + this->panda->set_safety_mode(panda::SAFETY_ALLOUTPUT); + this->panda->set_can_loopback(FALSE); + this->panda->set_alt_setting(0); + + this->thread_kill_event = CreateEvent(NULL, TRUE, FALSE, NULL); + + DWORD canListenThreadID; + this->can_recv_handle = CreateThread(NULL, 0, _can_recv_threadBootstrap, (LPVOID)this, 0, &canListenThreadID); + + DWORD canProcessThreadID; + this->can_process_handle = CreateThread(NULL, 0, _can_process_threadBootstrap, (LPVOID)this, 0, &canProcessThreadID); + + DWORD flowControlSendThreadID; + this->flow_control_wakeup_event = CreateEvent(NULL, TRUE, FALSE, NULL); + this->flow_control_thread_handle = CreateThread(NULL, 0, _msg_tx_threadBootstrap, (LPVOID)this, 0, &flowControlSendThreadID); +}; + +PandaJ2534Device::~PandaJ2534Device() { + SetEvent(this->thread_kill_event); + DWORD res = WaitForSingleObject(this->can_recv_handle, INFINITE); + CloseHandle(this->can_recv_handle); + + res = WaitForSingleObject(this->can_process_handle, INFINITE); + CloseHandle(this->can_process_handle); + + res = WaitForSingleObject(this->flow_control_thread_handle, INFINITE); + CloseHandle(this->flow_control_thread_handle); + + CloseHandle(this->flow_control_wakeup_event); + CloseHandle(this->thread_kill_event); +} + +std::shared_ptr PandaJ2534Device::openByName(std::string sn) { + auto p = panda::Panda::openPanda(""); + if (p == nullptr) + return nullptr; + return std::unique_ptr(new PandaJ2534Device(std::move(p))); +} + +DWORD PandaJ2534Device::closeChannel(unsigned long ChannelID) { + if (this->connections.size() <= ChannelID) return ERR_INVALID_CHANNEL_ID; + if (this->connections[ChannelID] == nullptr) return ERR_INVALID_CHANNEL_ID; + this->connections[ChannelID] = nullptr; + return STATUS_NOERROR; +} + +DWORD PandaJ2534Device::addChannel(std::shared_ptr& conn, unsigned long* channel_id) { + int channel_index = -1; + for (unsigned int i = 0; i < this->connections.size(); i++) + if (this->connections[i] == nullptr) { + channel_index = i; + break; + } + + if (channel_index == -1) { + if (this->connections.size() == 0xFFFF) //channelid max 16 bits + return ERR_FAILED; //Too many channels + this->connections.push_back(nullptr); + channel_index = this->connections.size() - 1; + } + + this->connections[channel_index] = conn; + + *channel_id = channel_index; + return STATUS_NOERROR; +} + +DWORD PandaJ2534Device::can_recv_thread() { + this->panda->can_clear(panda::PANDA_CAN_RX); + this->panda->can_rx_q_push(this->thread_kill_event); + + return 0; +} + +DWORD PandaJ2534Device::can_process_thread() { + panda::PANDA_CAN_MSG msg_recv[CAN_RX_MSG_LEN]; + + while (true) { + if (!WaitForSingleObject(this->thread_kill_event, 0)) { + break; + } + + int count = 0; + this->panda->can_rx_q_pop(msg_recv, count); + if (count == 0) { + continue; + } + + for (int i = 0; i < count; i++) { + auto msg_in = msg_recv[i]; + J2534Frame msg_out(msg_in); + + if (msg_in.is_receipt) { + synchronized(task_queue_mutex) { + if (txMsgsAwaitingEcho.size() > 0) { + auto msgtx = txMsgsAwaitingEcho.front(); + if (auto conn = msgtx->connection.lock()) { + if (conn->isProtoCan() && conn->getPort() == msg_in.bus) { + if (msgtx->checkTxReceipt(msg_out)) { + //Things to check: + // Frame not for this msg: Drop frame and alert. Error? + // Frame is for this msg, more tx frames required after a FC frame: Wait for FC frame to come and trigger next tx. + // Frame is for this msg, more tx frames required: Schedule next tx frame. + // Frame is for this msg, and is the final frame of the msg: Let conn process full msg, If another msg from this conn is available, register it. + txMsgsAwaitingEcho.pop(); //Remove the TX object and schedule record. + + if (msgtx->isFinished()) { + this->removeConnectionTopAction(conn, msgtx); + } else { + if (msgtx->txReady()) { //Not finished, ready to send next frame. + msgtx->schedule(msg_in.recv_time_point, TRUE); + this->insertActionIntoTaskList(msgtx); + } else { + //Not finished, but next frame not ready (maybe waiting for flow control). + //Do not schedule more messages from this connection. + //this->ConnTxSet.erase(conn); + //Removing this means new messages queued can kickstart the queue and overstep the current message. + } + } + } + } + } else { + //Connection has died. Clear out the tx entry from device records. + txMsgsAwaitingEcho.pop(); + this->ConnTxSet.erase(conn); //connection is already dead, no need to schedule future tx msgs. + } + } + } + } else { + for (auto& conn : this->connections) + if (conn != nullptr && conn->isProtoCan() && conn->getPort() == msg_in.bus) + conn->processMessage(msg_out); + } + } + } + + return 0; +} + +DWORD PandaJ2534Device::msg_tx_thread() { + const HANDLE subscriptions[] = { this->flow_control_wakeup_event, this->thread_kill_event }; + DWORD sleepDuration = INFINITE; + while (TRUE) { + DWORD res = WaitForMultipleObjects(2, subscriptions, FALSE, sleepDuration); + if (res == WAIT_OBJECT_0 + 1) return 0; + if (res != WAIT_OBJECT_0 && res != WAIT_TIMEOUT) { + printf("Got an unexpected wait result in flow_control_write_thread. Res: %d; GetLastError: %d\n. Terminating thread.", res, GetLastError()); + return 0; + } + ResetEvent(this->flow_control_wakeup_event); + + while (TRUE) { + synchronized(task_queue_mutex) { //implemented with for loop. Consumes breaks. + if (this->task_queue.size() == 0) { + sleepDuration = INFINITE; + goto break_flow_ctrl_loop; + } + if (std::chrono::steady_clock::now() >= this->task_queue.front()->expire) { + auto task = this->task_queue.front(); //Get the scheduled tx record. + this->task_queue.pop_front(); + task->execute(); + } else { //Ran out of things that need to be sent now. Sleep! + auto time_diff = std::chrono::duration_cast + (this->task_queue.front()->expire - std::chrono::steady_clock::now()); + sleepDuration = MAX(1, time_diff.count()); + goto break_flow_ctrl_loop; + } + } + } + break_flow_ctrl_loop: + continue; + } + return 0; +} + +//Place the Action in the task queue based on the Action's expiration time, +//then signal the thread that processes actions. +void PandaJ2534Device::insertActionIntoTaskList(std::shared_ptr action) { + synchronized(task_queue_mutex) { + auto iter = this->task_queue.begin(); + for (; iter != this->task_queue.end(); iter++) { + if (action->expire < (*iter)->expire) break; + } + this->task_queue.insert(iter, action); + } + SetEvent(this->flow_control_wakeup_event); +} + +void PandaJ2534Device::scheduleAction(std::shared_ptr msg, BOOL startdelayed) { + if(startdelayed) + msg->scheduleImmediateDelay(); + else + msg->scheduleImmediate(); + this->insertActionIntoTaskList(msg); +} + +void PandaJ2534Device::registerConnectionTx(std::shared_ptr conn) { + synchronized(connTXSet_mutex) { + auto ret = this->ConnTxSet.insert(conn); + if (ret.second == FALSE) return; //Conn already exists. + this->scheduleAction(conn->txbuff.front()); + } +} + +void PandaJ2534Device::unstallConnectionTx(std::shared_ptr conn) { + synchronized(connTXSet_mutex) { + auto ret = this->ConnTxSet.insert(conn); + if (ret.second == TRUE) return; //Conn already exists. + this->insertActionIntoTaskList(conn->txbuff.front()); + } +} + +void PandaJ2534Device::removeConnectionTopAction(std::shared_ptr conn, std::shared_ptr msg) { + synchronized(task_queue_mutex) { + if (conn->txbuff.size() == 0) + return; + if (conn->txbuff.front() != msg) + return; + conn->txbuff.pop(); //Remove the top TX message from the connection tx queue. + + //Remove the connection from the active connection list if no more messages are scheduled with this connection. + if (conn->txbuff.size() == 0) { + //Update records showing the connection no longer has a tx record scheduled. + this->ConnTxSet.erase(conn); + } else { + //Add the next scheduled tx from this conn + this->scheduleAction(conn->txbuff.front()); + } + } +} diff --git a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h new file mode 100644 index 0000000000..32004ffba5 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h @@ -0,0 +1,83 @@ +#pragma once +#include +#include +#include +#include +#include +#include "J2534_v0404.h" +#include "panda_shared/panda.h" +#include "synchronize.h" +#include "Action.h" +#include "MessageTx.h" +#include "J2534Connection.h" + +class J2534Connection; +class Action; +class MessageTx; + +/** +Class representing a physical panda adapter. Instances are created by +PassThruOpen in the J2534 API. A Device can create one or more +J2534Connections. +*/ +class PandaJ2534Device { +public: + PandaJ2534Device(std::unique_ptr new_panda); + + ~PandaJ2534Device(); + + static std::shared_ptr openByName(std::string sn); + + DWORD closeChannel(unsigned long ChannelID); + DWORD addChannel(std::shared_ptr& conn, unsigned long* channel_id); + + std::unique_ptr panda; + std::vector> connections; + + //Place the Action in the task queue based on the Action's expiration time, + //then signal the thread that processes actions. + void insertActionIntoTaskList(std::shared_ptr action); + + void scheduleAction(std::shared_ptr msg, BOOL startdelayed=FALSE); + + void registerConnectionTx(std::shared_ptr conn); + + //Resume sending messages from the provided Connection's TX queue. + void unstallConnectionTx(std::shared_ptr conn); + + //Cleans up several queues after a message completes, is canceled, or otherwise goes away. + void removeConnectionTopAction(std::shared_ptr conn, std::shared_ptr msg); + + //Messages that have been sent on the wire will be echoed by the panda when + //transmission is complete. This tracks what is still waiting to hear an echo. + std::queue> txMsgsAwaitingEcho; + +private: + HANDLE thread_kill_event; + + HANDLE can_recv_handle; + static DWORD WINAPI _can_recv_threadBootstrap(LPVOID This) { + return ((PandaJ2534Device*)This)->can_recv_thread(); + } + DWORD can_recv_thread(); + + HANDLE can_process_handle; + static DWORD WINAPI _can_process_threadBootstrap(LPVOID This) { + return ((PandaJ2534Device*)This)->can_process_thread(); + } + DWORD can_process_thread(); + + HANDLE flow_control_wakeup_event; + HANDLE flow_control_thread_handle; + static DWORD WINAPI _msg_tx_threadBootstrap(LPVOID This) { + return ((PandaJ2534Device*)This)->msg_tx_thread(); + } + DWORD msg_tx_thread(); + std::list> task_queue; + Mutex task_queue_mutex; + + std::queue> ConnTxQueue; + std::set> ConnTxSet; + Mutex connTXSet_mutex; + BOOL txInProgress; +}; diff --git a/drivers/windows/pandaJ2534DLL/Timer.cpp b/drivers/windows/pandaJ2534DLL/Timer.cpp new file mode 100644 index 0000000000..2f20f888ee --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/Timer.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "Timer.h" + + +Timer::Timer() +{ + start = std::chrono::time_point_cast(clock::now()); +} + +// gets the time elapsed from construction. +unsigned long long /*milliseconds*/ Timer::getTimePassed(){ + // get the new time + auto end = std::chrono::time_point_cast(clock::now()); + + // return the difference of the times + return (end - start).count(); +} \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/Timer.h b/drivers/windows/pandaJ2534DLL/Timer.h new file mode 100644 index 0000000000..d4888fc5e6 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/Timer.h @@ -0,0 +1,18 @@ +#pragma once +#include + +//Copied from https://stackoverflow.com/a/31488113 + +class Timer +{ + using clock = std::chrono::steady_clock; + using time_point_type = std::chrono::time_point < clock, std::chrono::milliseconds >; +public: + Timer(); + + // gets the time elapsed from construction. + unsigned long long /*milliseconds*/ getTimePassed(); + +private: + time_point_type start; +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/constants_ISO15765.h b/drivers/windows/pandaJ2534DLL/constants_ISO15765.h new file mode 100644 index 0000000000..86928f1436 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/constants_ISO15765.h @@ -0,0 +1,20 @@ +#pragma once + +#define msg_is_extaddr(msg) check_bmask(msg->TxFlags, ISO15765_ADDR_TYPE) +#define msg_is_padded(msg) check_bmask(msg->TxFlags, ISO15765_FRAME_PAD) + +#define FRAME_SINGLE 0x00 +#define FRAME_FIRST 0x10 +#define FRAME_CONSEC 0x20 +#define FRAME_FLOWCTRL 0x30 + +#define FLOWCTRL_CONTINUE 0 +#define FLOWCTRL_WAIT 1 +#define FLOWCTRL_ABORT 2 + +#define msg_get_type(msg, addrlen) ((msg).Data[addrlen] & 0xF0) + +#define is_single(msg, addrlen) (msg_get_type(msg, addrlen) == FRAME_SINGLE) +#define is_first(msg, addrlen) (msg_get_type(msg, addrlen) == FRAME_FIRST) +#define is_consecutive(msg, addrlen) (msg_get_type(msg, addrlen) == FRAME_CONSEC) +#define is_flowctrl(msg, addrlen) (msg_get_type(msg, addrlen) == FRAME_FLOWCTRL) \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/dllmain.cpp b/drivers/windows/pandaJ2534DLL/dllmain.cpp new file mode 100644 index 0000000000..d4122e0b1d --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/dllmain.cpp @@ -0,0 +1,22 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "dllmain.h" + +HMODULE thisdll; + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + thisdll = hModule; + + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/drivers/windows/pandaJ2534DLL/dllmain.h b/drivers/windows/pandaJ2534DLL/dllmain.h new file mode 100644 index 0000000000..f49819e2c4 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/dllmain.h @@ -0,0 +1,4 @@ +#pragma once +#include "stdafx.h" + +extern HMODULE thisdll; diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp new file mode 100644 index 0000000000..096e441988 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp @@ -0,0 +1,430 @@ +// pandaJ2534DLL.cpp : Defines the exported functions for the DLL application. +// Protocol derived from the following sites (which shall be referred to as The Protocol Reference #). +// https://web.archive.org/web/20130805013326/https://tunertools.com/prodimages/DrewTech/Manuals/PassThru_API-1.pdf +// http://web.archive.org/web/20170910063536/http://www.tiecar.net/downloads/SAE_J2534_2002.pdf + +#include "stdafx.h" +#include "J2534_v0404.h" +#include "panda_shared/panda.h" +#include "J2534Connection.h" +#include "J2534Connection_CAN.h" +#include "J2534Connection_ISO15765.h" +#include "PandaJ2534Device.h" +#include "dllmain.h" + +// A quick way to avoid the name mangling that __stdcall liked to do +#define EXPORT comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__) + +std::vector> pandas; + +int J25334LastError = 0; + +std::string GetProductAndVersion(TCHAR* szFilename)//std::string & strProductName, std::string & strProductVersion) +{ + // allocate a block of memory for the version info + DWORD dummy; + DWORD dwSize = GetFileVersionInfoSize(szFilename, &dummy); + if (dwSize == 0) { + return "error"; + } + std::vector data(dwSize); + + // load the version info + if (!GetFileVersionInfo(szFilename, NULL, dwSize, &data[0])) { + return "error"; + } + + // get the name and version strings + LPVOID pvProductName = NULL; + unsigned int iProductNameLen = 0; + LPVOID pvProductVersion = NULL; + unsigned int iProductVersionLen = 0; + + // 040904b0 is a language id. + if (!VerQueryValueA(&data[0], "\\StringFileInfo\\040904b0\\ProductName", &pvProductName, &iProductNameLen) || + !VerQueryValueA(&data[0], "\\StringFileInfo\\040904b0\\ProductVersion", &pvProductVersion, &iProductVersionLen)) { + return "error"; + } + + std::string ver_str = std::string((char*)pvProductVersion, iProductVersionLen-1); + std::string prod_str = std::string((char*)pvProductName, iProductNameLen-1); + std::string full_ver = prod_str + std::string(": ") + ver_str; + return full_ver; +} + +long ret_code(long code) { + J25334LastError = code; + return code; +} + +#define EXTRACT_DID(CID) ((CID & 0xFFFF) - 1) +#define EXTRACT_CID(CID) ((CID >> 16) & 0xFFFF) + +long check_valid_DeviceID(unsigned long DeviceID) { + uint16_t dev_id = EXTRACT_DID(DeviceID); + if (pandas.size() <= dev_id || pandas[dev_id] == nullptr) + return ret_code(ERR_INVALID_DEVICE_ID); + return ret_code(STATUS_NOERROR); +} + +long check_valid_ChannelID(unsigned long ChannelID) { + uint16_t dev_id = EXTRACT_DID(ChannelID); + uint16_t con_id = EXTRACT_CID(ChannelID); + + if (pandas.size() <= dev_id || pandas[dev_id] == nullptr) + return ret_code(ERR_INVALID_CHANNEL_ID); + + if (pandas[dev_id]->connections.size() <= con_id) return ret_code(ERR_INVALID_CHANNEL_ID); + if (pandas[dev_id]->connections[con_id] == nullptr) return ret_code(ERR_DEVICE_NOT_CONNECTED); + + return ret_code(STATUS_NOERROR); +} + +//Do not call without checking if the device/channel id exists first. +#define get_device(DeviceID) (pandas[EXTRACT_DID(DeviceID)]) +#define get_channel(ChannelID) (get_device(ChannelID)->connections[EXTRACT_CID(ChannelID)]) + +PANDAJ2534DLL_API long PTAPI PassThruOpen(void *pName, unsigned long *pDeviceID) { + #pragma EXPORT + if (pDeviceID == NULL) return ret_code(ERR_NULL_PARAMETER); + std::string sn = (pName == NULL) ? "" : std::string((char*)pName); + if (sn == "J2534-2:") + sn = ""; + + auto new_panda = PandaJ2534Device::openByName(sn); + if (new_panda == nullptr) { + if(sn == "" && pandas.size() == 1) + return ret_code(ERR_DEVICE_IN_USE); + for (auto& pn : pandas) { + if (pn->panda->get_usb_sn() == sn) + return ret_code(ERR_DEVICE_IN_USE); + } + return ret_code(ERR_DEVICE_NOT_CONNECTED); + } + + int panda_index = -1; + for (unsigned int i = 0; i < pandas.size(); i++) + if (pandas[i] == nullptr) { + panda_index = i; + pandas[panda_index] = std::move(new_panda); + break; + } + + if (panda_index == -1) { + if(pandas.size() == 0xFFFF) //device id will be 16 bit to fit channel next to it. + return ret_code(ERR_FAILED); //Too many pandas. Off the endangered species list. + pandas.push_back(std::move(new_panda)); + panda_index = pandas.size()-1; + } + + *pDeviceID = panda_index + 1; // TIS doesn't like it when ID == 0 + return ret_code(STATUS_NOERROR); +} +PANDAJ2534DLL_API long PTAPI PassThruClose(unsigned long DeviceID) { + #pragma EXPORT + if (check_valid_DeviceID(DeviceID) != STATUS_NOERROR) return J25334LastError; + get_device(DeviceID) = nullptr; + return ret_code(STATUS_NOERROR); +} +PANDAJ2534DLL_API long PTAPI PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID, + unsigned long Flags, unsigned long BaudRate, unsigned long *pChannelID) { + #pragma EXPORT + if (pChannelID == NULL) return ret_code(ERR_NULL_PARAMETER); + if (check_valid_DeviceID(DeviceID) != STATUS_NOERROR) return J25334LastError; + auto& panda = get_device(DeviceID); + + std::shared_ptr conn; + + //TODO check if channel can be made + try { + switch (ProtocolID) { + //SW seems to refer to Single Wire. https://www.nxp.com/files-static/training_pdf/20451_BUS_COMM_WBT.pdf + //SW_ protocols may be touched on here: https://www.iso.org/obp/ui/#iso:std:iso:22900:-2:ed-1:v1:en + //case J1850VPW: // These protocols are outdated and will not be supported. HDS wants them to not fail to open. + //case J1850PWM: // ^-- it appears HDS no longer needs this, and TIS needs it disabled --^ + //case J1850VPW_PS: + //case J1850PWM_PS: + case ISO9141: //This protocol could be implemented if 5 BAUD init support is added to the panda. + case ISO9141_PS: + case ISO14230: //Only supporting Fast init until panda adds support for 5 BAUD init. + case ISO14230_PS: + conn = std::make_shared(panda, ProtocolID, Flags, BaudRate); + break; + case CAN: + case CAN_PS: + //case SW_CAN_PS: + conn = std::make_shared(panda, ProtocolID, Flags, BaudRate); + break; + case ISO15765: + case ISO15765_PS: + conn = std::make_shared(panda, ProtocolID, Flags, BaudRate); + break; + //case SW_ISO15765_PS: // SW = Single Wire. GMLAN is a SW CAN protocol + //case GM_UART_PS: // PS = Pin Select. Handles different ports. + //Looks like SCI based protocols may not be compatible with the panda: + //http://mdhmotors.com/can-communications-vehicle-network-protocols/3/ + //case SCI_A_ENGINE: + //case SCI_A_TRANS: + //case SCI_B_ENGINE: + //case SCI_B_TRANS: + //case J2610_PS: + default: + return ret_code(ERR_INVALID_PROTOCOL_ID); + } + } catch (int e) { + return ret_code(e); + } + + unsigned long channel_index; + unsigned long err = panda->addChannel(conn, &channel_index); + if (err == STATUS_NOERROR) + *pChannelID = (channel_index << 16) | DeviceID; + + return ret_code(err); +} +PANDAJ2534DLL_API long PTAPI PassThruDisconnect(unsigned long ChannelID) { + #pragma EXPORT + unsigned long res = check_valid_DeviceID(ChannelID); + if (res == ERR_INVALID_DEVICE_ID) return ret_code(ERR_INVALID_CHANNEL_ID); + if (res != STATUS_NOERROR) return J25334LastError; + return ret_code(get_device(ChannelID)->closeChannel(EXTRACT_CID(ChannelID))); +} +PANDAJ2534DLL_API long PTAPI PassThruReadMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, + unsigned long *pNumMsgs, unsigned long Timeout) { + #pragma EXPORT + if (pMsg == NULL || pNumMsgs == NULL) return ret_code(ERR_NULL_PARAMETER); + if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError; + return ret_code(get_channel(ChannelID)->PassThruReadMsgs(pMsg, pNumMsgs, Timeout)); +} +PANDAJ2534DLL_API long PTAPI PassThruWriteMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) { + #pragma EXPORT + if (pMsg == NULL || pNumMsgs == NULL) return ret_code(ERR_NULL_PARAMETER); + if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError; + return ret_code(get_channel(ChannelID)->PassThruWriteMsgs(pMsg, pNumMsgs, Timeout)); +} +PANDAJ2534DLL_API long PTAPI PassThruStartPeriodicMsg(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval) { + #pragma EXPORT + if (pMsg == NULL || pMsgID == NULL) return ret_code(ERR_NULL_PARAMETER); + if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError; + return ret_code(get_channel(ChannelID)->PassThruStartPeriodicMsg(pMsg, pMsgID, TimeInterval)); +} +PANDAJ2534DLL_API long PTAPI PassThruStopPeriodicMsg(unsigned long ChannelID, unsigned long MsgID) { + #pragma EXPORT + if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError; + return ret_code(get_channel(ChannelID)->PassThruStopPeriodicMsg(MsgID)); +} +PANDAJ2534DLL_API long PTAPI PassThruStartMsgFilter(unsigned long ChannelID, unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, + PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) { + #pragma EXPORT + if (FilterType != PASS_FILTER && FilterType != BLOCK_FILTER && FilterType != FLOW_CONTROL_FILTER) return ret_code(ERR_NULL_PARAMETER); + if (!pFilterID || (!pMaskMsg && !pPatternMsg && !pFlowControlMsg)) return ret_code(ERR_NULL_PARAMETER); + if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError; + return ret_code(get_channel(ChannelID)->PassThruStartMsgFilter(FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID)); +} +PANDAJ2534DLL_API long PTAPI PassThruStopMsgFilter(unsigned long ChannelID, unsigned long FilterID) { + #pragma EXPORT + if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError; + return ret_code(get_channel(ChannelID)->PassThruStopMsgFilter(FilterID)); +} +PANDAJ2534DLL_API long PTAPI PassThruSetProgrammingVoltage(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage) { + #pragma EXPORT + //Unused + if (check_valid_DeviceID(DeviceID) != STATUS_NOERROR) return J25334LastError; + auto& panda = get_device(DeviceID); + + switch (Voltage) { + case SHORT_TO_GROUND: + break; + case VOLTAGE_OFF: + break; + default: + if (!(5000 <= Voltage && Voltage <= 20000)) + return ret_code(ERR_NOT_SUPPORTED); + break; + } + + return ret_code(STATUS_NOERROR); +} +PANDAJ2534DLL_API long PTAPI PassThruReadVersion(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion) { + #pragma EXPORT + if (!pFirmwareVersion || !pDllVersion || !pApiVersion) return ret_code(ERR_NULL_PARAMETER); + if (check_valid_DeviceID(DeviceID) != STATUS_NOERROR) return J25334LastError; + + auto& panda = get_device(DeviceID); + auto fw_version = panda->panda->get_version(); + strcpy_s(pFirmwareVersion, 80, fw_version.c_str()); + + std::string j2534dll_ver; + TCHAR pandalib_filename[MAX_PATH + 1] = { 0 }; + if (GetModuleFileName(thisdll, pandalib_filename, MAX_PATH) == 0) { + j2534dll_ver = "error"; + } else { + j2534dll_ver = GetProductAndVersion(pandalib_filename); + } + std::string fullver = "(" + j2534dll_ver + ")"; + strcpy_s(pDllVersion, 80, fullver.c_str()); + + strcpy_s(pApiVersion, 80, J2534_APIVER_NOVEMBER_2004); + return ret_code(STATUS_NOERROR); +} +PANDAJ2534DLL_API long PTAPI PassThruGetLastError(char *pErrorDescription) { + #pragma EXPORT + if (pErrorDescription == NULL) return ret_code(ERR_NULL_PARAMETER); + switch (J25334LastError) { + case STATUS_NOERROR: + strcpy_s(pErrorDescription, 80, "Function call successful."); + break; + case ERR_NOT_SUPPORTED: + strcpy_s(pErrorDescription, 80, "Device cannot support requested functionality mandated in J2534."); + break; + case ERR_INVALID_CHANNEL_ID: + strcpy_s(pErrorDescription, 80, "Invalid ChannelID value."); + break; + case ERR_INVALID_PROTOCOL_ID: + strcpy_s(pErrorDescription, 80, "Invalid or unsupported ProtocolID, or resource conflict."); + break; + case ERR_NULL_PARAMETER: + strcpy_s(pErrorDescription, 80, "NULL pointer supplied where a valid pointer is required."); + break; + case ERR_INVALID_IOCTL_VALUE: + strcpy_s(pErrorDescription, 80, "Invalid value for Ioctl parameter."); + break; + case ERR_INVALID_FLAGS: + strcpy_s(pErrorDescription, 80, "Invalid flag values."); + break; + case ERR_FAILED: + strcpy_s(pErrorDescription, 80, "Undefined error."); + break; + case ERR_DEVICE_NOT_CONNECTED: + strcpy_s(pErrorDescription, 80, "Unable to communicate with device."); + break; + case ERR_TIMEOUT: + strcpy_s(pErrorDescription, 80, "Read or write timeout:"); + // PassThruReadMsgs() - No message available to read or could not read the specified number of messages. The actual number of messages read is placed in . + // PassThruWriteMsgs() - Device could not write the specified number of messages. The actual number of messages sent on the vehicle network is placed in . + break; + case ERR_INVALID_MSG: + strcpy_s(pErrorDescription, 80, "Invalid message structure pointed to by pMsg."); + break; + case ERR_INVALID_TIME_INTERVAL: + strcpy_s(pErrorDescription, 80, "Invalid TimeInterval value."); + break; + case ERR_EXCEEDED_LIMIT: + strcpy_s(pErrorDescription, 80, "Exceeded maximum number of message IDs or allocated space."); + break; + case ERR_INVALID_MSG_ID: + strcpy_s(pErrorDescription, 80, "Invalid MsgID value."); + break; + case ERR_DEVICE_IN_USE: + strcpy_s(pErrorDescription, 80, "Device is currently open."); + break; + case ERR_INVALID_IOCTL_ID: + strcpy_s(pErrorDescription, 80, "Invalid IoctlID value."); + break; + case ERR_BUFFER_EMPTY: + strcpy_s(pErrorDescription, 80, "Protocol message buffer empty."); + break; + case ERR_BUFFER_FULL: + strcpy_s(pErrorDescription, 80, "Protocol message buffer full. Messages may have been lost."); + break; + case ERR_BUFFER_OVERFLOW: + strcpy_s(pErrorDescription, 80, "A buffer overflow occurred and messages were lost."); + break; + case ERR_PIN_INVALID: + strcpy_s(pErrorDescription, 80, "Invalid pin number, or pin number already in use."); + break; + case ERR_CHANNEL_IN_USE: + strcpy_s(pErrorDescription, 80, "Channel number is currently connected."); + break; + case ERR_MSG_PROTOCOL_ID: + strcpy_s(pErrorDescription, 80, "The Message's Protocol does not match the Channel's protocol."); + break; + case ERR_INVALID_FILTER_ID: + strcpy_s(pErrorDescription, 80, "Invalid Filter ID value."); + break; + case ERR_NO_FLOW_CONTROL: + strcpy_s(pErrorDescription, 80, "No flow control filter set or matched."); + break; + case ERR_NOT_UNIQUE: + strcpy_s(pErrorDescription, 80, "This filter already exists."); + break; + case ERR_INVALID_BAUDRATE: + strcpy_s(pErrorDescription, 80, "The desired baud rate cannot be achieved within SAE tolerance."); + break; + case ERR_INVALID_DEVICE_ID: + strcpy_s(pErrorDescription, 80, "Device ID invalid."); + break; + } + return ret_code(STATUS_NOERROR); +} +PANDAJ2534DLL_API long PTAPI PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID, + void *pInput, void *pOutput) { + #pragma EXPORT + if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError; + auto& dev_entry = get_device(ChannelID); + //get_channel(ChannelID) + + switch (IoctlID) { + case GET_CONFIG: + { + SCONFIG_LIST *inconfig = (SCONFIG_LIST*)pInput; + if (inconfig == NULL) + return ret_code(ERR_NULL_PARAMETER); + for (unsigned int i = 0; i < inconfig->NumOfParams; i++) { + try { + inconfig->ConfigPtr[i].Value = get_channel(ChannelID)->processIOCTLGetConfig(inconfig->ConfigPtr[i].Parameter); + } catch (int e) { + return ret_code(e); + } + } + break; + } + case SET_CONFIG: + { + SCONFIG_LIST *inconfig = (SCONFIG_LIST*)pInput; + if (inconfig == NULL) + return ret_code(ERR_NULL_PARAMETER); + for (unsigned int i = 0; i < inconfig->NumOfParams; i++) { + try { + get_channel(ChannelID)->processIOCTLSetConfig(inconfig->ConfigPtr[i].Parameter, inconfig->ConfigPtr[i].Value); + } catch (int e) { + return ret_code(e); + } + } + break; + } + case READ_VBATT: + panda::PANDA_HEALTH health = dev_entry->panda->get_health(); + *(unsigned long*)pOutput = health.voltage; + break; + case FIVE_BAUD_INIT: + if (!pInput || !pOutput) return ret_code(ERR_NULL_PARAMETER); + return ret_code(get_channel(ChannelID)->init5b((SBYTE_ARRAY*)pInput, (SBYTE_ARRAY*)pOutput)); + case FAST_INIT: + if (!pInput || !pOutput) return ret_code(ERR_NULL_PARAMETER); + return ret_code(get_channel(ChannelID)->initFast((PASSTHRU_MSG*)pInput, (PASSTHRU_MSG*)pOutput)); + case CLEAR_TX_BUFFER: + return ret_code(get_channel(ChannelID)->clearTXBuff()); + case CLEAR_RX_BUFFER: + return ret_code(get_channel(ChannelID)->clearRXBuff()); + case CLEAR_PERIODIC_MSGS: + return ret_code(get_channel(ChannelID)->clearPeriodicMsgs()); + case CLEAR_MSG_FILTERS: + return ret_code(get_channel(ChannelID)->clearMsgFilters()); + case CLEAR_FUNCT_MSG_LOOKUP_TABLE: // LOOKUP TABLE IS RELATED TO J1850 PWM. Unsupported. + if (!pInput) return ret_code(ERR_NULL_PARAMETER); + return ret_code(STATUS_NOERROR); + case ADD_TO_FUNCT_MSG_LOOKUP_TABLE: // LOOKUP TABLE IS RELATED TO J1850 PWM. Unsupported. + if (!pInput) return ret_code(ERR_NULL_PARAMETER); + return ret_code(STATUS_NOERROR); + case DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE: // LOOKUP TABLE IS RELATED TO J1850 PWM. Unsupported. + return ret_code(STATUS_NOERROR); + case READ_PROG_VOLTAGE: + *(unsigned long*)pOutput = 0; + break; + default: + printf("Got unknown IIOCTL %X\n", IoctlID); + } + + return ret_code(STATUS_NOERROR); +} diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.rc b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.rc new file mode 100644 index 0000000000..e359044825 Binary files /dev/null and b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.rc differ diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj new file mode 100644 index 0000000000..fd5eac56dd --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj @@ -0,0 +1,148 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77} + Win32Proj + pandaJ2534DLL + 10.0.16299.0 + + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + pandaJ2534_0404_32 + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + pandaJ2534_0404_32 + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;PANDAJ2534DLL_EXPORTS;%(PreprocessorDefinitions) + true + $(SolutionDir); + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);version.lib;winusb.lib;setupapi.lib + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;PANDAJ2534DLL_EXPORTS;%(PreprocessorDefinitions) + true + $(SolutionDir); + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);version.lib;winusb.lib;setupapi.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj.filters b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj.filters new file mode 100644 index 0000000000..57f7cefb08 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj.filters @@ -0,0 +1,155 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {a4cd0bce-0a2a-43d9-9c9f-b21a3b607e90} + + + {a85ee263-380d-4d37-b167-6629cfd5177f} + + + {010a0176-a146-4d3a-824a-fd683904774d} + + + {71c9502a-ee59-4d5e-873f-c9cc792e7c76} + + + {4fd3183a-c457-430c-b762-f767a5788bca} + + + {53cd179e-22d8-43e2-bc61-516d3861fae6} + + + {08d548b5-4d0b-4ce4-85e6-5ff3fc987758} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\J2534_CAN + + + Header Files\J2534_ISO15765 + + + Header Files\depends + + + Header Files\depends + + + Header Files\boilerplate + + + Header Files\boilerplate + + + Header Files\boilerplate + + + Header Files\boilerplate + + + Header Files + + + Header Files\J2534_ISO15765 + + + Header Files + + + Header Files\J2534_ISO15765 + + + Header Files\J2534_CAN + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\J2534_CAN + + + Source Files\J2534_ISO15765 + + + Source Files\boilerplate + + + Source Files\boilerplate + + + Source Files\J2534_ISO15765 + + + Source Files + + + Source Files\J2534_CAN + + + Source Files + + + + + Resource Files + + + + + + \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/resource.h b/drivers/windows/pandaJ2534DLL/resource.h new file mode 100644 index 0000000000..771e7b80bc --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by pandaJ2534DLL.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/drivers/windows/pandaJ2534DLL/stdafx.cpp b/drivers/windows/pandaJ2534DLL/stdafx.cpp new file mode 100644 index 0000000000..c27db9ee2d --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pandaJ2534DLL.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/pandaJ2534DLL/stdafx.h b/drivers/windows/pandaJ2534DLL/stdafx.h new file mode 100644 index 0000000000..bd4a4b6f78 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/stdafx.h @@ -0,0 +1,14 @@ +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/synchronize.h b/drivers/windows/pandaJ2534DLL/synchronize.h new file mode 100644 index 0000000000..446dfc19e3 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/synchronize.h @@ -0,0 +1,56 @@ +#pragma once +#define WIN32_LEAN_AND_MEAN +#include + +//Inspired/directly copied from https://www.codeproject.com/Articles/12362/A-quot-synchronized-quot-statement-for-C-like-in-J +//Enables easier synchronization +class Mutex { +public: + Mutex() { + InitializeCriticalSectionAndSpinCount(&critSection, 0x00000400); + //InitializeCriticalSection(&critSection); + } + + ~Mutex() { + DeleteCriticalSection(&critSection); + } + + void lock() { + EnterCriticalSection(&critSection); + } + + void unlock() { + LeaveCriticalSection(&critSection); + } + +private: + CRITICAL_SECTION critSection; +}; + +//Synchronization Controller Object +class Lock { +public: + Lock(Mutex &m) : mutex(m), locked(TRUE) { + m.lock(); + } + + ~Lock() { + mutex.unlock(); + } + + operator bool() const { + return locked; + } + + void setUnlock() { + locked = FALSE; + } + +private: + Mutex& mutex; + bool locked; +}; + +//A useful shorthand for locking and unlocking a mutex over a scope. +//CAUTION, implemented with a for loop, so break/continue are consumed. +#define synchronized(M) for(Lock M##_lock = M; M##_lock; M##_lock.setUnlock()) diff --git a/drivers/windows/pandaJ2534DLL/targetver.h b/drivers/windows/pandaJ2534DLL/targetver.h new file mode 100644 index 0000000000..1bf4ee6fee --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include + +#define WINVER _WIN32_WINNT_WIN7 +#define _WIN32_WINNT _WIN32_WINNT_WIN7 + +#include diff --git a/drivers/windows/panda_install.nsi b/drivers/windows/panda_install.nsi new file mode 100644 index 0000000000..47a4423f91 --- /dev/null +++ b/drivers/windows/panda_install.nsi @@ -0,0 +1,214 @@ +!define J2534_Reg_Path "Software\PassThruSupport.04.04\comma.ai - panda" +!define Install_Name "panda J2534 driver" + +;NOTE! The panda software requires a VC runtime to be installed in order to work. +;This installer must be bundled with the appropriate runtime installer, and have +;the installation registry key set so the installer can tell if the runtime is +;already installed. Copy vscruntimeinfo.nsh.sample to vscruntimeinfo.nsh and edit +;it for your version of Visual Studio. +!include "redist\vscruntimeinfo.nsh" + +;-------------------------------- +;Include Modern UI +!include "MUI2.nsh" +!include "x64.nsh" + +!define MUI_ICON "panda.ico" +;NSIS is ignoring the unicon unless it is the same as the normal icon +;!define MUI_UNICON "panda_remove.ico" + +;Properly display all languages (Installer will not work on Windows 95, 98 or ME!) +Unicode true + +# Set the installer display name +Name "${Install_Name}" + +# set the name of the installer +Outfile "${Install_Name} install.exe" + +; The default installation directory +InstallDir $PROGRAMFILES\comma.ai\panda + +; Request application privileges for UAC +RequestExecutionLevel admin + +; Registry key to check for directory (so if you install again, it will +; overwrite the old one automatically) +InstallDirRegKey HKLM "SOFTWARE\${Install_Name}" "Install_Dir" + +;-------------------------------- +; Pages +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE "..\..\LICENSE" +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_LANGUAGE "English" ;first language is the default language + +; ------------------------------------------------------------------------------------------------- +; Additional info (will appear in the "details" tab of the properties window for the installer) + +VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "panda OBD-II adapter" +VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "" +VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "comma.ai" +VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalTrademarks" "Application released under the MIT license" +;VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "© ${PRODUCT_NAME} Team" +;VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "Jessy Exum" +;VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "${PRODUCT_VERSION}" +VIProductVersion "1.0.0.0" + +;-------------------------------- +; Install Sections +Section "prerequisites" + + SectionIn RO + + SetOutPath "$INSTDIR" + + File "panda.ico" + + ;If the visual studio version this project is compiled with changes, this section + ;must be revisited. The registry key must be changed, and the VS redistributable + ;binary must be updated to the VS version used. + ClearErrors + ReadRegStr $0 HKCR ${VCRuntimeRegKey} "Version" + ${If} ${Errors} + DetailPrint "Installing Visual Studio C Runtime..." + File "${VCRuntimeSetupPath}\${VCRuntimeSetupFile}" + ExecWait '"$INSTDIR\${VCRuntimeSetupFile}" /passive /norestart' + ${Else} + DetailPrint "Visual Studio C Runtime already installed." + ${EndIf} + + ;Remove the now unnecessary runtime installer. + Delete "$INSTDIR\${VCRuntimeSetupFile}" + + ;Do the rest of the install + ; SetOutPath "$INSTDIR\driver" + + ; The inf file works for both 32 and 64 bit. + ; File "Debug_x86\panda Driver Package\panda.inf" + ; File "Debug_x86\panda Driver Package\panda.cat" + ; ${DisableX64FSRedirection} + ; nsExec::ExecToLog '"$SYSDIR\PnPutil.exe" /a "$INSTDIR\driver\panda.inf"' + ; ${EnableX64FSRedirection} + + ; Write the installation path into the registry + WriteRegStr HKLM "SOFTWARE\${Install_Name}" "Install_Dir" "$INSTDIR" + + ; Write the uninstall keys for Windows + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "DisplayVersion" "" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "DisplayIcon" '"$INSTDIR\panda.ico",0' + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "DisplayName" "${Install_Name}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "Publisher" "comma.ai" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "URLInfoAbout" "https://github.com/commaai/panda/" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "NoRepair" 1 + + SetOutPath $INSTDIR + WriteUninstaller "uninstall.exe" + +SectionEnd + +Section "J2534 Driver" + + SetOutPath $INSTDIR + + File Release_x86\pandaJ2534_0404_32.dll + + SetRegView 32 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "CAN" 00000001 + WriteRegStr HKLM "${J2534_Reg_Path}" "FunctionLibrary" "$INSTDIR\pandaJ2534_0404_32.dll" + WriteRegDWORD HKLM "${J2534_Reg_Path}" "ISO15765" 00000001 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "J1850VPW" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "SCI_A_ENGINE" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "SCI_A_TRANS" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "SCI_B_ENGINE" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "SCI_B_TRANS" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "J1850PWM" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "ISO9141" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "ISO14230" 00000001 + WriteRegStr HKLM "${J2534_Reg_Path}" "Name" "panda" + WriteRegStr HKLM "${J2534_Reg_Path}" "Vendor" "comma.ai" + WriteRegStr HKLM "${J2534_Reg_Path}" "ConfigApplication" "" + DetailPrint "Registered J2534 Driver" + +SectionEnd + +Section /o "Development lib/header" + + SetOutPath $SYSDIR + + File Release_x86\panda.dll + + ${If} ${RunningX64} + ${DisableX64FSRedirection} + ;Note that the x64 VS redistributable is not installed to prevent bloat. + ;If you are the rare person who uses the 64 bit raw panda driver, please + ;install the correct x64 VS runtime manually. + File Release_x64\panda.dll + ${EnableX64FSRedirection} + ${EndIf} + + SetOutPath "$INSTDIR\devel" + File panda_shared\panda.h + + SetOutPath "$INSTDIR\devel\x86" + File Release_x86\panda.lib + + SetOutPath "$INSTDIR\devel\x64" + File Release_x64\panda.lib + +SectionEnd + +;-------------------------------- +; Uninstaller +Section "Uninstall" + + ; Removing the inf file for winusb is not easy to do. + ; The best solution I can find is parsing the output + ; of the pnputil.exe /e command to find the oem#.inf + ; file that lists comma.ai as the provider. Not sure + ; if Microsoft wants these inf files to be removed. + ; Consider https://blog.sverrirs.com/2015/12/creating-windows-installer-and.html + ; These lines just remove the inf backups. + ; Delete "$INSTDIR\driver\panda.inf" + ; Delete "$INSTDIR\driver\panda.cat" + ; RMDir "$INSTDIR\driver" + + ; Remove WinUSB driver library + Delete $SYSDIR\panda.dll + ${If} ${RunningX64} + ${DisableX64FSRedirection} + Delete $SYSDIR\panda.dll + ${EnableX64FSRedirection} + ${EndIf} + + ; Remove devel files + Delete "$INSTDIR\devel\x86\panda.lib" + RMDir "$INSTDIR\devel\x86" + Delete "$INSTDIR\devel\x64\panda.lib" + RMDir "$INSTDIR\devel\x64" + Delete "$INSTDIR\devel\panda.h" + RMDir "$INSTDIR\devel" + + ; Remove registry keys + DeleteRegKey HKLM "${J2534_Reg_Path}" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" + DeleteRegKey HKLM "SOFTWARE\${Install_Name}" + + ; Remove files and uninstaller + Delete "$INSTDIR\uninstall.exe" + Delete "$INSTDIR\pandaJ2534_0404_32.dll" + Delete "$INSTDIR\panda.ico" + + ; Remove directories used + RMDir "$INSTDIR" + RMDir "$PROGRAMFILES\comma.ai" + +SectionEnd diff --git a/drivers/windows/panda_playground/ReadMe.txt b/drivers/windows/panda_playground/ReadMe.txt new file mode 100644 index 0000000000..37dba5d877 --- /dev/null +++ b/drivers/windows/panda_playground/ReadMe.txt @@ -0,0 +1,40 @@ +======================================================================== + CONSOLE APPLICATION : panda_playground Project Overview +======================================================================== + +AppWizard has created this panda_playground application for you. + +This file contains a summary of what you will find in each of the files that +make up your panda_playground application. + + +panda_playground.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +panda_playground.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +panda_playground.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named panda_playground.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/drivers/windows/panda_playground/panda_playground.cpp b/drivers/windows/panda_playground/panda_playground.cpp new file mode 100644 index 0000000000..0f51924dbc --- /dev/null +++ b/drivers/windows/panda_playground/panda_playground.cpp @@ -0,0 +1,86 @@ +// panda_playground.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include "pandaJ2534DLL Test\Loader4.h" +#include "ECUsim DLL\ECUsim.h" +#include + + +int _tmain(int Argc, _TCHAR *Argv) { + UNREFERENCED_PARAMETER(Argc); + UNREFERENCED_PARAMETER(Argv); + + ECUsim sim("", 500000); + + //if (LoadJ2534Dll("C:\\WINDOWS\\SysWOW64\\op20pt32.dll") != 0) { + if (LoadJ2534Dll("pandaJ2534.dll") != 0) { + auto err = GetLastError(); + return 1; + } + unsigned long did, cid, fid; + PassThruOpen("", &did); + PassThruConnect(did, ISO15765, CAN_29BIT_ID, 500000, &cid); + + PASSTHRU_MSG mask, pattern, flow; + + memcpy(mask.Data, "\xff\xff\xff\xff", 4); + mask.DataSize = 4; + mask.ProtocolID = ISO15765; + mask.TxFlags = CAN_29BIT_ID; + mask.ExtraDataIndex = 0; + mask.RxStatus = 0; + + ////////////////////////18//DA//F1//EF + memcpy(pattern.Data, "\x18\xda\xf1\xef", 4); + pattern.DataSize = 4; + pattern.ProtocolID = ISO15765; + pattern.TxFlags = CAN_29BIT_ID; + pattern.ExtraDataIndex = 0; + pattern.RxStatus = 0; + + memcpy(flow.Data, "\x18\xda\xef\xf1", 4); + flow.DataSize = 4; + flow.ProtocolID = ISO15765; + flow.TxFlags = CAN_29BIT_ID; + flow.ExtraDataIndex = 0; + flow.RxStatus = 0; + + auto res = PassThruStartMsgFilter(cid, FLOW_CONTROL_FILTER, &mask, &pattern, &flow, &fid); + if (res != STATUS_NOERROR) + return 1; + + SCONFIG_LIST list; + SCONFIG config; + config.Parameter = LOOPBACK; + config.Value = 0; + list.ConfigPtr = &config; + list.NumOfParams = 1; + + res = PassThruIoctl(cid, SET_CONFIG, &list, NULL); + if (res != STATUS_NOERROR) + return 1; + + PASSTHRU_MSG outmsg; + memcpy(outmsg.Data, "\x18\xda\xef\xf1""\xAA\xBB\xCC\xDD\xEE\xFF\x11\x22\x33\x44", 4 + 10); + outmsg.DataSize = 4 + 10; + outmsg.ProtocolID = ISO15765; + outmsg.TxFlags = CAN_29BIT_ID; + outmsg.ExtraDataIndex = 0; + outmsg.RxStatus = 0; + + unsigned long msgoutcount = 1; + + res = PassThruWriteMsgs(cid, &outmsg, &msgoutcount, 0); + if (res != STATUS_NOERROR) + return 1; + + PASSTHRU_MSG inmsg[8]; + unsigned long msgincount = 8; + + res = PassThruReadMsgs(cid, inmsg, &msgincount, 1000); + if (res != STATUS_NOERROR) + return 1; + + return 0; +} diff --git a/drivers/windows/panda_playground/panda_playground.vcxproj b/drivers/windows/panda_playground/panda_playground.vcxproj new file mode 100644 index 0000000000..2b5f3120c3 --- /dev/null +++ b/drivers/windows/panda_playground/panda_playground.vcxproj @@ -0,0 +1,191 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {691DB635-C272-4B98-897E-0505B970DCA9} + Win32Proj + panda_playground + 10.0.16299.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + $(ProjectName)2 + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + $(ProjectName) + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib;$(OutDir)ecusim.lib + + + + + Use + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib;$(OutDir)ecusim.lib + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib;$(OutDir)ecusim.lib + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib;$(OutDir)ecusim.lib + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + {96e0e646-ee76-444d-9a77-a0cd7f781deb} + + + {a2bb18a5-f26b-48d6-bbb5-b83d64473c77} + + + {5528aefb-638d-49af-b9d4-965154e7d531} + + + + + + \ No newline at end of file diff --git a/drivers/windows/panda_playground/panda_playground.vcxproj.filters b/drivers/windows/panda_playground/panda_playground.vcxproj.filters new file mode 100644 index 0000000000..b84fc23184 --- /dev/null +++ b/drivers/windows/panda_playground/panda_playground.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/drivers/windows/panda_playground/stdafx.cpp b/drivers/windows/panda_playground/stdafx.cpp new file mode 100644 index 0000000000..fefa8d7ec9 --- /dev/null +++ b/drivers/windows/panda_playground/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// panda_playground.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/panda_playground/stdafx.h b/drivers/windows/panda_playground/stdafx.h new file mode 100644 index 0000000000..f22759b086 --- /dev/null +++ b/drivers/windows/panda_playground/stdafx.h @@ -0,0 +1,17 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + +#include +#include +#include +#include diff --git a/drivers/windows/panda_playground/targetver.h b/drivers/windows/panda_playground/targetver.h new file mode 100644 index 0000000000..87c0086de7 --- /dev/null +++ b/drivers/windows/panda_playground/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/drivers/windows/panda_remove.ico b/drivers/windows/panda_remove.ico new file mode 100644 index 0000000000..74602e03e9 Binary files /dev/null and b/drivers/windows/panda_remove.ico differ diff --git a/drivers/windows/panda_shared/device.cpp b/drivers/windows/panda_shared/device.cpp new file mode 100644 index 0000000000..83e021b799 --- /dev/null +++ b/drivers/windows/panda_shared/device.cpp @@ -0,0 +1,169 @@ +#include "stdafx.h" + +#include +#include + +#include +#include + +#include + +#include "device.h" + +using namespace panda; + +//Returns the last Win32 error, in string format. Returns an empty string if there is no error. +tstring GetLastErrorAsString(){ + //Get the error message, if any. + DWORD errorMessageID = ::GetLastError(); + if (errorMessageID == 0) + return tstring(); //No error message has been recorded + + _TCHAR *messageBuffer = nullptr; + size_t size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (_TCHAR*)&messageBuffer, 0, NULL); + + tstring message(messageBuffer, size); + + //Free the buffer. + LocalFree(messageBuffer); + + return message; +} + +std::unordered_map panda::detect_pandas() { + HDEVINFO deviceInfo; + HRESULT hr; + SP_DEVINFO_DATA deviceInfoData; + SP_DEVICE_INTERFACE_DATA interfaceData; + unsigned int deviceIndex; + + std::unordered_map map_sn_to_devpath; + + deviceInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_panda, + NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); //DIGCF_ALLCLASSES + + if (deviceInfo == INVALID_HANDLE_VALUE) { + hr = HRESULT_FROM_WIN32(GetLastError()); + _tprintf(_T("Failed to get dev handle. HR: %d\n"), hr); + return map_sn_to_devpath; + } + + ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA)); + deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + deviceIndex = 0; + + while (SetupDiEnumDeviceInfo(deviceInfo, deviceIndex, &deviceInfoData)) { + deviceIndex++; + _tprintf(_T("Device info index %d\n"), deviceIndex); + + interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (SetupDiEnumDeviceInterfaces(deviceInfo, &deviceInfoData, + &GUID_DEVINTERFACE_panda, 0, &interfaceData) == FALSE) { + _tprintf(_T(" Got unexpected error while accessing interface %d\n"), GetLastError()); + continue; + } + + DWORD requiredLength; + if (SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, NULL, 0, &requiredLength, NULL) == FALSE + && ERROR_INSUFFICIENT_BUFFER != GetLastError()) { + _tprintf(_T(" Got unexpected error while reading interface details %d\n"), GetLastError()); + continue; + } + + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, requiredLength); + if (NULL == detailData) { + _tprintf(_T(" Failed to allocate %d bytes for interface data\n"), requiredLength); + continue; + } + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + DWORD length = requiredLength; + if (SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, detailData, length, &requiredLength, NULL) == FALSE) { + _tprintf(_T(" Got unexpected error while reading interface details (2nd time) %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + LocalFree(detailData); + continue; + } + + //_tprintf(_T(" Path: '%s'\n"), detailData->DevicePath); + HANDLE deviceHandle = CreateFile(detailData->DevicePath, + GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (INVALID_HANDLE_VALUE == deviceHandle) { + _tprintf(_T(" Error opening Device Handle %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + LocalFree(detailData); + continue; + } + + WINUSB_INTERFACE_HANDLE winusbHandle; + if (WinUsb_Initialize(deviceHandle, &winusbHandle) == FALSE) { + _tprintf(_T(" Error initializing WinUSB %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + CloseHandle(deviceHandle); + LocalFree(detailData); + continue; + } + + USB_DEVICE_DESCRIPTOR deviceDesc; + unsigned long lengthReceived; + if (WinUsb_GetDescriptor(winusbHandle, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, + (PBYTE)&deviceDesc, sizeof(deviceDesc), &lengthReceived) == FALSE + || lengthReceived != sizeof(deviceDesc)) { + _tprintf(_T(" Error getting device descriptor %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + WinUsb_Free(winusbHandle); + CloseHandle(deviceHandle); + LocalFree(detailData); + continue; + } + + #define SNDESCLEN 64 + PUSB_STRING_DESCRIPTOR psnDesc = (PUSB_STRING_DESCRIPTOR)LocalAlloc(LMEM_FIXED, SNDESCLEN); + if (NULL == psnDesc) { + _tprintf(_T(" Failed to allocate %d bytes for sn data\n"), SNDESCLEN); + continue; + } + + if (WinUsb_GetDescriptor(winusbHandle, USB_STRING_DESCRIPTOR_TYPE, deviceDesc.iSerialNumber, + 0x0409 /*Eng*/, (PBYTE)psnDesc, SNDESCLEN, &lengthReceived) == FALSE || lengthReceived == 0) { + _tprintf(_T(" Error getting serial number %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + LocalFree(psnDesc); + WinUsb_Free(winusbHandle); + CloseHandle(deviceHandle); + LocalFree(detailData); + continue; + } + //The minus 2 is for the two numbers, not the null term. + psnDesc->bString[(psnDesc->bLength - 2) / sizeof(_TCHAR)] = 0; + + char w_to_m_buff[256]; + size_t mbuff_len; + if (wcstombs_s(&mbuff_len, w_to_m_buff, sizeof(w_to_m_buff), psnDesc->bString, 24) != 0) { + _tprintf(_T(" Error generating mb SN string %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + LocalFree(psnDesc); + WinUsb_Free(winusbHandle); + CloseHandle(deviceHandle); + LocalFree(detailData); + continue; + } + std::string serialnum(w_to_m_buff, mbuff_len-1); + printf(" Device found: seriallen: %d; serial: %s\n", lengthReceived, serialnum.c_str()); + + map_sn_to_devpath[serialnum] = tstring(detailData->DevicePath); + + LocalFree(psnDesc); + WinUsb_Free(winusbHandle); + CloseHandle(deviceHandle); + LocalFree(detailData); + } + + if(deviceInfo) + SetupDiDestroyDeviceInfoList(deviceInfo); + + return map_sn_to_devpath; +} diff --git a/drivers/windows/panda_shared/device.h b/drivers/windows/panda_shared/device.h new file mode 100644 index 0000000000..f11baa1c67 --- /dev/null +++ b/drivers/windows/panda_shared/device.h @@ -0,0 +1,32 @@ +#ifndef __PANDA_DEVICE +#define __PANDA_DEVICE + +// +// Define below GUIDs +// +#include +#include + +#if defined(UNICODE) +#define _tcout std::wcout +#define tstring std::wstring +#else +#define _tcout std::cout +#define tstring std::string +#endif + +// +// Device Interface GUID. +// Used by all WinUsb devices that this application talks to. +// Must match "DeviceInterfaceGUIDs" registry value specified in the INF file. +// cce5291c-a69f-4995-a4c2-2ae57a51ade9 +// +DEFINE_GUID(GUID_DEVINTERFACE_panda, + 0xcce5291c,0xa69f,0x4995,0xa4,0xc2,0x2a,0xe5,0x7a,0x51,0xad,0xe9); + +tstring GetLastErrorAsString(); + +namespace panda { + std::unordered_map __declspec(dllexport) detect_pandas(); +} +#endif diff --git a/drivers/windows/panda_shared/panda.cpp b/drivers/windows/panda_shared/panda.cpp new file mode 100644 index 0000000000..5f711b02aa --- /dev/null +++ b/drivers/windows/panda_shared/panda.cpp @@ -0,0 +1,522 @@ +// panda.cpp : Defines the exported functions for the DLL application. +// +#include "stdafx.h" + +#include "device.h" +#include "panda.h" + +#define REQUEST_IN 0xC0 +#define REQUEST_OUT 0x40 + +#define CAN_TRANSMIT 1 +#define CAN_EXTENDED 4 + +using namespace panda; + +Panda::Panda( + WINUSB_INTERFACE_HANDLE WinusbHandle, + HANDLE DeviceHandle, + tstring devPath_, + std::string sn_ +) : usbh(WinusbHandle), devh(DeviceHandle), devPath(devPath_), sn(sn_) { + printf("CREATED A PANDA %s\n", this->sn.c_str()); + this->set_can_loopback(FALSE); + this->set_raw_io(TRUE); + this->set_alt_setting(0); +} + +Panda::~Panda() { + WinUsb_Free(this->usbh); + CloseHandle(this->devh); + printf("Cleanup Panda %s\n", this->sn.c_str()); +} + +std::vector Panda::listAvailablePandas() { + std::vector ret; + auto map_sn_to_devpath = detect_pandas(); + + for (auto kv : map_sn_to_devpath) { + ret.push_back(std::string(kv.first)); + } + + return ret; +} + +std::unique_ptr Panda::openPanda(std::string sn) +{ + auto map_sn_to_devpath = detect_pandas(); + + if (map_sn_to_devpath.empty()) return nullptr; + if (map_sn_to_devpath.find(sn) == map_sn_to_devpath.end() && sn != "") return nullptr; + + tstring devpath; + if (sn.empty()) { + sn = map_sn_to_devpath.begin()->first; + devpath = map_sn_to_devpath.begin()->second; + } else { + devpath = map_sn_to_devpath[sn]; + } + + HANDLE deviceHandle = CreateFile(devpath.c_str(), + GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (INVALID_HANDLE_VALUE == deviceHandle) { + _tprintf(_T(" Error opening Device Handle %d.\n"),// Msg: '%s'\n"), + GetLastError());// , GetLastErrorAsString().c_str()); + return nullptr; + } + + WINUSB_INTERFACE_HANDLE winusbHandle; + if (WinUsb_Initialize(deviceHandle, &winusbHandle) == FALSE) { + _tprintf(_T(" Error initializing WinUSB %d.\n"),// Msg: '%s'\n"), + GetLastError());// , GetLastErrorAsString().c_str()); + CloseHandle(deviceHandle); + return nullptr; + } + + return std::unique_ptr(new Panda(winusbHandle, deviceHandle, map_sn_to_devpath[sn], sn)); +} + +std::string Panda::get_usb_sn() { + return std::string(this->sn); +} + +int Panda::control_transfer( + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + void * data, + uint16_t wLength, + unsigned int timeout +) { + UNREFERENCED_PARAMETER(timeout); + + WINUSB_SETUP_PACKET SetupPacket; + ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET)); + ULONG cbSent = 0; + + //Create the setup packet + SetupPacket.RequestType = bmRequestType; + SetupPacket.Request = bRequest; + SetupPacket.Value = wValue; + SetupPacket.Index = wIndex; + SetupPacket.Length = wLength; + + //ULONG timeout = 10; // ms + //WinUsb_SetPipePolicy(interfaceHandle, pipeID, PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout); + + if (WinUsb_ControlTransfer(this->usbh, SetupPacket, (PUCHAR)data, wLength, &cbSent, 0) == FALSE) { + return -1; + } + + return cbSent; +} + +int Panda::bulk_write(UCHAR endpoint, const void * buff, ULONG length, PULONG transferred, ULONG timeout) { + if (this->usbh == INVALID_HANDLE_VALUE || !buff || !length || !transferred) return FALSE; + + if (WinUsb_WritePipe(this->usbh, endpoint, (PUCHAR)buff, length, transferred, NULL) == FALSE) { + _tprintf(_T(" Got error during bulk xfer: %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + return FALSE; + } + return TRUE; +} + +int Panda::bulk_read(UCHAR endpoint, void * buff, ULONG buff_size, PULONG transferred, ULONG timeout) { + if (this->usbh == INVALID_HANDLE_VALUE || !buff || !buff_size || !transferred) return FALSE; + + if (WinUsb_ReadPipe(this->usbh, endpoint, (PUCHAR)buff, buff_size, transferred, NULL) == FALSE) { + _tprintf(_T(" Got error during bulk xfer: %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + return FALSE; + } + return TRUE; +} + +bool Panda::set_alt_setting(UCHAR alt_setting) { + if (WinUsb_AbortPipe(this->usbh, 0x81) == FALSE) { + _tprintf(_T(" Error abobrting pipe before setting altsetting. continue. %d, Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + } + if (WinUsb_SetCurrentAlternateSetting(this->usbh, alt_setting) == FALSE) { + _tprintf(_T(" Error setting usb altsetting %d, Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + return FALSE; + } + + // Either the panda or the windows usb stack can drop messages + // if an odd number of messages are sent before an interrupt IN + // message is canceled. There are some other odd behaviors, but + // the best solution so far has been to send a few messages + // before using the device to clear out the pipe. No, the windows + // functions for clearing/resetting/etc the pipe did not work. + // This took way too to figure out a workaround. + // New info. The most repeatable behavior is losing the first + // message sent after setting alt setting to 1 (even without + // receiving). Something like this happened on linux sometimes. + bool loopback_backup = this->loopback; + this->set_can_loopback(TRUE); + Sleep(20); // Give time for any sent messages to appear in the RX buffer. + this->can_clear(PANDA_CAN_RX); + // send 4 messages becaus can_recv reads 4 messages at a time + for (int i = 0; i < 4; i++) { + printf("Sending PAD %d\n", i); + if (this->can_send(0x7FF, FALSE, {}, 0, PANDA_CAN1) == FALSE) { + auto err = GetLastError(); + printf("Got err on first send: %d\n", err); + } + } + Sleep(10); + //this->can_clear(PANDA_CAN_RX); + + //Read the messages so they do not contaimnate the real message stream. + this->can_recv(); + + //this->set_can_loopback(FALSE); + this->set_can_loopback(loopback_backup); + + return TRUE; +} + +UCHAR Panda::get_current_alt_setting() { + UCHAR alt_setting; + if (WinUsb_GetCurrentAlternateSetting(this->usbh, &alt_setting) == FALSE) { + _tprintf(_T(" Error getting usb altsetting %d, Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + return FALSE; + } + + return alt_setting; +} + +bool Panda::set_raw_io(bool val) { + UCHAR raw_io = val; + if (!WinUsb_SetPipePolicy(this->usbh, 0x81, RAW_IO, sizeof(raw_io), &raw_io)) { + _tprintf(_T(" Error setting usb raw I/O pipe policy %d, Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + return FALSE; + } + + return TRUE; +} + +PANDA_HEALTH Panda::get_health() +{ + WINUSB_SETUP_PACKET SetupPacket; + ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET)); + ULONG cbSent = 0; + + //Create the setup packet + SetupPacket.RequestType = REQUEST_IN; + SetupPacket.Request = 0xD2; + SetupPacket.Value = 0; + SetupPacket.Index = 0; + SetupPacket.Length = sizeof(UCHAR); + + //uint8_t health[13]; + PANDA_HEALTH health; + + if (WinUsb_ControlTransfer(this->usbh, SetupPacket, (PUCHAR)&health, sizeof(health), &cbSent, 0) == FALSE) { + _tprintf(_T(" Got unexpected error while reading panda health (2nd time) %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + } + + return health; +} + +bool Panda::enter_bootloader() { + return this->control_transfer(REQUEST_OUT, 0xd1, 0, 0, NULL, 0, 0) != -1; +} + +std::string Panda::get_version() { + char buff[0x40]; + ZeroMemory(&buff, sizeof(buff)); + + int xferCount = this->control_transfer(REQUEST_IN, 0xd6, 0, 0, buff, 0x40, 0); + if (xferCount == -1) return std::string(); + return std::string(buff); +} + +//TODO: Do hash stuff for calculating the serial. +std::string Panda::get_serial() { + char buff[0x20]; + ZeroMemory(&buff, sizeof(buff)); + + int xferCount = this->control_transfer(REQUEST_IN, 0xD0, 0, 0, buff, 0x20, 0); + if (xferCount == -1) return std::string(); + return std::string(buff); + + //dat = self._handle.controlRead(REQUEST_IN, 0xd0, 0, 0, 0x20); + //hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4] + // assert(hashsig == calc_hash) + // return[dat[0:0x10], dat[0x10:0x10 + 10]] +} + +//Secret appears to by raw bytes, not a string. TODO: Change returned type. +std::string Panda::get_secret() { + char buff[0x10]; + ZeroMemory(&buff, sizeof(buff)); + + int xferCount = this->control_transfer(REQUEST_IN, 0xd0, 1, 0, buff, 0x10, 0); + if (xferCount == -1) return std::string(); + return std::string(buff); +} + +bool Panda::set_usb_power(bool on) { + return this->control_transfer(REQUEST_OUT, 0xe6, (int)on, 0, NULL, 0, 0) != -1; +} + +bool Panda::set_esp_power(bool on) { + return this->control_transfer(REQUEST_OUT, 0xd9, (int)on, 0, NULL, 0, 0) != -1; +} + +bool Panda::esp_reset(uint16_t bootmode = 0) { + return this->control_transfer(REQUEST_OUT, 0xda, bootmode, 0, NULL, 0, 0) != -1; +} + +bool Panda::set_safety_mode(PANDA_SAFETY_MODE mode = SAFETY_NOOUTPUT) { + return this->control_transfer(REQUEST_OUT, 0xdc, mode, 0, NULL, 0, 0) != -1; +} + +bool Panda::set_can_forwarding(PANDA_CAN_PORT from_bus, PANDA_CAN_PORT to_bus) { + if (from_bus == PANDA_CAN_UNK) return FALSE; + return this->control_transfer(REQUEST_OUT, 0xdd, from_bus, to_bus, NULL, 0, 0) != -1; +} + +bool Panda::set_gmlan(PANDA_GMLAN_HOST_PORT bus = PANDA_GMLAN_CAN3) { + return this->control_transfer(REQUEST_OUT, 0xdb, 1, (bus == PANDA_GMLAN_CLEAR) ? 0 : bus, NULL, 0, 0) != -1; +} + +bool Panda::set_can_loopback(bool enable) { + this->loopback = enable; + return this->control_transfer(REQUEST_OUT, 0xe5, enable, 0, NULL, 0, 0) != -1; +} + +//Can not use the full range of 16 bit speed. +//cbps means centa bits per second (tento of kbps) +bool Panda::set_can_speed_cbps(PANDA_CAN_PORT bus, uint16_t speed) { + if (bus == PANDA_CAN_UNK) return FALSE; + return this->control_transfer(REQUEST_OUT, 0xde, bus, speed, NULL, 0, 0) != -1; +} + +//Can not use the full range of 16 bit speed. +bool Panda::set_can_speed_kbps(PANDA_CAN_PORT bus, uint16_t speed) { + return set_can_speed_cbps(bus, speed * 10); +} + +//Can not use full 32 bit range of rate +bool Panda::set_uart_baud(PANDA_SERIAL_PORT uart, uint32_t rate) { + return this->control_transfer(REQUEST_OUT, 0xe4, uart, rate / 300, NULL, 0, 0) != -1; +} + +bool Panda::set_uart_parity(PANDA_SERIAL_PORT uart, PANDA_SERIAL_PORT_PARITY parity) { + return this->control_transfer(REQUEST_OUT, 0xe2, uart, parity, NULL, 0, 0) != -1; +} + +bool Panda::can_send_many(const std::vector& can_msgs) { + std::vector formatted_msgs; + formatted_msgs.reserve(can_msgs.size()); + + for (auto msg : can_msgs) { + if (msg.bus == PANDA_CAN_UNK) continue; + if (msg.len > 8) continue; + PANDA_CAN_MSG_INTERNAL tmpmsg = {}; + tmpmsg.rir = (msg.addr_29b) ? + ((msg.addr << 3) | CAN_TRANSMIT | CAN_EXTENDED) : + (((msg.addr & 0x7FF) << 21) | CAN_TRANSMIT); + tmpmsg.f2 = msg.len | (msg.bus << 4); + memcpy(tmpmsg.dat, msg.dat, msg.len); + formatted_msgs.push_back(tmpmsg); + } + + if (formatted_msgs.size() == 0) return FALSE; + + unsigned int retcount; + return this->bulk_write(3, formatted_msgs.data(), + sizeof(PANDA_CAN_MSG_INTERNAL)*formatted_msgs.size(), (PULONG)&retcount, 0); +} + +bool Panda::can_send(uint32_t addr, bool addr_29b, const uint8_t *dat, uint8_t len, PANDA_CAN_PORT bus) { + if (bus == PANDA_CAN_UNK) return FALSE; + if (len > 8) return FALSE; + PANDA_CAN_MSG msg; + msg.addr_29b = addr_29b; + msg.addr = addr; + msg.len = len; + memcpy(msg.dat, dat, msg.len); + msg.bus = bus; + return this->can_send_many(std::vector{msg}); +} + +PANDA_CAN_MSG Panda::parse_can_recv(PANDA_CAN_MSG_INTERNAL *in_msg_raw) { + PANDA_CAN_MSG in_msg; + + in_msg.addr_29b = (bool)(in_msg_raw->rir & CAN_EXTENDED); + in_msg.addr = (in_msg.addr_29b) ? (in_msg_raw->rir >> 3) : (in_msg_raw->rir >> 21); + in_msg.recv_time = this->runningTime.getTimePassedUS(); + in_msg.recv_time_point = std::chrono::steady_clock::now(); + //The timestamp from the device is (in_msg_raw->f2 >> 16), + //but this 16 bit value is a little hard to use. Using a + //timer since the initialization of this device. + in_msg.len = in_msg_raw->f2 & 0xF; + memcpy(in_msg.dat, in_msg_raw->dat, 8); + + in_msg.is_receipt = ((in_msg_raw->f2 >> 4) & 0x80) == 0x80; + switch ((in_msg_raw->f2 >> 4) & 0x7F) { + case PANDA_CAN1: + in_msg.bus = PANDA_CAN1; + break; + case PANDA_CAN2: + in_msg.bus = PANDA_CAN2; + break; + case PANDA_CAN3: + in_msg.bus = PANDA_CAN3; + break; + default: + in_msg.bus = PANDA_CAN_UNK; + } + return in_msg; +} + +bool Panda::can_rx_q_push(HANDLE kill_event, DWORD timeoutms) { + while (1) { + auto w_ptr = this->w_ptr; + auto n_ptr = w_ptr + 1; + if (n_ptr == CAN_RX_QUEUE_LEN) { + n_ptr = 0; + } + + // Pause if there is not a slot available in the queue + if (n_ptr == this->r_ptr) { + printf("RX queue full!\n"); + Sleep(1); + continue; + } + + if (this->can_rx_q[n_ptr].complete) { + // TODO: is ResetEvent() faster? + CloseHandle(this->can_rx_q[n_ptr].complete); + } + + // Overlapped structure required for async read. + this->can_rx_q[n_ptr].complete = CreateEvent(NULL, TRUE, TRUE, NULL); + memset(&this->can_rx_q[n_ptr].overlapped, sizeof(OVERLAPPED), 0); + this->can_rx_q[n_ptr].overlapped.hEvent = this->can_rx_q[n_ptr].complete; + this->can_rx_q[n_ptr].error = 0; + + if (!WinUsb_ReadPipe(this->usbh, 0x81, this->can_rx_q[n_ptr].data, sizeof(this->can_rx_q[n_ptr].data), &this->can_rx_q[n_ptr].count, &this->can_rx_q[n_ptr].overlapped)) { + // An overlapped read will return true if done, or false with an + // error of ERROR_IO_PENDING if the transfer is still in process. + this->can_rx_q[n_ptr].error = GetLastError(); + } + + // Process the pipe read call from the previous invocation of this function + if (this->can_rx_q[w_ptr].error == ERROR_IO_PENDING) { + HANDLE phSignals[2] = { this->can_rx_q[w_ptr].complete, kill_event }; + auto dwError = WaitForMultipleObjects(kill_event ? 2 : 1, phSignals, FALSE, timeoutms); + + // Check if packet, timeout (nope), or break + if (dwError == WAIT_OBJECT_0) { + // Signal came from our usb object. Read the returned data. + if (!GetOverlappedResult(this->usbh, &this->can_rx_q[w_ptr].overlapped, &this->can_rx_q[w_ptr].count, TRUE)) { + // TODO: handle other error cases better. + dwError = GetLastError(); + printf("Got overlap error %d\n", dwError); + + continue; + } + } + else { + WinUsb_AbortPipe(this->usbh, 0x81); + + // Return FALSE to show that the optional signal + // was set instead of the wait breaking from a + // message or recoverable error. + if (dwError == (WAIT_OBJECT_0 + 1)) { + return FALSE; + } + continue; + } + } + else if (this->can_rx_q[w_ptr].error != 0) { // ERROR_BAD_COMMAND happens when device is unplugged. + return FALSE; + } + + this->w_ptr = n_ptr; + } + + return TRUE; +} + +void Panda::can_rx_q_pop(PANDA_CAN_MSG msg_out[], int &count) { + count = 0; + + // No data left in queue + if (this->r_ptr == this->w_ptr) { + Sleep(1); + return; + } + + auto r_ptr = this->r_ptr; + for (int i = 0; i < this->can_rx_q[r_ptr].count; i += sizeof(PANDA_CAN_MSG_INTERNAL)) { + auto in_msg_raw = (PANDA_CAN_MSG_INTERNAL *)(this->can_rx_q[r_ptr].data + i); + msg_out[count] = parse_can_recv(in_msg_raw); + ++count; + } + + // Advance read pointer (wrap around if needed) + ++r_ptr; + this->r_ptr = (r_ptr == CAN_RX_QUEUE_LEN ? 0 : r_ptr); +} + +std::vector Panda::can_recv() { + std::vector msg_recv; + int retcount; + char buff[sizeof(PANDA_CAN_MSG_INTERNAL) * 4]; + + if (this->bulk_read(0x81, buff, sizeof(buff), (PULONG)&retcount, 0) == FALSE) + return msg_recv; + + for (int i = 0; i < retcount; i += sizeof(PANDA_CAN_MSG_INTERNAL)) { + PANDA_CAN_MSG_INTERNAL *in_msg_raw = (PANDA_CAN_MSG_INTERNAL *)(buff + i); + auto in_msg = parse_can_recv(in_msg_raw); + msg_recv.push_back(in_msg); + } + + return msg_recv; +} + +bool Panda::can_clear(PANDA_CAN_PORT_CLEAR bus) { + /*Clears all messages from the specified internal CAN ringbuffer as though it were drained. + bus(int) : can bus number to clear a tx queue, or 0xFFFF to clear the global can rx queue.*/ + return this->control_transfer(REQUEST_OUT, 0xf1, bus, 0, NULL, 0, 0) != -1; +} + +std::string Panda::serial_read(PANDA_SERIAL_PORT port_number) { + std::string result; + char buff[0x40]; + while (TRUE) { + int retlen = this->control_transfer(REQUEST_IN, 0xe0, port_number, 0, &buff, 0x40, 0); + if (retlen <= 0) + break; + result += std::string(buff, retlen); + if (retlen < 0x40) break; + } + return result; +} + +int Panda::serial_write(PANDA_SERIAL_PORT port_number, const void* buff, uint16_t len) { + std::string dat; + dat += port_number; + dat += std::string((char*)buff, len); + int retcount; + if (this->bulk_write(2, dat.c_str(), len+1, (PULONG)&retcount, 0) == FALSE) return -1; + return retcount; +} + +bool Panda::serial_clear(PANDA_SERIAL_PORT port_number) { + return this->control_transfer(REQUEST_OUT, 0xf2, port_number, 0, NULL, 0, 0) != -1; +} diff --git a/drivers/windows/panda_shared/panda.h b/drivers/windows/panda_shared/panda.h new file mode 100644 index 0000000000..ade8fa36a8 --- /dev/null +++ b/drivers/windows/panda_shared/panda.h @@ -0,0 +1,237 @@ +#pragma once + +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the PANDA_EXPORTS +// symbol defined on the command line. This symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// PANDA_API functions as being imported from a DLL, whereas this DLL sees symbols +// defined with this macro as being exported. +#ifdef PANDA_EXPORTS +#define PANDA_API __declspec(dllexport) +#else +#define PANDA_API +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(UNICODE) +#define _tcout std::wcout +#define tstring std::wstring +#else +#define _tcout std::cout +#define tstring std::string +#endif + +#define LIN_MSG_MAX_LEN 10 +#define CAN_RX_QUEUE_LEN 10000 +#define CAN_RX_MSG_LEN 1000 + +//template class __declspec(dllexport) std::basic_string; + +namespace panda { + typedef enum _PANDA_SAFETY_MODE : uint16_t { + SAFETY_NOOUTPUT = 0, + SAFETY_HONDA = 1, + SAFETY_ALLOUTPUT = 0x1337, + } PANDA_SAFETY_MODE; + + typedef enum _PANDA_SERIAL_PORT : uint8_t { + SERIAL_DEBUG = 0, + SERIAL_ESP = 1, + SERIAL_LIN1 = 2, + SERIAL_LIN2 = 3, + } PANDA_SERIAL_PORT; + + typedef enum _PANDA_SERIAL_PORT_PARITY : uint8_t { + PANDA_PARITY_OFF = 0, + PANDA_PARITY_EVEN = 1, + PANDA_PARITY_ODD = 2, + } PANDA_SERIAL_PORT_PARITY; + + typedef enum _PANDA_CAN_PORT : uint8_t { + PANDA_CAN1 = 0, + PANDA_CAN2 = 1, + PANDA_CAN3 = 2, + PANDA_CAN_UNK = 0xFF, + } PANDA_CAN_PORT; + + typedef enum _PANDA_CAN_PORT_CLEAR : uint16_t { + PANDA_CAN1_TX = 0, + PANDA_CAN2_TX = 1, + PANDA_CAN3_TX = 2, + PANDA_CAN_RX = 0xFFFF, + } PANDA_CAN_PORT_CLEAR; + + typedef enum _PANDA_GMLAN_HOST_PORT : uint8_t { + PANDA_GMLAN_CLEAR = 0, + PANDA_GMLAN_CAN2 = 1, + PANDA_GMLAN_CAN3 = 2, + } PANDA_GMLAN_HOST_PORT; + + #pragma pack(1) + typedef struct _PANDA_HEALTH { + 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; + } PANDA_HEALTH, *PPANDA_HEALTH; + + typedef struct _PANDA_CAN_MSG { + uint32_t addr; + unsigned long long recv_time; //In microseconds since device initialization + std::chrono::time_point recv_time_point; + uint8_t dat[8]; + uint8_t len; + PANDA_CAN_PORT bus; + bool is_receipt; + bool addr_29b; + } PANDA_CAN_MSG; + + //Copied from https://stackoverflow.com/a/31488113 + class Timer + { + using clock = std::chrono::steady_clock; + using time_point_type = std::chrono::time_point < clock, std::chrono::microseconds >; + public: + Timer() { + start = std::chrono::time_point_cast(clock::now()); + } + + // gets the time elapsed from construction. + unsigned long long /*microseconds*/ Timer::getTimePassedUS() { + // get the new time + auto end = std::chrono::time_point_cast(clock::now()); + + // return the difference of the times + return (end - start).count(); + } + + // gets the time elapsed from construction. + unsigned long long /*milliseconds*/ Timer::getTimePassedMS() { + // get the new time + auto end = std::chrono::time_point_cast(clock::now()); + + // return the difference of the times + auto startms = std::chrono::time_point_cast(start); + return (end - startms).count(); + } + private: + time_point_type start; + }; + + // This class is exported from the panda.dll + class PANDA_API Panda { + public: + static std::vector listAvailablePandas(); + static std::unique_ptr openPanda(std::string sn); + + ~Panda(); + + std::string get_usb_sn(); + bool set_alt_setting(UCHAR alt_setting); + UCHAR get_current_alt_setting(); + bool Panda::set_raw_io(bool val); + + PANDA_HEALTH get_health(); + bool enter_bootloader(); + std::string get_version(); + std::string get_serial(); + std::string get_secret(); + + bool set_usb_power(bool on); + bool set_esp_power(bool on); + bool esp_reset(uint16_t bootmode); + bool set_safety_mode(PANDA_SAFETY_MODE mode); + bool set_can_forwarding(PANDA_CAN_PORT from_bus, PANDA_CAN_PORT to_bus); + bool set_gmlan(PANDA_GMLAN_HOST_PORT bus); + bool set_can_loopback(bool enable); + bool set_can_speed_cbps(PANDA_CAN_PORT bus, uint16_t speed); + bool set_can_speed_kbps(PANDA_CAN_PORT bus, uint16_t speed); + bool set_uart_baud(PANDA_SERIAL_PORT uart, uint32_t rate); + bool set_uart_parity(PANDA_SERIAL_PORT uart, PANDA_SERIAL_PORT_PARITY parity); + + bool can_send_many(const std::vector& can_msgs); + bool can_send(uint32_t addr, bool addr_29b, const uint8_t *dat, uint8_t len, PANDA_CAN_PORT bus); + std::vector can_recv(); + bool can_rx_q_push(HANDLE kill_event, DWORD timeoutms = INFINITE); + void can_rx_q_pop(PANDA_CAN_MSG msg_out[], int &count); + bool can_clear(PANDA_CAN_PORT_CLEAR bus); + + std::string serial_read(PANDA_SERIAL_PORT port_number); + int serial_write(PANDA_SERIAL_PORT port_number, const void* buff, uint16_t len); + bool serial_clear(PANDA_SERIAL_PORT port_number); + private: + Panda( + WINUSB_INTERFACE_HANDLE WinusbHandle, + HANDLE DeviceHandle, + tstring devPath_, + std::string sn_ + ); + + int control_transfer( + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + void * data, + uint16_t wLength, + unsigned int timeout + ); + + int bulk_write( + UCHAR endpoint, + const void * buff, + ULONG length, + PULONG transferred, + ULONG timeout + ); + + int Panda::bulk_read( + UCHAR endpoint, + void * buff, + ULONG buff_size, + PULONG transferred, + ULONG timeout + ); + + #pragma pack(1) + typedef struct _PANDA_CAN_MSG_INTERNAL { + uint32_t rir; + uint32_t f2; + uint8_t dat[8]; + } PANDA_CAN_MSG_INTERNAL; + + typedef struct _CAN_RX_PIPE_READ { + unsigned char data[sizeof(PANDA_CAN_MSG_INTERNAL) * CAN_RX_MSG_LEN]; + unsigned long count; + OVERLAPPED overlapped; + HANDLE complete; + DWORD error; + } CAN_RX_PIPE_READ; + + PANDA_CAN_MSG parse_can_recv(PANDA_CAN_MSG_INTERNAL *in_msg_raw); + + WINUSB_INTERFACE_HANDLE usbh; + HANDLE devh; + tstring devPath; + std::string sn; + bool loopback; + + Timer runningTime; + CAN_RX_PIPE_READ can_rx_q[CAN_RX_QUEUE_LEN]; + unsigned long w_ptr = 0; + unsigned long r_ptr = 0; + }; + +} diff --git a/drivers/windows/panda_shared/panda_shared.vcxitems b/drivers/windows/panda_shared/panda_shared.vcxitems new file mode 100644 index 0000000000..3f3b4765c0 --- /dev/null +++ b/drivers/windows/panda_shared/panda_shared.vcxitems @@ -0,0 +1,25 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {0c843279-68c7-4679-ae51-9bc463d50d1c} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/windows/panda_shared/targetver.h b/drivers/windows/panda_shared/targetver.h new file mode 100644 index 0000000000..1bf4ee6fee --- /dev/null +++ b/drivers/windows/panda_shared/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include + +#define WINVER _WIN32_WINNT_WIN7 +#define _WIN32_WINNT _WIN32_WINNT_WIN7 + +#include diff --git a/drivers/windows/redist/.gitignore b/drivers/windows/redist/.gitignore new file mode 100644 index 0000000000..90d431bfba --- /dev/null +++ b/drivers/windows/redist/.gitignore @@ -0,0 +1,2 @@ +*.exe +vscruntimeinfo.nsh diff --git a/drivers/windows/redist/README.md b/drivers/windows/redist/README.md new file mode 100644 index 0000000000..69565f11c6 --- /dev/null +++ b/drivers/windows/redist/README.md @@ -0,0 +1,7 @@ +When building the installer, please put the relevant vc_redist.x86.exe file into this folder. +Make sure that the uninstall registry key is correct in the panda_install.nsi file. + +Here is a list of the VC runtime downloads: https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads +An list of the registry keys has been maintained here: https://stackoverflow.com/a/34209692/627525 + +Copy vscruntimeinfo.nsh.sample to vscruntimeinfo.nsh and edit it for your version of Visual Studio. \ No newline at end of file diff --git a/drivers/windows/redist/vscruntimeinfo.nsh.sample b/drivers/windows/redist/vscruntimeinfo.nsh.sample new file mode 100644 index 0000000000..3e74ab1d9c --- /dev/null +++ b/drivers/windows/redist/vscruntimeinfo.nsh.sample @@ -0,0 +1,13 @@ +;NOTE! The panda software requires a VC runtime to be installed in order to work. +;This installer must be bundled with the appropriate runtime installer, and have +;the installation registry key set so the installer can tell if the runtime is +;already installed. + +;Here is a list of the VC runtime downloads: https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads +;An list of the registry keys has been maintained here: https://stackoverflow.com/a/34209692/627525 + + +;Microsoft Visual C++ 2015 Redistributable (x86) - 14.0.24123 +!define VCRuntimeRegKey "SOFTWARE\Classes\Installer\Dependencies\{206898cc-4b41-4d98-ac28-9f9ae57f91fe}" +!define VCRuntimeSetupPath "redist\" +!define VCRuntimeSetupFile "vc_redist.x86.exe" \ No newline at end of file diff --git a/drivers/windows/test certs/commaaiCertStore.pvk b/drivers/windows/test certs/commaaiCertStore.pvk new file mode 100644 index 0000000000..6db0a7885d Binary files /dev/null and b/drivers/windows/test certs/commaaiCertStore.pvk differ diff --git a/drivers/windows/test certs/commaaicert.cer b/drivers/windows/test certs/commaaicert.cer new file mode 100644 index 0000000000..8d0a38a7c0 Binary files /dev/null and b/drivers/windows/test certs/commaaicert.cer differ diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/can_bit_transition.md b/examples/can_bit_transition.md new file mode 100644 index 0000000000..fa0a6c6bb3 --- /dev/null +++ b/examples/can_bit_transition.md @@ -0,0 +1,25 @@ +# How to use can_bit_transition.py to reverse engineer a single bit field + +Let's say our goal is to find a brake pedal signal (caused by your foot pressing the brake pedal vs adaptive cruise control braking). + +The following process will allow you to quickly find bits that are always 0 during a period of time (when you know you were not pressing the brake with your foot) and always 1 in a different period of time (when you know you were pressing the brake with your foot). + +Open up a drive in cabana where you can find a place you used the brake pedal and another place where you did not use the brake pedal (and you can identify when you were on the brake pedal and when you were not). You may want to go out for a drive and put something in front of the camera after you put your foot on the brake and take it away before you take your foot off the brake so you can easily identify exactly when you had your foot on the brake based on the video in cabana. This is critical because this script needs the brake signal to always be high the entire time for one of the time frames. A 10 second time frame worked well for me. + +I found a drive where I knew I was not pressing the brake between timestamp 50.0 thru 65.0 and I was pressing the brake between timestamp 69.0 thru 79.0. Determine what the timestamps are in cabana by plotting any message and putting your mouse over the plot at the location you want to discover the timestamp. The tool tip on mouse hover has the format: timestamp: value + +Now download the log from cabana (Save Log button) and run the script passing in the timestamps +(replace csv file name with cabana log you downloaded and time ranges with your own) +``` +./can_bit_transition.py ./honda_crv_ex_2017_can-1520354796875.csv 50.0-65.0 69.0-79.0 +``` + +The script will output bits that were always low in the first time range and always high in the second time range (and vice versa) +``` +id 17c 0 -> 1 at byte 4 bitmask 1 +id 17c 0 -> 1 at byte 6 bitmask 32 +id 221 1 -> 0 at byte 0 bitmask 4 +id 1be 0 -> 1 at byte 0 bitmask 16 +``` + +Now I go back to cabana and graph the above bits by searching for the message by id, double clicking on the appropriate bit, and then selecting "show plot". I already knew that message id 0x17c is both user brake and adaptive cruise control braking combined, so I plotted one of those signals along side 0x221 and 0x1be. By replaying a drive I could see that 0x221 was not a brake signal (when high at random times that did not correspond to braking). Next I looked at 0x1be and I found it was the brake pedal signal I was looking for (went high whenever I pressed the brake pedal and did not go high when adaptive cruise control was braking). \ No newline at end of file diff --git a/examples/can_bit_transition.py b/examples/can_bit_transition.py new file mode 100755 index 0000000000..5c15e4bbc2 --- /dev/null +++ b/examples/can_bit_transition.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python + +import binascii +import csv +import sys + +class Message(): + """Details about a specific message ID.""" + def __init__(self, message_id): + self.message_id = message_id + self.ones = [0] * 8 # bit set if 1 is always seen + self.zeros = [0] * 8 # bit set if 0 is always seen + + def printBitDiff(self, other): + """Prints bits that transition from always zero to always 1 and vice versa.""" + for i in xrange(len(self.ones)): + zero_to_one = other.zeros[i] & self.ones[i] + if zero_to_one: + print 'id %s 0 -> 1 at byte %d bitmask %d' % (self.message_id, i, zero_to_one) + one_to_zero = other.ones[i] & self.zeros[i] + if one_to_zero: + print 'id %s 1 -> 0 at byte %d bitmask %d' % (self.message_id, i, one_to_zero) + + +class Info(): + """A collection of Messages.""" + + def __init__(self): + self.messages = {} # keyed by MessageID + + def load(self, filename, start, end): + """Given a CSV file, adds information about message IDs and their values.""" + with open(filename, 'rb') as input: + reader = csv.reader(input) + next(reader, None) # skip the CSV header + for row in reader: + if not len(row): continue + time = float(row[0]) + bus = int(row[2]) + if time < start or bus > 127: + continue + elif time > end: + break + if row[1].startswith('0x'): + message_id = row[1][2:] # remove leading '0x' + else: + message_id = hex(int(row[1]))[2:] # old message IDs are in decimal + message_id = '%s:%s' % (bus, message_id) + if row[3].startswith('0x'): + data = row[3][2:] # remove leading '0x' + else: + data = row[3] + new_message = False + if message_id not in self.messages: + self.messages[message_id] = Message(message_id) + new_message = True + message = self.messages[message_id] + bytes = bytearray.fromhex(data) + for i in xrange(len(bytes)): + ones = int(bytes[i]) + message.ones[i] = ones if new_message else message.ones[i] & ones + # Inverts the data and masks it to a byte to get the zeros as ones. + zeros = (~int(bytes[i])) & 0xff + message.zeros[i] = zeros if new_message else message.zeros[i] & zeros + +def PrintUnique(log_file, low_range, high_range): + # find messages with bits that are always low + start, end = map(float, low_range.split('-')) + low = Info() + low.load(log_file, start, end) + # find messages with bits that are always high + start, end = map(float, high_range.split('-')) + high = Info() + high.load(log_file, start, end) + # print messages that go from low to high + found = False + for message_id in sorted(high.messages): + if message_id in low.messages: + high.messages[message_id].printBitDiff(low.messages[message_id]) + found = True + if not found: print 'No messages that transition from always low to always high found!' + +if __name__ == "__main__": + if len(sys.argv) < 4: + print 'Usage:\n%s log.csv - -' % sys.argv[0] + sys.exit(0) + PrintUnique(sys.argv[1], sys.argv[2], sys.argv[3]) diff --git a/examples/can_logger.py b/examples/can_logger.py new file mode 100755 index 0000000000..05c28a26df --- /dev/null +++ b/examples/can_logger.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +from __future__ import print_function +import binascii +import csv +import sys +from panda import Panda + +def can_logger(): + + try: + print("Trying to connect to Panda over USB...") + p = Panda() + + except AssertionError: + print("USB connection failed. Trying WiFi...") + + try: + p = Panda("WIFI") + except: + print("WiFi connection timed out. Please make sure your Panda is connected and try again.") + sys.exit(0) + + try: + outputfile = open('output.csv', 'wb') + csvwriter = csv.writer(outputfile) + #Write Header + csvwriter.writerow(['Bus', 'MessageID', 'Message', 'MessageLength']) + print("Writing csv file output.csv. Press Ctrl-C to exit...\n") + + bus0_msg_cnt = 0 + bus1_msg_cnt = 0 + bus2_msg_cnt = 0 + + while True: + can_recv = p.can_recv() + + for address, _, dat, src in can_recv: + csvwriter.writerow([str(src), str(hex(address)), "0x" + binascii.hexlify(dat), len(dat)]) + + if src == 0: + bus0_msg_cnt += 1 + elif src == 1: + bus1_msg_cnt += 1 + elif src == 2: + bus2_msg_cnt += 1 + + print("Message Counts... Bus 0: " + str(bus0_msg_cnt) + " Bus 1: " + str(bus1_msg_cnt) + " Bus 2: " + str(bus2_msg_cnt), end='\r') + + except KeyboardInterrupt: + print("\nNow exiting. Final message Counts... Bus 0: " + str(bus0_msg_cnt) + " Bus 1: " + str(bus1_msg_cnt) + " Bus 2: " + str(bus2_msg_cnt)) + outputfile.close() + +if __name__ == "__main__": + can_logger() diff --git a/examples/can_unique.md b/examples/can_unique.md new file mode 100644 index 0000000000..bf316940d3 --- /dev/null +++ b/examples/can_unique.md @@ -0,0 +1,103 @@ +# How to use can_unique.py to reverse engineer a single bit field + +Let's say our goal is to find the CAN message indicating that the driver's door is either open or closed. +The following process is great for simple single-bit messages. +However for frequently changing values, such as RPM or speed, Cabana's graphical plots are probably better to use. + + +First record a few minutes of background CAN messages with all the doors closed and save it in background.csv: +``` +./can_logger.py +mv output.csv background.csv +``` +Then run can_logger.py for a few seconds while performing the action you're interested, such as opening and then closing the +front-left door and save it as door-fl-1.csv +Repeat the process and save it as door-f1-2.csv to have an easy way to confirm any suspicions. + +Now we'll use can_unique.py to look for unique bits: +``` +$ ./can_unique.py door-fl-1.csv background* +id 820 new one at byte 2 bitmask 2 +id 520 new one at byte 3 bitmask 7 +id 520 new zero at byte 3 bitmask 8 +id 520 new one at byte 5 bitmask 6 +id 520 new zero at byte 5 bitmask 9 +id 559 new zero at byte 6 bitmask 4 +id 804 new one at byte 5 bitmask 2 +id 804 new zero at byte 5 bitmask 1 + +$ ./can_unique.py door-fl-2.csv background* +id 672 new one at byte 3 bitmask 3 +id 820 new one at byte 2 bitmask 2 +id 520 new one at byte 3 bitmask 7 +id 520 new zero at byte 3 bitmask 8 +id 520 new one at byte 5 bitmask 6 +id 520 new zero at byte 5 bitmask 9 +id 559 new zero at byte 6 bitmask 4 +``` + +One of these bits hopefully indicates that the driver's door is open. +Let's go through each message ID to figure out which one is correct. +We expect any correct bits to have changed in both runs. +We can rule out 804 because it only occurred in the first run. +We can rule out 672 because it only occurred in the second run. +That leaves us with these message IDs: 820, 520, 559. Let's take a closer look at each one. + +``` +$ fgrep ,559, door-fl-1.csv |head +0,559,00ff0000000024f0 +0,559,00ff000000004464 +0,559,00ff0000000054a9 +0,559,00ff0000000064e3 +0,559,00ff00000000742e +0,559,00ff000000008451 +0,559,00ff00000000949c +0,559,00ff00000000a4d6 +0,559,00ff00000000b41b +0,559,00ff00000000c442 +``` +Message ID 559 looks like an incrementing value, so it's not what we're looking for. + +``` +$ fgrep ,520, door-fl-2.csv +0,520,26ff00f8a1890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a1890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a1890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a1890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a1890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a1890000 +``` +Message ID 520 oscillates between two values. However I only opened and closed the door once, so this is probably not it. + +``` +$ fgrep ,820, door-fl-1.csv +0,820,44000100a500c802 +0,820,44000100a500c803 +0,820,44000300a500c803 +0,820,44000300a500c802 +0,820,44000300a500c802 +0,820,44000300a500c802 +0,820,44000100a500c802 +0,820,44000100a500c802 +0,820,44000100a500c802 +``` +Message ID 820 looks promising! It starts off at 44000100a500c802 when the door is closed. +When the door is open it goes to 44000300a500c802. +Then when the door is closed again, it goes back to 44000100a500c802. +Let's confirm by looking at the data from our other run: +``` +$ fgrep ,820, door-fl-2.csv +0,820,44000100a500c802 +0,820,44000300a500c802 +0,820,44000100a500c802 +``` +Perfect! We now know that message id 820 at byte 2 bitmask 2 is set if the driver's door is open. +If we repeat the process with the front passenger's door, +then we'll find that message id 820 at byte 2 bitmask 4 is set if the front-right door is open. +This confirms our finding because it's common for similar signals to be near each other. diff --git a/examples/can_unique.py b/examples/can_unique.py new file mode 100755 index 0000000000..ad6de296ee --- /dev/null +++ b/examples/can_unique.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +# Given an interesting CSV file of CAN messages and a list of background CAN +# messages, print which bits in the interesting file have never appeared +# in the background files. + +# Expects the CSV file to be in the format from can_logger.py +# Bus,MessageID,Message,MessageLength +# 0,0x292,0x040000001068,6 + +# The old can_logger.py format is also supported: +# Bus,MessageID,Message +# 0,344,c000c00000000000 + + +import binascii +import csv +import sys +from panda import Panda + +class Message(): + """Details about a specific message ID.""" + def __init__(self, message_id): + self.message_id = message_id + self.data = {} # keyed by hex string encoded message data + self.ones = [0] * 8 # bit set if 1 is seen + self.zeros = [0] * 8 # bit set if 0 has been seen + + def printBitDiff(self, other): + """Prints bits that are set or cleared compared to other background.""" + for i in xrange(len(self.ones)): + new_ones = ((~other.ones[i]) & 0xff) & self.ones[i] + if new_ones: + print 'id %s new one at byte %d bitmask %d' % ( + self.message_id, i, new_ones) + new_zeros = ((~other.zeros[i]) & 0xff) & self.zeros[i] + if new_zeros: + print 'id %s new zero at byte %d bitmask %d' % ( + self.message_id, i, new_zeros) + + +class Info(): + """A collection of Messages.""" + + def __init__(self): + self.messages = {} # keyed by MessageID + + def load(self, filename): + """Given a CSV file, adds information about message IDs and their values.""" + with open(filename, 'rb') as input: + reader = csv.reader(input) + next(reader, None) # skip the CSV header + for row in reader: + bus = row[0] + if row[1].startswith('0x'): + message_id = row[1][2:] # remove leading '0x' + else: + message_id = hex(int(row[1]))[2:] # old message IDs are in decimal + message_id = '%s:%s' % (bus, message_id) + if row[1].startswith('0x'): + data = row[2][2:] # remove leading '0x' + else: + data = row[2] + if message_id not in self.messages: + self.messages[message_id] = Message(message_id) + message = self.messages[message_id] + if data not in self.messages[message_id].data: + message.data[data] = True + bytes = bytearray.fromhex(data) + for i in xrange(len(bytes)): + message.ones[i] = message.ones[i] | int(bytes[i]) + # Inverts the data and masks it to a byte to get the zeros as ones. + message.zeros[i] = message.zeros[i] | ( (~int(bytes[i])) & 0xff) + +def PrintUnique(interesting_file, background_files): + background = Info() + for background_file in background_files: + background.load(background_file) + interesting = Info() + interesting.load(interesting_file) + for message_id in sorted(interesting.messages): + if message_id not in background.messages: + print 'New message_id: %s' % message_id + else: + interesting.messages[message_id].printBitDiff( + background.messages[message_id]) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + print 'Usage:\n%s interesting.csv background*.csv' % sys.argv[0] + sys.exit(0) + PrintUnique(sys.argv[1], sys.argv[2:]) diff --git a/examples/get_panda_password.py b/examples/get_panda_password.py new file mode 100644 index 0000000000..11071d0353 --- /dev/null +++ b/examples/get_panda_password.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +from panda import Panda + +def get_panda_password(): + + try: + print("Trying to connect to Panda over USB...") + p = Panda() + + except AssertionError: + print("USB connection failed") + sys.exit(0) + + wifi = p.get_serial() + #print('[%s]' % ', '.join(map(str, wifi))) + print("SSID: " + wifi[0]) + print("Password: " + wifi[1]) + +if __name__ == "__main__": + get_panda_password() \ No newline at end of file diff --git a/examples/query_vin_and_stats.py b/examples/query_vin_and_stats.py new file mode 100755 index 0000000000..f3d6c198af --- /dev/null +++ b/examples/query_vin_and_stats.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +import time +import struct +from panda import Panda +from hexdump import hexdump +from panda.python.isotp import isotp_send, isotp_recv + +# 0x7e0 = Toyota +# 0x18DB33F1 for Honda? + +def get_current_data_for_pid(pid): + # 01 xx = Show current data + isotp_send(panda, "\x01"+chr(pid), 0x7e0) + return isotp_recv(panda, 0x7e8) + +def get_supported_pids(): + ret = [] + pid = 0 + while 1: + supported = struct.unpack(">I", get_current_data_for_pid(pid)[2:])[0] + for i in range(1+pid, 0x21+pid): + if supported & 0x80000000: + ret.append(i) + supported <<= 1 + pid += 0x20 + if pid not in ret: + break + return ret + +if __name__ == "__main__": + panda = Panda() + panda.set_safety_mode(Panda.SAFETY_ELM327) + panda.can_clear(0) + + # 09 02 = Get VIN + isotp_send(panda, "\x09\x02", 0x7df) + ret = isotp_recv(panda, 0x7e8) + hexdump(ret) + print "VIN: %s" % ret[2:] + + # 03 = get DTCS + isotp_send(panda, "\x03", 0x7e0) + dtcs = isotp_recv(panda, 0x7e8) + print "DTCs:", dtcs[2:].encode("hex") + + supported_pids = get_supported_pids() + print "Supported PIDs:",supported_pids + + while 1: + speed = struct.unpack(">B", get_current_data_for_pid(13)[2:])[0] # kph + rpm = struct.unpack(">H", get_current_data_for_pid(12)[2:])[0]/4.0 # revs + throttle = struct.unpack(">B", get_current_data_for_pid(17)[2:])[0]/255.0 * 100 # percent + temp = struct.unpack(">B", get_current_data_for_pid(5)[2:])[0] - 40 # degrees C + load = struct.unpack(">B", get_current_data_for_pid(4)[2:])[0]/255.0 * 100 # percent + print "%d KPH, %d RPM, %.1f%% Throttle, %d deg C, %.1f%% load" % (speed, rpm, throttle, temp, load) + time.sleep(0.2) + + + diff --git a/examples/tesla_tester.py b/examples/tesla_tester.py new file mode 100644 index 0000000000..99d8d92854 --- /dev/null +++ b/examples/tesla_tester.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +import sys +import binascii +from panda import Panda + +def tesla_tester(): + + try: + print("Trying to connect to Panda over USB...") + p = Panda() + + except AssertionError: + print("USB connection failed. Trying WiFi...") + + try: + p = Panda("WIFI") + except: + print("WiFi connection timed out. Please make sure your Panda is connected and try again.") + sys.exit(0) + + body_bus_speed = 125 # Tesla Body busses (B, BF) are 125kbps, rest are 500kbps + body_bus_num = 1 # My TDC to OBD adapter has PT on bus0 BDY on bus1 and CH on bus2 + p.set_can_speed_kbps(body_bus_num, body_bus_speed) + + # Now set the panda from its default of SAFETY_NOOUTPUT (read only) to SAFETY_ALLOUTPUT + # Careful, as this will let us send any CAN messages we want (which could be very bad!) + print("Setting Panda to output mode...") + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # BDY 0x248 is the MCU_commands message, which includes folding mirrors, opening the trunk, frunk, setting the cars lock state and more. For our test, we will edit the 3rd byte, which is MCU_lockRequest. 0x01 will lock, 0x02 will unlock: + print("Unlocking Tesla...") + p.can_send(0x248, "\x00\x00\x02\x00\x00\x00\x00\x00", body_bus_num) + + #Or, we can set the first byte, MCU_frontHoodCommand + MCU_liftgateSwitch, to 0x01 to pop the frunk, or 0x04 to open/close the trunk (0x05 should open both) + print("Opening Frunk...") + p.can_send(0x248, "\x01\x00\x00\x00\x00\x00\x00\x00", body_bus_num) + + #Back to safety... + print("Disabling output on Panda...") + p.set_safety_mode(Panda.SAFETY_NOOUTPUT) + + print("Reading VIN from 0x568. This is painfully slow and can take up to 3 minutes (1 minute per message; 3 messages needed for full VIN)...") + + vin = {} + while True: + #Read the VIN + can_recv = p.can_recv() + for address, _, dat, src in can_recv: + if src == body_bus_num: + if address == 1384: #0x568 is VIN + vin_index = int(binascii.hexlify(dat)[:2]) #first byte is the index, 00, 01, 02 + vin_string = binascii.hexlify(dat)[2:] #rest of the string is the actual VIN data + vin[vin_index] = vin_string.decode("hex") + print("Got VIN index " + str(vin_index) + " data " + vin[vin_index]) + #if we have all 3 parts of the VIN, print it and break out of our while loop + if 0 in vin and 1 in vin and 2 in vin: + print("VIN: " + vin[0] + vin[1] + vin[2][:3]) + break + +if __name__ == "__main__": + tesla_tester() diff --git a/panda.png b/panda.png new file mode 100644 index 0000000000..e18137d872 Binary files /dev/null and b/panda.png differ diff --git a/python/__init__.py b/python/__init__.py new file mode 100644 index 0000000000..bfca642e82 --- /dev/null +++ b/python/__init__.py @@ -0,0 +1,561 @@ +# python library to interface with panda +from __future__ import print_function +import binascii +import struct +import hashlib +import socket +import usb1 +import os +import time +import traceback +import subprocess +from dfu import PandaDFU +from esptool import ESPROM, CesantaFlasher +from flash_release import flash_release +from update import ensure_st_up_to_date +from serial import PandaSerial +from isotp import isotp_send, isotp_recv + +__version__ = '0.0.8' + +BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../") + +DEBUG = os.getenv("PANDADEBUG") is not None + +# *** wifi mode *** + +def build_st(target, mkfile="Makefile"): + from panda import BASEDIR + cmd = 'cd %s && make -f %s clean && make -f %s %s >/dev/null' % (os.path.join(BASEDIR, "board"), mkfile, mkfile, target) + try: + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as exception: + output = exception.output + returncode = exception.returncode + raise + +def parse_can_buffer(dat): + ret = [] + for j in range(0, len(dat), 0x10): + ddat = dat[j:j+0x10] + f1, f2 = struct.unpack("II", ddat[0:8]) + extended = 4 + if f1 & extended: + address = f1 >> 3 + else: + address = f1 >> 21 + dddat = ddat[8:8+(f2&0xF)] + if DEBUG: + print(" R %x: %s" % (address, str(dddat).encode("hex"))) + ret.append((address, f2>>16, dddat, (f2>>4)&0xFF)) + return ret + +class PandaWifiStreaming(object): + def __init__(self, ip="192.168.0.10", port=1338): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.setblocking(0) + self.ip = ip + self.port = port + self.kick() + + def kick(self): + # must be called at least every 5 seconds + self.sock.sendto("hello", (self.ip, self.port)) + + def can_recv(self): + ret = [] + while True: + try: + dat, addr = self.sock.recvfrom(0x200*0x10) + if addr == (self.ip, self.port): + ret += parse_can_buffer(dat) + except socket.error as e: + if e.errno != 35 and e.errno != 11: + traceback.print_exc() + break + return ret + +# stupid tunneling of USB over wifi and SPI +class WifiHandle(object): + def __init__(self, ip="192.168.0.10", port=1337): + self.sock = socket.create_connection((ip, port)) + + def __recv(self): + ret = self.sock.recv(0x44) + length = struct.unpack("I", ret[0:4])[0] + return ret[4:4+length] + + def controlWrite(self, request_type, request, value, index, data, timeout=0): + # ignore data in reply, panda doesn't use it + return self.controlRead(request_type, request, value, index, 0, timeout) + + def controlRead(self, request_type, request, value, index, length, timeout=0): + self.sock.send(struct.pack("HHBBHHH", 0, 0, request_type, request, value, index, length)) + return self.__recv() + + def bulkWrite(self, endpoint, data, timeout=0): + if len(data) > 0x10: + raise ValueError("Data must not be longer than 0x10") + self.sock.send(struct.pack("HH", endpoint, len(data))+data) + self.__recv() # to /dev/null + + def bulkRead(self, endpoint, length, timeout=0): + self.sock.send(struct.pack("HH", endpoint, 0)) + return self.__recv() + + def close(self): + self.sock.close() + +# *** normal mode *** + +class Panda(object): + SAFETY_NOOUTPUT = 0 + SAFETY_HONDA = 1 + SAFETY_TOYOTA = 2 + SAFETY_GM = 3 + SAFETY_HONDA_BOSCH = 4 + SAFETY_FORD = 5 + SAFETY_CADILLAC = 6 + SAFETY_HYUNDAI = 7 + SAFETY_TESLA = 8 + SAFETY_CHRYSLER = 9 + SAFETY_TOYOTA_IPAS = 0x1335 + SAFETY_TOYOTA_NOLIMITS = 0x1336 + SAFETY_ALLOUTPUT = 0x1337 + SAFETY_ELM327 = 0xE327 + + SERIAL_DEBUG = 0 + SERIAL_ESP = 1 + SERIAL_LIN1 = 2 + SERIAL_LIN2 = 3 + + GMLAN_CAN2 = 1 + GMLAN_CAN3 = 2 + + REQUEST_IN = usb1.ENDPOINT_IN | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE + REQUEST_OUT = usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE + + def __init__(self, serial=None, claim=True): + self._serial = serial + self._handle = None + self.connect(claim) + + def close(self): + self._handle.close() + self._handle = None + + def connect(self, claim=True, wait=False): + if self._handle != None: + self.close() + + if self._serial == "WIFI": + self._handle = WifiHandle() + print("opening WIFI device") + self.wifi = True + else: + context = usb1.USBContext() + self._handle = None + self.wifi = False + + while 1: + try: + for device in context.getDeviceList(skip_on_error=True): + #print(device) + if device.getVendorID() == 0xbbaa and device.getProductID() in [0xddcc, 0xddee]: + try: + this_serial = device.getSerialNumber() + except Exception: + continue + if self._serial is None or this_serial == self._serial: + self._serial = this_serial + print("opening device", self._serial, hex(device.getProductID())) + time.sleep(1) + self.bootstub = device.getProductID() == 0xddee + self.legacy = (device.getbcdDevice() != 0x2300) + self._handle = device.open() + if claim: + self._handle.claimInterface(0) + #self._handle.setInterfaceAltSetting(0, 0) #Issue in USB stack + break + except Exception as e: + print("exception", e) + traceback.print_exc() + if wait == False or self._handle != None: + break + context = usb1.USBContext() #New context needed so new devices show up + assert(self._handle != None) + print("connected") + + def reset(self, enter_bootstub=False, enter_bootloader=False): + # reset + try: + if enter_bootloader: + self._handle.controlWrite(Panda.REQUEST_IN, 0xd1, 0, 0, b'') + else: + if enter_bootstub: + self._handle.controlWrite(Panda.REQUEST_IN, 0xd1, 1, 0, b'') + else: + self._handle.controlWrite(Panda.REQUEST_IN, 0xd8, 0, 0, b'') + except Exception: + pass + if not enter_bootloader: + self.reconnect() + + def reconnect(self): + self.close() + time.sleep(1.0) + success = False + # wait up to 15 seconds + for i in range(0, 15): + try: + self.connect() + success = True + break + except Exception: + print("reconnecting is taking %d seconds..." % (i+1)) + try: + dfu = PandaDFU(PandaDFU.st_serial_to_dfu_serial(self._serial)) + dfu.recover() + except Exception: + pass + time.sleep(1.0) + if not success: + raise Exception("reconnect failed") + + @staticmethod + def flash_static(handle, code): + # confirm flasher is present + fr = handle.controlRead(Panda.REQUEST_IN, 0xb0, 0, 0, 0xc) + assert fr[4:8] == "\xde\xad\xd0\x0d" + + # unlock flash + print("flash: unlocking") + handle.controlWrite(Panda.REQUEST_IN, 0xb1, 0, 0, b'') + + # erase sectors 1 and 2 + print("flash: erasing") + handle.controlWrite(Panda.REQUEST_IN, 0xb2, 1, 0, b'') + handle.controlWrite(Panda.REQUEST_IN, 0xb2, 2, 0, b'') + + # flash over EP2 + STEP = 0x10 + print("flash: flashing") + for i in range(0, len(code), STEP): + handle.bulkWrite(2, code[i:i+STEP]) + + # reset + print("flash: resetting") + try: + handle.controlWrite(Panda.REQUEST_IN, 0xd8, 0, 0, b'') + except Exception: + pass + + def flash(self, fn=None, code=None, reconnect=True): + print("flash: main version is " + self.get_version()) + if not self.bootstub: + self.reset(enter_bootstub=True) + assert(self.bootstub) + + if fn is None and code is None: + if self.legacy: + fn = "obj/comma.bin" + print("building legacy st code") + build_st(fn, "Makefile.legacy") + else: + fn = "obj/panda.bin" + print("building panda st code") + build_st(fn) + fn = os.path.join(BASEDIR, "board", fn) + + if code is None: + with open(fn) as f: + code = f.read() + + # get version + print("flash: bootstub version is " + self.get_version()) + + # do flash + Panda.flash_static(self._handle, code) + + # reconnect + if reconnect: + self.reconnect() + + def recover(self, timeout=None): + self.reset(enter_bootloader=True) + t_start = time.time() + while len(PandaDFU.list()) == 0: + print("waiting for DFU...") + time.sleep(0.1) + if timeout is not None and (time.time() - t_start) > timeout: + return False + + dfu = PandaDFU(PandaDFU.st_serial_to_dfu_serial(self._serial)) + dfu.recover() + + # reflash after recover + self.connect(True, True) + self.flash() + return True + + @staticmethod + def flash_ota_st(): + ret = os.system("cd %s && make clean && make ota" % (os.path.join(BASEDIR, "board"))) + time.sleep(1) + return ret==0 + + @staticmethod + def flash_ota_wifi(release=False): + release_str = "RELEASE=1" if release else "" + ret = os.system("cd {} && make clean && {} make ota".format(os.path.join(BASEDIR, "boardesp"),release_str)) + time.sleep(1) + return ret==0 + + @staticmethod + def list(): + context = usb1.USBContext() + ret = [] + try: + for device in context.getDeviceList(skip_on_error=True): + if device.getVendorID() == 0xbbaa and device.getProductID() in [0xddcc, 0xddee]: + try: + ret.append(device.getSerialNumber()) + except Exception: + continue + except Exception: + pass + # TODO: detect if this is real + #ret += ["WIFI"] + return ret + + def call_control_api(self, msg): + self._handle.controlWrite(Panda.REQUEST_OUT, msg, 0, 0, b'') + + # ******************* health ******************* + + def health(self): + dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, 13) + a = struct.unpack("IIBBBBB", dat) + return {"voltage": a[0], "current": a[1], + "started": a[2], "controls_allowed": a[3], + "gas_interceptor_detected": a[4], + "started_signal_detected": a[5], + "started_alt": a[6]} + + # ******************* control ******************* + + def enter_bootloader(self): + try: + self._handle.controlWrite(Panda.REQUEST_OUT, 0xd1, 0, 0, b'') + except Exception as e: + print(e) + pass + + def get_version(self): + return self._handle.controlRead(Panda.REQUEST_IN, 0xd6, 0, 0, 0x40) + + def is_grey(self): + ret = self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40) + return ret == "\x01" + + def get_serial(self): + dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 0, 0, 0x20) + hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4] + assert(hashsig == calc_hash) + return [dat[0:0x10], dat[0x10:0x10+10]] + + def get_secret(self): + return self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 1, 0, 0x10) + + # ******************* configuration ******************* + + def set_usb_power(self, on): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xe6, int(on), 0, b'') + + def set_esp_power(self, on): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xd9, int(on), 0, b'') + + def esp_reset(self, bootmode=0): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xda, int(bootmode), 0, b'') + time.sleep(0.2) + + def set_safety_mode(self, mode=SAFETY_NOOUTPUT): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, 0, b'') + + def set_can_forwarding(self, from_bus, to_bus): + # TODO: This feature may not work correctly with saturated buses + self._handle.controlWrite(Panda.REQUEST_OUT, 0xdd, from_bus, to_bus, b'') + + def set_gmlan(self, bus=2): + if bus is None: + self._handle.controlWrite(Panda.REQUEST_OUT, 0xdb, 0, 0, b'') + elif bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: + self._handle.controlWrite(Panda.REQUEST_OUT, 0xdb, 1, bus, b'') + + def set_can_loopback(self, enable): + # set can loopback mode for all buses + self._handle.controlWrite(Panda.REQUEST_OUT, 0xe5, int(enable), 0, b'') + + def set_can_enable(self, bus_num, enable): + # sets the can transciever enable pin + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf4, int(bus_num), int(enable), b'') + + def set_can_speed_kbps(self, bus, speed): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xde, bus, int(speed*10), b'') + + def set_uart_baud(self, uart, rate): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xe4, uart, rate/300, b'') + + def set_uart_parity(self, uart, parity): + # parity, 0=off, 1=even, 2=odd + self._handle.controlWrite(Panda.REQUEST_OUT, 0xe2, uart, parity, b'') + + def set_uart_callback(self, uart, install): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xe3, uart, int(install), b'') + + # ******************* can ******************* + + def can_send_many(self, arr): + snds = [] + transmit = 1 + extended = 4 + for addr, _, dat, bus in arr: + assert len(dat) <= 8 + if DEBUG: + print(" W %x: %s" % (addr, dat.encode("hex"))) + if addr >= 0x800: + rir = (addr << 3) | transmit | extended + else: + rir = (addr << 21) | transmit + snd = struct.pack("II", rir, len(dat) | (bus << 4)) + dat + snd = snd.ljust(0x10, b'\x00') + snds.append(snd) + + while True: + try: + #print("DAT: %s"%b''.join(snds).__repr__()) + if self.wifi: + for s in snds: + self._handle.bulkWrite(3, s) + else: + self._handle.bulkWrite(3, b''.join(snds)) + break + except (usb1.USBErrorIO, usb1.USBErrorOverflow): + print("CAN: BAD SEND MANY, RETRYING") + + def can_send(self, addr, dat, bus): + self.can_send_many([[addr, None, dat, bus]]) + + def can_recv(self): + dat = bytearray() + while True: + try: + dat = self._handle.bulkRead(1, 0x10*256) + break + except (usb1.USBErrorIO, usb1.USBErrorOverflow): + print("CAN: BAD RECV, RETRYING") + return parse_can_buffer(dat) + + def can_clear(self, bus): + """Clears all messages from the specified internal CAN ringbuffer as + though it were drained. + + Args: + bus (int): can bus number to clear a tx queue, or 0xFFFF to clear the + global can rx queue. + + """ + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf1, bus, 0, b'') + + # ******************* isotp ******************* + + def isotp_send(self, addr, dat, bus, recvaddr=None, subaddr=None): + return isotp_send(self, dat, addr, bus, recvaddr, subaddr) + + def isotp_recv(self, addr, bus=0, sendaddr=None, subaddr=None): + return isotp_recv(self, addr, bus, sendaddr, subaddr) + + # ******************* serial ******************* + + def serial_read(self, port_number): + ret = [] + while 1: + lret = bytes(self._handle.controlRead(Panda.REQUEST_IN, 0xe0, port_number, 0, 0x40)) + if len(lret) == 0: + break + ret.append(lret) + return b''.join(ret) + + def serial_write(self, port_number, ln): + ret = 0 + for i in range(0, len(ln), 0x20): + ret += self._handle.bulkWrite(2, struct.pack("B", port_number) + ln[i:i+0x20]) + return ret + + def serial_clear(self, port_number): + """Clears all messages (tx and rx) from the specified internal uart + ringbuffer as though it were drained. + + Args: + port_number (int): port number of the uart to clear. + + """ + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf2, port_number, 0, b'') + + # ******************* kline ******************* + + # pulse low for wakeup + def kline_wakeup(self): + if DEBUG: + print("kline wakeup...") + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf0, 0, 0, b'') + if DEBUG: + print("kline wakeup done") + + def kline_drain(self, bus=2): + # drain buffer + bret = bytearray() + while True: + ret = self._handle.controlRead(Panda.REQUEST_IN, 0xe0, bus, 0, 0x40) + if len(ret) == 0: + break + elif DEBUG: + print("kline drain: "+str(ret).encode("hex")) + bret += ret + return bytes(bret) + + def kline_ll_recv(self, cnt, bus=2): + echo = bytearray() + while len(echo) != cnt: + ret = str(self._handle.controlRead(Panda.REQUEST_OUT, 0xe0, bus, 0, cnt-len(echo))) + if DEBUG and len(ret) > 0: + print("kline recv: "+ret.encode("hex")) + echo += ret + return str(echo) + + def kline_send(self, x, bus=2, checksum=True): + def get_checksum(dat): + result = 0 + result += sum(map(ord, dat)) if isinstance(b'dat', str) else sum(dat) + result = -result + return struct.pack("B", result % 0x100) + + self.kline_drain(bus=bus) + if checksum: + x += get_checksum(x) + for i in range(0, len(x), 0xf): + ts = x[i:i+0xf] + if DEBUG: + print("kline send: "+ts.encode("hex")) + self._handle.bulkWrite(2, chr(bus).encode()+ts) + echo = self.kline_ll_recv(len(ts), bus=bus) + if echo != ts: + print("**** ECHO ERROR %d ****" % i) + print(binascii.hexlify(echo)) + print(binascii.hexlify(ts)) + assert echo == ts + + def kline_recv(self, bus=2): + msg = self.kline_ll_recv(2, bus=bus) + msg += self.kline_ll_recv(ord(msg[1])-2, bus=bus) + return msg diff --git a/python/dfu.py b/python/dfu.py new file mode 100644 index 0000000000..02deed47bb --- /dev/null +++ b/python/dfu.py @@ -0,0 +1,121 @@ +from __future__ import print_function +import os +import usb1 +import struct +import time + +# *** DFU mode *** + +DFU_DNLOAD = 1 +DFU_UPLOAD = 2 +DFU_GETSTATUS = 3 +DFU_CLRSTATUS = 4 +DFU_ABORT = 6 + +class PandaDFU(object): + def __init__(self, dfu_serial): + context = usb1.USBContext() + for device in context.getDeviceList(skip_on_error=True): + if device.getVendorID() == 0x0483 and device.getProductID() == 0xdf11: + try: + this_dfu_serial = device.open().getASCIIStringDescriptor(3) + except Exception: + continue + if this_dfu_serial == dfu_serial or dfu_serial is None: + self._handle = device.open() + self.legacy = "07*128Kg" in self._handle.getASCIIStringDescriptor(4) + return + raise Exception("failed to open "+dfu_serial) + + @staticmethod + def list(): + context = usb1.USBContext() + dfu_serials = [] + try: + for device in context.getDeviceList(skip_on_error=True): + if device.getVendorID() == 0x0483 and device.getProductID() == 0xdf11: + try: + dfu_serials.append(device.open().getASCIIStringDescriptor(3)) + except Exception: + pass + except Exception: + pass + return dfu_serials + + @staticmethod + def st_serial_to_dfu_serial(st): + if st == None or st == "none": + return None + uid_base = struct.unpack("H"*6, st.decode("hex")) + return struct.pack("!HHH", uid_base[1] + uid_base[5], uid_base[0] + uid_base[4] + 0xA, uid_base[3]).encode("hex").upper() + + + def status(self): + while 1: + dat = str(self._handle.controlRead(0x21, DFU_GETSTATUS, 0, 0, 6)) + if dat[1] == "\x00": + break + + def clear_status(self): + # Clear status + stat = str(self._handle.controlRead(0x21, DFU_GETSTATUS, 0, 0, 6)) + if stat[4] == "\x0a": + self._handle.controlRead(0x21, DFU_CLRSTATUS, 0, 0, 0) + elif stat[4] == "\x09": + self._handle.controlWrite(0x21, DFU_ABORT, 0, 0, "") + self.status() + stat = str(self._handle.controlRead(0x21, DFU_GETSTATUS, 0, 0, 6)) + + def erase(self, address): + self._handle.controlWrite(0x21, DFU_DNLOAD, 0, 0, "\x41" + struct.pack("I", address)) + self.status() + + def program(self, address, dat, block_size=None): + if block_size == None: + block_size = len(dat) + + # Set Address Pointer + self._handle.controlWrite(0x21, DFU_DNLOAD, 0, 0, "\x21" + struct.pack("I", address)) + self.status() + + # Program + dat += "\xFF"*((block_size-len(dat)) % block_size) + for i in range(0, len(dat)/block_size): + ldat = dat[i*block_size:(i+1)*block_size] + print("programming %d with length %d" % (i, len(ldat))) + self._handle.controlWrite(0x21, DFU_DNLOAD, 2+i, 0, ldat) + self.status() + + def program_bootstub(self, code_bootstub): + self.clear_status() + self.erase(0x8004000) + self.erase(0x8000000) + self.program(0x8000000, code_bootstub, 0x800) + self.reset() + + def recover(self): + from panda import BASEDIR, build_st + if self.legacy: + fn = "obj/bootstub.comma.bin" + print("building legacy bootstub") + build_st(fn, "Makefile.legacy") + else: + fn = "obj/bootstub.panda.bin" + print("building panda bootstub") + build_st(fn) + fn = os.path.join(BASEDIR, "board", fn) + + with open(fn) as f: + code = f.read() + + self.program_bootstub(code) + + def reset(self): + # **** Reset **** + self._handle.controlWrite(0x21, DFU_DNLOAD, 0, 0, "\x21" + struct.pack("I", 0x8000000)) + self.status() + try: + self._handle.controlWrite(0x21, DFU_DNLOAD, 2, 0, "") + stat = str(self._handle.controlRead(0x21, DFU_GETSTATUS, 0, 0, 6)) + except Exception: + pass diff --git a/python/esptool.py b/python/esptool.py new file mode 100755 index 0000000000..e68e6cd6e0 --- /dev/null +++ b/python/esptool.py @@ -0,0 +1,1314 @@ +#!/usr/bin/env python +# NB: Before sending a PR to change the above line to '#!/usr/bin/env python2', please read https://github.com/themadinventor/esptool/issues/21 +# +# ESP8266 ROM Bootloader Utility +# https://github.com/themadinventor/esptool +# +# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, other contributors as noted. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +# Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import argparse +import hashlib +import inspect +import json +import os +#import serial +import struct +import subprocess +import sys +import tempfile +import time +import traceback +import usb1 + +__version__ = "1.2" + +class FakePort(object): + def __init__(self, serial=None): + from panda import Panda + self.panda = Panda(serial) + + # will only work on new st, old ones will stay @ 921600 + self.baudrate = 230400 + + @property + def baudrate(self): + return self._baudrate + + @baudrate.setter + def baudrate(self, x): + print "set baud to", x + self.panda.set_uart_baud(1, x) + + def write(self, buf): + SEND_STEP = 0x20 + for i in range(0, len(buf), SEND_STEP): + self.panda.serial_write(1, buf[i:i+SEND_STEP]) + + def flushInput(self): + self.panda.serial_clear(1) + + def flushOutput(self): + self.panda.serial_clear(1) + + def read(self, llen): + ret = self.panda._handle.controlRead(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xe0, 1, 0, 1) + if ret == '': + time.sleep(0.1) + ret = self.panda._handle.controlRead(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xe0, 1, 0, 1) + return str(ret) + + def reset(self): + self.panda.esp_reset(1) + + def inWaiting(self): + return False + +class ESPROM(object): + # These are the currently known commands supported by the ROM + ESP_FLASH_BEGIN = 0x02 + ESP_FLASH_DATA = 0x03 + ESP_FLASH_END = 0x04 + ESP_MEM_BEGIN = 0x05 + ESP_MEM_END = 0x06 + ESP_MEM_DATA = 0x07 + ESP_SYNC = 0x08 + ESP_WRITE_REG = 0x09 + ESP_READ_REG = 0x0a + + # Maximum block sized for RAM and Flash writes, respectively. + ESP_RAM_BLOCK = 0x1800 + ESP_FLASH_BLOCK = 0x400 + + # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. + ESP_ROM_BAUD = 115200 + + # First byte of the application image + ESP_IMAGE_MAGIC = 0xe9 + + # Initial state for the checksum routine + ESP_CHECKSUM_MAGIC = 0xef + + # OTP ROM addresses + ESP_OTP_MAC0 = 0x3ff00050 + ESP_OTP_MAC1 = 0x3ff00054 + ESP_OTP_MAC3 = 0x3ff0005c + + # Flash sector size, minimum unit of erase. + ESP_FLASH_SECTOR = 0x1000 + + def __init__(self, port=None, baud=ESP_ROM_BAUD): + self._port = FakePort(port) + self._slip_reader = slip_reader(self._port) + + """ Read a SLIP packet from the serial port """ + def read(self): + return self._slip_reader.next() + + """ Write bytes to the serial port while performing SLIP escaping """ + def write(self, packet): + buf = '\xc0' \ + + (packet.replace('\xdb','\xdb\xdd').replace('\xc0','\xdb\xdc')) \ + + '\xc0' + self._port.write(buf) + + """ Calculate checksum of a blob, as it is defined by the ROM """ + @staticmethod + def checksum(data, state=ESP_CHECKSUM_MAGIC): + for b in data: + state ^= ord(b) + return state + + """ Send a request and read the response """ + def command(self, op=None, data=None, chk=0): + if op is not None: + pkt = struct.pack('> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff) + elif ((mac1 >> 16) & 0xff) == 0: + oui = (0x18, 0xfe, 0x34) + elif ((mac1 >> 16) & 0xff) == 1: + oui = (0xac, 0xd0, 0x74) + else: + raise FatalError("Unknown OUI") + return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) + + """ Read Chip ID from OTP ROM - see http://esp8266-re.foogod.com/wiki/System_get_chip_id_%28IoT_RTOS_SDK_0.9.9%29 """ + def chip_id(self): + id0 = self.read_reg(self.ESP_OTP_MAC0) + id1 = self.read_reg(self.ESP_OTP_MAC1) + return (id0 >> 24) | ((id1 & 0xffffff) << 8) + + """ Read SPI flash manufacturer and device id """ + def flash_id(self): + self.flash_begin(0, 0) + self.write_reg(0x60000240, 0x0, 0xffffffff) + self.write_reg(0x60000200, 0x10000000, 0xffffffff) + flash_id = self.read_reg(0x60000240) + return flash_id + + """ Abuse the loader protocol to force flash to be left in write mode """ + def flash_unlock_dio(self): + # Enable flash write mode + self.flash_begin(0, 0) + # Reset the chip rather than call flash_finish(), which would have + # write protected the chip again (why oh why does it do that?!) + self.mem_begin(0,0,0,0x40100000) + self.mem_finish(0x40000080) + + """ Perform a chip erase of SPI flash """ + def flash_erase(self): + # Trick ROM to initialize SFlash + self.flash_begin(0, 0) + + # This is hacky: we don't have a custom stub, instead we trick + # the bootloader to jump to the SPIEraseChip() routine and then halt/crash + # when it tries to boot an unconfigured system. + self.mem_begin(0,0,0,0x40100000) + self.mem_finish(0x40004984) + + # Yup - there's no good way to detect if we succeeded. + # It it on the other hand unlikely to fail. + + def run_stub(self, stub, params, read_output=True): + stub = dict(stub) + stub['code'] = unhexify(stub['code']) + if 'data' in stub: + stub['data'] = unhexify(stub['data']) + + if stub['num_params'] != len(params): + raise FatalError('Stub requires %d params, %d provided' + % (stub['num_params'], len(params))) + + params = struct.pack('<' + ('I' * stub['num_params']), *params) + pc = params + stub['code'] + + # Upload + self.mem_begin(len(pc), 1, len(pc), stub['params_start']) + self.mem_block(pc, 0) + if 'data' in stub: + self.mem_begin(len(stub['data']), 1, len(stub['data']), stub['data_start']) + self.mem_block(stub['data'], 0) + self.mem_finish(stub['entry']) + + if read_output: + print 'Stub executed, reading response:' + while True: + p = self.read() + print hexify(p) + if p == '': + return + + +class ESPBOOTLOADER(object): + """ These are constants related to software ESP bootloader, working with 'v2' image files """ + + # First byte of the "v2" application image + IMAGE_V2_MAGIC = 0xea + + # First 'segment' value in a "v2" application image, appears to be a constant version value? + IMAGE_V2_SEGMENT = 4 + + +def LoadFirmwareImage(filename): + """ Load a firmware image, without knowing what kind of file (v1 or v2) it is. + + Returns a BaseFirmwareImage subclass, either ESPFirmwareImage (v1) or OTAFirmwareImage (v2). + """ + with open(filename, 'rb') as f: + magic = ord(f.read(1)) + f.seek(0) + if magic == ESPROM.ESP_IMAGE_MAGIC: + return ESPFirmwareImage(f) + elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC: + return OTAFirmwareImage(f) + else: + raise FatalError("Invalid image magic number: %d" % magic) + + +class BaseFirmwareImage(object): + """ Base class with common firmware image functions """ + def __init__(self): + self.segments = [] + self.entrypoint = 0 + + def add_segment(self, addr, data, pad_to=4): + """ Add a segment to the image, with specified address & data + (padded to a boundary of pad_to size) """ + # Data should be aligned on word boundary + l = len(data) + if l % pad_to: + data += b"\x00" * (pad_to - l % pad_to) + if l > 0: + self.segments.append((addr, len(data), data)) + + def load_segment(self, f, is_irom_segment=False): + """ Load the next segment from the image file """ + (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: + raise FatalError('Suspicious segment 0x%x, length %d' % (offset, size)) + segment_data = f.read(size) + if len(segment_data) < size: + raise FatalError('End of file reading segment 0x%x, length %d (actual length %d)' % (offset, size, len(segment_data))) + segment = (offset, size, segment_data) + self.segments.append(segment) + return segment + + def save_segment(self, f, segment, checksum=None): + """ Save the next segment to the image file, return next checksum value if provided """ + (offset, size, data) = segment + f.write(struct.pack(' 16: + raise FatalError('Invalid firmware image magic=%d segments=%d' % (magic, segments)) + + for i in xrange(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + def save(self, filename): + with open(filename, 'wb') as f: + self.write_v1_header(f, self.segments) + checksum = ESPROM.ESP_CHECKSUM_MAGIC + for segment in self.segments: + checksum = self.save_segment(f, segment, checksum) + self.append_checksum(f, checksum) + + +class OTAFirmwareImage(BaseFirmwareImage): + """ 'Version 2' firmware image, segments loaded by software bootloader stub + (ie Espressif bootloader or rboot) + """ + def __init__(self, load_file=None): + super(OTAFirmwareImage, self).__init__() + self.version = 2 + if load_file is not None: + (magic, segments, first_flash_mode, first_flash_size_freq, first_entrypoint) = struct.unpack(' 16: + raise FatalError('Invalid V2 second header magic=%d segments=%d' % (magic, segments)) + + # load all the usual segments + for _ in xrange(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + def save(self, filename): + with open(filename, 'wb') as f: + # Save first header for irom0 segment + f.write(struct.pack(' 0: + esp._port.baudrate = baud_rate + # Read the greeting. + p = esp.read() + if p != 'OHAI': + raise FatalError('Failed to connect to the flasher (got %s)' % hexify(p)) + + def flash_write(self, addr, data, show_progress=False): + assert addr % self._esp.ESP_FLASH_SECTOR == 0, 'Address must be sector-aligned' + assert len(data) % self._esp.ESP_FLASH_SECTOR == 0, 'Length must be sector-aligned' + sys.stdout.write('Writing %d @ 0x%x... ' % (len(data), addr)) + sys.stdout.flush() + self._esp.write(struct.pack(' length: + raise FatalError('Read more than expected') + p = self._esp.read() + if len(p) != 16: + raise FatalError('Expected digest, got: %s' % hexify(p)) + expected_digest = hexify(p).upper() + digest = hashlib.md5(data).hexdigest().upper() + print + if digest != expected_digest: + raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) + p = self._esp.read() + if len(p) != 1: + raise FatalError('Expected status, got: %s' % hexify(p)) + status_code = struct.unpack(', ) or a single +# argument. + +def load_ram(esp, args): + image = LoadFirmwareImage(args.filename) + + print 'RAM boot...' + for (offset, size, data) in image.segments: + print 'Downloading %d bytes at %08x...' % (size, offset), + sys.stdout.flush() + esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, offset) + + seq = 0 + while len(data) > 0: + esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq) + data = data[esp.ESP_RAM_BLOCK:] + seq += 1 + print 'done!' + + print 'All segments done, executing at %08x' % image.entrypoint + esp.mem_finish(image.entrypoint) + + +def read_mem(esp, args): + print '0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address)) + + +def write_mem(esp, args): + esp.write_reg(args.address, args.value, args.mask, 0) + print 'Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address) + + +def dump_mem(esp, args): + f = file(args.filename, 'wb') + for i in xrange(args.size / 4): + d = esp.read_reg(args.address + (i * 4)) + f.write(struct.pack('> 16 + args.flash_size = {18: '2m', 19: '4m', 20: '8m', 21: '16m', 22: '32m'}.get(size_id) + if args.flash_size is None: + print 'Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x), defaulting to 4m' % (flash_id, size_id) + args.flash_size = '4m' + else: + print 'Auto-detected Flash size:', args.flash_size + + +def write_flash(esp, args): + detect_flash_size(esp, args) + flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] + flash_size_freq = {'4m':0x00, '2m':0x10, '8m':0x20, '16m':0x30, '32m':0x40, '16m-c1': 0x50, '32m-c1':0x60, '32m-c2':0x70}[args.flash_size] + flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq] + flash_params = struct.pack('BB', flash_mode, flash_size_freq) + + flasher = CesantaFlasher(esp, args.baud) + + for address, argfile in args.addr_filename: + image = argfile.read() + argfile.seek(0) # rewind in case we need it again + if address + len(image) > int(args.flash_size.split('m')[0]) * (1 << 17): + print 'WARNING: Unlikely to work as data goes beyond end of flash. Hint: Use --flash_size' + # Fix sflash config data. + if address == 0 and image[0] == '\xe9': + print 'Flash params set to 0x%02x%02x' % (flash_mode, flash_size_freq) + image = image[0:2] + flash_params + image[4:] + # Pad to sector size, which is the minimum unit of writing (erasing really). + if len(image) % esp.ESP_FLASH_SECTOR != 0: + image += '\xff' * (esp.ESP_FLASH_SECTOR - (len(image) % esp.ESP_FLASH_SECTOR)) + t = time.time() + flasher.flash_write(address, image, not args.no_progress) + t = time.time() - t + print ('\rWrote %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' + % (len(image), address, t, len(image) / t * 8 / 1000)) + print 'Leaving...' + if args.verify: + print 'Verifying just-written flash...' + _verify_flash(flasher, args, flash_params) + flasher.boot_fw() + + +def image_info(args): + image = LoadFirmwareImage(args.filename) + print('Image version: %d' % image.version) + print('Entry point: %08x' % image.entrypoint) if image.entrypoint != 0 else 'Entry point not set' + print '%d segments' % len(image.segments) + print + checksum = ESPROM.ESP_CHECKSUM_MAGIC + for (idx, (offset, size, data)) in enumerate(image.segments): + if image.version == 2 and idx == 0: + print 'Segment 1: %d bytes IROM0 (no load address)' % size + else: + print 'Segment %d: %5d bytes at %08x' % (idx + 1, size, offset) + checksum = ESPROM.checksum(data, checksum) + print + print 'Checksum: %02x (%s)' % (image.checksum, 'valid' if image.checksum == checksum else 'invalid!') + + +def make_image(args): + image = ESPFirmwareImage() + if len(args.segfile) == 0: + raise FatalError('No segments specified') + if len(args.segfile) != len(args.segaddr): + raise FatalError('Number of specified files does not match number of specified addresses') + for (seg, addr) in zip(args.segfile, args.segaddr): + data = file(seg, 'rb').read() + image.add_segment(addr, data) + image.entrypoint = args.entrypoint + image.save(args.output) + + +def elf2image(args): + e = ELFFile(args.input) + if args.version == '1': + image = ESPFirmwareImage() + else: + image = OTAFirmwareImage() + irom_data = e.load_section('.irom0.text') + if len(irom_data) == 0: + raise FatalError(".irom0.text section not found in ELF file - can't create V2 image.") + image.add_segment(0, irom_data, 16) + image.entrypoint = e.get_entry_point() + for section, start in ((".text", "_text_start"), (".data", "_data_start"), (".rodata", "_rodata_start")): + data = e.load_section(section) + image.add_segment(e.get_symbol_addr(start), data) + + image.flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] + image.flash_size_freq = {'4m':0x00, '2m':0x10, '8m':0x20, '16m':0x30, '32m':0x40, '16m-c1': 0x50, '32m-c1':0x60, '32m-c2':0x70}[args.flash_size] + image.flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq] + + irom_offs = e.get_symbol_addr("_irom0_text_start") - 0x40200000 + + if args.version == '1': + if args.output is None: + args.output = args.input + '-' + image.save(args.output + "0x00000.bin") + data = e.load_section(".irom0.text") + if irom_offs < 0: + raise FatalError('Address of symbol _irom0_text_start in ELF is located before flash mapping address. Bad linker script?') + if (irom_offs & 0xFFF) != 0: # irom0 isn't flash sector aligned + print "WARNING: irom0 section offset is 0x%08x. ELF is probably linked for 'elf2image --version=2'" % irom_offs + with open(args.output + "0x%05x.bin" % irom_offs, "wb") as f: + f.write(data) + f.close() + else: # V2 OTA image + if args.output is None: + args.output = "%s-0x%05x.bin" % (os.path.splitext(args.input)[0], irom_offs & ~(ESPROM.ESP_FLASH_SECTOR - 1)) + image.save(args.output) + + +def read_mac(esp, args): + mac = esp.read_mac() + print 'MAC: %s' % ':'.join(map(lambda x: '%02x' % x, mac)) + + +def chip_id(esp, args): + chipid = esp.chip_id() + print 'Chip ID: 0x%08x' % chipid + + +def erase_flash(esp, args): + flasher = CesantaFlasher(esp, args.baud) + print 'Erasing flash (this may take a while)...' + t = time.time() + flasher.flash_erase_chip() + t = time.time() - t + print 'Erase took %.1f seconds' % t + + +def run(esp, args): + esp.run() + + +def flash_id(esp, args): + flash_id = esp.flash_id() + esp.flash_finish(False) + print 'Manufacturer: %02x' % (flash_id & 0xff) + print 'Device: %02x%02x' % ((flash_id >> 8) & 0xff, (flash_id >> 16) & 0xff) + + +def read_flash(esp, args): + flasher = CesantaFlasher(esp, args.baud) + t = time.time() + data = flasher.flash_read(args.address, args.size, not args.no_progress) + t = time.time() - t + print ('\rRead %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' + % (len(data), args.address, t, len(data) / t * 8 / 1000)) + file(args.filename, 'wb').write(data) + + +def _verify_flash(flasher, args, flash_params=None): + differences = False + for address, argfile in args.addr_filename: + image = argfile.read() + argfile.seek(0) # rewind in case we need it again + if address == 0 and image[0] == '\xe9' and flash_params is not None: + image = image[0:2] + flash_params + image[4:] + image_size = len(image) + print 'Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name) + # Try digest first, only read if there are differences. + digest, _ = flasher.flash_digest(address, image_size) + digest = hexify(digest).upper() + expected_digest = hashlib.md5(image).hexdigest().upper() + if digest == expected_digest: + print '-- verify OK (digest matched)' + continue + else: + differences = True + if getattr(args, 'diff', 'no') != 'yes': + print '-- verify FAILED (digest mismatch)' + continue + + flash = flasher.flash_read(address, image_size) + assert flash != image + diff = [i for i in xrange(image_size) if flash[i] != image[i]] + print '-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0]) + for d in diff: + print ' %08x %02x %02x' % (address + d, ord(flash[d]), ord(image[d])) + if differences: + raise FatalError("Verify failed.") + + +def verify_flash(esp, args, flash_params=None): + flasher = CesantaFlasher(esp) + _verify_flash(flasher, args, flash_params) + + +def version(args): + print __version__ + +# +# End of operations functions +# + + +def main(): + parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool') + + parser.add_argument( + '--port', '-p', + help='Serial port device', + default=os.environ.get('ESPTOOL_PORT', None)) + + parser.add_argument( + '--baud', '-b', + help='Serial port baud rate used when flashing/reading', + type=arg_auto_int, + default=os.environ.get('ESPTOOL_BAUD', ESPROM.ESP_ROM_BAUD)) + + subparsers = parser.add_subparsers( + dest='operation', + help='Run esptool {command} -h for additional help') + + parser_load_ram = subparsers.add_parser( + 'load_ram', + help='Download an image to RAM and execute') + parser_load_ram.add_argument('filename', help='Firmware image') + + parser_dump_mem = subparsers.add_parser( + 'dump_mem', + help='Dump arbitrary memory to disk') + parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int) + parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int) + parser_dump_mem.add_argument('filename', help='Name of binary dump') + + parser_read_mem = subparsers.add_parser( + 'read_mem', + help='Read arbitrary memory location') + parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int) + + parser_write_mem = subparsers.add_parser( + 'write_mem', + help='Read-modify-write to arbitrary memory location') + parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int) + parser_write_mem.add_argument('value', help='Value', type=arg_auto_int) + parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int) + + def add_spi_flash_subparsers(parent, auto_detect=False): + """ Add common parser arguments for SPI flash properties """ + parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency', + choices=['40m', '26m', '20m', '80m'], + default=os.environ.get('ESPTOOL_FF', '40m')) + parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', + choices=['qio', 'qout', 'dio', 'dout'], + default=os.environ.get('ESPTOOL_FM', 'qio')) + choices = ['4m', '2m', '8m', '16m', '32m', '16m-c1', '32m-c1', '32m-c2'] + default = '4m' + if auto_detect: + default = 'detect' + choices.insert(0, 'detect') + parent.add_argument('--flash_size', '-fs', help='SPI Flash size in Mbit', type=str.lower, + choices=choices, + default=os.environ.get('ESPTOOL_FS', default)) + + parser_write_flash = subparsers.add_parser( + 'write_flash', + help='Write a binary blob to flash') + parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', + action=AddrFilenamePairAction) + add_spi_flash_subparsers(parser_write_flash, auto_detect=True) + parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") + parser_write_flash.add_argument('--verify', help='Verify just-written data (only necessary if very cautious, data is already CRCed', action='store_true') + + subparsers.add_parser( + 'run', + help='Run application code in flash') + + parser_image_info = subparsers.add_parser( + 'image_info', + help='Dump headers from an application image') + parser_image_info.add_argument('filename', help='Image file to parse') + + parser_make_image = subparsers.add_parser( + 'make_image', + help='Create an application image from binary files') + parser_make_image.add_argument('output', help='Output image file') + parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file') + parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int) + parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0) + + parser_elf2image = subparsers.add_parser( + 'elf2image', + help='Create an application image from ELF file') + parser_elf2image.add_argument('input', help='Input ELF file') + parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str) + parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1','2'], default='1') + add_spi_flash_subparsers(parser_elf2image) + + subparsers.add_parser( + 'read_mac', + help='Read MAC address from OTP ROM') + + subparsers.add_parser( + 'chip_id', + help='Read Chip ID from OTP ROM') + + subparsers.add_parser( + 'flash_id', + help='Read SPI flash manufacturer and device ID') + + parser_read_flash = subparsers.add_parser( + 'read_flash', + help='Read SPI flash content') + parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int) + parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int) + parser_read_flash.add_argument('filename', help='Name of binary dump') + parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") + + parser_verify_flash = subparsers.add_parser( + 'verify_flash', + help='Verify a binary blob against flash') + parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space', + action=AddrFilenamePairAction) + parser_verify_flash.add_argument('--diff', '-d', help='Show differences', + choices=['no', 'yes'], default='no') + + subparsers.add_parser( + 'erase_flash', + help='Perform Chip Erase on SPI flash') + + subparsers.add_parser( + 'version', help='Print esptool version') + + # internal sanity check - every operation matches a module function of the same name + for operation in subparsers.choices.keys(): + assert operation in globals(), "%s should be a module function" % operation + + args = parser.parse_args() + + print 'esptool.py v%s' % __version__ + + # operation function can take 1 arg (args), 2 args (esp, arg) + # or be a member function of the ESPROM class. + + operation_func = globals()[args.operation] + operation_args,_,_,_ = inspect.getargspec(operation_func) + if operation_args[0] == 'esp': # operation function takes an ESPROM connection object + initial_baud = MIN(ESPROM.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate + esp = ESPROM(args.port, initial_baud) + esp.connect() + operation_func(esp, args) + else: + operation_func(args) + + +class AddrFilenamePairAction(argparse.Action): + """ Custom parser class for the address/filename pairs passed as arguments """ + def __init__(self, option_strings, dest, nargs='+', **kwargs): + super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + # validate pair arguments + pairs = [] + for i in range(0,len(values),2): + try: + address = int(values[i],0) + except ValueError as e: + raise argparse.ArgumentError(self,'Address "%s" must be a number' % values[i]) + try: + argfile = open(values[i + 1], 'rb') + except IOError as e: + raise argparse.ArgumentError(self, e) + except IndexError: + raise argparse.ArgumentError(self,'Must be pairs of an address and the binary filename to write there') + pairs.append((address, argfile)) + setattr(namespace, self.dest, pairs) + +# This is "wrapped" stub_flasher.c, to be loaded using run_stub. +_CESANTA_FLASHER_STUB = """\ +{"code_start": 1074790404, "code": "080000601C000060000000601000006031FCFF71FCFF\ +81FCFFC02000680332D218C020004807404074DCC48608005823C0200098081BA5A9239245005803\ +1B555903582337350129230B446604DFC6F3FF21EEFFC0200069020DF0000000010078480040004A\ +0040B449004012C1F0C921D911E901DD0209312020B4ED033C2C56C2073020B43C3C56420701F5FF\ +C000003C4C569206CD0EEADD860300202C4101F1FFC0000056A204C2DCF0C02DC0CC6CCAE2D1EAFF\ +0606002030F456D3FD86FBFF00002020F501E8FFC00000EC82D0CCC0C02EC0C73DEB2ADC46030020\ +2C4101E1FFC00000DC42C2DCF0C02DC056BCFEC602003C5C8601003C6C4600003C7C08312D0CD811\ +C821E80112C1100DF0000C180000140010400C0000607418000064180000801800008C1800008418\ +0000881800009018000018980040880F0040A80F0040349800404C4A0040740F0040800F0040980F\ +00400099004012C1E091F5FFC961CD0221EFFFE941F9310971D9519011C01A223902E2D1180C0222\ +6E1D21E4FF31E9FF2AF11A332D0F42630001EAFFC00000C030B43C2256A31621E1FF1A2228022030\ +B43C3256B31501ADFFC00000DD023C4256ED1431D6FF4D010C52D90E192E126E0101DDFFC0000021\ +D2FF32A101C020004802303420C0200039022C0201D7FFC00000463300000031CDFF1A333803D023\ +C03199FF27B31ADC7F31CBFF1A3328030198FFC0000056C20E2193FF2ADD060E000031C6FF1A3328\ +030191FFC0000056820DD2DD10460800000021BEFF1A2228029CE231BCFFC020F51A33290331BBFF\ +C02C411A332903C0F0F4222E1D22D204273D9332A3FFC02000280E27B3F721ABFF381E1A2242A400\ +01B5FFC00000381E2D0C42A40001B3FFC0000056120801B2FFC00000C02000280EC2DC0422D2FCC0\ +2000290E01ADFFC00000222E1D22D204226E1D281E22D204E7B204291E860000126E012198FF32A0\ +042A21C54C003198FF222E1D1A33380337B202C6D6FF2C02019FFFC000002191FF318CFF1A223A31\ +019CFFC00000218DFF1C031A22C549000C02060300003C528601003C624600003C72918BFF9A1108\ +71C861D851E841F83112C1200DF00010000068100000581000007010000074100000781000007C10\ +0000801000001C4B0040803C004091FDFF12C1E061F7FFC961E941F9310971D9519011C01A662906\ +21F3FFC2D1101A22390231F2FF0C0F1A33590331EAFFF26C1AED045C2247B3028636002D0C016DFF\ +C0000021E5FF41EAFF2A611A4469040622000021E4FF1A222802F0D2C0D7BE01DD0E31E0FF4D0D1A\ +3328033D0101E2FFC00000561209D03D2010212001DFFFC000004D0D2D0C3D01015DFFC0000041D5\ +FFDAFF1A444804D0648041D2FF1A4462640061D1FF106680622600673F1331D0FF10338028030C43\ +853A002642164613000041CAFF222C1A1A444804202FC047328006F6FF222C1A273F3861C2FF222C\ +1A1A6668066732B921BDFF3D0C1022800148FFC0000021BAFF1C031A2201BFFFC000000C02460300\ +5C3206020000005C424600005C5291B7FF9A110871C861D851E841F83112C1200DF0B0100000C010\ +0000D010000012C1E091FEFFC961D951E9410971F931CD039011C0ED02DD0431A1FF9C1422A06247\ +B302062D0021F4FF1A22490286010021F1FF1A223902219CFF2AF12D0F011FFFC00000461C0022D1\ +10011CFFC0000021E9FFFD0C1A222802C7B20621E6FF1A22F8022D0E3D014D0F0195FFC000008C52\ +22A063C6180000218BFF3D01102280F04F200111FFC00000AC7D22D1103D014D0F010DFFC0000021\ +D6FF32D110102280010EFFC0000021D3FF1C031A220185FFC00000FAEEF0CCC056ACF821CDFF317A\ +FF1A223A310105FFC0000021C9FF1C031A22017CFFC000002D0C91C8FF9A110871C861D851E841F8\ +3112C1200DF0000200600000001040020060FFFFFF0012C1E00C02290131FAFF21FAFF026107C961\ +C02000226300C02000C80320CC10564CFF21F5FFC02000380221F4FF20231029010C432D010163FF\ +C0000008712D0CC86112C1200DF00080FE3F8449004012C1D0C9A109B17CFC22C1110C13C51C0026\ +1202463000220111C24110B68202462B0031F5FF3022A02802A002002D011C03851A0066820A2801\ +32210105A6FF0607003C12C60500000010212032A01085180066A20F2221003811482105B3FF2241\ +10861A004C1206FDFF2D011C03C5160066B20E280138114821583185CFFF06F7FF005C1286F5FF00\ +10212032A01085140066A20D2221003811482105E1FF06EFFF0022A06146EDFF45F0FFC6EBFF0000\ +01D2FFC0000006E9FF000C022241100C1322C110C50F00220111060600000022C1100C13C50E0022\ +011132C2FA303074B6230206C8FF08B1C8A112C1300DF0000000000010404F484149007519031027\ +000000110040A8100040BC0F0040583F0040CC2E00401CE20040D83900408000004021F4FF12C1E0\ +C961C80221F2FF097129010C02D951C91101F4FFC0000001F3FFC00000AC2C22A3E801F2FFC00000\ +21EAFFC031412A233D0C01EFFFC000003D0222A00001EDFFC00000C1E4FF2D0C01E8FFC000002D01\ +32A004450400C5E7FFDD022D0C01E3FFC00000666D1F4B2131DCFF4600004B22C0200048023794F5\ +31D9FFC0200039023DF08601000001DCFFC000000871C861D85112C1200DF000000012C1F0026103\ +01EAFEC00000083112C1100DF000643B004012C1D0E98109B1C9A1D991F97129013911E2A0C001FA\ +FFC00000CD02E792F40C0DE2A0C0F2A0DB860D00000001F4FFC00000204220E71240F7921C226102\ +01EFFFC0000052A0DC482157120952A0DD571205460500004D0C3801DA234242001BDD3811379DC5\ +C6000000000C0DC2A0C001E3FFC00000C792F608B12D0DC8A1D891E881F87112C1300DF00000", "\ +entry": 1074792180, "num_params": 1, "params_start": 1074790400, "data": "FE0510\ +401A0610403B0610405A0610407A061040820610408C0610408C061040", "data_start": 10736\ +43520} +""" + +if __name__ == '__main__': + try: + main() + except FatalError as e: + print '\nA fatal error occurred: %s' % e + sys.exit(2) diff --git a/python/flash_release.py b/python/flash_release.py new file mode 100755 index 0000000000..51f6a72e7a --- /dev/null +++ b/python/flash_release.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +from __future__ import print_function +import sys +import time +import requests +import json +import StringIO + +def flash_release(path=None, st_serial=None): + from panda import Panda, PandaDFU, ESPROM, CesantaFlasher + from zipfile import ZipFile + + def status(x): + print("\033[1;32;40m"+x+"\033[00m") + + if st_serial == None: + # look for Panda + panda_list = Panda.list() + if len(panda_list) == 0: + raise Exception("panda not found, make sure it's connected and your user can access it") + elif len(panda_list) > 1: + raise Exception("Please only connect one panda") + st_serial = panda_list[0] + print("Using panda with serial %s" % st_serial) + + if path == None: + print("Fetching latest firmware from github.com/commaai/panda-artifacts") + r = requests.get("https://raw.githubusercontent.com/commaai/panda-artifacts/master/latest.json") + url = json.loads(r.text)['url'] + r = requests.get(url) + print("Fetching firmware from %s" % url) + path = StringIO.StringIO(r.content) + + zf = ZipFile(path) + zf.printdir() + + version = zf.read("version") + status("0. Preparing to flash "+version) + + code_bootstub = zf.read("bootstub.panda.bin") + code_panda = zf.read("panda.bin") + + code_boot_15 = zf.read("boot_v1.5.bin") + code_boot_15 = code_boot_15[0:2] + "\x00\x30" + code_boot_15[4:] + + code_user1 = zf.read("user1.bin") + code_user2 = zf.read("user2.bin") + + # enter DFU mode + status("1. Entering DFU mode") + panda = Panda(st_serial) + panda.enter_bootloader() + time.sleep(1) + + # program bootstub + status("2. Programming bootstub") + dfu = PandaDFU(PandaDFU.st_serial_to_dfu_serial(st_serial)) + dfu.program_bootstub(code_bootstub) + time.sleep(1) + + # flash main code + status("3. Flashing main code") + panda = Panda(st_serial) + panda.flash(code=code_panda) + panda.close() + + # flashing ESP + status("4. Flashing ESP (slow!)") + align = lambda x, sz=0x1000: x+"\xFF"*((sz-len(x)) % sz) + esp = ESPROM(st_serial) + esp.connect() + flasher = CesantaFlasher(esp, 230400) + flasher.flash_write(0x0, align(code_boot_15), True) + flasher.flash_write(0x1000, align(code_user1), True) + flasher.flash_write(0x81000, align(code_user2), True) + flasher.flash_write(0x3FE000, "\xFF"*0x1000) + flasher.boot_fw() + del flasher + del esp + time.sleep(1) + + # check for connection + status("5. Verifying version") + panda = Panda(st_serial) + my_version = panda.get_version() + print("dongle id: %s" % panda.get_serial()[0]) + print(my_version, "should be", version) + assert(str(version) == str(my_version)) + + # done! + status("6. Success!") + +if __name__ == "__main__": + flash_release(*sys.argv[1:]) + diff --git a/python/isotp.py b/python/isotp.py new file mode 100644 index 0000000000..d68aa4d70e --- /dev/null +++ b/python/isotp.py @@ -0,0 +1,135 @@ +DEBUG = False + +def msg(x): + if DEBUG: + print "S:",x.encode("hex") + if len(x) <= 7: + ret = chr(len(x)) + x + else: + assert False + return ret.ljust(8, "\x00") + +kmsgs = [] +def recv(panda, cnt, addr, nbus): + global kmsgs + ret = [] + + while len(ret) < cnt: + kmsgs += panda.can_recv() + nmsgs = [] + for ids, ts, dat, bus in kmsgs: + if ids == addr and bus == nbus and len(ret) < cnt: + ret.append(dat) + else: + # leave around + nmsgs.append((ids, ts, dat, bus)) + kmsgs = nmsgs[-256:] + return map(str, ret) + +def isotp_recv_subaddr(panda, addr, bus, sendaddr, subaddr): + msg = recv(panda, 1, addr, bus)[0] + + # TODO: handle other subaddr also communicating + assert ord(msg[0]) == subaddr + + if ord(msg[1])&0xf0 == 0x10: + # first + tlen = ((ord(msg[1]) & 0xf) << 8) | ord(msg[2]) + dat = msg[3:] + + # 0 block size? + CONTINUE = chr(subaddr) + "\x30" + "\x00"*6 + panda.can_send(sendaddr, CONTINUE, bus) + + idx = 1 + for mm in recv(panda, (tlen-len(dat) + 5)/6, addr, bus): + assert ord(mm[0]) == subaddr + assert ord(mm[1]) == (0x20 | (idx&0xF)) + dat += mm[2:] + idx += 1 + elif ord(msg[1])&0xf0 == 0x00: + # single + tlen = ord(msg[1]) & 0xf + dat = msg[2:] + else: + print msg.encode("hex") + assert False + + return dat[0:tlen] + +# **** import below this line **** + +def isotp_send(panda, x, addr, bus=0, recvaddr=None, subaddr=None): + if recvaddr is None: + recvaddr = addr+8 + + if len(x) <= 7 and subaddr is None: + panda.can_send(addr, msg(x), bus) + elif len(x) <= 6 and subaddr is not None: + panda.can_send(addr, chr(subaddr)+msg(x)[0:7], bus) + else: + if subaddr: + ss = chr(subaddr) + chr(0x10 + (len(x)>>8)) + chr(len(x)&0xFF) + x[0:5] + x = x[5:] + else: + ss = chr(0x10 + (len(x)>>8)) + chr(len(x)&0xFF) + x[0:6] + x = x[6:] + idx = 1 + sends = [] + while len(x) > 0: + if subaddr: + sends.append(((chr(subaddr) + chr(0x20 + (idx&0xF)) + x[0:6]).ljust(8, "\x00"))) + x = x[6:] + else: + sends.append(((chr(0x20 + (idx&0xF)) + x[0:7]).ljust(8, "\x00"))) + x = x[7:] + idx += 1 + + # actually send + panda.can_send(addr, ss, bus) + rr = recv(panda, 1, recvaddr, bus)[0] + if rr.find("\x30\x01") != -1: + for s in sends[:-1]: + panda.can_send(addr, s, 0) + rr = recv(panda, 1, recvaddr, bus)[0] + panda.can_send(addr, sends[-1], 0) + else: + panda.can_send_many([(addr, None, s, 0) for s in sends]) + +def isotp_recv(panda, addr, bus=0, sendaddr=None, subaddr=None): + if sendaddr is None: + sendaddr = addr-8 + + if subaddr is not None: + dat = isotp_recv_subaddr(panda, addr, bus, sendaddr, subaddr) + else: + msg = recv(panda, 1, addr, bus)[0] + + if ord(msg[0])&0xf0 == 0x10: + # first + tlen = ((ord(msg[0]) & 0xf) << 8) | ord(msg[1]) + dat = msg[2:] + + # 0 block size? + CONTINUE = "\x30" + "\x00"*7 + + panda.can_send(sendaddr, CONTINUE, bus) + + idx = 1 + for mm in recv(panda, (tlen-len(dat) + 6)/7, addr, bus): + assert ord(mm[0]) == (0x20 | (idx&0xF)) + dat += mm[1:] + idx += 1 + elif ord(msg[0])&0xf0 == 0x00: + # single + tlen = ord(msg[0]) & 0xf + dat = msg[1:] + else: + assert False + dat = dat[0:tlen] + + if DEBUG: + print "R:",dat.encode("hex") + + return dat + diff --git a/python/serial.py b/python/serial.py new file mode 100644 index 0000000000..1bcfebb32e --- /dev/null +++ b/python/serial.py @@ -0,0 +1,27 @@ +# mimic a python serial port +class PandaSerial(object): + def __init__(self, panda, port, baud): + self.panda = panda + self.port = port + self.panda.set_uart_parity(self.port, 0) + self.panda.set_uart_baud(self.port, baud) + self.buf = "" + + def read(self, l=1): + tt = self.panda.serial_read(self.port) + if len(tt) > 0: + #print "R: ", tt.encode("hex") + self.buf += tt + ret = self.buf[0:l] + self.buf = self.buf[l:] + return ret + + def write(self, dat): + #print "W: ", dat.encode("hex") + #print ' pigeon_send("' + ''.join(map(lambda x: "\\x%02X" % ord(x), dat)) + '");' + return self.panda.serial_write(self.port, dat) + + def close(self): + pass + + diff --git a/python/update.py b/python/update.py new file mode 100755 index 0000000000..ce730e4919 --- /dev/null +++ b/python/update.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +import os +import time + +def ensure_st_up_to_date(): + from panda import Panda, PandaDFU, BASEDIR + + with open(os.path.join(BASEDIR, "VERSION")) as f: + repo_version = f.read() + + repo_version += "-EON" if os.path.isfile('/EON') else "-DEV" + + panda = None + panda_dfu = None + should_flash_recover = False + + while 1: + # break on normal mode Panda + panda_list = Panda.list() + if len(panda_list) > 0: + panda = Panda(panda_list[0]) + break + + # flash on DFU mode Panda + panda_dfu = PandaDFU.list() + if len(panda_dfu) > 0: + panda_dfu = PandaDFU(panda_dfu[0]) + panda_dfu.recover() + + print "waiting for board..." + time.sleep(1) + + if panda.bootstub or not panda.get_version().startswith(repo_version): + panda.flash() + + if panda.bootstub: + panda.recover() + + assert(not panda.bootstub) + version = str(panda.get_version()) + print("%s should be %s" % (version, repo_version)) + assert(version.startswith(repo_version)) + +if __name__ == "__main__": + ensure_st_up_to_date() + diff --git a/release/.gitignore b/release/.gitignore new file mode 100644 index 0000000000..c4c4ffc6aa --- /dev/null +++ b/release/.gitignore @@ -0,0 +1 @@ +*.zip diff --git a/release/make_release.sh b/release/make_release.sh new file mode 100755 index 0000000000..7be994c82c --- /dev/null +++ b/release/make_release.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +if [ ! -d "../../pandaextra" ]; then + echo "No release cert found, cannot build release." + echo "You probably aren't looking to do this anyway." + exit +fi + +export RELEASE=1 + +# make ST + bootstub +pushd . +cd ../board +make clean +make obj/panda.bin +make obj/bootstub.panda.bin +popd + +# make ESP +pushd . +cd ../boardesp +make clean +make user1.bin +make user2.bin +popd + +# make release +mkdir obj +make -f ../common/version.mk +make obj/gitversion.h +RELEASE_NAME=$(python -c "import sys;sys.stdout.write(open('obj/gitversion.h').read().split('\"')[1])") +echo -en $RELEASE_NAME > /tmp/version +rm -rf obj + +# make zip file +pushd . +cd .. +zip -j release/panda-$RELEASE_NAME.zip ~/one/panda/board/obj/bootstub.panda.bin ~/one/panda/board/obj/panda.bin ~/one/panda/boardesp/user?.bin ~/one/panda/boardesp/esp-open-sdk/ESP8266_NONOS_SDK_V1.5.4_16_05_20/bin/boot_v1.5.bin /tmp/version +popd + diff --git a/release/ota_release.sh b/release/ota_release.sh new file mode 100755 index 0000000000..541a196ba7 --- /dev/null +++ b/release/ota_release.sh @@ -0,0 +1,18 @@ +#!/bin/bash +mkdir -p /tmp/panda_firmware +unzip -o $1 -d /tmp/panda_firmware + +curl http://192.168.0.10/ + +echo "flashing user1" +curl http://192.168.0.10/espupdate1 --upload-file /tmp/panda_firmware/user1.bin +echo "flashing user2" +curl http://192.168.0.10/espupdate2 --upload-file /tmp/panda_firmware/user2.bin +echo "waiting 10s for reboot" +sleep 10 +echo "flashing st" +curl http://192.168.0.10/stupdate --upload-file /tmp/panda_firmware/panda.bin +sleep 2 +curl http://192.168.0.10/ +echo "done" + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..ad6b4c76e0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +libusb1 == 1.6.6 +hexdump +pycrypto +tqdm +nose +parameterized +requests diff --git a/run_automated_tests.sh b/run_automated_tests.sh new file mode 100755 index 0000000000..4e07d329ca --- /dev/null +++ b/run_automated_tests.sh @@ -0,0 +1,14 @@ +#!/bin/bash +TEST_FILENAME=${TEST_FILENAME:-nosetests.xml} +if [ ! -f "/EON" ]; then + TESTSUITE_NAME="Panda_Test-EON" +else + TESTSUITE_NAME="Panda_Test-DEV" +fi + +cd boardesp +make flashall +cd .. + + +PYTHONPATH="." python $(which nosetests) -v --with-xunit --xunit-file=./$TEST_FILENAME --xunit-testsuite-name=$TESTSUITE_NAME -s tests/automated/$1*.py diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000..3480374bc2 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000000..2acd9b9e16 --- /dev/null +++ b/setup.py @@ -0,0 +1,65 @@ +#-*- coding: utf-8 -*- + +""" + Panda CAN Controller Dongle + ~~~~~ + + Setup + ````` + + $ pip install . # or python setup.py install +""" + +import codecs +import os +import re +from setuptools import setup, Extension + +here = os.path.abspath(os.path.dirname(__file__)) + +def read(*parts): + """Taken from pypa pip setup.py: + intentionally *not* adding an encoding option to open, See: + https://github.com/pypa/virtualenv/issues/201#issuecomment-3145690 + """ + return codecs.open(os.path.join(here, *parts), 'r').read() + + +def find_version(*file_paths): + version_file = read(*file_paths) + version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", + version_file, re.M) + if version_match: + return version_match.group(1) + raise RuntimeError("Unable to find version string.") + +setup( + name='pandacan', + version=find_version("python", "__init__.py"), + url='https://github.com/commaai/panda', + author='comma.ai', + author_email='', + packages=[ + 'panda', + ], + package_dir = {'panda': 'python'}, + platforms='any', + license='MIT', + install_requires=[ + 'libusb1 == 1.6.6', + 'hexdump >= 3.3', + 'pycrypto >= 2.6.1', + 'tqdm >= 4.14.0', + 'requests' + ], + ext_modules = [], + description="Code powering the comma.ai panda", + long_description='See https://github.com/commaai/panda', + classifiers=[ + 'Development Status :: 2 - Pre-Alpha', + "Natural Language :: English", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + "Topic :: System :: Hardware", + ], +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/all_wifi_test.py b/tests/all_wifi_test.py new file mode 100755 index 0000000000..4e9d3a5318 --- /dev/null +++ b/tests/all_wifi_test.py @@ -0,0 +1,29 @@ +#!/usr/bin/python +import requests +import json +from automated.helpers import _connect_wifi +from panda import Panda +from nose.tools import assert_equal + +if __name__ == "__main__": + print("Fetching latest firmware from github.com/commaai/panda-artifacts") + r = requests.get("https://raw.githubusercontent.com/commaai/panda-artifacts/master/latest.json") + latest_version = json.loads(r.text)['version'] + + for p in Panda.list(): + dongle_id, pw = Panda(p).get_serial() + print dongle_id, pw + assert(dongle_id.isalnum()) + _connect_wifi(dongle_id, pw) + + r = requests.get("http://192.168.0.10/") + print r.text + wifi_dongle_id = r.text.split("ssid: panda-")[1].split("
")[0] + st_version = r.text.split("st version:")[1].strip().split("
")[0] + esp_version = r.text.split("esp version:")[1].strip().split("
")[0] + + assert_equal(str(dongle_id), wifi_dongle_id) + assert_equal(latest_version, st_version) + assert_equal(latest_version, esp_version) + + diff --git a/tests/automated/0_builds.py b/tests/automated/0_builds.py new file mode 100644 index 0000000000..df4186cce4 --- /dev/null +++ b/tests/automated/0_builds.py @@ -0,0 +1,9 @@ +import os +from panda import build_st + +def test_build_panda(): + build_st("obj/panda.bin") + +def test_build_bootstub_panda(): + build_st("obj/bootstub.panda.bin") + diff --git a/tests/automated/1_program.py b/tests/automated/1_program.py new file mode 100644 index 0000000000..1e0beb8ae5 --- /dev/null +++ b/tests/automated/1_program.py @@ -0,0 +1,15 @@ +import os +from panda import Panda +from helpers import panda_color_to_serial, test_white_and_grey + +@test_white_and_grey +@panda_color_to_serial +def test_recover(serial=None): + p = Panda(serial=serial) + assert p.recover(timeout=30) + +@test_white_and_grey +@panda_color_to_serial +def test_flash(serial=None): + p = Panda(serial=serial) + p.flash() diff --git a/tests/automated/2_usb_to_can.py b/tests/automated/2_usb_to_can.py new file mode 100644 index 0000000000..7860d3290f --- /dev/null +++ b/tests/automated/2_usb_to_can.py @@ -0,0 +1,199 @@ +from __future__ import print_function +import os +import sys +import time +from panda import Panda +from nose.tools import assert_equal, assert_less, assert_greater +from helpers import time_many_sends, connect_wo_esp, test_white_and_grey, panda_color_to_serial + +SPEED_NORMAL = 500 +SPEED_GMLAN = 33.3 + +@test_white_and_grey +@panda_color_to_serial +def test_can_loopback(serial=None): + p = connect_wo_esp(serial) + + # enable output mode + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # enable CAN loopback mode + p.set_can_loopback(True) + + if p.legacy: + busses = [0,1] + else: + busses = [0,1,2] + + for bus in busses: + # set bus 0 speed to 250 + p.set_can_speed_kbps(bus, 250) + + # send a message on bus 0 + p.can_send(0x1aa, "message", bus) + + # confirm receive both on loopback and send receipt + time.sleep(0.05) + r = p.can_recv() + sr = filter(lambda x: x[3] == 0x80 | bus, r) + lb = filter(lambda x: x[3] == bus, r) + assert len(sr) == 1 + assert len(lb) == 1 + + # confirm data is correct + assert 0x1aa == sr[0][0] == lb[0][0] + assert "message" == sr[0][2] == lb[0][2] + +@test_white_and_grey +@panda_color_to_serial +def test_safety_nooutput(serial=None): + p = connect_wo_esp(serial) + + # enable output mode + p.set_safety_mode(Panda.SAFETY_NOOUTPUT) + + # enable CAN loopback mode + p.set_can_loopback(True) + + # send a message on bus 0 + p.can_send(0x1aa, "message", 0) + + # confirm receive nothing + time.sleep(0.05) + r = p.can_recv() + assert len(r) == 0 + +@test_white_and_grey +@panda_color_to_serial +def test_reliability(serial=None): + p = connect_wo_esp(serial) + + LOOP_COUNT = 100 + MSG_COUNT = 100 + + # enable output mode + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p.set_can_loopback(True) + p.set_can_speed_kbps(0, 1000) + + addrs = range(100, 100+MSG_COUNT) + ts = [(j, 0, "\xaa"*8, 0) for j in addrs] + + # 100 loops + for i in range(LOOP_COUNT): + st = time.time() + + p.can_send_many(ts) + + r = [] + while len(r) < 200 and (time.time() - st) < 0.5: + r.extend(p.can_recv()) + + sent_echo = filter(lambda x: x[3] == 0x80, r) + loopback_resp = filter(lambda x: x[3] == 0, r) + + assert_equal(sorted(map(lambda x: x[0], loopback_resp)), addrs) + assert_equal(sorted(map(lambda x: x[0], sent_echo)), addrs) + assert_equal(len(r), 200) + + # take sub 20ms + et = (time.time()-st)*1000.0 + assert_less(et, 20) + + sys.stdout.write("P") + sys.stdout.flush() + +@test_white_and_grey +@panda_color_to_serial +def test_throughput(serial=None): + p = connect_wo_esp(serial) + + # enable output mode + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # enable CAN loopback mode + p.set_can_loopback(True) + + for speed in [100,250,500,750,1000]: + # set bus 0 speed to speed + p.set_can_speed_kbps(0, speed) + time.sleep(0.05) + + comp_kbps = time_many_sends(p, 0) + + # bit count from https://en.wikipedia.org/wiki/CAN_bus + saturation_pct = (comp_kbps/speed) * 100.0 + assert_greater(saturation_pct, 80) + assert_less(saturation_pct, 100) + + print("loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct)) + +@test_white_and_grey +@panda_color_to_serial +def test_gmlan(serial=None): + p = connect_wo_esp(serial) + + if p.legacy: + return + + # enable output mode + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # enable CAN loopback mode + p.set_can_loopback(True) + + p.set_can_speed_kbps(1, SPEED_NORMAL) + p.set_can_speed_kbps(2, SPEED_NORMAL) + p.set_can_speed_kbps(3, SPEED_GMLAN) + + # set gmlan on CAN2 + for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3, Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: + p.set_gmlan(bus) + comp_kbps_gmlan = time_many_sends(p, 3) + assert_greater(comp_kbps_gmlan, 0.8 * SPEED_GMLAN) + assert_less(comp_kbps_gmlan, 1.0 * SPEED_GMLAN) + + p.set_gmlan(None) + comp_kbps_normal = time_many_sends(p, bus) + assert_greater(comp_kbps_normal, 0.8 * SPEED_NORMAL) + assert_less(comp_kbps_normal, 1.0 * SPEED_NORMAL) + + print("%d: %.2f kbps vs %.2f kbps" % (bus, comp_kbps_gmlan, comp_kbps_normal)) + +@test_white_and_grey +@panda_color_to_serial +def test_gmlan_bad_toggle(serial=None): + p = connect_wo_esp(serial) + + if p.legacy: + return + + # enable output mode + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # enable CAN loopback mode + p.set_can_loopback(True) + + # GMLAN_CAN2 + for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: + p.set_gmlan(bus) + comp_kbps_gmlan = time_many_sends(p, 3) + assert_greater(comp_kbps_gmlan, 0.6 * SPEED_GMLAN) + assert_less(comp_kbps_gmlan, 1.0 * SPEED_GMLAN) + + # normal + for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: + p.set_gmlan(None) + comp_kbps_normal = time_many_sends(p, bus) + assert_greater(comp_kbps_normal, 0.6 * SPEED_NORMAL) + assert_less(comp_kbps_normal, 1.0 * SPEED_NORMAL) + + +# this will fail if you have hardware serial connected +@test_white_and_grey +@panda_color_to_serial +def test_serial_debug(serial=None): + p = connect_wo_esp(serial) + junk = p.serial_read(Panda.SERIAL_DEBUG) + p.call_control_api(0xc0) + assert(p.serial_read(Panda.SERIAL_DEBUG).startswith("can ")) diff --git a/tests/automated/3_wifi.py b/tests/automated/3_wifi.py new file mode 100644 index 0000000000..1251663ba5 --- /dev/null +++ b/tests/automated/3_wifi.py @@ -0,0 +1,60 @@ +from __future__ import print_function +import os +import time +from panda import Panda +from helpers import connect_wifi, test_white, test_white_and_grey, panda_color_to_serial +import requests + +@test_white_and_grey +@panda_color_to_serial +def test_get_serial(serial=None): + p = Panda(serial) + print(p.get_serial()) + +@test_white_and_grey +@panda_color_to_serial +def test_get_serial_in_flash_mode(serial=None): + p = Panda(serial) + p.reset(enter_bootstub=True) + assert(p.bootstub) + print(p.get_serial()) + p.reset() + +@test_white +@panda_color_to_serial +def test_connect_wifi(serial=None): + connect_wifi(serial) + +@test_white +@panda_color_to_serial +def test_flash_wifi(serial=None): + connect_wifi(serial) + assert Panda.flash_ota_wifi(release=False), "OTA Wifi Flash Failed" + connect_wifi(serial) + +@test_white +@panda_color_to_serial +def test_wifi_flash_st(serial=None): + connect_wifi(serial) + assert Panda.flash_ota_st(), "OTA ST Flash Failed" + connected = False + st = time.time() + while not connected and (time.time() - st) < 20: + try: + p = Panda(serial=serial) + p.get_serial() + connected = True + except: + time.sleep(1) + + if not connected: + assert False, "Panda failed to connect on USB after flashing" + +@test_white +@panda_color_to_serial +def test_webpage_fetch(serial=None): + connect_wifi(serial) + r = requests.get("http://192.168.0.10/") + print(r.text) + + assert "This is your comma.ai panda" in r.text diff --git a/tests/automated/4_wifi_functionality.py b/tests/automated/4_wifi_functionality.py new file mode 100644 index 0000000000..0cf42d1f3f --- /dev/null +++ b/tests/automated/4_wifi_functionality.py @@ -0,0 +1,59 @@ +from __future__ import print_function +import time +from panda import Panda +from helpers import time_many_sends, connect_wifi, test_white, panda_color_to_serial +from nose.tools import timed, assert_equal, assert_less, assert_greater + +@test_white +@panda_color_to_serial +def test_get_serial_wifi(serial=None): + connect_wifi(serial) + + p = Panda("WIFI") + print(p.get_serial()) + +@test_white +@panda_color_to_serial +def test_throughput(serial=None): + connect_wifi(serial) + p = Panda(serial) + + # enable output mode + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # enable CAN loopback mode + p.set_can_loopback(True) + + p = Panda("WIFI") + + for speed in [100,250,500,750,1000]: + # set bus 0 speed to speed + p.set_can_speed_kbps(0, speed) + time.sleep(0.1) + + comp_kbps = time_many_sends(p, 0) + + # bit count from https://en.wikipedia.org/wiki/CAN_bus + saturation_pct = (comp_kbps/speed) * 100.0 + #assert_greater(saturation_pct, 80) + #assert_less(saturation_pct, 100) + + print("WIFI loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct)) + +@test_white +@panda_color_to_serial +def test_recv_only(serial=None): + connect_wifi(serial) + p = Panda(serial) + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p.set_can_loopback(True) + pwifi = Panda("WIFI") + + # TODO: msg_count=1000 drops packets, is this fixable? + for msg_count in [10,100,200]: + speed = 500 + p.set_can_speed_kbps(0, speed) + comp_kbps = time_many_sends(p, 0, pwifi, msg_count) + saturation_pct = (comp_kbps/speed) * 100.0 + + print("HT WIFI loopback %d messages at speed %d, comp speed is %.2f, percent %.2f" % (msg_count, speed, comp_kbps, saturation_pct)) diff --git a/tests/automated/5_wifi_udp.py b/tests/automated/5_wifi_udp.py new file mode 100644 index 0000000000..873f78bdb3 --- /dev/null +++ b/tests/automated/5_wifi_udp.py @@ -0,0 +1,66 @@ +from __future__ import print_function +import sys +import time +from helpers import time_many_sends, connect_wifi, test_white, panda_color_to_serial +from panda import Panda, PandaWifiStreaming +from nose.tools import timed, assert_equal, assert_less, assert_greater + +@test_white +@panda_color_to_serial +def test_udp_doesnt_drop(serial=None): + connect_wifi(serial) + + p = Panda(serial) + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p.set_can_loopback(True) + + pwifi = PandaWifiStreaming() + while 1: + if len(pwifi.can_recv()) == 0: + break + + for msg_count in [1, 100]: + saturation_pcts = [] + for i in range({1: 0x80, 100: 0x20}[msg_count]): + pwifi.kick() + + speed = 500 + p.set_can_speed_kbps(0, speed) + comp_kbps = time_many_sends(p, 0, pwifi, msg_count=msg_count, msg_id=0x100+i) + saturation_pct = (comp_kbps/speed) * 100.0 + + if msg_count == 1: + sys.stdout.write(".") + sys.stdout.flush() + else: + print("UDP WIFI loopback %d messages at speed %d, comp speed is %.2f, percent %.2f" % (msg_count, speed, comp_kbps, saturation_pct)) + assert_greater(saturation_pct, 15) #sometimes the wifi can be slow... + assert_less(saturation_pct, 100) + saturation_pcts.append(saturation_pct) + if len(saturation_pcts) > 0: + assert_greater(sum(saturation_pcts)/len(saturation_pcts), 60) + + time.sleep(5) + usb_ok_cnt = 0 + REQ_USB_OK_CNT = 500 + st = time.time() + msg_id = 0x1bb + bus = 0 + last_missing_msg = 0 + while usb_ok_cnt < REQ_USB_OK_CNT and (time.time() - st) < 40: + p.can_send(msg_id, "message", bus) + time.sleep(0.01) + r = [1] + missing = True + while len(r) > 0: + r = p.can_recv() + r = filter(lambda x: x[3] == bus and x[0] == msg_id, r) + if len(r) > 0: + missing = False + usb_ok_cnt += len(r) + if missing: + last_missing_msg = time.time() + et = time.time() - st + last_missing_msg = last_missing_msg - st + print("waited {} for panda to recv can on usb, {} msgs, last missing at {}".format(et, usb_ok_cnt, last_missing_msg)) + assert usb_ok_cnt >= REQ_USB_OK_CNT, "Unable to recv can on USB after UDP" diff --git a/tests/automated/6_two_panda.py b/tests/automated/6_two_panda.py new file mode 100644 index 0000000000..3c29a0e7a1 --- /dev/null +++ b/tests/automated/6_two_panda.py @@ -0,0 +1,121 @@ +from __future__ import print_function +import time +from panda import Panda +from nose.tools import assert_equal, assert_less, assert_greater +from helpers import time_many_sends, test_two_panda, panda_color_to_serial + +@test_two_panda +@panda_color_to_serial +def test_send_recv(serial_sender=None, serial_reciever=None): + p_send = Panda(serial_sender) + p_recv = Panda(serial_reciever) + + p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p_send.set_can_loopback(False) + + p_recv.set_can_loopback(False) + + assert not p_send.legacy + assert not p_recv.legacy + + p_send.can_send_many([(0x1ba, 0, "message", 0)]*2) + time.sleep(0.05) + p_recv.can_recv() + p_send.can_recv() + + busses = [0,1,2] + + for bus in busses: + for speed in [100, 250, 500, 750, 1000]: + p_send.set_can_speed_kbps(bus, speed) + p_recv.set_can_speed_kbps(bus, speed) + time.sleep(0.05) + + comp_kbps = time_many_sends(p_send, bus, p_recv, two_pandas=True) + + saturation_pct = (comp_kbps/speed) * 100.0 + assert_greater(saturation_pct, 80) + assert_less(saturation_pct, 100) + + print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct)) + +@test_two_panda +@panda_color_to_serial +def test_latency(serial_sender=None, serial_reciever=None): + p_send = Panda(serial_sender) + p_recv = Panda(serial_reciever) + + p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p_send.set_can_loopback(False) + + p_recv.set_can_loopback(False) + + assert not p_send.legacy + assert not p_recv.legacy + + p_send.set_can_speed_kbps(0, 100) + p_recv.set_can_speed_kbps(0, 100) + time.sleep(0.05) + + p_send.can_send_many([(0x1ba, 0, "testmsg", 0)]*10) + time.sleep(0.05) + p_recv.can_recv() + p_send.can_recv() + + busses = [0,1,2] + + for bus in busses: + for speed in [100, 250, 500, 750, 1000]: + p_send.set_can_speed_kbps(bus, speed) + p_recv.set_can_speed_kbps(bus, speed) + time.sleep(0.1) + #clear can buffers + r = [1] + while len(r) > 0: + r = p_send.can_recv() + r = [1] + while len(r) > 0: + r = p_recv.can_recv() + time.sleep(0.05) + + latencies = [] + comp_kbps_list = [] + saturation_pcts = [] + + num_messages = 100 + + for i in range(num_messages): + st = time.time() + p_send.can_send(0x1ab, "message", bus) + r = [] + while len(r) < 1 and (time.time() - st) < 5: + r = p_recv.can_recv() + et = time.time() + r_echo = [] + while len(r_echo) < 1 and (time.time() - st) < 10: + r_echo = p_send.can_recv() + + if len(r) == 0 or len(r_echo) == 0: + print("r: {}, r_echo: {}".format(r, r_echo)) + + assert_equal(len(r),1) + assert_equal(len(r_echo),1) + + et = (et - st)*1000.0 + comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7) / et + latency = et - ((1+11+1+1+1+4+8*8+15+1+1+1+7) / speed) + + assert_less(latency, 5.0) + + saturation_pct = (comp_kbps/speed) * 100.0 + latencies.append(latency) + comp_kbps_list.append(comp_kbps) + saturation_pcts.append(saturation_pct) + + average_latency = sum(latencies)/num_messages + assert_less(average_latency, 1.0) + average_comp_kbps = sum(comp_kbps_list)/num_messages + average_saturation_pct = sum(saturation_pcts)/num_messages + + print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\ + .format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct)) diff --git a/tests/automated/__init__.py b/tests/automated/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/automated/helpers.py b/tests/automated/helpers.py new file mode 100644 index 0000000000..9e92f56bfe --- /dev/null +++ b/tests/automated/helpers.py @@ -0,0 +1,192 @@ +import os +import sys +import time +import random +import subprocess +import requests +from functools import wraps +from panda import Panda +from nose.tools import timed, assert_equal, assert_less, assert_greater +from parameterized import parameterized, param + +test_white_and_grey = parameterized([param(panda_color="White"), + param(panda_color="Grey")]) +test_white = parameterized([param(panda_color="White")]) +test_grey = parameterized([param(panda_color="Grey")]) +test_two_panda = parameterized([param(panda_color=["Grey", "White"]), + param(panda_color=["White", "Grey"])]) + +_serials = {} +def get_panda_serial(is_grey=None): + global _serials + if is_grey not in _serials: + for serial in Panda.list(): + p = Panda(serial=serial) + if is_grey is None or p.is_grey() == is_grey: + _serials[is_grey] = serial + return serial + raise IOError("Panda not found. is_grey: {}".format(is_grey)) + else: + return _serials[is_grey] + +def connect_wo_esp(serial=None): + # connect to the panda + p = Panda(serial=serial) + + # power down the ESP + p.set_esp_power(False) + + # clear old junk + while len(p.can_recv()) > 0: + pass + + return p + +def connect_wifi(serial=None): + p = Panda(serial=serial) + p.set_esp_power(True) + dongle_id, pw = p.get_serial() + assert(dongle_id.isalnum()) + _connect_wifi(dongle_id, pw) + +FNULL = open(os.devnull, 'w') +def _connect_wifi(dongle_id, pw, insecure_okay=False): + ssid = str("panda-" + dongle_id) + + r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) + if not r: + #Can already ping, try connecting on wifi + try: + p = Panda("WIFI") + p.get_serial() + print("Already connected") + return + except: + pass + + print("WIFI: connecting to %s" % ssid) + + while 1: + if sys.platform == "darwin": + os.system("networksetup -setairportnetwork en0 %s %s" % (ssid, pw)) + else: + wlan_interface = subprocess.check_output(["sh", "-c", "iw dev | awk '/Interface/ {print $2}'"]).strip() + cnt = 0 + MAX_TRIES = 10 + while cnt < MAX_TRIES: + print("WIFI: scanning %d" % cnt) + os.system("iwlist %s scanning > /dev/null" % wlan_interface) + os.system("nmcli device wifi rescan") + wifi_scan = filter(lambda x: ssid in x, subprocess.check_output(["nmcli","dev", "wifi", "list"]).split("\n")) + if len(wifi_scan) != 0: + break + time.sleep(0.1) + # MAX_TRIES tries, ~10 seconds max + cnt += 1 + assert cnt < MAX_TRIES + if "-pair" in wifi_scan[0]: + os.system("nmcli d wifi connect %s-pair" % (ssid)) + connect_cnt = 0 + MAX_TRIES = 20 + while connect_cnt < MAX_TRIES: + connect_cnt += 1 + r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) + if r: + print("Waiting for panda to ping...") + time.sleep(0.1) + else: + break + if insecure_okay: + break + # fetch webpage + print("connecting to insecure network to secure") + try: + r = requests.get("http://192.168.0.10/") + except requests.ConnectionError: + r = requests.get("http://192.168.0.10/") + assert r.status_code==200 + + print("securing") + try: + r = requests.get("http://192.168.0.10/secure", timeout=0.01) + except requests.exceptions.Timeout: + print("timeout http request to secure") + pass + else: + ret = os.system("nmcli d wifi connect %s password %s" % (ssid, pw)) + if os.WEXITSTATUS(ret) == 0: + #check ping too + ping_ok = False + connect_cnt = 0 + MAX_TRIES = 10 + while connect_cnt < MAX_TRIES: + connect_cnt += 1 + r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) + if r: + print("Waiting for panda to ping...") + time.sleep(0.1) + else: + ping_ok = True + break + if ping_ok: + break + + # TODO: confirm that it's connected to the right panda + +def time_many_sends(p, bus, precv=None, msg_count=100, msg_id=None, two_pandas=False): + if precv == None: + precv = p + if msg_id == None: + msg_id = random.randint(0x100, 0x200) + if p == precv and two_pandas: + raise ValueError("Cannot have two pandas that are the same panda") + + st = time.time() + p.can_send_many([(msg_id, 0, "\xaa"*8, bus)]*msg_count) + r = [] + r_echo = [] + r_len_expected = msg_count if two_pandas else msg_count*2 + r_echo_len_exected = msg_count if two_pandas else 0 + + while len(r) < r_len_expected and (time.time() - st) < 5: + r.extend(precv.can_recv()) + et = time.time() + if two_pandas: + while len(r_echo) < r_echo_len_exected and (time.time() - st) < 10: + r_echo.extend(p.can_recv()) + + sent_echo = filter(lambda x: x[3] == 0x80 | bus and x[0] == msg_id, r) + sent_echo.extend(filter(lambda x: x[3] == 0x80 | bus and x[0] == msg_id, r_echo)) + resp = filter(lambda x: x[3] == bus and x[0] == msg_id, r) + + leftovers = filter(lambda x: (x[3] != 0x80 | bus and x[3] != bus) or x[0] != msg_id, r) + assert_equal(len(leftovers), 0) + + assert_equal(len(resp), msg_count) + assert_equal(len(sent_echo), msg_count) + + et = (et-st)*1000.0 + comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7)*msg_count / et + + return comp_kbps + + +def panda_color_to_serial(fn): + @wraps(fn) + def wrapper(panda_color=None, **kwargs): + pandas_is_grey = [] + if panda_color is not None: + if not isinstance(panda_color, list): + panda_color = [panda_color] + panda_color = [s.lower() for s in panda_color] + for p in panda_color: + if p is None: + pandas_is_grey.append(None) + elif p in ["grey", "gray"]: + pandas_is_grey.append(True) + elif p in ["white"]: + pandas_is_grey.append(False) + else: + raise ValueError("Invalid Panda Color {}".format(p)) + return fn(*[get_panda_serial(is_grey) for is_grey in pandas_is_grey], **kwargs) + return wrapper diff --git a/tests/build/Dockerfile b/tests/build/Dockerfile new file mode 100644 index 0000000000..276a25ed0b --- /dev/null +++ b/tests/build/Dockerfile @@ -0,0 +1,17 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y gcc-arm-none-eabi libnewlib-arm-none-eabi python python-pip gcc g++ git autoconf gperf bison flex automake texinfo wget help2man gawk libtool libtool-bin ncurses-dev unzip unrar-free libexpat-dev sed bzip2 + +RUN pip install pycrypto==2.6.1 + +# Build esp toolchain +RUN mkdir -p /panda/boardesp +WORKDIR /panda/boardesp +RUN git clone --recursive https://github.com/pfalcon/esp-open-sdk.git +WORKDIR /panda/boardesp/esp-open-sdk +RUN git checkout 03f5e898a059451ec5f3de30e7feff30455f7ce +RUN CT_ALLOW_BUILD_AS_ROOT_SURE=1 make STANDALONE=y + +COPY . /panda + +WORKDIR /panda diff --git a/tests/build_strict/Dockerfile b/tests/build_strict/Dockerfile new file mode 100644 index 0000000000..b1c75c0258 --- /dev/null +++ b/tests/build_strict/Dockerfile @@ -0,0 +1,9 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y gcc-arm-none-eabi libnewlib-arm-none-eabi python python-pip gcc g++ + +RUN pip install pycrypto==2.6.1 + +COPY . /panda + +WORKDIR /panda diff --git a/tests/build_strict/test_build_strict.sh b/tests/build_strict/test_build_strict.sh new file mode 100755 index 0000000000..ee57ba8ad8 --- /dev/null +++ b/tests/build_strict/test_build_strict.sh @@ -0,0 +1,15 @@ +#!/bin/bash -e + +cd ../../board/ + +make -f Makefile.strict clean +make -f Makefile.strict bin 2> compiler_output.txt + + +if [[ -s "compiler_output.txt" ]] +then + echo "Found alerts from the compiler:" + cat compiler_output.txt + exit 1 +fi + diff --git a/tests/can_printer.py b/tests/can_printer.py new file mode 100755 index 0000000000..e863889c16 --- /dev/null +++ b/tests/can_printer.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +from __future__ import print_function +import os +import sys +import time +from collections import defaultdict +import binascii + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda + +# fake +def sec_since_boot(): + return time.time() + +def can_printer(): + p = Panda() + p.set_safety_mode(0x1337) + + start = sec_since_boot() + lp = sec_since_boot() + msgs = defaultdict(list) + canbus = int(os.getenv("CAN", 0)) + while True: + can_recv = p.can_recv() + for address, _, dat, src in can_recv: + if src == canbus: + msgs[address].append(dat) + + if sec_since_boot() - lp > 0.1: + dd = chr(27) + "[2J" + dd += "%5.2f\n" % (sec_since_boot() - start) + for k,v in sorted(zip(msgs.keys(), map(lambda x: binascii.hexlify(x[-1]), msgs.values()))): + dd += "%s(%6d) %s\n" % ("%04X(%4d)" % (k,k),len(msgs[k]), v) + print(dd) + lp = sec_since_boot() + +if __name__ == "__main__": + can_printer() diff --git a/tests/debug_console.py b/tests/debug_console.py new file mode 100755 index 0000000000..0238ed7897 --- /dev/null +++ b/tests/debug_console.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +from __future__ import print_function +import os +import sys +import time +import select + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda + +setcolor = ["\033[1;32;40m", "\033[1;31;40m"] +unsetcolor = "\033[00m" + +if __name__ == "__main__": + port_number = int(os.getenv("PORT", 0)) + claim = os.getenv("CLAIM") is not None + + serials = Panda.list() + if os.getenv("SERIAL"): + serials = filter(lambda x: x==os.getenv("SERIAL"), serials) + + pandas = list(map(lambda x: Panda(x, claim=claim), serials)) + + if not len(pandas): + sys.exit("no pandas found") + + if os.getenv("BAUD") is not None: + for panda in pandas: + panda.set_uart_baud(port_number, int(os.getenv("BAUD"))) + + while True: + for i, panda in enumerate(pandas): + while True: + ret = panda.serial_read(port_number) + if len(ret) > 0: + sys.stdout.write(setcolor[i] + str(ret) + unsetcolor) + sys.stdout.flush() + else: + break + if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []): + ln = sys.stdin.readline() + if claim: + panda.serial_write(port_number, ln) + time.sleep(0.01) diff --git a/tests/disable_esp.py b/tests/disable_esp.py new file mode 100755 index 0000000000..abebd9c17b --- /dev/null +++ b/tests/disable_esp.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python +from panda import Panda +Panda().set_esp_power(False) + diff --git a/tests/elm_car_simulator.py b/tests/elm_car_simulator.py new file mode 100755 index 0000000000..bcee821cd0 --- /dev/null +++ b/tests/elm_car_simulator.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python +"""Used to Reverse/Test ELM protocol auto detect and OBD message response without a car.""" +from __future__ import print_function +import sys +import os +import struct +import binascii +import time +import threading +from collections import deque + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda + +def lin_checksum(dat): + return sum(dat) % 0x100 + +class ELMCarSimulator(): + def __init__(self, sn, silent=False, can_kbaud=500, + can=True, can11b=True, can29b=True, + lin=True): + self.__p = Panda(sn if sn else Panda.list()[0]) + self.__on = True + self.__stop = False + self.__silent = silent + + self.__lin_timer = None + self.__lin_active = False + self.__lin_enable = lin + self.__lin_monitor_thread = threading.Thread(target=self.__lin_monitor) + + self.__can_multipart_data = None + self.__can_kbaud = can_kbaud + self.__can_extra_noise_msgs = deque() + self.__can_enable = can + self.__can11b = can11b + self.__can29b = can29b + self.__can_monitor_thread = threading.Thread(target=self.__can_monitor) + + @property + def panda(self): + return self.__p + + def stop(self): + if self.__lin_timer: + self.__lin_timer.cancel() + self.__lin_timeout_handler() + + self.__stop = True + + def join(self): + if self.__lin_monitor_thread.is_alive(): + self.__lin_monitor_thread.join() + if self.__can_monitor_thread.is_alive(): + self.__can_monitor_thread.join() + if self.__p: + print("closing handle") + self.__p.close() + + def set_enable(self, on): + self.__on = on + + def start(self): + self.panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + if self.__lin_enable: + self.__lin_monitor_thread.start() + if self.__can_enable: + self.__can_monitor_thread.start() + + ######################### + # LIN related functions # + ######################### + + def __lin_monitor(self): + print("STARTING LIN THREAD") + self.panda.set_uart_baud(2, 10400) + self.panda.kline_drain() # Toss whatever was already there + + lin_buff = bytearray() + + while not self.__stop: + lin_msg = self.panda.serial_read(2) + if not lin_msg: + continue + + lin_buff += lin_msg + #print(" ** Buff", lin_buff) + if lin_buff.endswith(b'\x00\xc1\x33\xf1\x81\x66'): # Leading 0 is wakeup + lin_buff = bytearray() + self.__lin_active = True + print("GOT LIN (KWP FAST) WAKEUP SIGNAL") + self._lin_send(0x10, b'\xC1\x8F\xE9') + self.__reset_lin_timeout() + continue + if self.__lin_active: + msglen = lin_buff[0] & 0x7 + if lin_buff[0] & 0xF8 not in (0x80, 0xC0): + print("Invalid bytes at start of message") + print(" BUFF", lin_buff) + continue + if len(lin_buff) < msglen + 4: continue + if lin_checksum(lin_buff[:-1]) != lin_buff[-1]: continue + self.__lin_process_msg(lin_buff[0] & 0xF8, #Priority + lin_buff[1], lin_buff[2], lin_buff[3:-1]) + lin_buff = bytearray() + + def _lin_send(self, to_addr, msg): + if not self.__silent: + print(" LIN Reply (%x)" % to_addr, binascii.hexlify(msg)) + + PHYS_ADDR = 0x80 + FUNC_ADDR = 0xC0 + RECV = 0xF1 + SEND = 0x33 # Car OBD Functional Address + headers = struct.pack("BBB", PHYS_ADDR | len(msg), RECV, to_addr) + if not self.__silent: + print(" Sending LIN", binascii.hexlify(headers+msg), + hex(sum(bytearray(headers+msg))%0x100)) + self.panda.kline_send(headers + msg) + + def __reset_lin_timeout(self): + if self.__lin_timer: + self.__lin_timer.cancel() + self.__lin_timer = threading.Timer(5, self.__lin_timeout_handler) + self.__lin_timer.start() + + def __lin_timeout_handler(self): + print("LIN TIMEOUT") + self.__lin_timer = None + self.__lin_active = False + + @property + def lin_active(self): + return self.__lin_active + + def __lin_process_msg(self, priority, toaddr, fromaddr, data): + self.__reset_lin_timeout() + + if not self.__silent and data != b'\x3E': + print("LIN MSG", "Addr:", hex(toaddr), "obdLen:", len(data), + binascii.hexlify(data)) + + outmsg = None + #if data == b'\x3E': + # print("KEEP ALIVE") + #el + if len(data) > 1: + outmsg = self._process_obd(data[0], data[1]) + + if outmsg: + obd_header = struct.pack("BB", 0x40 | data[0], data[1]) + if len(outmsg) <= 5: + self._lin_send(0x10, obd_header + outmsg) + else: + first_msg_len = MIN(4, len(outmsg)%4) or 4 + self._lin_send(0x10, obd_header + b'\x01' + + b'\x00'*(4-first_msg_len) + + outmsg[:first_msg_len]) + + for num, i in enumerate(range(first_msg_len, len(outmsg), 4)): + self._lin_send(0x10, obd_header + + struct.pack('B', (num+2)%0x100) + outmsg[i:i+4]) + + ######################### + # CAN related functions # + ######################### + + def __can_monitor(self): + print("STARTING CAN THREAD") + self.panda.set_can_speed_kbps(0, self.__can_kbaud) + self.panda.can_recv() # Toss whatever was already there + + while not self.__stop: + for address, ts, data, src in self.panda.can_recv(): + if self.__on and src is 0 and len(data) == 8 and data[0] >= 2: + if not self.__silent: + print("Processing CAN message", src, hex(address), binascii.hexlify(data)) + self.__can_process_msg(data[1], data[2], address, ts, data, src) + elif not self.__silent: + print("Rejecting CAN message", src, hex(address), binascii.hexlify(data)) + + def can_mode_11b(self): + self.__can11b = True + self.__can29b = False + + def can_mode_29b(self): + self.__can11b = False + self.__can29b = True + + def can_mode_11b_29b(self): + self.__can11b = True + self.__can29b = True + + def change_can_baud(self, kbaud): + self.__can_kbaud = kbaud + self.panda.set_can_speed_kbps(0, self.__can_kbaud) + + def can_add_extra_noise(self, noise_msg, addr=None): + self.__can_extra_noise_msgs.append((addr, noise_msg)) + + def _can_send(self, addr, msg): + if not self.__silent: + print(" CAN Reply (%x)" % addr, binascii.hexlify(msg)) + self.panda.can_send(addr, msg + b'\x00'*(8-len(msg)), 0) + if self.__can_extra_noise_msgs: + noise = self.__can_extra_noise_msgs.popleft() + self.panda.can_send(noise[0] if noise[0] is not None else addr, + noise[1] + b'\x00'*(8-len(noise[1])), 0) + + def _can_addr_matches(self, addr): + if self.__can11b and (addr == 0x7DF or (addr & 0x7F8) == 0x7E0): + return True + if self.__can29b and (addr == 0x18db33f1 or (addr & 0x1FFF00FF) == 0x18da00f1): + return True + return False + + def __can_process_msg(self, mode, pid, address, ts, data, src): + if not self.__silent: + print("CAN MSG", binascii.hexlify(data[1:1+data[0]]), + "Addr:", hex(address), "Mode:", hex(mode)[2:].zfill(2), + "PID:", hex(pid)[2:].zfill(2), "canLen:", len(data), + binascii.hexlify(data)) + + if self._can_addr_matches(address) and len(data) == 8: + outmsg = None + if data[:3] == b'\x30\x00\x00' and len(self.__can_multipart_data): + if not self.__silent: + print("Request for more data"); + outaddr = 0x7E8 if address == 0x7DF or address == 0x7E0 else 0x18DAF110 + msgnum = 1 + while(self.__can_multipart_data): + datalen = MIN(7, len(self.__can_multipart_data)) + msgpiece = struct.pack("B", 0x20 | msgnum) + self.__can_multipart_data[:datalen] + self._can_send(outaddr, msgpiece) + self.__can_multipart_data = self.__can_multipart_data[7:] + msgnum = (msgnum+1)%0x10 + time.sleep(0.01) + + else: + outmsg = self._process_obd(mode, pid) + + if outmsg: + outaddr = 0x7E8 if address == 0x7DF or address == 0x7E0 else 0x18DAF110 + + if len(outmsg) <= 5: + self._can_send(outaddr, + struct.pack("BBB", len(outmsg)+2, 0x40|data[1], pid) + outmsg) + else: + first_msg_len = MIN(3, len(outmsg)%7) + payload_len = len(outmsg)+3 + msgpiece = struct.pack("BBBBB", 0x10 | ((payload_len>>8)&0xF), + payload_len&0xFF, + 0x40|data[1], pid, 1) + outmsg[:first_msg_len] + self._can_send(outaddr, msgpiece) + self.__can_multipart_data = outmsg[first_msg_len:] + + ######################### + # General OBD functions # + ######################### + + def _process_obd(self, mode, pid): + if mode == 0x01: # Mode: Show current data + if pid == 0x00: #List supported things + return b"\xff\xff\xff\xfe"#b"\xBE\x1F\xB8\x10" #Bitfield, random features + elif pid == 0x01: # Monitor Status since DTC cleared + return b"\x00\x00\x00\x00" #Bitfield, random features + elif pid == 0x04: # Calculated engine load + return b"\x2f" + elif pid == 0x05: # Engine coolant temperature + return b"\x3c" + elif pid == 0x0B: # Intake manifold absolute pressure + return b"\x90" + elif pid == 0x0C: # Engine RPM + return b"\x1A\xF8" + elif pid == 0x0D: # Vehicle Speed + return b"\x53" + elif pid == 0x10: # MAF air flow rate + return b"\x01\xA0" + elif pid == 0x11: # Throttle Position + return b"\x90" + elif pid == 0x33: # Absolute Barometric Pressure + return b"\x90" + elif mode == 0x09: # Mode: Request vehicle information + if pid == 0x02: # Show VIN + return b"1D4GP00R55B123456" + if pid == 0xFC: # test long multi message. Ligned up for LIN responses + return b''.join((struct.pack(">BBH", 0xAA, 0xAA, num+1) for num in range(80))) + if pid == 0xFD: # test long multi message + parts = (b'\xAA\xAA\xAA' + struct.pack(">I", num) for num in range(80)) + return b'\xAA\xAA\xAA' + b''.join(parts) + if pid == 0xFE: # test very long multi message + parts = (b'\xAA\xAA\xAA' + struct.pack(">I", num) for num in range(584)) + return b'\xAA\xAA\xAA' + b''.join(parts) + b'\xAA' + if pid == 0xFF: + return b'\xAA\x00\x00' +\ + b"".join(((b'\xAA'*5)+struct.pack(">H", num+1) for num in range(584))) + #return b"\xAA"*100#(0xFFF-3) + + +if __name__ == "__main__": + serial = os.getenv("SERIAL") if os.getenv("SERIAL") else None + kbaud = int(os.getenv("CANKBAUD")) if os.getenv("CANKBAUD") else 500 + bitwidth = int(os.getenv("CANBITWIDTH")) if os.getenv("CANBITWIDTH") else 0 + canenable = bool(int(os.getenv("CANENABLE"))) if os.getenv("CANENABLE") else True + linenable = bool(int(os.getenv("LINENABLE"))) if os.getenv("LINENABLE") else True + sim = ELMCarSimulator(serial, can_kbaud=kbaud, can=canenable, lin=linenable) + if(bitwidth == 0): + sim.can_mode_11b_29b() + if(bitwidth == 11): + sim.can_mode_11b() + if(bitwidth == 29): + sim.can_mode_29b() + + import signal + def signal_handler(signal, frame): + print('\nShutting down simulator') + sim.stop() + sim.join() + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + + sim.start() + + signal.pause() diff --git a/tests/elm_throughput.py b/tests/elm_throughput.py new file mode 100755 index 0000000000..3962572889 --- /dev/null +++ b/tests/elm_throughput.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +from __future__ import print_function +import socket +import threading +import select + +class Reader(threading.Thread): + def __init__(self, s, *args, **kwargs): + super(Reader, self).__init__(*args, **kwargs) + self._s = s + self.__stop = False + + def stop(self): + self.__stop = True + + def run(self): + while not self.__stop: + s.recv(1000) + +def read_or_fail(s): + ready = select.select([s], [], [], 4) + assert ready[0], "Socket did not receive data within the timeout duration." + return s.recv(1000) + +def send_msg(s, msg): + s.send(msg) + res = b'' + while not res.endswith(">"): + res += read_or_fail(s) + return res + +if __name__ == "__main__": + s = socket.create_connection(("192.168.0.10", 35000)) + #t1 = Reader(s) + #t1.start() + send_msg(s, b"ATZ\r") + send_msg(s, b"ATL1\r") + print(send_msg(s, b"ATE0\r")) + print(send_msg(s, b"ATS0\r")) + print(send_msg(s, b"ATSP6\r")) + + print("\nLOOP\n") + + while True: + print(send_msg(s, b"0100\r")) + print(send_msg(s, b"010d\r")) diff --git a/tests/elm_wifi.py b/tests/elm_wifi.py new file mode 100644 index 0000000000..f5a8849833 --- /dev/null +++ b/tests/elm_wifi.py @@ -0,0 +1,669 @@ +from __future__ import print_function +import os +import sys +import time +import socket +import select +import pytest +import struct + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +import elm_car_simulator +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "..")) +from panda import Panda + +def elm_connect(): + s = socket.create_connection(("192.168.0.10", 35000)) + s.setblocking(0) + return s + +def read_or_fail(s): + ready = select.select([s], [], [], 4) + assert ready[0], "Socket did not receive data within the timeout duration." + return s.recv(1000) + +def sendrecv(s, dat): + s.send(dat) + return read_or_fail(s) + +def send_compare(s, dat, ret, timeout=4): + s.send(dat) + res = b'' + while ret.startswith(res) and ret != res: + print("Waiting") + ready = select.select([s], [], [], timeout) + if not ready[0]: + print("current recv data:", repr(res)) + break; + res += s.recv(1000) + #print("final recv data: '%s'" % repr(res)) + assert ret == res#, "Data does not agree (%s) (%s)"%(repr(ret), repr(res)) + +def sync_reset(s): + s.send("ATZ\r") + res = b'' + while not res.endswith("ELM327 v1.5\r\r>"): + res += read_or_fail(s) + print("Reset response is '%s'" % repr(res)) + +def test_reset(): + s = socket.create_connection(("192.168.0.10", 35000)) + s.setblocking(0) + + try: + sync_reset(s) + finally: + s.close() + +def test_elm_cli(): + s = elm_connect() + + try: + sync_reset(s) + + send_compare(s, b'ATI\r', b'ATI\rELM327 v1.5\r\r>') + + #Test Echo Off + #Expected to be misimplimentation, but this is how the reference device behaved. + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') #Here is the odd part + send_compare(s, b'ATE0\r', b'OK\r\r>') #Should prob show this immediately + send_compare(s, b'ATI\r', b'ELM327 v1.5\r\r>') + + #Test Newline On + send_compare(s, b'ATL1\r', b'OK\r\n\r\n>') + send_compare(s, b'ATI\r', b'ELM327 v1.5\r\n\r\n>') + send_compare(s, b'ATL0\r', b'OK\r\r>') + send_compare(s, b'ATI\r', b'ELM327 v1.5\r\r>') + + send_compare(s, b'ATI\r', b'ELM327 v1.5\r\r>') #Test repeat command no echo + send_compare(s, b'\r', b'ELM327 v1.5\r\r>') + + send_compare(s, b'aTi\r', b'ELM327 v1.5\r\r>') #Test different case + + send_compare(s, b' a T i\r', b'ELM327 v1.5\r\r>') #Test with white space + + send_compare(s, b'ATCATHAT\r', b'?\r\r>') #Test Invalid AT command + + send_compare(s, b'01 00 00 00 00 00 00 00\r', b'?\r\r>') #Test Invalid (too long) OBD command + send_compare(s, b'01 GZ\r', b'?\r\r>') #Test Invalid (Non hex chars) OBD command + finally: + s.close() + +def test_elm_setget_protocol(): + s = elm_connect() + + try: + sync_reset(s) + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + + send_compare(s, b'ATSP0\r', b"OK\r\r>") # Set auto + send_compare(s, b'ATDP\r', b"AUTO\r\r>") + send_compare(s, b'ATDPN\r', b"A0\r\r>") + + send_compare(s, b'ATSP6\r', b"OK\r\r>") # Set protocol + send_compare(s, b'ATDP\r', b"ISO 15765-4 (CAN 11/500)\r\r>") + send_compare(s, b'ATDPN\r', b"6\r\r>") + + send_compare(s, b'ATSPA6\r', b"OK\r\r>") # Set auto with protocol default + send_compare(s, b'ATDP\r', b"AUTO, ISO 15765-4 (CAN 11/500)\r\r>") + send_compare(s, b'ATDPN\r', b"A6\r\r>") + + send_compare(s, b'ATSP7\r', b"OK\r\r>") + send_compare(s, b'ATDP\r', b"ISO 15765-4 (CAN 29/500)\r\r>") + send_compare(s, b'ATDPN\r', b"7\r\r>") #Test Does not accept invalid protocols + send_compare(s, b'ATSPD\r', b"?\r\r>") + send_compare(s, b'ATDP\r', b"ISO 15765-4 (CAN 29/500)\r\r>") + send_compare(s, b'ATDPN\r', b"7\r\r>") + finally: + s.close() + +def test_elm_protocol_failure(): + s = elm_connect() + + try: + sync_reset(s) + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + + send_compare(s, b'ATSP0\r', b"OK\r\r>") + send_compare(s, b'0100\r', b"SEARCHING...\rUNABLE TO CONNECT\r\r>", timeout=10) + + send_compare(s, b'ATSP1\r', b"OK\r\r>") + send_compare(s, b'0100\r', b"NO DATA\r\r>") + + send_compare(s, b'ATSP2\r', b"OK\r\r>") + send_compare(s, b'0100\r', b"NO DATA\r\r>") + + send_compare(s, b'ATSP3\r', b"OK\r\r>") + send_compare(s, b'0100\r', b"BUS INIT: ...ERROR\r\r>") + + send_compare(s, b'ATSP4\r', b"OK\r\r>") + send_compare(s, b'0100\r', b"BUS INIT: ...ERROR\r\r>") + + send_compare(s, b'ATSP5\r', b"OK\r\r>") + send_compare(s, b'0100\r', b"BUS INIT: ERROR\r\r>") + + #send_compare(s, b'ATSP6\r', b"OK\r\r>") + #send_compare(s, b'0100\r', b"NO DATA\r\r>") + # + #send_compare(s, b'ATSP7\r', b"OK\r\r>") + #send_compare(s, b'0100\r', b"NO DATA\r\r>") + # + #send_compare(s, b'ATSP8\r', b"OK\r\r>") + #send_compare(s, b'0100\r', b"NO DATA\r\r>") + # + #send_compare(s, b'ATSP9\r', b"OK\r\r>") + #send_compare(s, b'0100\r', b"NO DATA\r\r>") + # + #send_compare(s, b'ATSPA\r', b"OK\r\r>") + #send_compare(s, b'0100\r', b"NO DATA\r\r>") + # + #send_compare(s, b'ATSPB\r', b"OK\r\r>") + #send_compare(s, b'0100\r', b"NO DATA\r\r>") + # + #send_compare(s, b'ATSPC\r', b"OK\r\r>") + #send_compare(s, b'0100\r', b"NO DATA\r\r>") + finally: + s.close() + +def test_elm_protocol_autodetect_ISO14230_KWP_FAST(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, can=False)#, silent=True) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'ATH0\r', b'OK\r\r>') # Headers ON + send_compare(s, b'ATS0\r', b"OK\r\r>") + + send_compare(s, b'ATSP0\r', b"OK\r\r>") + send_compare(s, b'010D\r', b"SEARCHING...\r410D53\r\r>", timeout=10) + send_compare(s, b'ATDPN\r', b"A5\r\r>") + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_basic_send_lin(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, can=False)#, silent=True) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATSP5\r', b"ATSP5\rOK\r\r>") # Set Proto + + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'0100\r', b"BUS INIT: OK\r41 00 FF FF FF FE \r\r>") + send_compare(s, b'010D\r', b"41 0D 53 \r\r>") + + send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces Off + send_compare(s, b'0100\r', b"4100FFFFFFFE\r\r>") + send_compare(s, b'010D\r', b"410D53\r\r>") + + send_compare(s, b'ATH1\r', b'OK\r\r>') # Spaces Off Headers On + send_compare(s, b'0100\r', b"86F1104100FFFFFFFEC3\r\r>") + send_compare(s, b'010D\r', b"83F110410D5325\r\r>") + + send_compare(s, b'ATS1\r', b'OK\r\r>') # Spaces On Headers On + send_compare(s, b'0100\r', b"86 F1 10 41 00 FF FF FF FE C3 \r\r>") + send_compare(s, b'010D\r', b"83 F1 10 41 0D 53 25 \r\r>") + + send_compare(s, b'1F00\r', b"NO DATA\r\r>") # Unhandled msg, no response. + + # Repeat last check to see if it still works after NO DATA was received + send_compare(s, b'0100\r', b"86 F1 10 41 00 FF FF FF FE C3 \r\r>") + send_compare(s, b'010D\r', b"83 F1 10 41 0D 53 25 \r\r>") + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_send_lin_multiline_msg(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, can=False) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'ATSP5\r', b"OK\r\r>") # Set Proto + + send_compare(s, b'0902\r', # headers OFF, Spaces ON + b"BUS INIT: OK\r" + "49 02 01 00 00 00 31 \r" + "49 02 02 44 34 47 50 \r" + "49 02 03 30 30 52 35 \r" + "49 02 04 35 42 31 32 \r" + "49 02 05 33 34 35 36 \r\r>") + + send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF + send_compare(s, b'0902\r', # Headers OFF, Spaces OFF + b"49020100000031\r" + "49020244344750\r" + "49020330305235\r" + "49020435423132\r" + "49020533343536\r\r>") + + send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON + send_compare(s, b'0902\r', # Headers ON, Spaces OFF + b"87F1104902010000003105\r" + "87F11049020244344750E4\r" + "87F11049020330305235BD\r" + "87F11049020435423132B1\r" + "87F11049020533343536AA\r\r>") + + send_compare(s, b'ATS1\r', b'OK\r\r>') # Spaces ON + send_compare(s, b'0902\r', # Headers ON, Spaces ON + b"87 F1 10 49 02 01 00 00 00 31 05 \r" + "87 F1 10 49 02 02 44 34 47 50 E4 \r" + "87 F1 10 49 02 03 30 30 52 35 BD \r" + "87 F1 10 49 02 04 35 42 31 32 B1 \r" + "87 F1 10 49 02 05 33 34 35 36 AA \r\r>") + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_send_lin_multiline_msg_throughput(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, can=False, silent=True) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATSP5\r', b"ATSP5\rOK\r\r>") # Set Proto + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF + send_compare(s, b'ATH0\r', b'OK\r\r>') # Headers OFF + + send_compare(s, b'09fc\r', # headers OFF, Spaces OFF + b"BUS INIT: OK\r" + + b''.join((b'49FC' + hex(num+1)[2:].upper().zfill(2) + + b'AAAA' + hex(num+1)[2:].upper().zfill(4) + b'\r' + for num in range(80))) + + b"\r>", + timeout=10 + ) + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_panda_safety_mode_KWPFast(): + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + p_car = Panda(serial) # Configure this so the messages will send + p_car.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p_car.kline_drain() + + p_elm = Panda("WIFI") + p_elm.set_safety_mode(0xE327); + + def get_checksum(dat): + result = 0 + result += sum(map(ord, dat)) if isinstance(b'dat', str) else sum(dat) + return struct.pack("B", result % 0x100) + + def timed_recv_check(p, bus, goodmsg): + t = time.time() + msg = bytearray() + + while time.time()-t < 0.5 and len(msg) != len(goodmsg): + msg += p._handle.controlRead(Panda.REQUEST_OUT, 0xe0, bus, 0, len(goodmsg)-len(msg)) + #print("Received", repr(msg)) + if msg == goodmsg: + return True + time.sleep(0.01) + return False + + def kline_send(p, x, bus=2): + p.kline_drain(bus=bus) + p._handle.bulkWrite(2, chr(bus).encode()+x) + return timed_recv_check(p, bus, x) + + def did_send(priority, toaddr, fromaddr, dat, bus=2, checkbyte=None): + msgout = struct.pack("BBB", priority | len(dat), toaddr, fromaddr) + dat + msgout += get_checksum(msgout) if checkbyte is None else checkbyte + print("Sending", hex(priority), hex(toaddr), hex(fromaddr), repr(msgout)) + + if not kline_send(p_elm, msgout, bus=bus): + return False + return timed_recv_check(p_car, bus, msgout) + + assert not did_send(0xC0, 0x33, 0xF1, b'\x01\x0F', bus=3) #wrong bus + assert not did_send(0xC0, 0x33, 0xF1, b'') #wrong length + assert not did_send(0xB0, 0x33, 0xF1, b'\x01\x0E') #bad priority + assert not did_send(0xC0, 0x00, 0xF1, b'\x01\x0D') #bad addr + assert not did_send(0xC0, 0x33, 0x00, b'\x01\x0C') #bad addr + + assert did_send(0xC0, 0x33, 0xF1, b'\x01\x0B') #good! (obd func req) + +def test_elm_lin_keepalive(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, can=False, silent=True) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATSP5\r', b"ATSP5\rOK\r\r>") # Set Proto + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF + send_compare(s, b'ATH0\r', b'OK\r\r>') # Headers OFF + + send_compare(s, b'0100\r', b"BUS INIT: OK\r4100FFFFFFFE\r\r>") + assert sim.lin_active + time.sleep(6) + assert sim.lin_active + + send_compare(s, b'ATPC\r', b"OK\r\r>") #STOP KEEPALIVE + assert sim.lin_active + time.sleep(6) + assert not sim.lin_active + + finally: + sim.stop() + sim.join() + s.close() + +#//////////// +def test_elm_protocol_autodetect_ISO15765(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, lin=False, silent=True) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON + send_compare(s, b'ATS0\r', b"OK\r\r>") + + sim.can_mode_11b() + send_compare(s, b'ATSP0\r', b"OK\r\r>") + send_compare(s, b'010D\r', b"SEARCHING...\r7E803410D53\r\r>", timeout=10) + send_compare(s, b'ATDPN\r', b"A6\r\r>") + + sim.can_mode_29b() + send_compare(s, b'ATSP0\r', b"OK\r\r>") + send_compare(s, b'010D\r', b"SEARCHING...\r18DAF11003410D53\r\r>", timeout=10) + send_compare(s, b'ATDPN\r', b"A7\r\r>") + + sim.change_can_baud(250) + + sim.can_mode_11b() + send_compare(s, b'ATSP0\r', b"OK\r\r>") + send_compare(s, b'010D\r', b"SEARCHING...\r7E803410D53\r\r>", timeout=10) + send_compare(s, b'ATDPN\r', b"A8\r\r>") + + sim.can_mode_29b() + send_compare(s, b'ATSP0\r', b"OK\r\r>") + send_compare(s, b'010D\r', b"SEARCHING...\r18DAF11003410D53\r\r>", timeout=10) + send_compare(s, b'ATDPN\r', b"A9\r\r>") + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_basic_send_can(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, lin=False, silent=True) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATSP6\r', b"ATSP6\rOK\r\r>") # Set Proto + + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'0100\r', b"41 00 FF FF FF FE \r\r>") + send_compare(s, b'010D\r', b"41 0D 53 \r\r>") + + send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces Off + send_compare(s, b'0100\r', b"4100FFFFFFFE\r\r>") + send_compare(s, b'010D\r', b"410D53\r\r>") + + send_compare(s, b'ATH1\r', b'OK\r\r>') # Spaces Off Headers On + send_compare(s, b'0100\r', b"7E8064100FFFFFFFE\r\r>") + send_compare(s, b'010D\r', b"7E803410D53\r\r>") + + send_compare(s, b'ATS1\r', b'OK\r\r>') # Spaces On Headers On + send_compare(s, b'0100\r', b"7E8 06 41 00 FF FF FF FE \r\r>") + send_compare(s, b'010D\r', b"7E8 03 41 0D 53 \r\r>") + + send_compare(s, b'1F00\r', b"NO DATA\r\r>") # Unhandled msg, no response. + + # Repeat last check to see if it still works after NO DATA was received + send_compare(s, b'0100\r', b"7E8 06 41 00 FF FF FF FE \r\r>") + send_compare(s, b'010D\r', b"7E8 03 41 0D 53 \r\r>") + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_send_can_multimsg(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, lin=False) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'ATS1\r', b'OK\r\r>') # Spaces OFF + send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON + + send_compare(s, b'ATSP6\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/500) + sim.can_add_extra_noise(b'\x03\x41\x0D\xFA', addr=0x7E9)# Inject message into the stream + send_compare(s, b'010D\r', + b"7E8 03 41 0D 53 \r" + "7E9 03 41 0D FA \r\r>") # Check it was ignored. + finally: + sim.stop() + sim.join() + s.close() + +"""The ability to correctly filter out messages with the wrong PID is not +implemented correctly in the reference device.""" +def test_elm_can_check_mode_pid(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, lin=False) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF + send_compare(s, b'ATH0\r', b'OK\r\r>') # Headers OFF + + send_compare(s, b'ATSP6\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/500) + sim.can_add_extra_noise(b'\x03\x41\x0E\xFA')# Inject message into the stream + send_compare(s, b'010D\r', b"410D53\r\r>") # Check it was ignored. + send_compare(s, b'0100\r', b"4100FFFFFFFE\r\r>") # Check it was ignored again. + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_send_can_multiline_msg(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, lin=False) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATSP6\r', b"ATSP6\rOK\r\r>") # Set Proto + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + + send_compare(s, b'0902\r', # headers OFF, Spaces ON + b"014 \r" + "0: 49 02 01 31 44 34 \r" + "1: 47 50 30 30 52 35 35 \r" + "2: 42 31 32 33 34 35 36 \r\r>") + + send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF + send_compare(s, b'0902\r', # Headers OFF, Spaces OFF + b"014\r" + "0:490201314434\r" + "1:47503030523535\r" + "2:42313233343536\r\r>") + + send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON + send_compare(s, b'0902\r', # Headers ON, Spaces OFF + b"7E81014490201314434\r" + "7E82147503030523535\r" + "7E82242313233343536\r\r>") + + send_compare(s, b'ATS1\r', b'OK\r\r>') # Spaces ON + send_compare(s, b'0902\r', # Headers ON, Spaces ON + b"7E8 10 14 49 02 01 31 44 34 \r" + "7E8 21 47 50 30 30 52 35 35 \r" + "7E8 22 42 31 32 33 34 35 36 \r\r>") + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_send_can_multiline_msg_throughput(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, lin=False, silent=True) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATSP6\r', b"ATSP6\rOK\r\r>") # Set Proto + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF + send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON + + rows = 584 + send_compare(s, b'09ff\r', # headers ON, Spaces OFF + ("7E8" + "1" + hex((rows*7)+6)[2:].upper().zfill(3) + "49FF01"+"AA0000\r" + + "".join( + ("7E82"+hex((num+1)%0x10)[2:].upper()+("AA"*5) + + hex(num+1)[2:].upper().zfill(4) + "\r" for num in range(rows)) + ) + "\r>").encode(), + timeout=10 + ) + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_interrupted_obd_cmd_resets_state(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, lin=False, silent=True) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF + s.send(b"09fd\r") + ready = select.select([s], [], [], 4) + assert ready[0], "Socket did not receive data within the timeout duration." + s.send(b"ATI\r") + + assert b"236\r0:49FD01AAAAAA\r" in s.recv(10000) + + #Will likely have to be improved to scan for STOPPED if the FW gets more responsive. + ready = select.select([s], [], [], 4) + assert ready[0], "Socket did not receive data within the timeout duration." + + assert b"STOPPED" in s.recv(10000) + + sim.set_enable(False) + send_compare(s, b'09fd\r', b"NO DATA\r\r>") + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_can_baud(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + sim = elm_car_simulator.ELMCarSimulator(serial, lin=False) + sim.start() + + try: + sync_reset(s) + send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF + send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF + send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON + + send_compare(s, b'ATSP6\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/500) + send_compare(s, b'0100\r', b"7E8064100FFFFFFFE\r\r>") + + send_compare(s, b'ATSP8\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/250) + send_compare(s, b'0100\r', b"CAN ERROR\r\r>") + + sim.change_can_baud(250) + + send_compare(s, b'ATSP6\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/500) + send_compare(s, b'0100\r', b"CAN ERROR\r\r>") + + send_compare(s, b'ATSP8\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/250) + send_compare(s, b'0100\r', b"7E8064100FFFFFFFE\r\r>") + finally: + sim.stop() + sim.join() + s.close() + +def test_elm_panda_safety_mode_ISO15765(): + s = elm_connect() + serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None + p_car = Panda(serial) # Configure this so the messages will send + p_car.set_can_speed_kbps(0, 500) + p_car.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + p_elm = Panda("WIFI") + p_elm.set_safety_mode(0xE327); + + #sim = elm_car_simulator.ELMCarSimulator(serial, lin=False) + #sim.start() + + def did_send(p, addr, dat, bus): + p.can_send(addr, dat, bus) + t = time.time() + while time.time()-t < 0.5: + msg = p.can_recv() + for addrin, _, datin, busin in msg: + if (0x80 | bus) == busin and addr == addrin and datin == dat: + return True + time.sleep(0.01) + return False + + try: + sync_reset(s) # Reset elm (which requests the ELM327 safety mode) + + #29 bit + assert not did_send(p_elm, 0x18DB33F1, b'\x02\x01\x00\x00\x00\x00\x00\x00', 1) #wrong canid + assert not did_send(p_elm, 0x18DB33F1, b'\x02\x01\x00', 0) #wrong length + assert not did_send(p_elm, 0x10000000, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr + assert not did_send(p_elm, 0x18DAF133, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr (phy addr) + assert not did_send(p_elm, 0x18DAF000, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr + assert not did_send(p_elm, 0x18DAF1F3, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr! (phys rsp to elm) + + assert did_send(p_elm, 0x18DB33F1, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #good! (obd func req) + assert did_send(p_elm, 0x18DA10F1, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #good! (phys response) + + #11 bit + assert not did_send(p_elm, 0X7DF, b'\x02\x01\x00\x00\x00\x00\x00\x00', 1) #wrong canid + assert not did_send(p_elm, 0X7DF, b'\x02\x01\x00', 0) #wrong length + assert not did_send(p_elm, 0xAA, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr + assert not did_send(p_elm, 0x7DA, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr (phy addr) + assert not did_send(p_elm, 0x7E8, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr (sending 'response') + + assert did_send(p_elm, 0x7DF, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #good! (obd func req) + assert did_send(p_elm, 0x7E1, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #good! (phys response) + + finally: + s.close() diff --git a/tests/flashing_loop.sh b/tests/flashing_loop.sh new file mode 100755 index 0000000000..2eaf850c83 --- /dev/null +++ b/tests/flashing_loop.sh @@ -0,0 +1,9 @@ +#!/bin/bash +cd ../board +make clean + +while true; do + make ota + sleep 10 +done + diff --git a/tests/get_version.py b/tests/get_version.py new file mode 100755 index 0000000000..0cd7795fdc --- /dev/null +++ b/tests/get_version.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +from panda import Panda + +if __name__ == "__main__": + for p in Panda.list(): + pp = Panda(p) + print("%s: %s" % (pp.get_serial()[0], pp.get_version())) + + diff --git a/tests/gmbitbang/recv.py b/tests/gmbitbang/recv.py new file mode 100755 index 0000000000..6eb70aa4ad --- /dev/null +++ b/tests/gmbitbang/recv.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +import time +from panda import Panda + +p = Panda() +p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) +p.set_gmlan(bus=2) +#p.can_send(0xaaa, "\x00\x00", bus=3) +last_add = None +while 1: + ret = p.can_recv() + if len(ret) > 0: + add = ret[0][0] + if last_add is not None and add != last_add+1: + print "MISS %d %d" % (last_add, add) + last_add = add + print ret diff --git a/tests/gmbitbang/rigol.py b/tests/gmbitbang/rigol.py new file mode 100755 index 0000000000..3d690fdd84 --- /dev/null +++ b/tests/gmbitbang/rigol.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +import numpy as np +import visa +import matplotlib.pyplot as plt + +resources = visa.ResourceManager() +print resources.list_resources() + +scope = resources.open_resource('USB0::0x1AB1::0x04CE::DS1ZA184652242::INSTR', timeout=2000, chunk_size=1024000) +print(scope.query('*IDN?').strip()) + +#voltscale = scope.ask_for_values(':CHAN1:SCAL?')[0] +#voltoffset = scope.ask_for_values(":CHAN1:OFFS?")[0] + +#scope.write(":STOP") +scope.write(":WAV:POIN:MODE RAW") +scope.write(":WAV:DATA? CHAN1")[10:] +rawdata = scope.read_raw() +data = np.frombuffer(rawdata, 'B') +print data.shape + +s1 = data[0:650] +s2 = data[650:] +s1i = np.argmax(s1 > 100) +s2i = np.argmax(s2 > 100) +s1 = s1[s1i:] +s2 = s2[s2i:] + +plt.plot(s1) +plt.plot(s2) +plt.show() +#data = (data - 130.0 - voltoffset/voltscale*25) / 25 * voltscale + +print data + diff --git a/tests/gmbitbang/test.py b/tests/gmbitbang/test.py new file mode 100755 index 0000000000..652ac1ddd8 --- /dev/null +++ b/tests/gmbitbang/test.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +import time +from panda import Panda + +p1 = Panda('380016000551363338383037') +p2 = Panda('430026000951363338383037') + +# this is a test, no safety +p1.set_safety_mode(Panda.SAFETY_ALLOUTPUT) +p2.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + +# get versions +print(p1.get_version()) +print(p2.get_version()) + +# this sets bus 2 to actually be GMLAN +p2.set_gmlan(bus=2) + +# send w bitbang then without +#iden = 123 +iden = 18000 +#dat = "\x01\x02" +dat = "\x01\x02\x03\x04\x05\x06\x07\x08" +while 1: + iden += 1 + p1.set_gmlan(bus=None) + p1.can_send(iden, dat, bus=3) + #p1.set_gmlan(bus=2) + #p1.can_send(iden, dat, bus=3) + time.sleep(0.01) + print p2.can_recv() + #exit(0) + diff --git a/tests/gmbitbang/test_one.py b/tests/gmbitbang/test_one.py new file mode 100755 index 0000000000..a398e27803 --- /dev/null +++ b/tests/gmbitbang/test_one.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +import time +from panda import Panda + +p = Panda() +p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + +# ack any crap on bus +p.set_gmlan(bus=2) +time.sleep(0.1) +while len(p.can_recv()) > 0: + print "clearing" + time.sleep(0.1) +print "cleared" +p.set_gmlan(bus=None) + +iden = 18000 +dat = "\x01\x02\x03\x04\x05\x06\x07\x08" +while 1: + iden += 1 + p.can_send(iden, dat, bus=3) + time.sleep(0.01) + diff --git a/tests/gmbitbang/test_packer.c b/tests/gmbitbang/test_packer.c new file mode 100644 index 0000000000..f056dd4812 --- /dev/null +++ b/tests/gmbitbang/test_packer.c @@ -0,0 +1,32 @@ +#include +#include + +typedef struct { + uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +#include "../../board/drivers/canbitbang.h" + +int main() { + char out[300]; + CAN_FIFOMailBox_TypeDef to_bang = {0}; + to_bang.RIR = 20 << 21; + to_bang.RDTR = 1; + to_bang.RDLR = 1; + + int len = get_bit_message(out, &to_bang); + printf("T:"); + for (int i = 0; i < len; i++) { + printf("%d", out[i]); + } + printf("\n"); + printf("R:0000010010100000100010000010011110111010100111111111111111"); + printf("\n"); + return 0; +} + + + diff --git a/tests/location_listener.py b/tests/location_listener.py new file mode 100755 index 0000000000..cbbb00d794 --- /dev/null +++ b/tests/location_listener.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +import os +import time +import sys + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda, PandaSerial + +def add_nmea_checksum(msg): + d = msg[1:] + cs = 0 + for i in d: + cs ^= ord(i) + return msg + "*%02X" % cs + +if __name__ == "__main__": + panda = Panda() + ser = PandaSerial(panda, 1, 9600) + + # power cycle by toggling reset + print "resetting" + panda.set_esp_power(0) + time.sleep(0.5) + panda.set_esp_power(1) + time.sleep(0.5) + print "done" + print ser.read(1024) + + # upping baud rate + baudrate = 460800 + + print "upping baud rate" + msg = add_nmea_checksum("$PUBX,41,1,0007,0003,%d,0" % baudrate)+"\r\n" + print msg + ser.write(msg) + time.sleep(0.1) # needs a wait for it to actually send + + # new panda serial + ser = PandaSerial(panda, 1, baudrate) + + while True: + ret = ser.read(1024) + if len(ret) > 0: + sys.stdout.write(ret) + sys.stdout.flush() + #print str(ret).encode("hex") + diff --git a/tests/loopback_test.py b/tests/loopback_test.py new file mode 100755 index 0000000000..a871295ad6 --- /dev/null +++ b/tests/loopback_test.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +from __future__ import print_function +import os +import sys +import time +import random +import argparse + +from hexdump import hexdump +from itertools import permutations + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda + +def get_test_string(): + return b"test"+os.urandom(10) + +def run_test(sleep_duration): + pandas = Panda.list() + print(pandas) + + if len(pandas) == 0: + print("NO PANDAS") + assert False + + if len(pandas) == 1: + # if we only have one on USB, assume the other is on wifi + pandas.append("WIFI") + run_test_w_pandas(pandas, sleep_duration) + +def run_test_w_pandas(pandas, sleep_duration): + h = list(map(lambda x: Panda(x), pandas)) + print("H", h) + + for hh in h: + hh.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # test both directions + for ho in permutations(range(len(h)), r=2): + print("***************** TESTING", ho) + + panda0, panda1 = h[ho[0]], h[ho[1]] + + if(panda0._serial == "WIFI"): + print(" *** Can not send can data over wifi panda. Skipping! ***") + continue + + # **** test health packet **** + print("health", ho[0], h[ho[0]].health()) + + # **** test K/L line loopback **** + for bus in [2,3]: + # flush the output + h[ho[1]].kline_drain(bus=bus) + + # send the characters + st = get_test_string() + st = b"\xaa"+chr(len(st)+3).encode()+st + h[ho[0]].kline_send(st, bus=bus, checksum=False) + + # check for receive + ret = h[ho[1]].kline_drain(bus=bus) + + print("ST Data:") + hexdump(st) + print("RET Data:") + hexdump(ret) + assert st == ret + print("K/L pass", bus, ho, "\n") + time.sleep(sleep_duration) + + # **** test can line loopback **** + for bus, gmlan in [(0, False), (1, False), (2, False), (1, True), (2, True)]: + print("\ntest can", bus) + # flush + cans_echo = panda0.can_recv() + cans_loop = panda1.can_recv() + + panda0.set_gmlan(None) + panda1.set_gmlan(None) + + if gmlan is True: + panda0.set_gmlan(bus) + panda1.set_gmlan(bus) + bus = 3 + + # send the characters + at = random.randint(1, 2000) + st = get_test_string()[0:8] + panda0.can_send(at, st, bus) + time.sleep(0.1) + + # check for receive + cans_echo = panda0.can_recv() + cans_loop = panda1.can_recv() + + print("Bus", bus, "echo", cans_echo, "loop", cans_loop) + + assert len(cans_echo) == 1 + assert len(cans_loop) == 1 + + assert cans_echo[0][0] == at + assert cans_loop[0][0] == at + + assert cans_echo[0][2] == st + assert cans_loop[0][2] == st + + assert cans_echo[0][3] == 0x80 | bus + if cans_loop[0][3] != bus: + print("EXPECTED %d GOT %d" % (bus, cans_loop[0][3])) + assert cans_loop[0][3] == bus + + print("CAN pass", bus, ho) + time.sleep(sleep_duration) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-n", type=int, help="Number of test iterations to run") + parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0) + args = parser.parse_args() + + if args.n is None: + while True: + run_test(sleep_duration=args.sleep) + else: + for i in range(args.n): + run_test(sleep_duration=args.sleep) diff --git a/tests/misra/Dockerfile b/tests/misra/Dockerfile new file mode 100644 index 0000000000..e63cc7e9e1 --- /dev/null +++ b/tests/misra/Dockerfile @@ -0,0 +1,6 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y make python python-pip git +COPY tests/safety/requirements.txt /panda/tests/safety/requirements.txt +RUN pip install -r /panda/tests/safety/requirements.txt +COPY . /panda diff --git a/tests/misra/test_misra.sh b/tests/misra/test_misra.sh new file mode 100755 index 0000000000..835f4ebcfd --- /dev/null +++ b/tests/misra/test_misra.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e + +git clone https://github.com/danmar/cppcheck.git || true +cd cppcheck +git fetch +git checkout 44d6066c6fad32e2b0332b3f2b24bd340febaef8 +make -j4 +cd ../../../ + +# whole panda code +tests/misra/cppcheck/cppcheck --dump --enable=all --inline-suppr board/main.c 2>/tmp/misra/cppcheck_output.txt || true +python tests/misra/cppcheck/addons/misra.py board/main.c.dump 2>/tmp/misra/misra_output.txt || true + +# violations in safety files +(cat /tmp/misra/misra_output.txt | grep safety) > /tmp/misra/misra_safety_output.txt || true +(cat /tmp/misra/cppcheck_output.txt | grep safety) > /tmp/misra/cppcheck_safety_output.txt || true + +if [[ -s "/tmp/misra/misra_safety_output.txt" ]] || [[ -s "/tmp/misra/cppcheck_safety_output.txt" ]] +then + echo "Found Misra violations in the safety code:" + cat /tmp/misra/misra_safety_output.txt + cat /tmp/misra/cppcheck_safety_output.txt + exit 1 +fi diff --git a/tests/pedal/enter_canloader.py b/tests/pedal/enter_canloader.py new file mode 100755 index 0000000000..c6f06ca354 --- /dev/null +++ b/tests/pedal/enter_canloader.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +import sys +import time +import struct +import argparse +import signal +from panda import Panda + +class CanHandle(object): + def __init__(self, p): + self.p = p + + def transact(self, dat): + #print "W:",dat.encode("hex") + self.p.isotp_send(1, dat, 0, recvaddr=2) + + def _handle_timeout(signum, frame): + # will happen on reset + raise Exception("timeout") + + signal.signal(signal.SIGALRM, _handle_timeout) + signal.alarm(1) + try: + ret = self.p.isotp_recv(2, 0, sendaddr=1) + finally: + signal.alarm(0) + + #print "R:",ret.encode("hex") + return ret + + def controlWrite(self, request_type, request, value, index, data, timeout=0): + # ignore data in reply, panda doesn't use it + return self.controlRead(request_type, request, value, index, 0, timeout) + + def controlRead(self, request_type, request, value, index, length, timeout=0): + dat = struct.pack("HHBBHHH", 0, 0, request_type, request, value, index, length) + return self.transact(dat) + + def bulkWrite(self, endpoint, data, timeout=0): + if len(data) > 0x10: + raise ValueError("Data must not be longer than 0x10") + dat = struct.pack("HH", endpoint, len(data))+data + return self.transact(dat) + + def bulkRead(self, endpoint, length, timeout=0): + dat = struct.pack("HH", endpoint, 0) + return self.transact(dat) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Flash pedal over can') + parser.add_argument('--recover', action='store_true') + parser.add_argument("fn", type=str, nargs='?', help="flash file") + args = parser.parse_args() + + p = Panda() + p.set_safety_mode(0x1337) + + while 1: + if len(p.can_recv()) == 0: + break + + if args.recover: + p.can_send(0x200, "\xce\xfa\xad\xde\x1e\x0b\xb0\x02", 0) + exit(0) + else: + p.can_send(0x200, "\xce\xfa\xad\xde\x1e\x0b\xb0\x0a", 0) + + if args.fn: + time.sleep(0.1) + print "flashing", args.fn + code = open(args.fn).read() + Panda.flash_static(CanHandle(p), code) + + print "can flash done" + + diff --git a/tests/read_st_flash.sh b/tests/read_st_flash.sh new file mode 100755 index 0000000000..ffcfd7bbfe --- /dev/null +++ b/tests/read_st_flash.sh @@ -0,0 +1,6 @@ +#!/bin/bash +rm -f /tmp/dump_bootstub +rm -f /tmp/dump_main +dfu-util -a 0 -s 0x08000000 -U /tmp/dump_bootstub +dfu-util -a 0 -s 0x08004000 -U /tmp/dump_main + diff --git a/tests/read_winusb_descriptors.py b/tests/read_winusb_descriptors.py new file mode 100644 index 0000000000..e38df8d1ca --- /dev/null +++ b/tests/read_winusb_descriptors.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +from panda import Panda +from hexdump import hexdump + +DEBUG = False + +if __name__ == "__main__": + p = Panda() + + len = p._handle.controlRead(Panda.REQUEST_IN, 0x06, 3 << 8 | 238, 0, 1) + print 'Microsoft OS String Descriptor' + dat = p._handle.controlRead(Panda.REQUEST_IN, 0x06, 3 << 8 | 238, 0, len[0]) + if DEBUG: print 'LEN: {}'.format(hex(len[0])) + hexdump("".join(map(chr, dat))) + + ms_vendor_code = dat[16] + if DEBUG: print 'MS_VENDOR_CODE: {}'.format(hex(len[0])) + + print '\nMicrosoft Compatible ID Feature Descriptor' + len = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 4, 1) + if DEBUG: print 'LEN: {}'.format(hex(len[0])) + dat = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 4, len[0]) + hexdump("".join(map(chr, dat))) + + print '\nMicrosoft Extended Properties Feature Descriptor' + len = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 5, 1) + if DEBUG: print 'LEN: {}'.format(hex(len[0])) + dat = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 5, len[0]) + hexdump("".join(map(chr, dat))) diff --git a/tests/safety/Dockerfile b/tests/safety/Dockerfile new file mode 100644 index 0000000000..9381fdc408 --- /dev/null +++ b/tests/safety/Dockerfile @@ -0,0 +1,6 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y clang make python python-pip +COPY tests/safety/requirements.txt /panda/tests/safety/requirements.txt +RUN pip install -r /panda/tests/safety/requirements.txt +COPY . /panda diff --git a/tests/safety/Makefile b/tests/safety/Makefile new file mode 100644 index 0000000000..334a29427d --- /dev/null +++ b/tests/safety/Makefile @@ -0,0 +1,18 @@ +CC = clang +CCFLAGS = -O3 -fPIC -DPANDA -I. + +.PHONY: all +all: libpandasafety.so + +libpandasafety.so: test.o + $(CC) -shared -o '$@' $^ -lm + +test.o: test.c + @echo "[ CC ] $@" + $(CC) $(CCFLAGS) -MMD -c -I../../board -o '$@' '$<' + +.PHONY: clean +clean: + rm -f libpandasafety.so test.o test.d + +-include test.d diff --git a/tests/safety/__init__.py b/tests/safety/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/safety/libpandasafety_py.py b/tests/safety/libpandasafety_py.py new file mode 100644 index 0000000000..dc5e5be5af --- /dev/null +++ b/tests/safety/libpandasafety_py.py @@ -0,0 +1,97 @@ +import os +import subprocess + +from cffi import FFI + +can_dir = os.path.dirname(os.path.abspath(__file__)) +libpandasafety_fn = os.path.join(can_dir, "libpandasafety.so") +subprocess.check_call(["make"], cwd=can_dir) + +ffi = FFI() +ffi.cdef(""" +typedef struct +{ + uint32_t TIR; /*!< CAN TX mailbox identifier register */ + uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + uint32_t TDLR; /*!< CAN mailbox data low register */ + uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +typedef struct +{ + uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +typedef struct +{ + uint32_t CNT; +} TIM_TypeDef; + +void set_controls_allowed(bool c); +bool get_controls_allowed(void); +void set_long_controls_allowed(bool c); +bool get_long_controls_allowed(void); +void set_gas_interceptor_detected(bool c); +bool get_gas_interceptor_detetcted(void); +int get_gas_interceptor_prev(void); +void set_timer(uint32_t t); +void reset_angle_control(void); + +void safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_send); +int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_push); +int safety_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd); +int safety_set_mode(uint16_t mode, int16_t param); + +void init_tests_toyota(void); +int get_toyota_torque_meas_min(void); +int get_toyota_torque_meas_max(void); +int get_toyota_gas_prev(void); +void set_toyota_torque_meas(int min, int max); +void set_toyota_desired_torque_last(int t); +void set_toyota_camera_forwarded(int t); +void set_toyota_rt_torque_last(int t); + +void init_tests_honda(void); +int get_honda_ego_speed(void); +int get_honda_brake_prev(void); +int get_honda_gas_prev(void); +void set_honda_alt_brake_msg(bool); +void set_honda_bosch_hardware(bool); + +void init_tests_cadillac(void); +void set_cadillac_desired_torque_last(int t); +void set_cadillac_rt_torque_last(int t); +void set_cadillac_torque_driver(int min, int max); + +void init_tests_gm(void); +void set_gm_desired_torque_last(int t); +void set_gm_rt_torque_last(int t); +void set_gm_torque_driver(int min, int max); + +void init_tests_hyundai(void); +void set_hyundai_desired_torque_last(int t); +void set_hyundai_rt_torque_last(int t); +void set_hyundai_torque_driver(int min, int max); +void set_hyundai_giraffe_switch_2(int t); +void set_hyundai_camera_bus(int t); + +void init_tests_chrysler(void); +void set_chrysler_desired_torque_last(int t); +void set_chrysler_rt_torque_last(int t); +void set_chrysler_camera_detected(int t); +int get_chrysler_torque_meas_min(void); +int get_chrysler_torque_meas_max(void); +void set_chrysler_torque_meas(int min, int max); + +void init_tests_subaru(void); +void set_subaru_desired_torque_last(int t); +void set_subaru_rt_torque_last(int t); +void set_subaru_torque_driver(int min, int max); + + +""") + +libpandasafety = ffi.dlopen(libpandasafety_fn) diff --git a/tests/safety/requirements.txt b/tests/safety/requirements.txt new file mode 100644 index 0000000000..8bbfb1d7df --- /dev/null +++ b/tests/safety/requirements.txt @@ -0,0 +1,2 @@ +cffi==1.11.4 +numpy==1.14.1 diff --git a/tests/safety/test.c b/tests/safety/test.c new file mode 100644 index 0000000000..be13d346af --- /dev/null +++ b/tests/safety/test.c @@ -0,0 +1,290 @@ +#include +#include + +typedef struct +{ + uint32_t TIR; /*!< CAN TX mailbox identifier register */ + uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + uint32_t TDLR; /*!< CAN mailbox data low register */ + uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +typedef struct +{ + uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +typedef struct +{ + uint32_t CNT; +} TIM_TypeDef; + +struct sample_t toyota_torque_meas; +struct sample_t cadillac_torque_driver; +struct sample_t gm_torque_driver; +struct sample_t hyundai_torque_driver; +struct sample_t chrysler_torque_meas; +struct sample_t subaru_torque_driver; + +TIM_TypeDef timer; +TIM_TypeDef *TIM2 = &timer; + +#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 UNUSED(x) (void)(x) + +#define PANDA +#define NULL ((void*)0) +#define static +#include "safety.h" + +void set_controls_allowed(bool c){ + controls_allowed = c; +} + +void set_long_controls_allowed(bool c){ + long_controls_allowed = c; +} + +void set_gas_interceptor_detected(bool c){ + gas_interceptor_detected = c; +} + +void reset_angle_control(void){ + angle_control = 0; +} + +bool get_controls_allowed(void){ + return controls_allowed; +} + +bool get_long_controls_allowed(void){ + return long_controls_allowed; +} + +bool get_gas_interceptor_detected(void){ + return gas_interceptor_detected; +} + +int get_gas_interceptor_prev(void){ + return gas_interceptor_prev; +} + +void set_timer(uint32_t t){ + timer.CNT = t; +} + +void set_toyota_camera_forwarded(int t){ + toyota_camera_forwarded = t; +} + +void set_toyota_torque_meas(int min, int max){ + toyota_torque_meas.min = min; + toyota_torque_meas.max = max; +} + +void set_cadillac_torque_driver(int min, int max){ + cadillac_torque_driver.min = min; + cadillac_torque_driver.max = max; +} + +void set_gm_torque_driver(int min, int max){ + gm_torque_driver.min = min; + gm_torque_driver.max = max; +} + +void set_hyundai_torque_driver(int min, int max){ + hyundai_torque_driver.min = min; + hyundai_torque_driver.max = max; +} + +void set_hyundai_camera_bus(int t){ + hyundai_camera_bus = t; +} + +void set_hyundai_giraffe_switch_2(int t){ + hyundai_giraffe_switch_2 = t; +} + +void set_chrysler_camera_detected(int t){ + chrysler_camera_detected = t; +} + +void set_chrysler_torque_meas(int min, int max){ + chrysler_torque_meas.min = min; + chrysler_torque_meas.max = max; +} + +void set_subaru_torque_driver(int min, int max){ + subaru_torque_driver.min = min; + subaru_torque_driver.max = max; +} + +int get_chrysler_torque_meas_min(void){ + return chrysler_torque_meas.min; +} + +int get_chrysler_torque_meas_max(void){ + return chrysler_torque_meas.max; +} + +int get_toyota_gas_prev(void){ + return toyota_gas_prev; +} + +int get_toyota_torque_meas_min(void){ + return toyota_torque_meas.min; +} + +int get_toyota_torque_meas_max(void){ + return toyota_torque_meas.max; +} + +void set_toyota_rt_torque_last(int t){ + toyota_rt_torque_last = t; +} + +void set_cadillac_rt_torque_last(int t){ + cadillac_rt_torque_last = t; +} + +void set_gm_rt_torque_last(int t){ + gm_rt_torque_last = t; +} + +void set_hyundai_rt_torque_last(int t){ + hyundai_rt_torque_last = t; +} + +void set_chrysler_rt_torque_last(int t){ + chrysler_rt_torque_last = t; +} + +void set_subaru_rt_torque_last(int t){ + subaru_rt_torque_last = t; +} + +void set_toyota_desired_torque_last(int t){ + toyota_desired_torque_last = t; +} + +void set_cadillac_desired_torque_last(int t){ + for (int i = 0; i < 4; i++) cadillac_desired_torque_last[i] = t; +} + +void set_gm_desired_torque_last(int t){ + gm_desired_torque_last = t; +} + +void set_hyundai_desired_torque_last(int t){ + hyundai_desired_torque_last = t; +} + +void set_chrysler_desired_torque_last(int t){ + chrysler_desired_torque_last = t; +} + +void set_subaru_desired_torque_last(int t){ + subaru_desired_torque_last = t; +} + +int get_honda_ego_speed(void){ + return honda_ego_speed; +} + +int get_honda_brake_prev(void){ + return honda_brake_prev; +} + +int get_honda_gas_prev(void){ + return honda_gas_prev; +} + +void set_honda_alt_brake_msg(bool c){ + honda_alt_brake_msg = c; +} + +void set_honda_bosch_hardware(bool c){ + honda_bosch_hardware = c; +} + +void init_tests_toyota(void){ + toyota_torque_meas.min = 0; + toyota_torque_meas.max = 0; + toyota_desired_torque_last = 0; + toyota_rt_torque_last = 0; + toyota_ts_last = 0; + set_timer(0); +} + +void init_tests_cadillac(void){ + cadillac_torque_driver.min = 0; + cadillac_torque_driver.max = 0; + for (int i = 0; i < 4; i++) cadillac_desired_torque_last[i] = 0; + cadillac_rt_torque_last = 0; + cadillac_ts_last = 0; + set_timer(0); +} + +void init_tests_gm(void){ + gm_torque_driver.min = 0; + gm_torque_driver.max = 0; + gm_desired_torque_last = 0; + gm_rt_torque_last = 0; + gm_ts_last = 0; + set_timer(0); +} + +void init_tests_hyundai(void){ + hyundai_torque_driver.min = 0; + hyundai_torque_driver.max = 0; + hyundai_desired_torque_last = 0; + hyundai_rt_torque_last = 0; + hyundai_ts_last = 0; + set_timer(0); +} + +void init_tests_chrysler(void){ + chrysler_torque_meas.min = 0; + chrysler_torque_meas.max = 0; + chrysler_desired_torque_last = 0; + chrysler_rt_torque_last = 0; + chrysler_ts_last = 0; + set_timer(0); +} + +void init_tests_subaru(void){ + subaru_torque_driver.min = 0; + subaru_torque_driver.max = 0; + subaru_desired_torque_last = 0; + subaru_rt_torque_last = 0; + subaru_ts_last = 0; + set_timer(0); +} + +void init_tests_honda(void){ + honda_ego_speed = 0; + honda_brake_prev = 0; + honda_gas_prev = 0; +} + +void set_gmlan_digital_output(int to_set){ +} + +void reset_gmlan_switch_timeout(void){ +} + +void gmlan_switch_init(int timeout_enable){ +} + diff --git a/tests/safety/test.sh b/tests/safety/test.sh new file mode 100755 index 0000000000..83d8f5b31f --- /dev/null +++ b/tests/safety/test.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +python -m unittest discover . diff --git a/tests/safety/test_cadillac.py b/tests/safety/test_cadillac.py new file mode 100644 index 0000000000..af89e69cc7 --- /dev/null +++ b/tests/safety/test_cadillac.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python2 +import unittest +import numpy as np +import libpandasafety_py + +MAX_RATE_UP = 2 +MAX_RATE_DOWN = 5 +MAX_TORQUE = 150 + +MAX_RT_DELTA = 75 +RT_INTERVAL = 250000 + +DRIVER_TORQUE_ALLOWANCE = 50; +DRIVER_TORQUE_FACTOR = 4; + +IPAS_OVERRIDE_THRESHOLD = 200 + +def twos_comp(val, bits): + if val >= 0: + return val + else: + return (2**bits) + val + +def sign(a): + if a > 0: + return 1 + else: + return -1 + +class TestCadillacSafety(unittest.TestCase): + @classmethod + def setUp(cls): + cls.safety = libpandasafety_py.libpandasafety + cls.safety.safety_set_mode(6, 0) + cls.safety.init_tests_cadillac() + + def _send_msg(self, bus, addr, length): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = addr << 21 + to_send[0].RDTR = length + to_send[0].RDTR = bus << 4 + return to_send + + def _set_prev_torque(self, t): + self.safety.set_cadillac_desired_torque_last(t) + self.safety.set_cadillac_rt_torque_last(t) + + def _torque_driver_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x164 << 21 + + t = twos_comp(torque, 11) + to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8) + return to_send + + def _torque_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x151 << 21 + + t = twos_comp(torque, 14) + to_send[0].RDLR = ((t >> 8) & 0x3F) | ((t & 0xFF) << 8) + return to_send + + def test_default_controls_not_allowed(self): + self.assertFalse(self.safety.get_controls_allowed()) + + def test_manually_enable_controls_allowed(self): + self.safety.set_controls_allowed(1) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.set_controls_allowed(0) + + def test_enable_control_allowed_from_cruise(self): + to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_push[0].RIR = 0x370 << 21 + to_push[0].RDLR = 0x800000 + to_push[0].RDTR = 0 + + self.safety.safety_rx_hook(to_push) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_disable_control_allowed_from_cruise(self): + to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_push[0].RIR = 0x370 << 21 + to_push[0].RDLR = 0 + to_push[0].RDTR = 0 + + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(to_push) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_torque_absolute_limits(self): + for controls_allowed in [True, False]: + for torque in np.arange(-MAX_TORQUE - 1000, MAX_TORQUE + 1000, MAX_RATE_UP): + self.safety.set_controls_allowed(controls_allowed) + self.safety.set_cadillac_rt_torque_last(torque) + self.safety.set_cadillac_torque_driver(0, 0) + self.safety.set_cadillac_desired_torque_last(torque - MAX_RATE_UP) + + if controls_allowed: + send = (-MAX_TORQUE <= torque <= MAX_TORQUE) + else: + send = torque == 0 + + self.assertEqual(send, self.safety.safety_tx_hook(self._torque_msg(torque))) + + def test_non_realtime_limit_up(self): + self.safety.set_cadillac_torque_driver(0, 0) + self.safety.set_controls_allowed(True) + + self._set_prev_torque(0) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP))) + self._set_prev_torque(0) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP))) + + self._set_prev_torque(0) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1))) + self.safety.set_controls_allowed(True) + self._set_prev_torque(0) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP - 1))) + + def test_non_realtime_limit_down(self): + self.safety.set_cadillac_torque_driver(0, 0) + self.safety.set_controls_allowed(True) + + def test_exceed_torque_sensor(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1): + t *= -sign + self.safety.set_cadillac_torque_driver(t, t) + self._set_prev_torque(MAX_TORQUE * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_TORQUE * sign))) + + self.safety.set_cadillac_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_TORQUE))) + + # spot check some individual cases + for sign in [-1, 1]: + driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign + torque_desired = (MAX_TORQUE - 10 * DRIVER_TORQUE_FACTOR) * sign + delta = 1 * sign + self._set_prev_torque(torque_desired) + self.safety.set_cadillac_torque_driver(-driver_torque, -driver_torque) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(torque_desired))) + self._set_prev_torque(torque_desired + delta) + self.safety.set_cadillac_torque_driver(-driver_torque, -driver_torque) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(torque_desired + delta))) + + self._set_prev_torque(MAX_TORQUE * sign) + self.safety.set_cadillac_torque_driver(-MAX_TORQUE * sign, -MAX_TORQUE * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg((MAX_TORQUE - MAX_RATE_DOWN) * sign))) + self._set_prev_torque(MAX_TORQUE * sign) + self.safety.set_cadillac_torque_driver(-MAX_TORQUE * sign, -MAX_TORQUE * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(0))) + self._set_prev_torque(MAX_TORQUE * sign) + self.safety.set_cadillac_torque_driver(-MAX_TORQUE * sign, -MAX_TORQUE * sign) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg((MAX_TORQUE - MAX_RATE_DOWN + 1) * sign))) + + + def test_realtime_limits(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self.safety.init_tests_cadillac() + self._set_prev_torque(0) + self.safety.set_cadillac_torque_driver(0, 0) + for t in np.arange(0, MAX_RT_DELTA, 1): + t *= sign + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) + + self._set_prev_torque(0) + for t in np.arange(0, MAX_RT_DELTA, 1): + t *= sign + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + # Increase timer to update rt_torque_last + self.safety.set_timer(RT_INTERVAL + 1) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1)))) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) + + + def test_fwd_hook(self): + # nothing allowed + buss = range(0x0, 0x3) + msgs = range(0x1, 0x800) + + for b in buss: + for m in msgs: + # assume len 8 + self.assertEqual(-1, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/safety/test_chrysler.py b/tests/safety/test_chrysler.py new file mode 100755 index 0000000000..09aa424dd9 --- /dev/null +++ b/tests/safety/test_chrysler.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python2 +import csv +import glob +import unittest +import numpy as np +import libpandasafety_py + +MAX_RATE_UP = 3 +MAX_RATE_DOWN = 3 +MAX_STEER = 261 + +MAX_RT_DELTA = 112 +RT_INTERVAL = 250000 + +MAX_TORQUE_ERROR = 80 + +def twos_comp(val, bits): + if val >= 0: + return val + else: + return (2**bits) + val + +def sign(a): + if a > 0: + return 1 + else: + return -1 + +def swap_bytes(data_str): + """Accepts string with hex, returns integer with order swapped for CAN.""" + a = int(data_str, 16) + return ((a & 0xff) << 24) + ((a & 0xff00) << 8) + ((a & 0x00ff0000) >> 8) + ((a & 0xff000000) >> 24) + +class TestChryslerSafety(unittest.TestCase): + @classmethod + def setUp(cls): + cls.safety = libpandasafety_py.libpandasafety + cls.safety.safety_set_mode(9, 0) + cls.safety.init_tests_chrysler() + + def _send_msg(self, bus, addr, length): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = addr << 21 + to_send[0].RDTR = length + to_send[0].RDTR = bus << 4 + return to_send + + def _button_msg(self, buttons): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 1265 << 21 + to_send[0].RDLR = buttons + return to_send + + def _set_prev_torque(self, t): + self.safety.set_chrysler_desired_torque_last(t) + self.safety.set_chrysler_rt_torque_last(t) + self.safety.set_chrysler_torque_meas(t, t) + + def _torque_meas_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 544 << 21 + to_send[0].RDHR = ((torque + 1024) >> 8) + (((torque + 1024) & 0xff) << 8) + return to_send + + def _torque_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x292 << 21 + to_send[0].RDLR = ((torque + 1024) >> 8) + (((torque + 1024) & 0xff) << 8) + return to_send + + def test_default_controls_not_allowed(self): + self.assertFalse(self.safety.get_controls_allowed()) + + def test_steer_safety_check(self): + for enabled in [0, 1]: + for t in range(-MAX_STEER*2, MAX_STEER*2): + self.safety.set_controls_allowed(enabled) + self._set_prev_torque(t) + if abs(t) > MAX_STEER or (not enabled and abs(t) > 0): + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(t))) + else: + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + def test_manually_enable_controls_allowed(self): + self.safety.set_controls_allowed(1) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.set_controls_allowed(0) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_enable_control_allowed_from_cruise(self): + to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_push[0].RIR = 0x1f4 << 21 + to_push[0].RDLR = 0x380000 + + self.safety.safety_rx_hook(to_push) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_disable_control_allowed_from_cruise(self): + to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_push[0].RIR = 0x1f4 << 21 + to_push[0].RDLR = 0 + + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(to_push) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_non_realtime_limit_up(self): + self.safety.set_controls_allowed(True) + + self._set_prev_torque(0) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP))) + + self._set_prev_torque(0) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1))) + + def test_non_realtime_limit_down(self): + self.safety.set_controls_allowed(True) + + self.safety.set_chrysler_rt_torque_last(MAX_STEER) + torque_meas = MAX_STEER - MAX_TORQUE_ERROR - 20 + self.safety.set_chrysler_torque_meas(torque_meas, torque_meas) + self.safety.set_chrysler_desired_torque_last(MAX_STEER) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER - MAX_RATE_DOWN))) + + self.safety.set_chrysler_rt_torque_last(MAX_STEER) + self.safety.set_chrysler_torque_meas(torque_meas, torque_meas) + self.safety.set_chrysler_desired_torque_last(MAX_STEER) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER - MAX_RATE_DOWN + 1))) + + def test_exceed_torque_sensor(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self._set_prev_torque(0) + for t in np.arange(0, MAX_TORQUE_ERROR + 2, 2): # step needs to be smaller than MAX_TORQUE_ERROR + t *= sign + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_TORQUE_ERROR + 2)))) + + def test_realtime_limit_up(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self.safety.init_tests_chrysler() + self._set_prev_torque(0) + for t in np.arange(0, MAX_RT_DELTA+1, 1): + t *= sign + self.safety.set_chrysler_torque_meas(t, t) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) + + self._set_prev_torque(0) + for t in np.arange(0, MAX_RT_DELTA+1, 1): + t *= sign + self.safety.set_chrysler_torque_meas(t, t) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + # Increase timer to update rt_torque_last + self.safety.set_timer(RT_INTERVAL + 1) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * MAX_RT_DELTA))) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) + + def test_torque_measurements(self): + self.safety.safety_rx_hook(self._torque_meas_msg(50)) + self.safety.safety_rx_hook(self._torque_meas_msg(-50)) + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + + self.assertEqual(-50, self.safety.get_chrysler_torque_meas_min()) + self.assertEqual(50, self.safety.get_chrysler_torque_meas_max()) + + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + self.assertEqual(0, self.safety.get_chrysler_torque_meas_max()) + self.assertEqual(-50, self.safety.get_chrysler_torque_meas_min()) + + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + self.assertEqual(0, self.safety.get_chrysler_torque_meas_max()) + self.assertEqual(0, self.safety.get_chrysler_torque_meas_min()) + + def test_fwd_hook(self): + buss = range(0x0, 0x3) + msgs = range(0x1, 0x800) + chrysler_camera_detected = [0, 1] + + for ccd in chrysler_camera_detected: + self.safety.set_chrysler_camera_detected(ccd) + blocked_msgs = [658, 678] + for b in buss: + for m in msgs: + if not ccd: + if b == 0: + fwd_bus = 2 + elif b == 1: + fwd_bus = -1 + elif b == 2: + fwd_bus = -1 if m in blocked_msgs else 0 + else: + fwd_bus = -1 + + # assume len 8 + self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/safety/test_gm.py b/tests/safety/test_gm.py new file mode 100644 index 0000000000..d9a1d39ec7 --- /dev/null +++ b/tests/safety/test_gm.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python2 +import unittest +import numpy as np +import libpandasafety_py + +MAX_RATE_UP = 7 +MAX_RATE_DOWN = 17 +MAX_STEER = 300 +MAX_BRAKE = 350 +MAX_GAS = 3072 +MAX_REGEN = 1404 + +MAX_RT_DELTA = 128 +RT_INTERVAL = 250000 + +DRIVER_TORQUE_ALLOWANCE = 50; +DRIVER_TORQUE_FACTOR = 4; + +def twos_comp(val, bits): + if val >= 0: + return val + else: + return (2**bits) + val + +def sign(a): + if a > 0: + return 1 + else: + return -1 + +class TestGmSafety(unittest.TestCase): + @classmethod + def setUp(cls): + cls.safety = libpandasafety_py.libpandasafety + cls.safety.safety_set_mode(3, 0) + cls.safety.init_tests_gm() + + def _send_msg(self, bus, addr, length): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = addr << 21 + to_send[0].RDTR = length + to_send[0].RDTR = bus << 4 + return to_send + + def _speed_msg(self, speed): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 842 << 21 + to_send[0].RDLR = speed + return to_send + + def _button_msg(self, buttons): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 481 << 21 + to_send[0].RDHR = buttons << 12 + return to_send + + def _brake_msg(self, brake): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 241 << 21 + to_send[0].RDLR = 0xa00 if brake else 0x900 + return to_send + + def _gas_msg(self, gas): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 417 << 21 + to_send[0].RDHR = (1 << 16) if gas else 0 + return to_send + + def _send_brake_msg(self, brake): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 789 << 21 + brake = (-brake) & 0xfff + to_send[0].RDLR = (brake >> 8) | ((brake &0xff) << 8) + return to_send + + def _send_gas_msg(self, gas): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 715 << 21 + to_send[0].RDLR = ((gas & 0x1f) << 27) | ((gas & 0xfe0) << 11) + return to_send + + def _set_prev_torque(self, t): + self.safety.set_gm_desired_torque_last(t) + self.safety.set_gm_rt_torque_last(t) + + def _torque_driver_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 388 << 21 + + t = twos_comp(torque, 11) + to_send[0].RDHR = (((t >> 8) & 0x7) << 16) | ((t & 0xFF) << 24) + return to_send + + def _torque_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 384 << 21 + + t = twos_comp(torque, 11) + to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8) + return to_send + + def test_default_controls_not_allowed(self): + self.assertFalse(self.safety.get_controls_allowed()) + + def test_resume_button(self): + RESUME_BTN = 2 + self.safety.set_controls_allowed(0) + self.safety.safety_rx_hook(self._button_msg(RESUME_BTN)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_set_button(self): + SET_BTN = 3 + self.safety.set_controls_allowed(0) + self.safety.safety_rx_hook(self._button_msg(SET_BTN)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_cancel_button(self): + CANCEL_BTN = 6 + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._button_msg(CANCEL_BTN)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_disengage_on_brake(self): + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._brake_msg(True)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_allow_brake_at_zero_speed(self): + # Brake was already pressed + self.safety.safety_rx_hook(self._brake_msg(True)) + self.safety.set_controls_allowed(1) + + self.safety.safety_rx_hook(self._brake_msg(True)) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._brake_msg(False)) + + def test_not_allow_brake_when_moving(self): + # Brake was already pressed + self.safety.safety_rx_hook(self._brake_msg(True)) + self.safety.safety_rx_hook(self._speed_msg(100)) + self.safety.set_controls_allowed(1) + + self.safety.safety_rx_hook(self._brake_msg(True)) + self.assertFalse(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._brake_msg(False)) + + def test_disengage_on_gas(self): + for long_controls_allowed in [0, 1]: + self.safety.set_long_controls_allowed(long_controls_allowed) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._gas_msg(True)) + if long_controls_allowed: + self.assertFalse(self.safety.get_controls_allowed()) + else: + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._gas_msg(False)) + + def test_allow_engage_with_gas_pressed(self): + self.safety.safety_rx_hook(self._gas_msg(True)) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._gas_msg(True)) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._gas_msg(False)) + + def test_brake_safety_check(self): + for long_controls_allowed in [0, 1]: + self.safety.set_long_controls_allowed(long_controls_allowed) + for enabled in [0, 1]: + for b in range(0, 500): + self.safety.set_controls_allowed(enabled) + if abs(b) > MAX_BRAKE or ((not enabled or not long_controls_allowed) and b != 0): + self.assertFalse(self.safety.safety_tx_hook(self._send_brake_msg(b))) + else: + self.assertTrue(self.safety.safety_tx_hook(self._send_brake_msg(b))) + self.safety.set_long_controls_allowed(True) + + def test_gas_safety_check(self): + for long_controls_allowed in [0, 1]: + self.safety.set_long_controls_allowed(long_controls_allowed) + for enabled in [0, 1]: + for g in range(0, 2**12-1): + self.safety.set_controls_allowed(enabled) + if abs(g) > MAX_GAS or ((not enabled or not long_controls_allowed) and g != MAX_REGEN): + self.assertFalse(self.safety.safety_tx_hook(self._send_gas_msg(g))) + else: + self.assertTrue(self.safety.safety_tx_hook(self._send_gas_msg(g))) + self.safety.set_long_controls_allowed(True) + + def test_steer_safety_check(self): + for enabled in [0, 1]: + for t in range(-0x200, 0x200): + self.safety.set_controls_allowed(enabled) + self._set_prev_torque(t) + if abs(t) > MAX_STEER or (not enabled and abs(t) > 0): + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(t))) + else: + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + def test_manually_enable_controls_allowed(self): + self.safety.set_controls_allowed(1) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.set_controls_allowed(0) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_non_realtime_limit_up(self): + self.safety.set_gm_torque_driver(0, 0) + self.safety.set_controls_allowed(True) + + self._set_prev_torque(0) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP))) + self._set_prev_torque(0) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP))) + + self._set_prev_torque(0) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1))) + self.safety.set_controls_allowed(True) + self._set_prev_torque(0) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP - 1))) + + def test_non_realtime_limit_down(self): + self.safety.set_gm_torque_driver(0, 0) + self.safety.set_controls_allowed(True) + + def test_against_torque_driver(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1): + t *= -sign + self.safety.set_gm_torque_driver(t, t) + self._set_prev_torque(MAX_STEER * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER * sign))) + + self.safety.set_gm_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_STEER))) + + # spot check some individual cases + for sign in [-1, 1]: + driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign + torque_desired = (MAX_STEER - 10 * DRIVER_TORQUE_FACTOR) * sign + delta = 1 * sign + self._set_prev_torque(torque_desired) + self.safety.set_gm_torque_driver(-driver_torque, -driver_torque) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(torque_desired))) + self._set_prev_torque(torque_desired + delta) + self.safety.set_gm_torque_driver(-driver_torque, -driver_torque) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(torque_desired + delta))) + + self._set_prev_torque(MAX_STEER * sign) + self.safety.set_gm_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN) * sign))) + self._set_prev_torque(MAX_STEER * sign) + self.safety.set_gm_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(0))) + self._set_prev_torque(MAX_STEER * sign) + self.safety.set_gm_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN + 1) * sign))) + + + def test_realtime_limits(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self.safety.init_tests_gm() + self._set_prev_torque(0) + self.safety.set_gm_torque_driver(0, 0) + for t in np.arange(0, MAX_RT_DELTA, 1): + t *= sign + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) + + self._set_prev_torque(0) + for t in np.arange(0, MAX_RT_DELTA, 1): + t *= sign + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + # Increase timer to update rt_torque_last + self.safety.set_timer(RT_INTERVAL + 1) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1)))) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) + + + def test_fwd_hook(self): + # nothing allowed + buss = range(0x0, 0x3) + msgs = range(0x1, 0x800) + + for b in buss: + for m in msgs: + # assume len 8 + self.assertEqual(-1, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/safety/test_honda.py b/tests/safety/test_honda.py new file mode 100755 index 0000000000..bc5e8d192f --- /dev/null +++ b/tests/safety/test_honda.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python2 +import unittest +import numpy as np +import libpandasafety_py + +MAX_BRAKE = 255 + +class TestHondaSafety(unittest.TestCase): + @classmethod + def setUp(cls): + cls.safety = libpandasafety_py.libpandasafety + cls.safety.safety_set_mode(1, 0) + cls.safety.init_tests_honda() + + def _send_msg(self, bus, addr, length): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = addr << 21 + to_send[0].RDTR = length + to_send[0].RDTR = bus << 4 + + return to_send + + def _speed_msg(self, speed): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x158 << 21 + to_send[0].RDLR = speed + + return to_send + + def _button_msg(self, buttons, msg): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = msg << 21 + to_send[0].RDLR = buttons << 5 + + return to_send + + def _brake_msg(self, brake): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x17C << 21 + to_send[0].RDHR = 0x200000 if brake else 0 + + return to_send + + def _alt_brake_msg(self, brake): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x1BE << 21 + to_send[0].RDLR = 0x10 if brake else 0 + + return to_send + + def _gas_msg(self, gas): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x17C << 21 + to_send[0].RDLR = 1 if gas else 0 + + return to_send + + def _send_brake_msg(self, brake): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x1FA << 21 + to_send[0].RDLR = ((brake & 0x3) << 8) | ((brake & 0x3FF) >> 2) + + return to_send + + def _send_interceptor_msg(self, gas, addr): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = addr << 21 + to_send[0].RDTR = 6 + to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) + + return to_send + + def _send_steer_msg(self, steer): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0xE4 << 21 + to_send[0].RDLR = steer + + return to_send + + def test_default_controls_not_allowed(self): + self.assertFalse(self.safety.get_controls_allowed()) + + def test_resume_button(self): + RESUME_BTN = 4 + self.safety.set_controls_allowed(0) + self.safety.safety_rx_hook(self._button_msg(RESUME_BTN, 0x1A6)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_set_button(self): + SET_BTN = 3 + self.safety.set_controls_allowed(0) + self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_cancel_button(self): + CANCEL_BTN = 2 + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._button_msg(CANCEL_BTN, 0x1A6)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_sample_speed(self): + self.assertEqual(0, self.safety.get_honda_ego_speed()) + self.safety.safety_rx_hook(self._speed_msg(100)) + self.assertEqual(100, self.safety.get_honda_ego_speed()) + + def test_prev_brake(self): + self.assertFalse(self.safety.get_honda_brake_prev()) + self.safety.safety_rx_hook(self._brake_msg(True)) + self.assertTrue(self.safety.get_honda_brake_prev()) + + def test_disengage_on_brake(self): + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._brake_msg(1)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_alt_disengage_on_brake(self): + self.safety.set_honda_alt_brake_msg(1) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._alt_brake_msg(1)) + self.assertFalse(self.safety.get_controls_allowed()) + + self.safety.set_honda_alt_brake_msg(0) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._alt_brake_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_allow_brake_at_zero_speed(self): + # Brake was already pressed + self.safety.safety_rx_hook(self._brake_msg(True)) + self.safety.set_controls_allowed(1) + + self.safety.safety_rx_hook(self._brake_msg(True)) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._brake_msg(False)) # reset no brakes + + def test_not_allow_brake_when_moving(self): + # Brake was already pressed + self.safety.safety_rx_hook(self._brake_msg(True)) + self.safety.safety_rx_hook(self._speed_msg(100)) + self.safety.set_controls_allowed(1) + + self.safety.safety_rx_hook(self._brake_msg(True)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_prev_gas(self): + self.safety.safety_rx_hook(self._gas_msg(False)) + self.assertFalse(self.safety.get_honda_gas_prev()) + self.safety.safety_rx_hook(self._gas_msg(True)) + self.assertTrue(self.safety.get_honda_gas_prev()) + + def test_prev_gas_interceptor(self): + self.safety.safety_rx_hook(self._send_interceptor_msg(0x0, 0x201)) + self.assertFalse(self.safety.get_gas_interceptor_prev()) + self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201)) + self.assertTrue(self.safety.get_gas_interceptor_prev()) + self.safety.safety_rx_hook(self._send_interceptor_msg(0x0, 0x201)) + self.safety.set_gas_interceptor_detected(False) + + def test_disengage_on_gas(self): + for long_controls_allowed in [0, 1]: + self.safety.set_long_controls_allowed(long_controls_allowed) + self.safety.safety_rx_hook(self._gas_msg(0)) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._gas_msg(1)) + if long_controls_allowed: + self.assertFalse(self.safety.get_controls_allowed()) + else: + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.set_long_controls_allowed(True) + + def test_allow_engage_with_gas_pressed(self): + self.safety.safety_rx_hook(self._gas_msg(1)) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._gas_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_disengage_on_gas_interceptor(self): + for long_controls_allowed in [0, 1]: + self.safety.set_long_controls_allowed(long_controls_allowed) + self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201)) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201)) + if long_controls_allowed: + self.assertFalse(self.safety.get_controls_allowed()) + else: + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201)) + self.safety.set_gas_interceptor_detected(False) + self.safety.set_long_controls_allowed(True) + + def test_allow_engage_with_gas_interceptor_pressed(self): + self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201)) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201)) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201)) + self.safety.set_gas_interceptor_detected(False) + + def test_brake_safety_check(self): + for long_controls_allowed in [0, 1]: + self.safety.set_long_controls_allowed(long_controls_allowed) + for brake in np.arange(0, MAX_BRAKE + 10, 1): + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + if controls_allowed and long_controls_allowed: + send = MAX_BRAKE >= brake >= 0 + else: + send = brake == 0 + self.assertEqual(send, self.safety.safety_tx_hook(self._send_brake_msg(brake))) + self.safety.set_long_controls_allowed(True) + + def test_gas_interceptor_safety_check(self): + for long_controls_allowed in [0, 1]: + self.safety.set_long_controls_allowed(long_controls_allowed) + for gas in np.arange(0, 4000, 100): + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + if controls_allowed and long_controls_allowed: + send = True + else: + send = gas == 0 + self.assertEqual(send, self.safety.safety_tx_hook(self._send_interceptor_msg(gas, 0x200))) + self.safety.set_long_controls_allowed(True) + + def test_steer_safety_check(self): + self.safety.set_controls_allowed(0) + self.assertTrue(self.safety.safety_tx_hook(self._send_steer_msg(0x0000))) + self.assertFalse(self.safety.safety_tx_hook(self._send_steer_msg(0x1000))) + + def test_spam_cancel_safety_check(self): + RESUME_BTN = 4 + SET_BTN = 3 + CANCEL_BTN = 2 + BUTTON_MSG = 0x296 + self.safety.set_honda_bosch_hardware(1) + self.safety.set_controls_allowed(0) + self.assertTrue(self.safety.safety_tx_hook(self._button_msg(CANCEL_BTN, BUTTON_MSG))) + self.assertFalse(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN, BUTTON_MSG))) + self.assertFalse(self.safety.safety_tx_hook(self._button_msg(SET_BTN, BUTTON_MSG))) + # do not block resume if we are engaged already + self.safety.set_controls_allowed(1) + self.assertTrue(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN, BUTTON_MSG))) + + def test_fwd_hook(self): + buss = range(0x0, 0x3) + msgs = range(0x1, 0x800) + long_controls_allowed = [0, 1] + + self.safety.set_honda_bosch_hardware(0) + + for l in long_controls_allowed: + self.safety.set_long_controls_allowed(l) + blocked_msgs = [0xE4, 0x194, 0x33D] + if l: + blocked_msgs += [0x1FA ,0x30C, 0x39F] + for b in buss: + for m in msgs: + if b == 0: + fwd_bus = 2 + elif b == 1: + fwd_bus = -1 + elif b == 2: + fwd_bus = -1 if m in blocked_msgs else 0 + + # assume len 8 + self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) + + self.safety.set_long_controls_allowed(True) + + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/safety/test_honda_bosch.py b/tests/safety/test_honda_bosch.py new file mode 100755 index 0000000000..11c9391403 --- /dev/null +++ b/tests/safety/test_honda_bosch.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python2 +import unittest +import numpy as np +import libpandasafety_py + +MAX_BRAKE = 255 + +class TestHondaSafety(unittest.TestCase): + @classmethod + def setUp(cls): + cls.safety = libpandasafety_py.libpandasafety + cls.safety.safety_set_mode(4, 0) + cls.safety.init_tests_honda() + + def _send_msg(self, bus, addr, length): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = addr << 21 + to_send[0].RDTR = length + to_send[0].RDTR = bus << 4 + + return to_send + + def test_fwd_hook(self): + buss = range(0x0, 0x3) + msgs = range(0x1, 0x800) + + blocked_msgs = [0xE4, 0x33D] + for b in buss: + for m in msgs: + if b == 0: + fwd_bus = -1 + elif b == 1: + fwd_bus = -1 if m in blocked_msgs else 2 + elif b == 2: + fwd_bus = 1 + + # assume len 8 + self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/safety/test_hyundai.py b/tests/safety/test_hyundai.py new file mode 100644 index 0000000000..539982fab9 --- /dev/null +++ b/tests/safety/test_hyundai.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python2 +import unittest +import numpy as np +import libpandasafety_py + +MAX_RATE_UP = 3 +MAX_RATE_DOWN = 7 +MAX_STEER = 255 + +MAX_RT_DELTA = 112 +RT_INTERVAL = 250000 + +DRIVER_TORQUE_ALLOWANCE = 50; +DRIVER_TORQUE_FACTOR = 2; + +def twos_comp(val, bits): + if val >= 0: + return val + else: + return (2**bits) + val + +def sign(a): + if a > 0: + return 1 + else: + return -1 + +class TestHyundaiSafety(unittest.TestCase): + @classmethod + def setUp(cls): + cls.safety = libpandasafety_py.libpandasafety + cls.safety.safety_set_mode(7, 0) + cls.safety.init_tests_hyundai() + + def _send_msg(self, bus, addr, length): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = addr << 21 + to_send[0].RDTR = length + to_send[0].RDTR = bus << 4 + return to_send + + def _button_msg(self, buttons): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 1265 << 21 + to_send[0].RDLR = buttons + return to_send + + def _set_prev_torque(self, t): + self.safety.set_hyundai_desired_torque_last(t) + self.safety.set_hyundai_rt_torque_last(t) + + def _torque_driver_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 897 << 21 + to_send[0].RDLR = (torque + 2048) << 11 + return to_send + + def _torque_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 832 << 21 + to_send[0].RDLR = (torque + 1024) << 16 + return to_send + + def test_default_controls_not_allowed(self): + self.assertFalse(self.safety.get_controls_allowed()) + + def test_steer_safety_check(self): + for enabled in [0, 1]: + for t in range(-0x200, 0x200): + self.safety.set_controls_allowed(enabled) + self._set_prev_torque(t) + if abs(t) > MAX_STEER or (not enabled and abs(t) > 0): + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(t))) + else: + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + def test_manually_enable_controls_allowed(self): + self.safety.set_controls_allowed(1) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.set_controls_allowed(0) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_enable_control_allowed_from_cruise(self): + to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_push[0].RIR = 1057 << 21 + to_push[0].RDLR = 1 << 13 + + self.safety.safety_rx_hook(to_push) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_disable_control_allowed_from_cruise(self): + to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_push[0].RIR = 1057 << 21 + to_push[0].RDLR = 0 + + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(to_push) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_non_realtime_limit_up(self): + self.safety.set_hyundai_torque_driver(0, 0) + self.safety.set_controls_allowed(True) + + self._set_prev_torque(0) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP))) + self._set_prev_torque(0) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP))) + + self._set_prev_torque(0) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1))) + self.safety.set_controls_allowed(True) + self._set_prev_torque(0) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP - 1))) + + def test_non_realtime_limit_down(self): + self.safety.set_hyundai_torque_driver(0, 0) + self.safety.set_controls_allowed(True) + + def test_against_torque_driver(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1): + t *= -sign + self.safety.set_hyundai_torque_driver(t, t) + self._set_prev_torque(MAX_STEER * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER * sign))) + + self.safety.set_hyundai_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_STEER))) + + # spot check some individual cases + for sign in [-1, 1]: + driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign + torque_desired = (MAX_STEER - 10 * DRIVER_TORQUE_FACTOR) * sign + delta = 1 * sign + self._set_prev_torque(torque_desired) + self.safety.set_hyundai_torque_driver(-driver_torque, -driver_torque) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(torque_desired))) + self._set_prev_torque(torque_desired + delta) + self.safety.set_hyundai_torque_driver(-driver_torque, -driver_torque) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(torque_desired + delta))) + + self._set_prev_torque(MAX_STEER * sign) + self.safety.set_hyundai_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN) * sign))) + self._set_prev_torque(MAX_STEER * sign) + self.safety.set_hyundai_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(0))) + self._set_prev_torque(MAX_STEER * sign) + self.safety.set_hyundai_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN + 1) * sign))) + + + def test_realtime_limits(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self.safety.init_tests_hyundai() + self._set_prev_torque(0) + self.safety.set_hyundai_torque_driver(0, 0) + for t in np.arange(0, MAX_RT_DELTA, 1): + t *= sign + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) + + self._set_prev_torque(0) + for t in np.arange(0, MAX_RT_DELTA, 1): + t *= sign + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + # Increase timer to update rt_torque_last + self.safety.set_timer(RT_INTERVAL + 1) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1)))) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) + + + #def test_spam_cancel_safety_check(self): + # RESUME_BTN = 1 + # SET_BTN = 2 + # CANCEL_BTN = 4 + # BUTTON_MSG = 1265 + # self.safety.set_controls_allowed(0) + # self.assertTrue(self.safety.safety_tx_hook(self._button_msg(CANCEL_BTN))) + # self.assertFalse(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN))) + # self.assertFalse(self.safety.safety_tx_hook(self._button_msg(SET_BTN))) + # # do not block resume if we are engaged already + # self.safety.set_controls_allowed(1) + # self.assertTrue(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN))) + + def test_fwd_hook(self): + + buss = range(0x0, 0x3) + msgs = range(0x1, 0x800) + hyundai_giraffe_switch_2 = [0, 1] + + self.safety.set_hyundai_camera_bus(2) + for hgs in hyundai_giraffe_switch_2: + self.safety.set_hyundai_giraffe_switch_2(hgs) + blocked_msgs = [832] + for b in buss: + for m in msgs: + if hgs: + if b == 0: + fwd_bus = 2 + elif b == 1: + fwd_bus = -1 + elif b == 2: + fwd_bus = -1 if m in blocked_msgs else 0 + else: + fwd_bus = -1 + + # assume len 8 + self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/safety/test_subaru.py b/tests/safety/test_subaru.py new file mode 100644 index 0000000000..13fe1fb14f --- /dev/null +++ b/tests/safety/test_subaru.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python2 +import unittest +import numpy as np +import libpandasafety_py + +MAX_RATE_UP = 50 +MAX_RATE_DOWN = 70 +MAX_STEER = 2047 + +MAX_RT_DELTA = 940 +RT_INTERVAL = 250000 + +DRIVER_TORQUE_ALLOWANCE = 60; +DRIVER_TORQUE_FACTOR = 10; + +def twos_comp(val, bits): + if val >= 0: + return val + else: + return (2**bits) + val + +def sign(a): + if a > 0: + return 1 + else: + return -1 + +class TestSubaruSafety(unittest.TestCase): + @classmethod + def setUp(cls): + cls.safety = libpandasafety_py.libpandasafety + cls.safety.safety_set_mode(10, 0) + cls.safety.init_tests_subaru() + + def _send_msg(self, bus, addr, length): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = addr << 21 + to_send[0].RDTR = length + to_send[0].RDTR = bus << 4 + return to_send + + def _set_prev_torque(self, t): + self.safety.set_subaru_desired_torque_last(t) + self.safety.set_subaru_rt_torque_last(t) + + def _torque_driver_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x119 << 21 + + t = twos_comp(torque, 11) + to_send[0].RDLR = ((t & 0x7FF) << 16) + return to_send + + def _torque_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x122 << 21 + + t = twos_comp(torque, 13) + to_send[0].RDLR = (t << 16) + return to_send + + def test_default_controls_not_allowed(self): + self.assertFalse(self.safety.get_controls_allowed()) + + def test_enable_control_allowed_from_cruise(self): + to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_push[0].RIR = 0x240 << 21 + to_push[0].RDHR = 1 << 9 + + self.safety.safety_rx_hook(to_push) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_disable_control_allowed_from_cruise(self): + to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_push[0].RIR = 0x240 << 21 + to_push[0].RDHR = 0 + + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(to_push) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_steer_safety_check(self): + for enabled in [0, 1]: + for t in range(-3000, 3000): + self.safety.set_controls_allowed(enabled) + self._set_prev_torque(t) + if abs(t) > MAX_STEER or (not enabled and abs(t) > 0): + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(t))) + else: + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + def test_manually_enable_controls_allowed(self): + self.safety.set_controls_allowed(1) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.set_controls_allowed(0) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_non_realtime_limit_up(self): + self.safety.set_subaru_torque_driver(0, 0) + self.safety.set_controls_allowed(True) + + self._set_prev_torque(0) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP))) + self._set_prev_torque(0) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP))) + + self._set_prev_torque(0) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1))) + self.safety.set_controls_allowed(True) + self._set_prev_torque(0) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP - 1))) + + def test_non_realtime_limit_down(self): + self.safety.set_subaru_torque_driver(0, 0) + self.safety.set_controls_allowed(True) + + def test_against_torque_driver(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1): + t *= -sign + self.safety.set_subaru_torque_driver(t, t) + self._set_prev_torque(MAX_STEER * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER * sign))) + + self.safety.set_subaru_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_STEER))) + + # spot check some individual cases + for sign in [-1, 1]: + driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign + torque_desired = (MAX_STEER - 10 * DRIVER_TORQUE_FACTOR) * sign + delta = 1 * sign + self._set_prev_torque(torque_desired) + self.safety.set_subaru_torque_driver(-driver_torque, -driver_torque) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(torque_desired))) + self._set_prev_torque(torque_desired + delta) + self.safety.set_subaru_torque_driver(-driver_torque, -driver_torque) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(torque_desired + delta))) + + self._set_prev_torque(MAX_STEER * sign) + self.safety.set_subaru_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN) * sign))) + self._set_prev_torque(MAX_STEER * sign) + self.safety.set_subaru_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(0))) + self._set_prev_torque(MAX_STEER * sign) + self.safety.set_subaru_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN + 1) * sign))) + + + def test_realtime_limits(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self.safety.init_tests_subaru() + self._set_prev_torque(0) + self.safety.set_subaru_torque_driver(0, 0) + for t in np.arange(0, MAX_RT_DELTA, 1): + t *= sign + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) + + self._set_prev_torque(0) + for t in np.arange(0, MAX_RT_DELTA, 1): + t *= sign + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + # Increase timer to update rt_torque_last + self.safety.set_timer(RT_INTERVAL + 1) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1)))) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) + + + def test_fwd_hook(self): + buss = range(0x0, 0x3) + msgs = range(0x1, 0x800) + blocked_msgs = [290, 356, 545, 802] + for b in buss: + for m in msgs: + if b == 0: + fwd_bus = 2 + elif b == 1: + fwd_bus = -1 + elif b == 2: + fwd_bus = -1 if m in blocked_msgs else 0 + + # assume len 8 + self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/safety/test_toyota.py b/tests/safety/test_toyota.py new file mode 100644 index 0000000000..7dd1601d77 --- /dev/null +++ b/tests/safety/test_toyota.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python2 +import unittest +import numpy as np +import libpandasafety_py + +MAX_RATE_UP = 10 +MAX_RATE_DOWN = 25 +MAX_TORQUE = 1500 + +MAX_ACCEL = 1500 +MIN_ACCEL = -3000 + +MAX_RT_DELTA = 375 +RT_INTERVAL = 250000 + +MAX_TORQUE_ERROR = 350 + +def twos_comp(val, bits): + if val >= 0: + return val + else: + return (2**bits) + val + +def sign(a): + if a > 0: + return 1 + else: + return -1 + +class TestToyotaSafety(unittest.TestCase): + @classmethod + def setUp(cls): + cls.safety = libpandasafety_py.libpandasafety + cls.safety.safety_set_mode(2, 100) + cls.safety.init_tests_toyota() + + def _send_msg(self, bus, addr, length): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = addr << 21 + to_send[0].RDTR = length + to_send[0].RDTR = bus << 4 + return to_send + + def _set_prev_torque(self, t): + self.safety.set_toyota_desired_torque_last(t) + self.safety.set_toyota_rt_torque_last(t) + self.safety.set_toyota_torque_meas(t, t) + + def _torque_meas_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x260 << 21 + + t = twos_comp(torque, 16) + to_send[0].RDHR = t | ((t & 0xFF) << 16) + return to_send + + def _torque_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x2E4 << 21 + + t = twos_comp(torque, 16) + to_send[0].RDLR = t | ((t & 0xFF) << 16) + return to_send + + def _accel_msg(self, accel): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x343 << 21 + + a = twos_comp(accel, 16) + to_send[0].RDLR = (a & 0xFF) << 8 | (a >> 8) + return to_send + + def _send_gas_msg(self, gas): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x2C1 << 21 + to_send[0].RDHR = (gas & 0xFF) << 16 + + return to_send + + def _send_interceptor_msg(self, gas, addr): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = addr << 21 + to_send[0].RDTR = 6 + to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) + + return to_send + + def _pcm_cruise_msg(self, cruise_on): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x1D2 << 21 + to_send[0].RDLR = cruise_on << 5 + + return to_send + + def test_default_controls_not_allowed(self): + self.assertFalse(self.safety.get_controls_allowed()) + + def test_manually_enable_controls_allowed(self): + self.safety.set_controls_allowed(1) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_enable_control_allowed_from_cruise(self): + self.safety.safety_rx_hook(self._pcm_cruise_msg(False)) + self.assertFalse(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._pcm_cruise_msg(True)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_disable_control_allowed_from_cruise(self): + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._pcm_cruise_msg(False)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_prev_gas(self): + for g in range(0, 256): + self.safety.safety_rx_hook(self._send_gas_msg(g)) + self.assertEqual(g, self.safety.get_toyota_gas_prev()) + + def test_prev_gas_interceptor(self): + self.safety.safety_rx_hook(self._send_interceptor_msg(0x0, 0x201)) + self.assertFalse(self.safety.get_gas_interceptor_prev()) + self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201)) + self.assertTrue(self.safety.get_gas_interceptor_prev()) + self.safety.safety_rx_hook(self._send_interceptor_msg(0x0, 0x201)) + self.safety.set_gas_interceptor_detected(False) + + def test_disengage_on_gas(self): + for long_controls_allowed in [0, 1]: + self.safety.set_long_controls_allowed(long_controls_allowed) + self.safety.safety_rx_hook(self._send_gas_msg(0)) + self.safety.set_controls_allowed(True) + self.safety.safety_rx_hook(self._send_gas_msg(1)) + if long_controls_allowed: + self.assertFalse(self.safety.get_controls_allowed()) + else: + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.set_long_controls_allowed(True) + + def test_allow_engage_with_gas_pressed(self): + self.safety.safety_rx_hook(self._send_gas_msg(1)) + self.safety.set_controls_allowed(True) + self.safety.safety_rx_hook(self._send_gas_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._send_gas_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_disengage_on_gas_interceptor(self): + for long_controls_allowed in [0, 1]: + self.safety.set_long_controls_allowed(long_controls_allowed) + self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201)) + self.safety.set_controls_allowed(True) + self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201)) + if long_controls_allowed: + self.assertFalse(self.safety.get_controls_allowed()) + else: + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201)) + self.safety.set_gas_interceptor_detected(False) + self.safety.set_long_controls_allowed(True) + + def test_allow_engage_with_gas_interceptor_pressed(self): + self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201)) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201)) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201)) + self.safety.set_gas_interceptor_detected(False) + + def test_accel_actuation_limits(self): + for long_controls_allowed in [0, 1]: + self.safety.set_long_controls_allowed(long_controls_allowed) + for accel in np.arange(MIN_ACCEL - 1000, MAX_ACCEL + 1000, 100): + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + if controls_allowed and long_controls_allowed: + send = MIN_ACCEL <= accel <= MAX_ACCEL + else: + send = accel == 0 + self.assertEqual(send, self.safety.safety_tx_hook(self._accel_msg(accel))) + self.safety.set_long_controls_allowed(True) + + def test_torque_absolute_limits(self): + for controls_allowed in [True, False]: + for torque in np.arange(-MAX_TORQUE - 1000, MAX_TORQUE + 1000, MAX_RATE_UP): + self.safety.set_controls_allowed(controls_allowed) + self.safety.set_toyota_rt_torque_last(torque) + self.safety.set_toyota_torque_meas(torque, torque) + self.safety.set_toyota_desired_torque_last(torque - MAX_RATE_UP) + + if controls_allowed: + send = (-MAX_TORQUE <= torque <= MAX_TORQUE) + else: + send = torque == 0 + + self.assertEqual(send, self.safety.safety_tx_hook(self._torque_msg(torque))) + + def test_non_realtime_limit_up(self): + self.safety.set_controls_allowed(True) + + self._set_prev_torque(0) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP))) + + self._set_prev_torque(0) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1))) + + def test_non_realtime_limit_down(self): + self.safety.set_controls_allowed(True) + + self.safety.set_toyota_rt_torque_last(1000) + self.safety.set_toyota_torque_meas(500, 500) + self.safety.set_toyota_desired_torque_last(1000) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(1000 - MAX_RATE_DOWN))) + + self.safety.set_toyota_rt_torque_last(1000) + self.safety.set_toyota_torque_meas(500, 500) + self.safety.set_toyota_desired_torque_last(1000) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(1000 - MAX_RATE_DOWN + 1))) + + def test_exceed_torque_sensor(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self._set_prev_torque(0) + for t in np.arange(0, MAX_TORQUE_ERROR + 10, 10): + t *= sign + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_TORQUE_ERROR + 10)))) + + def test_realtime_limit_up(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self.safety.init_tests_toyota() + self._set_prev_torque(0) + for t in np.arange(0, 380, 10): + t *= sign + self.safety.set_toyota_torque_meas(t, t) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * 380))) + + self._set_prev_torque(0) + for t in np.arange(0, 370, 10): + t *= sign + self.safety.set_toyota_torque_meas(t, t) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t))) + + # Increase timer to update rt_torque_last + self.safety.set_timer(RT_INTERVAL + 1) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * 370))) + self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * 380))) + + def test_torque_measurements(self): + self.safety.safety_rx_hook(self._torque_meas_msg(50)) + self.safety.safety_rx_hook(self._torque_meas_msg(-50)) + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + + self.assertEqual(-51, self.safety.get_toyota_torque_meas_min()) + self.assertEqual(51, self.safety.get_toyota_torque_meas_max()) + + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + self.assertEqual(1, self.safety.get_toyota_torque_meas_max()) + self.assertEqual(-51, self.safety.get_toyota_torque_meas_min()) + + self.safety.safety_rx_hook(self._torque_meas_msg(0)) + self.assertEqual(1, self.safety.get_toyota_torque_meas_max()) + self.assertEqual(-1, self.safety.get_toyota_torque_meas_min()) + + def test_gas_interceptor_safety_check(self): + + self.safety.set_controls_allowed(0) + self.assertTrue(self.safety.safety_tx_hook(self._send_interceptor_msg(0, 0x200))) + self.assertFalse(self.safety.safety_tx_hook(self._send_interceptor_msg(0x1000, 0x200))) + self.safety.set_controls_allowed(1) + self.assertTrue(self.safety.safety_tx_hook(self._send_interceptor_msg(0x1000, 0x200))) + + def test_fwd_hook(self): + + buss = range(0x0, 0x3) + msgs = range(0x1, 0x800) + long_controls_allowed = [0, 1] + toyota_camera_forwarded = [0, 1] + + for tcf in toyota_camera_forwarded: + self.safety.set_toyota_camera_forwarded(tcf) + for lca in long_controls_allowed: + self.safety.set_long_controls_allowed(lca) + blocked_msgs = [0x2E4, 0x412, 0x191] + if lca: + blocked_msgs += [0x343] + for b in buss: + for m in msgs: + if tcf: + if b == 0: + fwd_bus = 2 + elif b == 1: + fwd_bus = -1 + elif b == 2: + fwd_bus = -1 if m in blocked_msgs else 0 + else: + fwd_bus = -1 + + # assume len 8 + self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) + + self.safety.set_long_controls_allowed(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/safety/test_toyota_ipas.py b/tests/safety/test_toyota_ipas.py new file mode 100644 index 0000000000..7a382093e0 --- /dev/null +++ b/tests/safety/test_toyota_ipas.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python2 +import unittest +import numpy as np +import libpandasafety_py + +IPAS_OVERRIDE_THRESHOLD = 200 + +ANGLE_DELTA_BP = [0., 5., 15.] +ANGLE_DELTA_V = [5., .8, .15] # windup limit +ANGLE_DELTA_VU = [5., 3.5, 0.4] # unwind limit + +def twos_comp(val, bits): + if val >= 0: + return val + else: + return (2**bits) + val + +def sign(a): + if a > 0: + return 1 + else: + return -1 + +class TestToyotaSafety(unittest.TestCase): + @classmethod + def setUp(cls): + cls.safety = libpandasafety_py.libpandasafety + cls.safety.safety_set_mode(0x1335, 66) + cls.safety.init_tests_toyota() + + def _torque_driver_msg(self, torque): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x260 << 21 + + t = twos_comp(torque, 16) + to_send[0].RDLR = t | ((t & 0xFF) << 16) + return to_send + + def _torque_driver_msg_array(self, torque): + for i in range(6): + self.safety.safety_rx_hook(self._torque_driver_msg(torque)) + + def _angle_meas_msg(self, angle): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x25 << 21 + + t = twos_comp(angle, 12) + to_send[0].RDLR = ((t & 0xF00) >> 8) | ((t & 0xFF) << 8) + return to_send + + def _angle_meas_msg_array(self, angle): + for i in range(6): + self.safety.safety_rx_hook(self._angle_meas_msg(angle)) + + def _ipas_state_msg(self, state): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x262 << 21 + + to_send[0].RDLR = state & 0xF + return to_send + + def _ipas_control_msg(self, angle, state): + # note: we command 2/3 of the angle due to CAN conversion + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0x266 << 21 + + t = twos_comp(angle, 12) + to_send[0].RDLR = ((t & 0xF00) >> 8) | ((t & 0xFF) << 8) + to_send[0].RDLR |= ((state & 0xf) << 4) + + return to_send + + def _speed_msg(self, speed): + to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + to_send[0].RIR = 0xb4 << 21 + speed = int(speed * 100 * 3.6) + + to_send[0].RDHR = ((speed & 0xFF) << 16) | (speed & 0xFF00) + return to_send + + def test_ipas_override(self): + + ## angle control is not active + self.safety.set_controls_allowed(1) + + # 3 consecutive msgs where driver exceeds threshold but angle_control isn't active + self.safety.set_controls_allowed(1) + self._torque_driver_msg_array(IPAS_OVERRIDE_THRESHOLD + 1) + self.assertTrue(self.safety.get_controls_allowed()) + + self._torque_driver_msg_array(-IPAS_OVERRIDE_THRESHOLD - 1) + self.assertTrue(self.safety.get_controls_allowed()) + + # ipas state is override + self.safety.safety_rx_hook(self._ipas_state_msg(5)) + self.assertTrue(self.safety.get_controls_allowed()) + + ## now angle control is active + self.safety.safety_tx_hook(self._ipas_control_msg(0, 0)) + self.safety.safety_rx_hook(self._ipas_state_msg(0)) + + # 3 consecutive msgs where driver does exceed threshold + self.safety.set_controls_allowed(1) + self._torque_driver_msg_array(IPAS_OVERRIDE_THRESHOLD + 1) + self.assertFalse(self.safety.get_controls_allowed()) + + self.safety.set_controls_allowed(1) + self._torque_driver_msg_array(-IPAS_OVERRIDE_THRESHOLD - 1) + self.assertFalse(self.safety.get_controls_allowed()) + + # ipas state is override and torque isn't overriding any more + self.safety.set_controls_allowed(1) + self._torque_driver_msg_array(0) + self.safety.safety_rx_hook(self._ipas_state_msg(5)) + self.assertFalse(self.safety.get_controls_allowed()) + + # 3 consecutive msgs where driver does not exceed threshold and + # ipas state is not override + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._ipas_state_msg(0)) + self.assertTrue(self.safety.get_controls_allowed()) + + self._torque_driver_msg_array(IPAS_OVERRIDE_THRESHOLD) + self.assertTrue(self.safety.get_controls_allowed()) + + self._torque_driver_msg_array(-IPAS_OVERRIDE_THRESHOLD) + self.assertTrue(self.safety.get_controls_allowed()) + + # reset no angle control at the end of the test + self.safety.reset_angle_control() + + def test_angle_cmd_when_disabled(self): + + self.safety.set_controls_allowed(0) + + # test angle cmd too far from actual + angle_refs = [-10, 10] + deltas = range(-2, 3) + expected_results = [False, True, True, True, False] + + for a in angle_refs: + self._angle_meas_msg_array(a) + for i, d in enumerate(deltas): + self.assertEqual(expected_results[i], self.safety.safety_tx_hook(self._ipas_control_msg(a + d, 1))) + + # test ipas state cmd enabled + self._angle_meas_msg_array(0) + self.assertEqual(0, self.safety.safety_tx_hook(self._ipas_control_msg(0, 3))) + + # reset no angle control at the end of the test + self.safety.reset_angle_control() + + def test_angle_cmd_when_enabled(self): + + # ipas angle cmd should pass through when controls are enabled + + self.safety.set_controls_allowed(1) + self._angle_meas_msg_array(0) + self.safety.safety_rx_hook(self._speed_msg(0.1)) + + self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(0, 1))) + self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(4, 1))) + self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(0, 3))) + self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(-4, 3))) + self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(-8, 3))) + + # reset no angle control at the end of the test + self.safety.reset_angle_control() + + def test_angle_cmd_rate_when_disabled(self): + + # as long as the command is close to the measured, no rate limit is enforced when + # controls are disabled + self.safety.set_controls_allowed(0) + self.safety.safety_rx_hook(self._angle_meas_msg(0)) + self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(0, 1))) + self.safety.safety_rx_hook(self._angle_meas_msg(100)) + self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(100, 1))) + self.safety.safety_rx_hook(self._angle_meas_msg(-100)) + self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(-100, 1))) + + # reset no angle control at the end of the test + self.safety.reset_angle_control() + + def test_angle_cmd_rate_when_enabled(self): + + # when controls are allowed, angle cmd rate limit is enforced + # test 1: no limitations if we stay within limits + speeds = [0., 1., 5., 10., 15., 100.] + angles = [-300, -100, -10, 0, 10, 100, 300] + for a in angles: + for s in speeds: + + # first test against false positives + self._angle_meas_msg_array(a) + self.safety.safety_tx_hook(self._ipas_control_msg(a, 1)) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._speed_msg(s)) + max_delta_up = int(np.interp(s, ANGLE_DELTA_BP, ANGLE_DELTA_V) * 2 / 3. + 1.) + max_delta_down = int(np.interp(s, ANGLE_DELTA_BP, ANGLE_DELTA_VU) * 2 / 3. + 1.) + self.assertEqual(True, self.safety.safety_tx_hook(self._ipas_control_msg(a + sign(a) * max_delta_up, 1))) + self.assertTrue(self.safety.get_controls_allowed()) + self.assertEqual(True, self.safety.safety_tx_hook(self._ipas_control_msg(a, 1))) + self.assertTrue(self.safety.get_controls_allowed()) + self.assertEqual(True, self.safety.safety_tx_hook(self._ipas_control_msg(a - sign(a) * max_delta_down, 1))) + self.assertTrue(self.safety.get_controls_allowed()) + + # now inject too high rates + self.assertEqual(False, self.safety.safety_tx_hook(self._ipas_control_msg(a + sign(a) * + (max_delta_up + 1), 1))) + self.assertFalse(self.safety.get_controls_allowed()) + self.safety.set_controls_allowed(1) + self.assertEqual(True, self.safety.safety_tx_hook(self._ipas_control_msg(a + sign(a) * max_delta_up, 1))) + self.assertTrue(self.safety.get_controls_allowed()) + self.assertEqual(True, self.safety.safety_tx_hook(self._ipas_control_msg(a, 1))) + self.assertTrue(self.safety.get_controls_allowed()) + self.assertEqual(False, self.safety.safety_tx_hook(self._ipas_control_msg(a - sign(a) * + (max_delta_down + 1), 1))) + self.assertFalse(self.safety.get_controls_allowed()) + + # reset no angle control at the end of the test + self.safety.reset_angle_control() + + def test_angle_measured_rate(self): + + speeds = [0., 1., 5., 10., 15., 100.] + angles = [-300, -100, -10, 0, 10, 100, 300] + angles = [10] + for a in angles: + for s in speeds: + self._angle_meas_msg_array(a) + self.safety.safety_tx_hook(self._ipas_control_msg(a, 1)) + self.safety.set_controls_allowed(1) + self.safety.safety_rx_hook(self._speed_msg(s)) + max_delta_up = int(np.interp(s, ANGLE_DELTA_BP, ANGLE_DELTA_V) * 2 / 3. + 1.) + max_delta_down = int(np.interp(s, ANGLE_DELTA_BP, ANGLE_DELTA_VU) * 2 / 3. + 1.) + self.safety.safety_rx_hook(self._angle_meas_msg(a)) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.safety_rx_hook(self._angle_meas_msg(a + 150)) + self.assertFalse(self.safety.get_controls_allowed()) + + # reset no angle control at the end of the test + self.safety.reset_angle_control() + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/safety_replay/Dockerfile b/tests/safety_replay/Dockerfile new file mode 100644 index 0000000000..bf2b7f2c1f --- /dev/null +++ b/tests/safety_replay/Dockerfile @@ -0,0 +1,20 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y make clang python python-pip git libarchive-dev libusb-1.0-0 + +COPY tests/safety_replay/requirements.txt requirements.txt +RUN pip install -r requirements.txt +COPY tests/safety_replay/install_capnp.sh install_capnp.sh +RUN ./install_capnp.sh + +RUN mkdir /openpilot +WORKDIR /openpilot +RUN git clone https://github.com/commaai/cereal.git || true +WORKDIR /openpilot/cereal +RUN git checkout f7043fde062cbfd49ec90af669901a9caba52de9 +COPY . /openpilot/panda + +WORKDIR /openpilot/panda/tests/safety_replay +RUN git clone https://github.com/commaai/openpilot-tools.git tools || true +WORKDIR tools +RUN git checkout b6461274d684915f39dc45efc5292ea890698da9 diff --git a/tests/safety_replay/__init__.py b/tests/safety_replay/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/safety_replay/helpers.py b/tests/safety_replay/helpers.py new file mode 100644 index 0000000000..05bbe08930 --- /dev/null +++ b/tests/safety_replay/helpers.py @@ -0,0 +1,104 @@ +import struct +import panda.tests.safety.libpandasafety_py as libpandasafety_py + +safety_modes = { + "NOOUTPUT": 0, + "HONDA": 1, + "TOYOTA": 2, + "GM": 3, + "HONDA_BOSCH": 4, + "FORD": 5, + "CADILLAC": 6, + "HYUNDAI": 7, + "TESLA": 8, + "CHRYSLER": 9, + "SUBARU": 10, + "GM_ASCM": 0x1334, + "TOYOTA_IPAS": 0x1335, + "ALLOUTPUT": 0x1337, + "ELM327": 0xE327 +} + +def to_signed(d, bits): + ret = d + if d >= (1 << (bits - 1)): + ret = d - (1 << bits) + return ret + +def is_steering_msg(mode, addr): + ret = False + if mode == safety_modes["HONDA"] or mode == safety_modes["HONDA_BOSCH"]: + ret = (addr == 0xE4) or (addr == 0x194) or (addr == 0x33D) + elif mode == safety_modes["TOYOTA"]: + ret = addr == 0x2E4 + elif mode == safety_modes["GM"]: + ret = addr == 384 + elif mode == safety_modes["HYUNDAI"]: + ret = addr == 832 + elif mode == safety_modes["CHRYSLER"]: + ret = addr == 0x292 + elif mode == safety_modes["SUBARU"]: + ret = addr == 0x122 + return ret + +def get_steer_torque(mode, to_send): + ret = 0 + if mode == safety_modes["HONDA"] or mode == safety_modes["HONDA_BOSCH"]: + ret = to_send.RDLR & 0xFFFF0000 + elif mode == safety_modes["TOYOTA"]: + ret = (to_send.RDLR & 0xFF00) | ((to_send.RDLR >> 16) & 0xFF) + ret = to_signed(ret, 16) + elif mode == safety_modes["GM"]: + ret = ((to_send.RDLR & 0x7) << 8) + ((to_send.RDLR & 0xFF00) >> 8) + ret = to_signed(ret, 11) + elif mode == safety_modes["HYUNDAI"]: + ret = ((to_send.RDLR >> 16) & 0x7ff) - 1024 + elif mode == safety_modes["CHRYSLER"]: + ret = ((to_send.RDLR & 0x7) << 8) + ((to_send.RDLR & 0xFF00) >> 8) - 1024 + elif mode == safety_modes["SUBARU"]: + ret = ((to_send.RDLR >> 16) & 0x1FFF) + ret = to_signed(ret, 13) + return ret + +def set_desired_torque_last(safety, mode, torque): + if mode == safety_modes["HONDA"] or mode == safety_modes["HONDA_BOSCH"]: + pass # honda safety mode doesn't enforce a rate on steering msgs + elif mode == safety_modes["TOYOTA"]: + safety.set_toyota_desired_torque_last(torque) + elif mode == safety_modes["GM"]: + safety.set_gm_desired_torque_last(torque) + elif mode == safety_modes["HYUNDAI"]: + safety.set_hyundai_desired_torque_last(torque) + elif mode == safety_modes["CHRYSLER"]: + safety.set_chrysler_desired_torque_last(torque) + elif mode == safety_modes["SUBARU"]: + safety.set_subaru_desired_torque_last(torque) + +def package_can_msg(msg): + addr_shift = 3 if msg.address >= 0x800 else 21 + rdlr, rdhr = struct.unpack('II', msg.dat.ljust(8, b'\x00')) + + ret = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') + ret[0].RIR = msg.address << addr_shift + ret[0].RDTR = len(msg.dat) | ((msg.src & 0xF) << 4) + ret[0].RDHR = rdhr + ret[0].RDLR = rdlr + + return ret + +def init_segment(safety, lr, mode): + sendcan = (msg for msg in lr if msg.which() == 'sendcan') + steering_msgs = (can for msg in sendcan for can in msg.sendcan if is_steering_msg(mode, can.address)) + + msg = next(steering_msgs, None) + if msg is None: + # no steering msgs + return + + to_send = package_can_msg(msg) + torque = get_steer_torque(mode, to_send) + if torque != 0: + safety.set_controls_allowed(1) + set_desired_torque_last(safety, mode, torque) + assert safety.safety_tx_hook(to_send), "failed to initialize panda safety for segment" + diff --git a/tests/safety_replay/install_capnp.sh b/tests/safety_replay/install_capnp.sh new file mode 100755 index 0000000000..e13ab48c24 --- /dev/null +++ b/tests/safety_replay/install_capnp.sh @@ -0,0 +1,20 @@ +#!/bin/bash -e + +apt-get install -y autoconf curl libtool +curl -O https://capnproto.org/capnproto-c++-0.6.1.tar.gz +tar xvf capnproto-c++-0.6.1.tar.gz +cd capnproto-c++-0.6.1 +./configure --prefix=/usr/local CPPFLAGS=-DPIC CFLAGS=-fPIC CXXFLAGS=-fPIC LDFLAGS=-fPIC --disable-shared --enable-static +make -j4 +make install + +cd .. +git clone https://github.com/commaai/c-capnproto.git +cd c-capnproto +git checkout 2e625acacf58a5f5c8828d8453d1f8dacc700a96 +git submodule update --init --recursive +autoreconf -f -i -s +CFLAGS="-fPIC" ./configure --prefix=/usr/local +make -j4 +make install + diff --git a/tests/safety_replay/replay_drive.py b/tests/safety_replay/replay_drive.py new file mode 100755 index 0000000000..1b2ba082ac --- /dev/null +++ b/tests/safety_replay/replay_drive.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python2 + +import os +import sys +import panda.tests.safety.libpandasafety_py as libpandasafety_py +from panda.tests.safety_replay.helpers import is_steering_msg, get_steer_torque, \ + set_desired_torque_last, package_can_msg, \ + init_segment, safety_modes +from tools.lib.logreader import LogReader + +# replay a drive to check for safety violations +def replay_drive(lr, safety_mode, param): + safety = libpandasafety_py.libpandasafety + + err = safety.safety_set_mode(safety_mode, param) + assert err == 0, "invalid safety mode: %d" % safety_mode + + if "SEGMENT" in os.environ: + init_segment(safety, lr, mode) + + tx_tot, tx_blocked, tx_controls, tx_controls_blocked = 0, 0, 0, 0 + blocked_addrs = set() + start_t = None + + for msg in lr: + if start_t is None: + start_t = msg.logMonoTime + safety.set_timer(((msg.logMonoTime / 1000)) % 0xFFFFFFFF) + + if msg.which() == 'sendcan': + for canmsg in msg.sendcan: + to_send = package_can_msg(canmsg) + sent = safety.safety_tx_hook(to_send) + if not sent: + tx_blocked += 1 + tx_controls_blocked += safety.get_controls_allowed() + blocked_addrs.add(canmsg.address) + + if "DEBUG" in os.environ: + print "blocked %d at %f" % (canmsg.address, (msg.logMonoTime - start_t)/(1e9)) + tx_controls += safety.get_controls_allowed() + tx_tot += 1 + elif msg.which() == 'can': + for canmsg in msg.can: + # ignore msgs we sent + if canmsg.src >= 128: + continue + to_push = package_can_msg(canmsg) + safety.safety_rx_hook(to_push) + + print "total openpilot msgs:", tx_tot + print "total msgs with controls allowed:", tx_controls + print "blocked msgs:", tx_blocked + print "blocked with controls allowed:", tx_controls_blocked + print "blocked addrs:", blocked_addrs + + return tx_controls_blocked == 0 + +if __name__ == "__main__": + if sys.argv[2] in safety_modes: + mode = safety_modes[sys.argv[2]] + else: + mode = int(sys.argv[2]) + param = 0 if len(sys.argv) < 4 else int(sys.argv[3]) + lr = LogReader(sys.argv[1]) + + print "replaying drive %s with safety mode %d and param %d" % (sys.argv[1], mode, param) + + replay_drive(lr, mode, param) + diff --git a/tests/safety_replay/requirements.txt b/tests/safety_replay/requirements.txt new file mode 100644 index 0000000000..4c9d301dce --- /dev/null +++ b/tests/safety_replay/requirements.txt @@ -0,0 +1,8 @@ +aenum +cffi==1.11.4 +libusb1==1.6.6 +numpy==1.14.5 +requests +subprocess32 +libarchive +pycapnp diff --git a/tests/safety_replay/test_safety_replay.py b/tests/safety_replay/test_safety_replay.py new file mode 100755 index 0000000000..ecc34161f2 --- /dev/null +++ b/tests/safety_replay/test_safety_replay.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python2 + +import os +import requests + +from helpers import safety_modes +from replay_drive import replay_drive +from tools.lib.logreader import LogReader + +BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/" + +# (route, safety mode, param) +logs = [ + ("b0c9d2329ad1606b|2019-05-30--20-23-57.bz2", "HONDA", 0), # HONDA.CIVIC + ("38bfd238edecbcd7|2019-06-07--10-15-25.bz2", "TOYOTA", 66), # TOYOTA.PRIUS + ("f89c604cf653e2bf|2018-09-29--13-46-50.bz2", "GM", 0), # GM.VOLT + ("0375fdf7b1ce594d|2019-05-21--20-10-33.bz2", "HONDA_BOSCH", 1), # HONDA.ACCORD + ("02ec6bea180a4d36|2019-04-17--11-21-35.bz2", "HYUNDAI", 0), # HYUNDAI.SANTA_FE + ("03efb1fda29e30fe|2019-02-21--18-03-45.bz2", "CHRYSLER", 0), # CHRYSLER.PACIFICA_2018_HYBRID + ("791340bc01ed993d|2019-04-08--10-26-00.bz2", "SUBARU", 0), # SUBARU.IMPREZA +] + +if __name__ == "__main__": + for route, _, _ in logs: + if not os.path.isfile(route): + with open(route, "w") as f: + f.write(requests.get(BASE_URL + route).content) + + failed = [] + for route, mode, param in logs: + lr = LogReader(route) + m = safety_modes.get(mode, mode) + + print "\nreplaying %s with safety mode %d and param %s" % (route, m, param) + if not replay_drive(lr, m, int(param)): + failed.append(route) + + for f in failed: + print "\n**** failed on %s ****" % f + assert len(failed) == 0, "\nfailed on %d logs" % len(failed) + diff --git a/tests/standalone_test.py b/tests/standalone_test.py new file mode 100755 index 0000000000..ca6ea49f10 --- /dev/null +++ b/tests/standalone_test.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +import os +import sys +import struct +import time + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda + +if __name__ == "__main__": + if os.getenv("WIFI") is not None: + p = Panda("WIFI") + else: + p = Panda() + print(p.get_serial()) + print(p.health()) + + t1 = time.time() + for i in range(100): + p.get_serial() + t2 = time.time() + print("100 requests took %.2f ms" % ((t2-t1)*1000)) + + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + a = 0 + while True: + # flood + msg = b"\xaa"*4 + struct.pack("I", a) + p.can_send(0xaa, msg, 0) + p.can_send(0xaa, msg, 1) + p.can_send(0xaa, msg, 4) + time.sleep(0.01) + + dat = p.can_recv() + if len(dat) > 0: + print(dat) + a += 1 diff --git a/tests/throughput_test.py b/tests/throughput_test.py new file mode 100755 index 0000000000..5812ce42c2 --- /dev/null +++ b/tests/throughput_test.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +from __future__ import print_function +import os +import sys +import struct +import time +from tqdm import tqdm + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda, PandaWifiStreaming + +# test throughput between USB and wifi + +if __name__ == "__main__": + print(Panda.list()) + p_out = Panda("108018800f51363038363036") + print(p_out.get_serial()) + #p_in = Panda("02001b000f51363038363036") + p_in = Panda("WIFI") + print(p_in.get_serial()) + + p_in = PandaWifiStreaming() + + #while True: + # p_in.can_recv() + #sys.exit(0) + + p_out.set_controls_allowed(True) + + set_out, set_in = set(), set() + + # drain + p_out.can_recv() + p_in.can_recv() + + BATCH_SIZE = 16 + for a in tqdm(range(0, 10000, BATCH_SIZE)): + for b in range(0, BATCH_SIZE): + msg = b"\xaa"*4 + struct.pack("I", a+b) + if a%1 == 0: + p_out.can_send(0xaa, msg, 0) + + dat_out, dat_in = p_out.can_recv(), p_in.can_recv() + if len(dat_in) != 0: + print(len(dat_in)) + + num_out = [struct.unpack("I", i[4:])[0] for _, _, i, _ in dat_out] + num_in = [struct.unpack("I", i[4:])[0] for _, _, i, _ in dat_in] + + set_in.update(num_in) + set_out.update(num_out) + + # swag + print("waiting for packets") + time.sleep(2.0) + dat_in = p_in.can_recv() + print(len(dat_in)) + num_in = [struct.unpack("I", i[4:])[0] for _, _, i, _ in dat_in] + set_in.update(num_in) + + if len(set_out - set_in): + print("MISSING %d" % len(set_out - set_in)) + if len(set_out - set_in) < 256: + print(map(hex, sorted(list(set_out - set_in)))) diff --git a/tests/tucan_loopback.py b/tests/tucan_loopback.py new file mode 100755 index 0000000000..a3f3e6e2d7 --- /dev/null +++ b/tests/tucan_loopback.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +from __future__ import print_function +import os +import sys +import time +import random +import argparse + +from hexdump import hexdump +from itertools import permutations + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) +from panda import Panda + +def get_test_string(): + return b"test"+os.urandom(10) + +def run_test(sleep_duration): + pandas = Panda.list() + print(pandas) + + if len(pandas) == 0: + print("NO PANDAS") + assert False + + if len(pandas) == 1: + # if we only have one on USB, assume the other is on wifi + pandas.append("WIFI") + run_test_w_pandas(pandas, sleep_duration) + +def run_test_w_pandas(pandas, sleep_duration): + h = list(map(lambda x: Panda(x), pandas)) + print("H", h) + + for hh in h: + hh.set_controls_allowed(True) + + # test both directions + for ho in permutations(range(len(h)), r=2): + print("***************** TESTING", ho) + + panda0, panda1 = h[ho[0]], h[ho[1]] + + if(panda0._serial == "WIFI"): + print(" *** Can not send can data over wifi panda. Skipping! ***") + continue + + # **** test health packet **** + print("health", ho[0], h[ho[0]].health()) + + # **** test K/L line loopback **** + for bus in [2,3]: + # flush the output + h[ho[1]].kline_drain(bus=bus) + + # send the characters + st = get_test_string() + st = b"\xaa"+chr(len(st)+3).encode()+st + h[ho[0]].kline_send(st, bus=bus, checksum=False) + + # check for receive + ret = h[ho[1]].kline_drain(bus=bus) + + print("ST Data:") + hexdump(st) + print("RET Data:") + hexdump(ret) + assert st == ret + print("K/L pass", bus, ho, "\n") + time.sleep(sleep_duration) + + # **** test can line loopback **** +# for bus, gmlan in [(0, None), (1, False), (2, False), (1, True), (2, True)]: +for bus, gmlan in [(0, None), (1, None)]: + print("\ntest can", bus) + # flush + cans_echo = panda0.can_recv() + cans_loop = panda1.can_recv() + + if gmlan is not None: + panda0.set_gmlan(gmlan, bus) + panda1.set_gmlan(gmlan, bus) + + # send the characters + # pick addresses high enough to not conflict with honda code + at = random.randint(1024, 2000) + st = get_test_string()[0:8] + panda0.can_send(at, st, bus) + time.sleep(0.1) + + # check for receive + cans_echo = panda0.can_recv() + cans_loop = panda1.can_recv() + + print("Bus", bus, "echo", cans_echo, "loop", cans_loop) + + assert len(cans_echo) == 1 + assert len(cans_loop) == 1 + + assert cans_echo[0][0] == at + assert cans_loop[0][0] == at + + assert cans_echo[0][2] == st + assert cans_loop[0][2] == st + + assert cans_echo[0][3] == 0x80 | bus + if cans_loop[0][3] != bus: + print("EXPECTED %d GOT %d" % (bus, cans_loop[0][3])) + assert cans_loop[0][3] == bus + + print("CAN pass", bus, ho) + time.sleep(sleep_duration) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-n", type=int, help="Number of test iterations to run") + parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0) + args = parser.parse_args() + + if args.n is None: + while True: + run_test(sleep_duration=args.sleep) + else: + for i in range(args.n): + run_test(sleep_duration=args.sleep)