diff options
author | Christian Pointner <equinox@spreadspace.org> | 2018-11-29 22:40:46 +0100 |
---|---|---|
committer | Christian Pointner <equinox@spreadspace.org> | 2018-11-29 22:40:46 +0100 |
commit | b9860ad78b65ef0bae80041ac1d3e187b6f180d3 (patch) | |
tree | c12190e2f95f6414efe4321ef45e6652a273fec0 | |
parent | some cleanups (diff) | |
parent | added feather32u4lora board (diff) |
Merge branch 'radiohead'
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | contrib/Makefile | 30 | ||||
-rw-r--r-- | contrib/radiohead.patch | 305 | ||||
-rw-r--r-- | defines.mk | 14 | ||||
-rw-r--r-- | include.mk | 64 | ||||
-rw-r--r-- | lib/Arduino-SPI.cpp | 201 | ||||
-rw-r--r-- | lib/Arduino-SPI.h | 324 | ||||
-rw-r--r-- | lib/Arduino.h | 27 | ||||
-rw-r--r-- | lib/arduino-stub.cpp | 285 | ||||
-rw-r--r-- | lib/led.c | 4 | ||||
-rw-r--r-- | lib/stdc++-minimal.cpp | 53 | ||||
-rw-r--r-- | radiohead.mk | 55 | ||||
-rw-r--r-- | rweather-crypto.mk | 44 | ||||
-rw-r--r-- | usb-crypto/Makefile | 51 | ||||
-rwxr-xr-x | usb-crypto/decrypt.py | 36 | ||||
-rw-r--r-- | usb-crypto/usb-crypto.cpp | 157 | ||||
-rw-r--r-- | usb-lora-crypto/Makefile | 52 | ||||
-rw-r--r-- | usb-lora-crypto/usb-lora-crypto.cpp | 118 | ||||
-rw-r--r-- | usb-lora/Makefile | 50 | ||||
-rw-r--r-- | usb-lora/usb-lora.cpp | 112 |
20 files changed, 1976 insertions, 10 deletions
@@ -11,6 +11,10 @@ contrib/FastLED-* contrib/PJON-* contrib/BMP180-* contrib/vusb-* +contrib/RadioHead-* +contrib/RadioHead/ +contrib/.rweather-crypto.prepared +contrib/rweather-crypto contrib/.teensy-loader.prepared contrib/teensy_loader_cli contrib/.micronucleus.prepared diff --git a/contrib/Makefile b/contrib/Makefile index 05fe264..37eba4f 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -26,8 +26,10 @@ LUFA_VERSION := 151115 FASTLED_VERSION := 3.1.0 PJON_VERSION := 3.0 VUSB_VERSION := 20121206 +RADIOHEAD_VERSION := 1.89 -all: download-lufa download-fastled download-pjon download-vusb update-teensy-loader update-micronucleus +all: download-lufa download-fastled download-pjon download-vusb download-radiohead clone-rweather-crypto clone-teensy-loader clone-micronucleus +update-all: update-rweather-crypto update-teensy-loader update-micronucleus download-lufa: LUFA-${LUFA_VERSION}.zip @@ -35,6 +37,7 @@ LUFA-${LUFA_VERSION}.zip: wget "http://www.github.com/abcminiuser/lufa/archive/$@" -O "$@" unzip $@ + download-fastled: FastLED-${FASTLED_VERSION}.zip FastLED-${FASTLED_VERSION}.zip: @@ -49,22 +52,47 @@ PJON-${PJON_VERSION}.zip: wget "https://github.com/gioblu/PJON/archive/${PJON_VERSION}.zip" -O "$@" unzip $@ + download-vusb: vusb-${VUSB_VERSION}.zip vusb-${VUSB_VERSION}.zip: wget "https://www.obdev.at/downloads/vusb/vusb-${VUSB_VERSION}.zip" -O "$@" unzip $@ + +download-radiohead: RadioHead-${RADIOHEAD_VERSION}.zip + +RadioHead-${RADIOHEAD_VERSION}.zip: + wget "http://www.airspayce.com/mikem/arduino/RadioHead/$@" -O "$@" + unzip $@ + cd RadioHead; patch -p1 < ../radiohead.patch + + +.rweather-crypto.prepared: + git clone https://github.com/rweather/arduinolibs.git rweather-crypto + touch $@ + +clone-rweather-crypto: .rweather-crypto.prepared + +update-rweather-crypto: .rweather-crypto.prepared + cd rweather-crypto; git pull + + .teensy-loader.prepared: git clone https://github.com/PaulStoffregen/teensy_loader_cli.git touch $@ +clone-teensy-loader: .teensy-loader.prepared + update-teensy-loader: .teensy-loader.prepared cd teensy_loader_cli; git pull; make + .micronucleus.prepared: git clone https://github.com/micronucleus/micronucleus/ touch $@ +clone-micronucleus: .micronucleus.prepared + update-micronucleus: .micronucleus.prepared cd micronucleus/commandline; git pull; make diff --git a/contrib/radiohead.patch b/contrib/radiohead.patch new file mode 100644 index 0000000..1636f91 --- /dev/null +++ b/contrib/radiohead.patch @@ -0,0 +1,305 @@ +diff -Nur RadioHead-orig/RadioHead.h RadioHead/RadioHead.h +--- RadioHead-orig/RadioHead.h 2018-11-15 11:40:24.000000000 +0100 ++++ RadioHead/RadioHead.h 2018-11-28 23:58:44.998151593 +0100 +@@ -317,8 +317,6 @@ + cd /tmp + mkdir RadioHead + cd RadioHead +-cp /usr/local/projects/arduino/libraries/RadioHead/*.h . +-cp /usr/local/projects/arduino/libraries/RadioHead/*.cpp . + cp /usr/local/projects/arduino/libraries/RadioHead/examples/cc110/cc110_client/cc110_client.pde application.cpp + \endcode + - Edit application.cpp and comment out any \#include <SPI.h> so it looks like: +@@ -1283,11 +1281,14 @@ + #elif (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8) + #include <avr/io.h> + #include <avr/interrupt.h> ++ #include <avr/pgmspace.h> + #include <util/delay.h> + #include <string.h> + #include <stdbool.h> + #define RH_HAVE_HARDWARE_SPI +- #include <SPI.h> ++ #include <Arduino.h> ++ #include <Arduino-SPI.h> ++ #define RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER + + // For Steve Childress port to ARM M4 w/CMSIS with STM's Hardware Abstraction lib. + // See ArduinoWorkarounds.h (not supplied) +diff -Nur RadioHead-orig/RHAEADDriver.cpp RadioHead/RHAEADDriver.cpp +--- RadioHead-orig/RHAEADDriver.cpp 1970-01-01 01:00:00.000000000 +0100 ++++ RadioHead/RHAEADDriver.cpp 2018-11-28 23:59:30.373774012 +0100 +@@ -0,0 +1,48 @@ ++// RHAEADDriver.cpp ++// ++// Author: Philippe.Rochat'at'gmail.com ++// Contributed to the RadioHead project by the author ++// $Id: RHAEADDriver.cpp,v 1.4 2018/09/23 23:54:01 mikem Exp $ ++ ++#include <RHAEADDriver.h> ++#ifdef RH_ENABLE_ENCRYPTION_MODULE ++ ++RHAEADDriver::RHAEADDriver(RHGenericDriver& driver, AuthenticatedCipher& aead) ++ : _driver(driver), ++ _aead(aead) ++{ ++ _buffer = (uint8_t *)calloc(_driver.maxMessageLength(), sizeof(uint8_t)); ++} ++ ++bool RHAEADDriver::recv(uint8_t* buf, uint8_t* len) ++{ ++ bool status = _driver.recv(_buffer, len); ++ if (status && buf && len) { ++ // TODO: implement this ++ } ++ return status; ++} ++ ++bool RHAEADDriver::send(const uint8_t* data, uint8_t len) ++{ ++ if (len > maxMessageLength()) ++ return false; ++ ++ bool status = true; ++ if (len == 0) // PassThru ++ return _driver.send(data, len); ++ ++ uint8_t out_len = 0; ++ // TODO: implement this ++ return _driver.send(_buffer, out_len); ++} ++ ++uint8_t RHAEADDriver::maxMessageLength() ++{ ++ int driver_len = _driver.maxMessageLength(); ++ // IV? ++ driver_len -= _aead.tagSize(); ++ return driver_len; ++} ++ ++#endif +diff -Nur RadioHead-orig/RHAEADDriver.h RadioHead/RHAEADDriver.h +--- RadioHead-orig/RHAEADDriver.h 1970-01-01 01:00:00.000000000 +0100 ++++ RadioHead/RHAEADDriver.h 2018-11-28 23:59:09.441948151 +0100 +@@ -0,0 +1,221 @@ ++// RHAEADDriver.h ++ ++// Generic encryption and authentication layer that could use any driver ++// But will encrypt and authenticate all data. ++// Requires the Arduinolibs/Crypto library: ++// https://github.com/rweather/arduinolibs ++// ++// Author: Christain Pointner equinox'at'spreadspace.org ++// Contributed to the RadioHead project by the author ++ ++#ifndef RHAEADDriver_h ++#define RHAEADDriver_h ++ ++#include <RHGenericDriver.h> ++#ifdef RH_ENABLE_ENCRYPTION_MODULE ++#include <AuthenticatedCipher.h> ++ ++///////////////////////////////////////////////////////////////////// ++/// \class RHAEADDriver RHAEADDriver <RHAEADDriver.h> ++/// \brief Virtual Driver to encrypt and authenticate data. Can be used with any other RadioHead driver. ++/// ++/// This driver acts as a wrapper for any other RadioHead driver, adding encryption and authentication of ++/// messages that are passed to and from the actual radio driver. Only the message payload is encrypted but ++/// the to/from address or flags is part of the authenticated portion. Any of the authenticated-ciphers ++/// supported by ArduinoLibs Cryptographic Library http://rweather.github.io/arduinolibs/crypto.html may be used. ++/// ++/// For successful communications, both sender and receiver must use the same cipher and the same key. ++/// ++/// In order to enable this module you must uncomment #define RH_ENABLE_AEAD_MODULE at the bottom of RadioHead.h ++/// But ensure you have installed the Crypto directory from arduinolibs first: ++/// http://rweather.github.io/arduinolibs/index.html ++ ++class RHAEADDriver : public RHGenericDriver ++{ ++public: ++ /// Constructor. ++ /// Adds a ciphering layer to messages sent and received by the actual transport driver. ++ /// \param[in] driver The RadioHead driver to use to transport messages. ++ /// \param[in] blockcipher The blockcipher (from arduinolibs) that crypt/decrypt data. Ensure that ++ /// the blockcipher has had its key set before sending or receiving messages. ++ RHAEADDriver(RHGenericDriver& driver, AuthenticatedCipher& aead); ++ ++ /// Calls the real driver's init() ++ /// \return The value returned from the driver init() method; ++ virtual bool init() { return _driver.init();}; ++ ++ /// Tests whether a new message is available ++ /// from the Driver. ++ /// On most drivers, this will also put the Driver into RHModeRx mode until ++ /// a message is actually received by the transport, when it wil be returned to RHModeIdle. ++ /// This can be called multiple times in a timeout loop ++ /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv() ++ virtual bool available() { return _driver.available();}; ++ ++ /// Turns the receiver on if it not already on. ++ /// If there is a valid message available, copy it to buf and return true ++ /// else return false. ++ /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). ++ /// You should be sure to call this function frequently enough to not miss any messages ++ /// It is recommended that you call it in your main loop. ++ /// \param[in] buf Location to copy the received message ++ /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. ++ /// \return true if a valid message was copied to buf ++ virtual bool recv(uint8_t* buf, uint8_t* len); ++ ++ /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). ++ /// Then optionally waits for Channel Activity Detection (CAD) ++ /// to show the channnel is clear (if the radio supports CAD) by calling waitCAD(). ++ /// Then loads a message into the transmitter and starts the transmitter. Note that a message length ++ /// of 0 is permitted. ++ /// \param[in] data Array of data to be sent ++ /// \param[in] len Number of bytes of data to send ++ /// specify the maximum time in ms to wait. If 0 (the default) do not wait for CAD before transmitting. ++ /// \return true if the message length was valid and it was correctly queued for transmit. Return false ++ /// if CAD was requested and the CAD timeout timed out before clear channel was detected. ++ virtual bool send(const uint8_t* data, uint8_t len); ++ ++ /// Returns the maximum message length ++ /// available in this Driver, which depends on the maximum length supported by the underlying transport driver. ++ /// \return The maximum legal message length ++ virtual uint8_t maxMessageLength(); ++ ++ /// Blocks until the transmitter ++ /// is no longer transmitting. ++ virtual bool waitPacketSent() { return _driver.waitPacketSent();} ; ++ ++ /// Blocks until the transmitter is no longer transmitting. ++ /// or until the timeout occuers, whichever happens first ++ /// \param[in] timeout Maximum time to wait in milliseconds. ++ /// \return true if the radio completed transmission within the timeout period. False if it timed out. ++ virtual bool waitPacketSent(uint16_t timeout) {return _driver.waitPacketSent(timeout);} ; ++ ++ /// Starts the receiver and blocks until a received message is available or a timeout ++ /// \param[in] timeout Maximum time to wait in milliseconds. ++ /// \return true if a message is available ++ virtual bool waitAvailableTimeout(uint16_t timeout) {return _driver.waitAvailableTimeout(timeout);}; ++ ++ /// Calls the waitCAD method in the driver ++ /// \return The return value from teh drivers waitCAD() method ++ virtual bool waitCAD() { return _driver.waitCAD();}; ++ ++ /// Sets the Channel Activity Detection timeout in milliseconds to be used by waitCAD(). ++ /// The default is 0, which means do not wait for CAD detection. ++ /// CAD detection depends on support for isChannelActive() by your particular radio. ++ void setCADTimeout(unsigned long cad_timeout) {_driver.setCADTimeout(cad_timeout);}; ++ ++ /// Determine if the currently selected radio channel is active. ++ /// This is expected to be subclassed by specific radios to implement their Channel Activity Detection ++ /// if supported. If the radio does not support CAD, returns true immediately. If a RadioHead radio ++ /// supports isChannelActive() it will be documented in the radio specific documentation. ++ /// This is called automatically by waitCAD(). ++ /// \return true if the radio-specific CAD (as returned by override of isChannelActive()) shows the ++ /// current radio channel as active, else false. If there is no radio-specific CAD, returns false. ++ virtual bool isChannelActive() { return _driver.isChannelActive();}; ++ ++ /// Sets the address of this node. Defaults to 0xFF. Subclasses or the user may want to change this. ++ /// This will be used to test the adddress in incoming messages. In non-promiscuous mode, ++ /// only messages with a TO header the same as thisAddress or the broadcast addess (0xFF) will be accepted. ++ /// In promiscuous mode, all messages will be accepted regardless of the TO header. ++ /// In a conventional multinode system, all nodes will have a unique address ++ /// (which you could store in EEPROM). ++ /// You would normally set the header FROM address to be the same as thisAddress (though you dont have to, ++ /// allowing the possibilty of address spoofing). ++ /// \param[in] thisAddress The address of this node. ++ virtual void setThisAddress(uint8_t thisAddress) { _driver.setThisAddress(thisAddress);}; ++ ++ /// Sets the TO header to be sent in all subsequent messages ++ /// \param[in] to The new TO header value ++ virtual void setHeaderTo(uint8_t to){ _driver.setHeaderTo(to);}; ++ ++ /// Sets the FROM header to be sent in all subsequent messages ++ /// \param[in] from The new FROM header value ++ virtual void setHeaderFrom(uint8_t from){ _driver.setHeaderFrom(from);}; ++ ++ /// Sets the ID header to be sent in all subsequent messages ++ /// \param[in] id The new ID header value ++ virtual void setHeaderId(uint8_t id){ _driver.setHeaderId(id);}; ++ ++ /// Sets and clears bits in the FLAGS header to be sent in all subsequent messages ++ /// First it clears he FLAGS according to the clear argument, then sets the flags according to the ++ /// set argument. The default for clear always clears the application specific flags. ++ /// \param[in] set bitmask of bits to be set. Flags are cleared with the clear mask before being set. ++ /// \param[in] clear bitmask of flags to clear. Defaults to RH_FLAGS_APPLICATION_SPECIFIC ++ /// which clears the application specific flags, resulting in new application specific flags ++ /// identical to the set. ++ virtual void setHeaderFlags(uint8_t set, uint8_t clear = RH_FLAGS_APPLICATION_SPECIFIC) { _driver.setHeaderFlags(set, clear);}; ++ ++ /// Tells the receiver to accept messages with any TO address, not just messages ++ /// addressed to thisAddress or the broadcast address ++ /// \param[in] promiscuous true if you wish to receive messages with any TO address ++ virtual void setPromiscuous(bool promiscuous){ _driver.setPromiscuous(promiscuous);}; ++ ++ /// Returns the TO header of the last received message ++ /// \return The TO header ++ virtual uint8_t headerTo() { return _driver.headerTo();}; ++ ++ /// Returns the FROM header of the last received message ++ /// \return The FROM header ++ virtual uint8_t headerFrom() { return _driver.headerFrom();}; ++ ++ /// Returns the ID header of the last received message ++ /// \return The ID header ++ virtual uint8_t headerId() { return _driver.headerId();}; ++ ++ /// Returns the FLAGS header of the last received message ++ /// \return The FLAGS header ++ virtual uint8_t headerFlags() { return _driver.headerFlags();}; ++ ++ /// Returns the most recent RSSI (Receiver Signal Strength Indicator). ++ /// Usually it is the RSSI of the last received message, which is measured when the preamble is received. ++ /// If you called readRssi() more recently, it will return that more recent value. ++ /// \return The most recent RSSI measurement in dBm. ++ int16_t lastRssi() { return _driver.lastRssi();}; ++ ++ /// Returns the operating mode of the library. ++ /// \return the current mode, one of RF69_MODE_* ++ RHMode mode() { return _driver.mode();}; ++ ++ /// Sets the operating mode of the transport. ++ void setMode(RHMode mode) { _driver.setMode(mode);}; ++ ++ /// Sets the transport hardware into low-power sleep mode ++ /// (if supported). May be overridden by specific drivers to initialte sleep mode. ++ /// If successful, the transport will stay in sleep mode until woken by ++ /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc) ++ /// \return true if sleep mode is supported by transport hardware and the RadioHead driver, and if sleep mode ++ /// was successfully entered. If sleep mode is not suported, return false. ++ virtual bool sleep() { return _driver.sleep();}; ++ ++ /// Returns the count of the number of bad received packets (ie packets with bad lengths, checksum etc) ++ /// which were rejected and not delivered to the application. ++ /// Caution: not all drivers can correctly report this count. Some underlying hardware only report ++ /// good packets. ++ /// \return The number of bad packets received. ++ virtual uint16_t rxBad() { return _driver.rxBad();}; ++ ++ /// Returns the count of the number of ++ /// good received packets ++ /// \return The number of good packets received. ++ virtual uint16_t rxGood() { return _driver.rxGood();}; ++ ++ /// Returns the count of the number of ++ /// packets successfully transmitted (though not necessarily received by the destination) ++ /// \return The number of packets successfully transmitted ++ virtual uint16_t txGood() { return _driver.txGood();}; ++ ++private: ++ /// The underlying transport river we are to use ++ RHGenericDriver& _driver; ++ ++ /// The AuthenticatedCipher we are to use for encrypting/decrypting ++ AuthenticatedCipher& _aead; ++ ++ /// Buffer to store encrypted/decrypted message ++ uint8_t* _buffer; ++}; ++ ++ ++ ++#endif ++#endif @@ -145,6 +145,19 @@ ifeq ($(BOARD_TYPE),feather32u4) PROG_TYPE := avr109 AVRDUDE_PORT := /dev/ttyACM0 LUFA_BOARD = NONE + ARDUINO_PINS = leonardo +endif +ifeq ($(BOARD_TYPE),feather32u4lora) + MCU := atmega32u4 + ARCH = AVR8 + F_CPU := 8000000 + F_USB = $(F_CPU) + PROG := avrdude + UPLOAD_RATE := 57600 + PROG_TYPE := avr109 + AVRDUDE_PORT := /dev/ttyACM0 + LUFA_BOARD = NONE + ARDUINO_PINS = leonardo endif ifeq ($(BOARD_TYPE),elecrow32u4lora) MCU := atmega32u4 @@ -285,7 +298,6 @@ COMMON += -funsigned-char COMMON += -funsigned-bitfields COMMON += -fdata-sections COMMON += -ffunction-sections -COMMON += -fpack-struct COMMON += -fshort-enums COMMON += -Wall COMMON += -I$(LIB_DIR) @@ -41,7 +41,7 @@ CXX_OBJ_LIB := $(CXX_LIBS:%=lib-%.o) CXX_SRC_LIB := $(CXX_LIBS:%=$(LIB_DIR)/%.cpp) CXX_DEP_LIB := $(CXX_LIBS:%=lib-%.d) -.PHONY: prepare clean clean-external distclean clean-lufa clean-fastled clean-pjon clean-vusb program erase flash reset run +.PHONY: prepare clean clean-external distclean clean-lufa clean-fastled clean-pjon clean-vusb clean-radiohead clean-rweather-crypto program erase flash reset run ELFFILE := $(NAME).elf HEXFILE := $(NAME).hex @@ -82,6 +82,22 @@ CFLAGS += -DUSES_VUSB CFLAGS += $(VUSB_OPTS) endif +ifdef RADIOHEAD_PATH +CXXFLAGS += -I$(RADIOHEAD_PATH) +CXXFLAGS += -DUSES_RADIOHEAD +CXXFLAGS += -DRH_PLATFORM=RH_PLATFORM_GENERIC_AVR8 +ifdef RWEATHER_CRYPTO_PATH +CXXFLAGS += -DRH_ENABLE_ENCRYPTION_MODULE +endif +CXXFLAGS += $(RADIOHEAD_OPTS) +endif + +ifdef RWEATHER_CRYPTO_PATH +CXXFLAGS += -I$(RWEATHER_CRYPTO_PATH)/libraries/Crypto +CXXFLAGS += -DUSES_RWEATHER_CRYPTO +CXXFLAGS += $(RWEATHER_CRYPTO_OPTS) +endif + prepare: $(EXTERNAL_LIBS:%=build-%) clean-external: $(EXTERNAL_LIBS:%=clean-%) @@ -179,6 +195,52 @@ clean-vusb: @echo "****************************************************" +build-radiohead: libradiohead.a + +libradiohead.a: Makefile + @echo "" + @echo "****************************************************" + @echo "building external RadioHead lib ($(RADIOHEAD_PATH))" + @echo "" + make -f $(SPREADAVR_PATH)/radiohead.mk libradiohead.a + make -f $(SPREADAVR_PATH)/radiohead.mk clean + @echo "" + @echo "****************************************************" + +clean-radiohead: + @echo "" + @echo "****************************************************" + @echo "cleaning external RadioHead lib ($(RADIOHEAD_PATH))" + @echo "" + make -f $(SPREADAVR_PATH)/radiohead.mk clean + rm -f libradiohead.a + @echo "" + @echo "****************************************************" + + +build-rweather-crypto: librweather-crypto.a + +librweather-crypto.a: Makefile + @echo "" + @echo "****************************************************" + @echo "building external rweather/crypto lib ($(RWEATHER_CRYPTO_PATH))" + @echo "" + make -f $(SPREADAVR_PATH)/rweather-crypto.mk librweather-crypto.a + make -f $(SPREADAVR_PATH)/rweather-crypto.mk clean + @echo "" + @echo "****************************************************" + +clean-rweather-crypto: + @echo "" + @echo "****************************************************" + @echo "cleaning external rweather/crypto lib ($(RWEATHER_CRYPTO_PATH))" + @echo "" + make -f $(SPREADAVR_PATH)/rweather-crypto.mk clean + rm -f librweather-crypto.a + @echo "" + @echo "****************************************************" + + ## project-specific objects %.d: %.c Makefile @set -e; rm -f $@; \ diff --git a/lib/Arduino-SPI.cpp b/lib/Arduino-SPI.cpp new file mode 100644 index 0000000..0a7ac91 --- /dev/null +++ b/lib/Arduino-SPI.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2010 by Cristian Maglie <c.maglie@arduino.cc> + * Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API) + * Copyright (c) 2014 by Matthijs Kooijman <matthijs@stdin.nl> (SPISettings AVR) + * Copyright (c) 2014 by Andrew J. Kroll <xxxajk@gmail.com> (atomicity fixes) + * SPI Master library for arduino. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + +#include "Arduino-SPI.h" + +SPIClass SPI; + +uint8_t SPIClass::initialized = 0; +uint8_t SPIClass::interruptMode = 0; +uint8_t SPIClass::interruptMask = 0; +uint8_t SPIClass::interruptSave = 0; +#ifdef SPI_TRANSACTION_MISMATCH_LED +uint8_t SPIClass::inTransactionFlag = 0; +#endif + +void SPIClass::begin() +{ + uint8_t sreg = SREG; + noInterrupts(); // Protect from a scheduler and prevent transactionBegin + if (!initialized) { + // Set SS to high so a connected chip will be "deselected" by default + uint8_t port = digitalPinToPort(SS); + uint8_t bit = digitalPinToBitMask(SS); + volatile uint8_t *reg = portModeRegister(port); + + // if the SS pin is not already configured as an output + // then set it high (to enable the internal pull-up resistor) + if(!(*reg & bit)){ + digitalWrite(SS, HIGH); + } + + // When the SS pin is set as OUTPUT, it can be used as + // a general purpose output port (it doesn't influence + // SPI operations). + pinMode(SS, OUTPUT); + + // Warning: if the SS pin ever becomes a LOW INPUT then SPI + // automatically switches to Slave, so the data direction of + // the SS pin MUST be kept as OUTPUT. + SPCR |= _BV(MSTR); + SPCR |= _BV(SPE); + + // Set direction register for SCK and MOSI pin. + // MISO pin automatically overrides to INPUT. + // By doing this AFTER enabling SPI, we avoid accidentally + // clocking in a single bit since the lines go directly + // from "input" to SPI control. + // http://code.google.com/p/arduino/issues/detail?id=888 + pinMode(SCK, OUTPUT); + pinMode(MOSI, OUTPUT); + } + initialized++; // reference count + SREG = sreg; +} + +void SPIClass::end() { + uint8_t sreg = SREG; + noInterrupts(); // Protect from a scheduler and prevent transactionBegin + // Decrease the reference counter + if (initialized) + initialized--; + // If there are no more references disable SPI + if (!initialized) { + SPCR &= ~_BV(SPE); + interruptMode = 0; + #ifdef SPI_TRANSACTION_MISMATCH_LED + inTransactionFlag = 0; + #endif + } + SREG = sreg; +} + +// mapping of interrupt numbers to bits within SPI_AVR_EIMSK +#if defined(__AVR_ATmega32U4__) + #define SPI_INT0_MASK (1<<INT0) + #define SPI_INT1_MASK (1<<INT1) + #define SPI_INT2_MASK (1<<INT2) + #define SPI_INT3_MASK (1<<INT3) + #define SPI_INT4_MASK (1<<INT6) +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) + #define SPI_INT0_MASK (1<<INT0) + #define SPI_INT1_MASK (1<<INT1) + #define SPI_INT2_MASK (1<<INT2) + #define SPI_INT3_MASK (1<<INT3) + #define SPI_INT4_MASK (1<<INT4) + #define SPI_INT5_MASK (1<<INT5) + #define SPI_INT6_MASK (1<<INT6) + #define SPI_INT7_MASK (1<<INT7) +#elif defined(EICRA) && defined(EICRB) && defined(EIMSK) + #define SPI_INT0_MASK (1<<INT4) + #define SPI_INT1_MASK (1<<INT5) + #define SPI_INT2_MASK (1<<INT0) + #define SPI_INT3_MASK (1<<INT1) + #define SPI_INT4_MASK (1<<INT2) + #define SPI_INT5_MASK (1<<INT3) + #define SPI_INT6_MASK (1<<INT6) + #define SPI_INT7_MASK (1<<INT7) +#else + #ifdef INT0 + #define SPI_INT0_MASK (1<<INT0) + #endif + #ifdef INT1 + #define SPI_INT1_MASK (1<<INT1) + #endif + #ifdef INT2 + #define SPI_INT2_MASK (1<<INT2) + #endif +#endif + +void SPIClass::usingInterrupt(uint8_t interruptNumber) +{ + uint8_t mask = 0; + uint8_t sreg = SREG; + noInterrupts(); // Protect from a scheduler and prevent transactionBegin + switch (interruptNumber) { + #ifdef SPI_INT0_MASK + case 0: mask = SPI_INT0_MASK; break; + #endif + #ifdef SPI_INT1_MASK + case 1: mask = SPI_INT1_MASK; break; + #endif + #ifdef SPI_INT2_MASK + case 2: mask = SPI_INT2_MASK; break; + #endif + #ifdef SPI_INT3_MASK + case 3: mask = SPI_INT3_MASK; break; + #endif + #ifdef SPI_INT4_MASK + case 4: mask = SPI_INT4_MASK; break; + #endif + #ifdef SPI_INT5_MASK + case 5: mask = SPI_INT5_MASK; break; + #endif + #ifdef SPI_INT6_MASK + case 6: mask = SPI_INT6_MASK; break; + #endif + #ifdef SPI_INT7_MASK + case 7: mask = SPI_INT7_MASK; break; + #endif + default: + interruptMode = 2; + break; + } + interruptMask |= mask; + if (!interruptMode) + interruptMode = 1; + SREG = sreg; +} + +void SPIClass::notUsingInterrupt(uint8_t interruptNumber) +{ + // Once in mode 2 we can't go back to 0 without a proper reference count + if (interruptMode == 2) + return; + uint8_t mask = 0; + uint8_t sreg = SREG; + noInterrupts(); // Protect from a scheduler and prevent transactionBegin + switch (interruptNumber) { + #ifdef SPI_INT0_MASK + case 0: mask = SPI_INT0_MASK; break; + #endif + #ifdef SPI_INT1_MASK + case 1: mask = SPI_INT1_MASK; break; + #endif + #ifdef SPI_INT2_MASK + case 2: mask = SPI_INT2_MASK; break; + #endif + #ifdef SPI_INT3_MASK + case 3: mask = SPI_INT3_MASK; break; + #endif + #ifdef SPI_INT4_MASK + case 4: mask = SPI_INT4_MASK; break; + #endif + #ifdef SPI_INT5_MASK + case 5: mask = SPI_INT5_MASK; break; + #endif + #ifdef SPI_INT6_MASK + case 6: mask = SPI_INT6_MASK; break; + #endif + #ifdef SPI_INT7_MASK + case 7: mask = SPI_INT7_MASK; break; + #endif + default: + break; + // this case can't be reached + } + interruptMask &= ~mask; + if (!interruptMask) + interruptMode = 0; + SREG = sreg; +} diff --git a/lib/Arduino-SPI.h b/lib/Arduino-SPI.h new file mode 100644 index 0000000..5206a09 --- /dev/null +++ b/lib/Arduino-SPI.h @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2010 by Cristian Maglie <c.maglie@arduino.cc> + * Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API) + * Copyright (c) 2014 by Matthijs Kooijman <matthijs@stdin.nl> (SPISettings AVR) + * Copyright (c) 2014 by Andrew J. Kroll <xxxajk@gmail.com> (atomicity fixes) + * SPI Master library for arduino. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + +#ifndef _SPI_H_INCLUDED +#define _SPI_H_INCLUDED + +#include <Arduino.h> + +// SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(), +// usingInterrupt(), and SPISetting(clock, bitOrder, dataMode) +#define SPI_HAS_TRANSACTION 1 + +// SPI_HAS_NOTUSINGINTERRUPT means that SPI has notUsingInterrupt() method +#define SPI_HAS_NOTUSINGINTERRUPT 1 + +// SPI_ATOMIC_VERSION means that SPI has atomicity fixes and what version. +// This way when there is a bug fix you can check this define to alert users +// of your code if it uses better version of this library. +// This also implies everything that SPI_HAS_TRANSACTION as documented above is +// available too. +#define SPI_ATOMIC_VERSION 1 + +// Uncomment this line to add detection of mismatched begin/end transactions. +// A mismatch occurs if other libraries fail to use SPI.endTransaction() for +// each SPI.beginTransaction(). Connect an LED to this pin. The LED will turn +// on if any mismatch is ever detected. +//#define SPI_TRANSACTION_MISMATCH_LED 5 + +#ifndef LSBFIRST +#define LSBFIRST 0 +#endif +#ifndef MSBFIRST +#define MSBFIRST 1 +#endif + +#define SPI_CLOCK_DIV4 0x00 +#define SPI_CLOCK_DIV16 0x01 +#define SPI_CLOCK_DIV64 0x02 +#define SPI_CLOCK_DIV128 0x03 +#define SPI_CLOCK_DIV2 0x04 +#define SPI_CLOCK_DIV8 0x05 +#define SPI_CLOCK_DIV32 0x06 + +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 +#define SPI_MODE2 0x08 +#define SPI_MODE3 0x0C + +#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR +#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR +#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR + +// define SPI_AVR_EIMSK for AVR boards with external interrupt pins +#if defined(EIMSK) + #define SPI_AVR_EIMSK EIMSK +#elif defined(GICR) + #define SPI_AVR_EIMSK GICR +#elif defined(GIMSK) + #define SPI_AVR_EIMSK GIMSK +#endif + +class SPISettings { +public: + SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { + if (__builtin_constant_p(clock)) { + init_AlwaysInline(clock, bitOrder, dataMode); + } else { + init_MightInline(clock, bitOrder, dataMode); + } + } + SPISettings() { + init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); + } +private: + void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { + init_AlwaysInline(clock, bitOrder, dataMode); + } + void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) + __attribute__((__always_inline__)) { + // Clock settings are defined as follows. Note that this shows SPI2X + // inverted, so the bits form increasing numbers. Also note that + // fosc/64 appears twice + // SPR1 SPR0 ~SPI2X Freq + // 0 0 0 fosc/2 + // 0 0 1 fosc/4 + // 0 1 0 fosc/8 + // 0 1 1 fosc/16 + // 1 0 0 fosc/32 + // 1 0 1 fosc/64 + // 1 1 0 fosc/64 + // 1 1 1 fosc/128 + + // We find the fastest clock that is less than or equal to the + // given clock rate. The clock divider that results in clock_setting + // is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the + // slowest (128 == 2 ^^ 7, so clock_div = 6). + uint8_t clockDiv; + + // When the clock is known at compiletime, use this if-then-else + // cascade, which the compiler knows how to completely optimize + // away. When clock is not known, use a loop instead, which generates + // shorter code. + if (__builtin_constant_p(clock)) { + if (clock >= F_CPU / 2) { + clockDiv = 0; + } else if (clock >= F_CPU / 4) { + clockDiv = 1; + } else if (clock >= F_CPU / 8) { + clockDiv = 2; + } else if (clock >= F_CPU / 16) { + clockDiv = 3; + } else if (clock >= F_CPU / 32) { + clockDiv = 4; + } else if (clock >= F_CPU / 64) { + clockDiv = 5; + } else { + clockDiv = 6; + } + } else { + uint32_t clockSetting = F_CPU / 2; + clockDiv = 0; + while (clockDiv < 6 && clock < clockSetting) { + clockSetting /= 2; + clockDiv++; + } + } + + // Compensate for the duplicate fosc/64 + if (clockDiv == 6) + clockDiv = 7; + + // Invert the SPI2X bit + clockDiv ^= 0x1; + + // Pack into the SPISettings class + spcr = _BV(SPE) | _BV(MSTR) | ((bitOrder == LSBFIRST) ? _BV(DORD) : 0) | + (dataMode & SPI_MODE_MASK) | ((clockDiv >> 1) & SPI_CLOCK_MASK); + spsr = clockDiv & SPI_2XCLOCK_MASK; + } + uint8_t spcr; + uint8_t spsr; + friend class SPIClass; +}; + + +class SPIClass { +public: + // Initialize the SPI library + static void begin(); + + // If SPI is used from within an interrupt, this function registers + // that interrupt with the SPI library, so beginTransaction() can + // prevent conflicts. The input interruptNumber is the number used + // with attachInterrupt. If SPI is used from a different interrupt + // (eg, a timer), interruptNumber should be 255. + static void usingInterrupt(uint8_t interruptNumber); + // And this does the opposite. + static void notUsingInterrupt(uint8_t interruptNumber); + // Note: the usingInterrupt and notUsingInterrupt functions should + // not to be called from ISR context or inside a transaction. + // For details see: + // https://github.com/arduino/Arduino/pull/2381 + // https://github.com/arduino/Arduino/pull/2449 + + // Before using SPI.transfer() or asserting chip select pins, + // this function is used to gain exclusive access to the SPI bus + // and configure the correct settings. + inline static void beginTransaction(SPISettings settings) { + if (interruptMode > 0) { + uint8_t sreg = SREG; + noInterrupts(); + + #ifdef SPI_AVR_EIMSK + if (interruptMode == 1) { + interruptSave = SPI_AVR_EIMSK; + SPI_AVR_EIMSK &= ~interruptMask; + SREG = sreg; + } else + #endif + { + interruptSave = sreg; + } + } + + #ifdef SPI_TRANSACTION_MISMATCH_LED + if (inTransactionFlag) { + pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); + digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); + } + inTransactionFlag = 1; + #endif + + SPCR = settings.spcr; + SPSR = settings.spsr; + } + + // Write to the SPI bus (MOSI pin) and also receive (MISO pin) + inline static uint8_t transfer(uint8_t data) { + SPDR = data; + /* + * The following NOP introduces a small delay that can prevent the wait + * loop form iterating when running at the maximum speed. This gives + * about 10% more speed, even if it seems counter-intuitive. At lower + * speeds it is unnoticed. + */ + asm volatile("nop"); + while (!(SPSR & _BV(SPIF))) ; // wait + return SPDR; + } + inline static uint16_t transfer16(uint16_t data) { + union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } in, out; + in.val = data; + if (!(SPCR & _BV(DORD))) { + SPDR = in.msb; + asm volatile("nop"); // See transfer(uint8_t) function + while (!(SPSR & _BV(SPIF))) ; + out.msb = SPDR; + SPDR = in.lsb; + asm volatile("nop"); + while (!(SPSR & _BV(SPIF))) ; + out.lsb = SPDR; + } else { + SPDR = in.lsb; + asm volatile("nop"); + while (!(SPSR & _BV(SPIF))) ; + out.lsb = SPDR; + SPDR = in.msb; + asm volatile("nop"); + while (!(SPSR & _BV(SPIF))) ; + out.msb = SPDR; + } + return out.val; + } + inline static void transfer(void *buf, size_t count) { + if (count == 0) return; + uint8_t *p = (uint8_t *)buf; + SPDR = *p; + while (--count > 0) { + uint8_t out = *(p + 1); + while (!(SPSR & _BV(SPIF))) ; + uint8_t in = SPDR; + SPDR = out; + *p++ = in; + } + while (!(SPSR & _BV(SPIF))) ; + *p = SPDR; + } + // After performing a group of transfers and releasing the chip select + // signal, this function allows others to access the SPI bus + inline static void endTransaction(void) { + #ifdef SPI_TRANSACTION_MISMATCH_LED + if (!inTransactionFlag) { + pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); + digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); + } + inTransactionFlag = 0; + #endif + + if (interruptMode > 0) { + #ifdef SPI_AVR_EIMSK + uint8_t sreg = SREG; + #endif + noInterrupts(); + #ifdef SPI_AVR_EIMSK + if (interruptMode == 1) { + SPI_AVR_EIMSK = interruptSave; + SREG = sreg; + } else + #endif + { + SREG = interruptSave; + } + } + } + + // Disable the SPI bus + static void end(); + + // This function is deprecated. New applications should use + // beginTransaction() to configure SPI settings. + inline static void setBitOrder(uint8_t bitOrder) { + if (bitOrder == LSBFIRST) SPCR |= _BV(DORD); + else SPCR &= ~(_BV(DORD)); + } + // This function is deprecated. New applications should use + // beginTransaction() to configure SPI settings. + inline static void setDataMode(uint8_t dataMode) { + SPCR = (SPCR & ~SPI_MODE_MASK) | dataMode; + } + // This function is deprecated. New applications should use + // beginTransaction() to configure SPI settings. + inline static void setClockDivider(uint8_t clockDiv) { + SPCR = (SPCR & ~SPI_CLOCK_MASK) | (clockDiv & SPI_CLOCK_MASK); + SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((clockDiv >> 2) & SPI_2XCLOCK_MASK); + } + // These undocumented functions should not be used. SPI.transfer() + // polls the hardware flag which is automatically cleared as the + // AVR responds to SPI's interrupt + inline static void attachInterrupt() { SPCR |= _BV(SPIE); } + inline static void detachInterrupt() { SPCR &= ~_BV(SPIE); } + +private: + static uint8_t initialized; + static uint8_t interruptMode; // 0=none, 1=mask, 2=global + static uint8_t interruptMask; // which interrupts to mask + static uint8_t interruptSave; // temp storage, to restore state + #ifdef SPI_TRANSACTION_MISMATCH_LED + static uint8_t inTransactionFlag; + #endif +}; + +extern SPIClass SPI; + +#endif diff --git a/lib/Arduino.h b/lib/Arduino.h index 3698392..ac7031d 100644 --- a/lib/Arduino.h +++ b/lib/Arduino.h @@ -143,8 +143,8 @@ void delayMicroseconds(unsigned int us); // void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); // uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); -// void attachInterrupt(uint8_t, void (*)(void), int mode); -// void detachInterrupt(uint8_t); +void attachInterrupt(uint8_t, void (*)(void), int mode); +void detachInterrupt(uint8_t); // Get the bit location within the hardware port of the given virtual pin. // This comes from the pins_*.c file for the active board configuration. @@ -215,6 +215,29 @@ extern const uint8_t PROGMEM digital_pin_to_timer_PGM[]; #define TIMER5B 17 #define TIMER5C 18 + +#define EXTERNAL_INT_0 0 +#define EXTERNAL_INT_1 1 +#define EXTERNAL_INT_2 2 +#define EXTERNAL_INT_3 3 +#define EXTERNAL_INT_4 4 +#define EXTERNAL_INT_5 5 +#define EXTERNAL_INT_6 6 +#define EXTERNAL_INT_7 7 + +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega128RFA1__) || defined(__AVR_ATmega256RFR2__) || \ + defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega8U2__) +#define EXTERNAL_NUM_INTERRUPTS 8 +#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) +#define EXTERNAL_NUM_INTERRUPTS 3 +#elif defined(__AVR_ATmega32U4__) +#define EXTERNAL_NUM_INTERRUPTS 5 +#else +#define EXTERNAL_NUM_INTERRUPTS 2 +#endif + +typedef void (*voidFuncPtr)(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/lib/arduino-stub.cpp b/lib/arduino-stub.cpp index d848e8d..38bc168 100644 --- a/lib/arduino-stub.cpp +++ b/lib/arduino-stub.cpp @@ -329,7 +329,7 @@ void init() * real cooperative scheduler. */ static void __empty() { - // Empty + // Empty } void yield(void) __attribute__ ((weak, alias("__empty"))); @@ -762,6 +762,287 @@ void analogWrite(uint8_t pin, int val) } // ****************** +// this is from Arduino's WInterrupts.c + +static void nothing(void) { +} + +static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS] = { +#if EXTERNAL_NUM_INTERRUPTS > 8 + #warning There are more than 8 external interrupts. Some callbacks may not be initialized. + nothing, +#endif +#if EXTERNAL_NUM_INTERRUPTS > 7 + nothing, +#endif +#if EXTERNAL_NUM_INTERRUPTS > 6 + nothing, +#endif +#if EXTERNAL_NUM_INTERRUPTS > 5 + nothing, +#endif +#if EXTERNAL_NUM_INTERRUPTS > 4 + nothing, +#endif +#if EXTERNAL_NUM_INTERRUPTS > 3 + nothing, +#endif +#if EXTERNAL_NUM_INTERRUPTS > 2 + nothing, +#endif +#if EXTERNAL_NUM_INTERRUPTS > 1 + nothing, +#endif +#if EXTERNAL_NUM_INTERRUPTS > 0 + nothing, +#endif +}; +// volatile static voidFuncPtr twiIntFunc; + +void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) { + if(interruptNum < EXTERNAL_NUM_INTERRUPTS) { + intFunc[interruptNum] = userFunc; + + // Configure the interrupt mode (trigger on low input, any change, rising + // edge, or falling edge). The mode constants were chosen to correspond + // to the configuration bits in the hardware register, so we simply shift + // the mode into place. + + // Enable the interrupt. + + switch (interruptNum) { +#if defined(__AVR_ATmega32U4__) + // I hate doing this, but the register assignment differs between the 1280/2560 + // and the 32U4. Since avrlib defines registers PCMSK1 and PCMSK2 that aren't + // even present on the 32U4 this is the only way to distinguish between them. + case 0: + EICRA = (EICRA & ~((1<<ISC00) | (1<<ISC01))) | (mode << ISC00); + EIMSK |= (1<<INT0); + break; + case 1: + EICRA = (EICRA & ~((1<<ISC10) | (1<<ISC11))) | (mode << ISC10); + EIMSK |= (1<<INT1); + break; + case 2: + EICRA = (EICRA & ~((1<<ISC20) | (1<<ISC21))) | (mode << ISC20); + EIMSK |= (1<<INT2); + break; + case 3: + EICRA = (EICRA & ~((1<<ISC30) | (1<<ISC31))) | (mode << ISC30); + EIMSK |= (1<<INT3); + break; + case 4: + EICRB = (EICRB & ~((1<<ISC60) | (1<<ISC61))) | (mode << ISC60); + EIMSK |= (1<<INT6); + break; +#elif defined(EICRA) && defined(EICRB) && defined(EIMSK) + case 2: + EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00); + EIMSK |= (1 << INT0); + break; + case 3: + EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10); + EIMSK |= (1 << INT1); + break; + case 4: + EICRA = (EICRA & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20); + EIMSK |= (1 << INT2); + break; + case 5: + EICRA = (EICRA & ~((1 << ISC30) | (1 << ISC31))) | (mode << ISC30); + EIMSK |= (1 << INT3); + break; + case 0: + EICRB = (EICRB & ~((1 << ISC40) | (1 << ISC41))) | (mode << ISC40); + EIMSK |= (1 << INT4); + break; + case 1: + EICRB = (EICRB & ~((1 << ISC50) | (1 << ISC51))) | (mode << ISC50); + EIMSK |= (1 << INT5); + break; + case 6: + EICRB = (EICRB & ~((1 << ISC60) | (1 << ISC61))) | (mode << ISC60); + EIMSK |= (1 << INT6); + break; + case 7: + EICRB = (EICRB & ~((1 << ISC70) | (1 << ISC71))) | (mode << ISC70); + EIMSK |= (1 << INT7); + break; +#else + case 0: + #if defined(EICRA) && defined(ISC00) && defined(EIMSK) + EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00); + EIMSK |= (1 << INT0); + #elif defined(MCUCR) && defined(ISC00) && defined(GICR) + MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00); + GICR |= (1 << INT0); + #elif defined(MCUCR) && defined(ISC00) && defined(GIMSK) + MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00); + GIMSK |= (1 << INT0); + #else + #error attachInterrupt not finished for this CPU (case 0) + #endif + break; + + case 1: + #if defined(EICRA) && defined(ISC10) && defined(ISC11) && defined(EIMSK) + EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10); + EIMSK |= (1 << INT1); + #elif defined(MCUCR) && defined(ISC10) && defined(ISC11) && defined(GICR) + MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10); + GICR |= (1 << INT1); + #elif defined(MCUCR) && defined(ISC10) && defined(GIMSK) && defined(GIMSK) + MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10); + GIMSK |= (1 << INT1); + #else + #warning attachInterrupt may need some more work for this cpu (case 1) + #endif + break; + + case 2: + #if defined(EICRA) && defined(ISC20) && defined(ISC21) && defined(EIMSK) + EICRA = (EICRA & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20); + EIMSK |= (1 << INT2); + #elif defined(MCUCR) && defined(ISC20) && defined(ISC21) && defined(GICR) + MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20); + GICR |= (1 << INT2); + #elif defined(MCUCR) && defined(ISC20) && defined(GIMSK) && defined(GIMSK) + MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20); + GIMSK |= (1 << INT2); + #endif + break; +#endif + } + } +} + +void detachInterrupt(uint8_t interruptNum) { + if(interruptNum < EXTERNAL_NUM_INTERRUPTS) { + // Disable the interrupt. (We can't assume that interruptNum is equal + // to the number of the EIMSK bit to clear, as this isn't true on the + // ATmega8. There, INT0 is 6 and INT1 is 7.) + switch (interruptNum) { +#if defined(__AVR_ATmega32U4__) + case 0: + EIMSK &= ~(1<<INT0); + break; + case 1: + EIMSK &= ~(1<<INT1); + break; + case 2: + EIMSK &= ~(1<<INT2); + break; + case 3: + EIMSK &= ~(1<<INT3); + break; + case 4: + EIMSK &= ~(1<<INT6); + break; +#elif defined(EICRA) && defined(EICRB) && defined(EIMSK) + case 2: + EIMSK &= ~(1 << INT0); + break; + case 3: + EIMSK &= ~(1 << INT1); + break; + case 4: + EIMSK &= ~(1 << INT2); + break; + case 5: + EIMSK &= ~(1 << INT3); + break; + case 0: + EIMSK &= ~(1 << INT4); + break; + case 1: + EIMSK &= ~(1 << INT5); + break; + case 6: + EIMSK &= ~(1 << INT6); + break; + case 7: + EIMSK &= ~(1 << INT7); + break; +#else + case 0: + #if defined(EIMSK) && defined(INT0) + EIMSK &= ~(1 << INT0); + #elif defined(GICR) && defined(ISC00) + GICR &= ~(1 << INT0); // atmega32 + #elif defined(GIMSK) && defined(INT0) + GIMSK &= ~(1 << INT0); + #else + #error detachInterrupt not finished for this cpu + #endif + break; + + case 1: + #if defined(EIMSK) && defined(INT1) + EIMSK &= ~(1 << INT1); + #elif defined(GICR) && defined(INT1) + GICR &= ~(1 << INT1); // atmega32 + #elif defined(GIMSK) && defined(INT1) + GIMSK &= ~(1 << INT1); + #else + #warning detachInterrupt may need some more work for this cpu (case 1) + #endif + break; + + case 2: + #if defined(EIMSK) && defined(INT2) + EIMSK &= ~(1 << INT2); + #elif defined(GICR) && defined(INT2) + GICR &= ~(1 << INT2); // atmega32 + #elif defined(GIMSK) && defined(INT2) + GIMSK &= ~(1 << INT2); + #elif defined(INT2) + #warning detachInterrupt may need some more work for this cpu (case 2) + #endif + break; +#endif + } + + intFunc[interruptNum] = nothing; + } +} + +#define IMPLEMENT_ISR(vect, interrupt) \ + ISR(vect) { \ + intFunc[interrupt](); \ + } + +#if defined(__AVR_ATmega32U4__) + +IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0) +IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1) +IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_2) +IMPLEMENT_ISR(INT3_vect, EXTERNAL_INT_3) +IMPLEMENT_ISR(INT6_vect, EXTERNAL_INT_4) + +#elif defined(EICRA) && defined(EICRB) + +IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_2) +IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_3) +IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_4) +IMPLEMENT_ISR(INT3_vect, EXTERNAL_INT_5) +IMPLEMENT_ISR(INT4_vect, EXTERNAL_INT_0) +IMPLEMENT_ISR(INT5_vect, EXTERNAL_INT_1) +IMPLEMENT_ISR(INT6_vect, EXTERNAL_INT_6) +IMPLEMENT_ISR(INT7_vect, EXTERNAL_INT_7) + +#else + +IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0) +IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1) + +#if defined(EICRA) && defined(ISC20) +IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_2) +#endif + +#endif + + +// ****************** // this is from Arduino's WMath.cpp void randomSeed(unsigned long seed) @@ -795,5 +1076,3 @@ long map(long x, long in_min, long in_max, long out_min, long out_max) unsigned int makeWord(unsigned int w) { return w; } unsigned int makeWord(unsigned char h, unsigned char l) { return (h << 8) | l; } - -// end WMath.cpp @@ -29,7 +29,7 @@ #define NUM_LEDS 2 #elif defined(__BOARD_teensy1__) || defined(__BOARD_teensy1pp__) || defined(__BOARD_teensy2__) || defined(__BOARD_teensy2pp__) || \ defined(__BOARD_arduinoNano__) || defined(__BOARD_arduinoNG__) || defined(__BOARD_arduino2009v2__) || defined(__BOARD_arduino2009__) || \ - defined(__BOARD_arduino10000__) || defined(__BOARD_arduinoUno__) || defined(__BOARD_feather32u4__) || \ + defined(__BOARD_arduino10000__) || defined(__BOARD_arduinoUno__) || defined(__BOARD_feather32u4__) || defined(__BOARD_feather32u4lora__) || \ defined(__BOARD_hhd70dongle__) || defined(__BOARD_rda1846dongle__) || defined(__BOARD_culV3__) || \ defined(__BOARD_slowpandongle1__) || defined(__BOARD_slowpandongle2__) || defined(__BOARD_teenstep__) || \ defined(__BOARD_rhmixxx__) || defined(__BOARD_digispark__) || defined(__BOARD_robotdynMega2560__) @@ -62,7 +62,7 @@ #define LED2_PINNUM 6 #endif -#if defined(__BOARD_feather32u4__) +#if defined(__BOARD_feather32u4__) || defined(__BOARD_feather32u4lora__) #define LED_PORT PORTC #define LED_DDR DDRC #define LED_PINNUM 7 diff --git a/lib/stdc++-minimal.cpp b/lib/stdc++-minimal.cpp new file mode 100644 index 0000000..982719b --- /dev/null +++ b/lib/stdc++-minimal.cpp @@ -0,0 +1,53 @@ +/* + * spreadspace avr utils + * + * + * Copyright (C) 2014-2018 Christian Pointner <equinox@spreadspace.org> + * + * This file is part of spreadspace avr utils. + * + * spreadspace avr utils 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 3 of the License, or + * any later version. + * + * spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "stdlib.h" +#include "stdio.h" + +extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__)); +extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__)); + +void __cxa_pure_virtual(void) { + // this only works with serialio, it won't work with usbio + // because we would need to call usbio_task() ... + printf("PANIC: __cxa_pure_virtual() got called ...\r\n"); + for(;;); +} + +void __cxa_deleted_virtual(void) { + // this only works with serialio, it won't work with usbio + // because we would need to call usbio_task() ... + printf("PANIC: __cxa_deleted_virtual() got called ...\r\n"); + for(;;); +} + +void * operator new(size_t n) +{ + void * const p = malloc(n); + // handle p == 0 + return p; +} + +void operator delete(void * p) // or delete(void *, std::size_t) +{ + free(p); +} diff --git a/radiohead.mk b/radiohead.mk new file mode 100644 index 0000000..55fe943 --- /dev/null +++ b/radiohead.mk @@ -0,0 +1,55 @@ +## +## spreadspace avr utils +## +## +## Copyright (C) 2013-2018 Christian Pointner <equinox@spreadspace.org> +## +## This file is part of spreadspace avr utils. +## +## spreadspace avr utils 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 3 of the License, or +## any later version. +## +## spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>. +## + +include $(SPREADAVR_PATH)/defines.mk + + # TODO: add other components dependent on driver... +RADIOHEAD_COMPONENTS := RH_$(RADIOHEAD_DRIVER) RHSPIDriver RHGenericDriver RHGenericSPI RHHardwareSPI + +CXXFLAGS += -I$(RADIOHEAD_PATH) +CXXFLAGS += -DRH_PLATFORM=RH_PLATFORM_GENERIC_AVR8 +CXXFLAGS += $(RADIOHEAD_OPTS) + +ifdef RWEATHER_CRYPTO_PATH +CXXFLAGS += -DRH_ENABLE_ENCRYPTION_MODULE +CXXFLAGS += -I$(RWEATHER_CRYPTO_PATH)/libraries/Crypto +CXXFLAGS += $(RWEATHER_CRYPTO_OPTS) +RADIOHEAD_COMPONENTS += RHAEADDriver # RHEncryptedDriver +endif + +SRC:=$(foreach COMP,$(RADIOHEAD_COMPONENTS),$(RADIOHEAD_PATH)/$(COMP).cpp) +DEPLIBS := Arduino-SPI arduino-stub stdc++-minimal + +OBJ = $(SRC:%.cpp=%.o) +OBJ_DEPLIB = $(DEPLIBS:%=deplib-radiohead--%.o) + +libradiohead.a: $(OBJ) $(OBJ_DEPLIB) + $(AR) $@ $(OBJ) $(OBJ_DEPLIB) + +%.o: %.cpp + $(CXX) -c $(CXXFLAGS) $< -o $@ + +deplib-radiohead--%.o: $(LIB_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +clean: + rm -f $(SRC:%.cpp=%.o) $(OBJ_DEPLIB) diff --git a/rweather-crypto.mk b/rweather-crypto.mk new file mode 100644 index 0000000..1d39ecc --- /dev/null +++ b/rweather-crypto.mk @@ -0,0 +1,44 @@ +## +## spreadspace avr utils +## +## +## Copyright (C) 2013-2016 Christian Pointner <equinox@spreadspace.org> +## +## This file is part of spreadspace avr utils. +## +## spreadspace avr utils 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 3 of the License, or +## any later version. +## +## spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>. +## + +include $(SPREADAVR_PATH)/defines.mk + +SRC:=$(wildcard $(RWEATHER_CRYPTO_PATH)/libraries/Crypto/*.cpp) +DEPLIBS := stdc++-minimal + +CXXFLAGS += -I$(RWEATHER_CRYPTO_PATH)/libraries/Crypto +CXXFLAGS += $(RWEATHER_CRYPTO_OPTS) + +OBJ = $(SRC:%.cpp=%.o) +OBJ_DEPLIB = $(DEPLIBS:%=deplib-rweather-crypto--%.o) + +librweather-crypto.a: $(OBJ) $(OBJ_DEPLIB) + $(AR) $@ $(OBJ) $(OBJ_DEPLIB) + +%.o: %.cpp + $(CXX) -c $(CXXFLAGS) $< -o $@ + +deplib-rweather-crypto--%.o: $(LIB_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +clean: + rm -f $(SRC:%.cpp=%.o) $(OBJ_DEPLIB) diff --git a/usb-crypto/Makefile b/usb-crypto/Makefile new file mode 100644 index 0000000..bae846a --- /dev/null +++ b/usb-crypto/Makefile @@ -0,0 +1,51 @@ +## +## spreadspace avr utils +## +## +## Copyright (C) 2013-2016 Christian Pointner <equinox@spreadspace.org> +## +## This file is part of spreadspace avr utils. +## +## spreadspace avr utils 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 3 of the License, or +## any later version. +## +## spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>. +## + +NAME := usb-crypto +BOARD_TYPE := teensy2 +CXX_OBJ := $(NAME).o +LIBS := util led lufa-descriptor-usbserial usbio +EXTERNAL_LIBS := lufa rweather-crypto +SPREADAVR_PATH := .. +RESET_FUNC := $(SPREADAVR_PATH)/tools/reset_lufa_cdc +RESET_PARAM := 'r' + +LUFA_PATH := $(SPREADAVR_PATH)/contrib/lufa-LUFA-151115 +LUFA_OPTS = -D USB_DEVICE_ONLY +LUFA_OPTS += -D DEVICE_STATE_AS_GPIOR=0 +LUFA_OPTS += -D ORDERED_EP_CONFIG +LUFA_OPTS += -D FIXED_CONTROL_ENDPOINT_SIZE=8 +LUFA_OPTS += -D FIXED_NUM_CONFIGURATIONS=1 +LUFA_OPTS += -D USE_FLASH_DESCRIPTORS +LUFA_OPTS += -D USE_STATIC_OPTIONS="(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)" +LUFA_OPTS += -D INTERRUPT_CONTROL_ENDPOINT + +LUFA_OPTS += -D USB_MANUFACTURER="L\"equinox\"" +LUFA_OPTS += -D USB_PRODUCT="L\"spreadspace usb-crypto example\"" + +LUFA_COMPONENTS := USB USBCLASS + + +RWEATHER_CRYPTO_PATH := $(SPREADAVR_PATH)/contrib/rweather-crypto + + +include $(SPREADAVR_PATH)/include.mk diff --git a/usb-crypto/decrypt.py b/usb-crypto/decrypt.py new file mode 100755 index 0000000..7f7151a --- /dev/null +++ b/usb-crypto/decrypt.py @@ -0,0 +1,36 @@ +#!/usr/bin/python +# +# spreadspace avr utils +# +# +# Copyright (C) 2013-2016 Christian Pointner <equinox@spreadspace.org> +# +# This file is part of spreadspace avr utils. +# +# spreadspace avr utils 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 3 of the License, or +# any later version. +# +# spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>. +# + +import binascii +from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 + +hdr = "hello world!" +body_enc = binascii.unhexlify("5535BB923FB4BDCA401D3AE05C85FF314BD22423FE339CD00259551AECA534FABBEB16B39CC9AC14DF") + +key = binascii.unhexlify("7043b69bde20446661ba579e83fda0830e3e61c95b5ac8deeb79973ba0df02d8") +iv = binascii.unhexlify("6fac1c6a94a5788761cf9ecd") + +cipher = ChaCha20Poly1305(key) +msg = cipher.decrypt(iv, body_enc, hdr) + +print(str(msg)) diff --git a/usb-crypto/usb-crypto.cpp b/usb-crypto/usb-crypto.cpp new file mode 100644 index 0000000..0f7a9be --- /dev/null +++ b/usb-crypto/usb-crypto.cpp @@ -0,0 +1,157 @@ +/* + * spreadspace avr utils + * + * + * Copyright (C) 2013-2016 Christian Pointner <equinox@spreadspace.org> + * + * This file is part of spreadspace avr utils. + * + * spreadspace avr utils 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 3 of the License, or + * any later version. + * + * spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <avr/io.h> +#include <avr/wdt.h> +#include <avr/interrupt.h> +#include <avr/power.h> +#include <stdio.h> +#include <string.h> + +#include "util.h" +#include "led.h" +#include "usbio.h" + +#include <Crypto.h> +#include <ChaChaPoly.h> + +ChaChaPoly cipher; + +void print_hex_dump(const uint8_t* data, size_t len) { + for(size_t i=0; i<len; ++i) { + printf(" 0x%02X", data[i]); + if((i+1)%8 == 0) printf("\r\n"); + } + printf("\r\n"); +} + +void run_crypto() +{ + printf("running encrypt/decrypt test using ChaCha20Poly1305:\r\n"); + + printf("\r\nencrypting...\r\n"); + uint8_t hdr[] = "hello world!"; + size_t hdr_len = sizeof(hdr)-1; + uint8_t body[] = "this is a secret message."; + size_t body_len = sizeof(body)-1; + + uint8_t key[] = {0x70, 0x43, 0xb6, 0x9b, 0xde, 0x20, 0x44, 0x66, + 0x61, 0xba, 0x57, 0x9e, 0x83, 0xfd, 0xa0, 0x83, + 0x0e, 0x3e, 0x61, 0xc9, 0x5b, 0x5a, 0xc8, 0xde, + 0xeb, 0x79, 0x97, 0x3b, 0xa0, 0xdf, 0x02, 0xd8}; + + uint8_t iv[] = {0x6f, 0xac, 0x1c, 0x6a, 0x94, 0xa5, 0x78, 0x87, + 0x61, 0xcf, 0x9e, 0xcd}; + + uint8_t buf[256]; + uint8_t tag[16]; + memset(buf, 0, sizeof(buf)); + memset(tag, 0, sizeof(tag)); + + led_on(); + cipher.clear(); + if(!cipher.setKey(key, sizeof(key))) { + printf("failed to set key\r\n"); + return; + } + if(!cipher.setIV(iv, sizeof(iv))) { + printf("failed to set iv\r\n"); + return; + } + led_off(); + + led_on(); + cipher.addAuthData(hdr, hdr_len); + cipher.encrypt(buf, body, body_len); + cipher.computeTag(tag, sizeof(tag)); + led_off(); + + printf("encrypted data (%d bytes):\r\n", body_len); + print_hex_dump(buf, body_len); + printf("\r\n"); + printf("auth tag (%d bytes):\r\n", sizeof(tag)); + print_hex_dump(tag, sizeof(tag)); + + printf("\r\ndecrypting...\r\n"); + memcpy(body, buf, body_len); + memset(buf, 0, sizeof(buf)); + + led_on(); + cipher.clear(); + if(!cipher.setKey(key, sizeof(key))) { + printf("failed to set key\r\n"); + return; + } + if(!cipher.setIV(iv, sizeof(iv))) { + printf("failed to set iv\r\n"); + return; + } + led_off(); + + led_on(); + cipher.addAuthData(hdr, hdr_len); + cipher.decrypt(buf, body, body_len); + bool ct = cipher.checkTag(tag, sizeof(tag)); + led_off(); + + if(!ct) { + printf("auth tag mismatch!\r\n"); + return; + } else { + printf("auth tag correct!\r\n"); + } + printf("decrypted body: '%s'\r\n", buf); +} + +void handle_cmd(uint8_t cmd) +{ + switch(cmd) { + case 'c': run_crypto(); break; + case 'r': reset2bootloader(); break; + default: printf("unknown command"); break; + } + printf("\r\n"); +} + +int main(void) +{ + MCUSR &= ~(1 << WDRF); + wdt_disable(); + + cpu_init(); + led_init(); + usbio_init(); + sei(); + + for(;;) { + int16_t BytesReceived = usbio_bytes_received(); + while(BytesReceived > 0) { + int ReceivedByte = fgetc(stdin); + if(ReceivedByte != EOF) { + handle_cmd(ReceivedByte); + } + BytesReceived--; + } + + usbio_task(); + } +} diff --git a/usb-lora-crypto/Makefile b/usb-lora-crypto/Makefile new file mode 100644 index 0000000..0b61c94 --- /dev/null +++ b/usb-lora-crypto/Makefile @@ -0,0 +1,52 @@ +## +## spreadspace avr utils +## +## +## Copyright (C) 2013-2018 Christian Pointner <equinox@spreadspace.org> +## +## This file is part of spreadspace avr utils. +## +## spreadspace avr utils 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 3 of the License, or +## any later version. +## +## spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>. +## + +NAME := usb-lora-crypto +BOARD_TYPE := elecrow32u4lora +CXX_OBJ := $(NAME).o +LIBS := util led lufa-descriptor-usbserial usbio +EXTERNAL_LIBS := lufa radiohead rweather-crypto +SPREADAVR_PATH := .. + +LUFA_PATH := $(SPREADAVR_PATH)/contrib/lufa-LUFA-151115 +LUFA_OPTS = -D USB_DEVICE_ONLY +LUFA_OPTS += -D DEVICE_STATE_AS_GPIOR=0 +LUFA_OPTS += -D ORDERED_EP_CONFIG +LUFA_OPTS += -D FIXED_CONTROL_ENDPOINT_SIZE=8 +LUFA_OPTS += -D FIXED_NUM_CONFIGURATIONS=1 +LUFA_OPTS += -D USE_FLASH_DESCRIPTORS +LUFA_OPTS += -D USE_STATIC_OPTIONS="(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)" +LUFA_OPTS += -D INTERRUPT_CONTROL_ENDPOINT + +LUFA_OPTS += -D USB_MANUFACTURER="L\"equinox\"" +LUFA_OPTS += -D USB_PRODUCT="L\"spreadspace usb-lora-crypto example\"" + +LUFA_COMPONENTS := USB USBCLASS + + +RADIOHEAD_PATH := $(SPREADAVR_PATH)/contrib/RadioHead + +RADIOHEAD_DRIVER := RF95 + +RWEATHER_CRYPTO_PATH := $(SPREADAVR_PATH)/contrib/rweather-crypto + +include $(SPREADAVR_PATH)/include.mk diff --git a/usb-lora-crypto/usb-lora-crypto.cpp b/usb-lora-crypto/usb-lora-crypto.cpp new file mode 100644 index 0000000..3af1dcb --- /dev/null +++ b/usb-lora-crypto/usb-lora-crypto.cpp @@ -0,0 +1,118 @@ +/* + * spreadspace avr utils + * + * + * Copyright (C) 2013-2016 Christian Pointner <equinox@spreadspace.org> + * + * This file is part of spreadspace avr utils. + * + * spreadspace avr utils 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 3 of the License, or + * any later version. + * + * spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <avr/io.h> +#include <avr/wdt.h> +#include <avr/interrupt.h> +#include <avr/power.h> +#include <stdio.h> + +#include "util.h" +#include "led.h" +#include "usbio.h" + +#include "RadioHead.h" +#include "RH_RF95.h" +#include <Crypto.h> +#include <ChaChaPoly.h> +#include "RHAEADDriver.h" + +ChaChaPoly cipher; +RH_RF95 lora; +RHAEADDriver aead(lora, cipher); + +void recv_aead_msg() +{ + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN+1]; + uint8_t len = sizeof(buf)-1; + + led2_on(); + if(!aead.recv(buf, &len)) { + printf("aead.recv(): failed\r\n"); + led2_off(); + return; + } + buf[len] = 0; + + printf("aead.recv() got message: RSSI = %d, data = ", aead.lastRssi()); + for(uint8_t i = 0; i < len; ++i) printf("%s0x%02X", (i==0) ? "" : " ", buf[i]); + printf("\r\n"); + // printf("aead.recv() got message: RSSI = %d, data = %s\r\n", aead.lastRssi(), buf); + led2_off(); +} + +void send_aead_msg() +{ + uint8_t data[] = "spreadspace.org/avr-utils usb-aead test using radiohead library"; + printf("aead: sending message with %d bytes\r\n", sizeof(data)); + + led_on(); + if(!aead.send(data, sizeof(data))) { + printf("aead.send(): failed\r\n"); + led_off(); + return; + } + aead.waitPacketSent(); + printf("aead.send(): success\r\n"); + led_off(); +} + +void handle_cmd(uint8_t cmd) +{ + switch(cmd) { + case 's': send_aead_msg(); break; + default: printf("error\r\n"); return; + } +} + +int main(void) +{ + MCUSR &= ~(1 << WDRF); + wdt_disable(); + + cpu_init(); + led_init(); + usbio_init(); + arduino_init(); + sei(); + + lora.init(); + lora.setFrequency(868.0); + aead.init(); + + for(;;) { + if(aead.available()) { + recv_aead_msg(); + } + + int16_t BytesReceived = usbio_bytes_received(); + while(BytesReceived > 0) { + int ReceivedByte = fgetc(stdin); + if(ReceivedByte != EOF) { + handle_cmd(ReceivedByte); + } + BytesReceived--; + } + + usbio_task(); + } +} diff --git a/usb-lora/Makefile b/usb-lora/Makefile new file mode 100644 index 0000000..8e2c751 --- /dev/null +++ b/usb-lora/Makefile @@ -0,0 +1,50 @@ +## +## spreadspace avr utils +## +## +## Copyright (C) 2013-2018 Christian Pointner <equinox@spreadspace.org> +## +## This file is part of spreadspace avr utils. +## +## spreadspace avr utils 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 3 of the License, or +## any later version. +## +## spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>. +## + +NAME := usb-lora +BOARD_TYPE := feather32u4lora +CXX_OBJ := $(NAME).o +LIBS := util led lufa-descriptor-usbserial usbio +EXTERNAL_LIBS := lufa radiohead +SPREADAVR_PATH := .. + +LUFA_PATH := $(SPREADAVR_PATH)/contrib/lufa-LUFA-151115 +LUFA_OPTS = -D USB_DEVICE_ONLY +LUFA_OPTS += -D DEVICE_STATE_AS_GPIOR=0 +LUFA_OPTS += -D ORDERED_EP_CONFIG +LUFA_OPTS += -D FIXED_CONTROL_ENDPOINT_SIZE=8 +LUFA_OPTS += -D FIXED_NUM_CONFIGURATIONS=1 +LUFA_OPTS += -D USE_FLASH_DESCRIPTORS +LUFA_OPTS += -D USE_STATIC_OPTIONS="(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)" +LUFA_OPTS += -D INTERRUPT_CONTROL_ENDPOINT + +LUFA_OPTS += -D USB_MANUFACTURER="L\"equinox\"" +LUFA_OPTS += -D USB_PRODUCT="L\"spreadspace usb-lora example\"" + +LUFA_COMPONENTS := USB USBCLASS + + +RADIOHEAD_PATH := $(SPREADAVR_PATH)/contrib/RadioHead + +RADIOHEAD_DRIVER := RF95 + +include $(SPREADAVR_PATH)/include.mk diff --git a/usb-lora/usb-lora.cpp b/usb-lora/usb-lora.cpp new file mode 100644 index 0000000..561f543 --- /dev/null +++ b/usb-lora/usb-lora.cpp @@ -0,0 +1,112 @@ +/* + * spreadspace avr utils + * + * + * Copyright (C) 2013-2016 Christian Pointner <equinox@spreadspace.org> + * + * This file is part of spreadspace avr utils. + * + * spreadspace avr utils 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 3 of the License, or + * any later version. + * + * spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <avr/io.h> +#include <avr/wdt.h> +#include <avr/interrupt.h> +#include <avr/power.h> +#include <stdio.h> + +#include "util.h" +#include "led.h" +#include "usbio.h" + +#include "RadioHead.h" +#include "RH_RF95.h" + +RH_RF95 lora; + +void recv_lora_msg() +{ + uint8_t buf[RH_RF95_MAX_MESSAGE_LEN+1]; + uint8_t len = sizeof(buf)-1; + + led2_on(); + if(!lora.recv(buf, &len)) { + printf("lora.recv(): failed\r\n"); + led2_off(); + return; + } + buf[len] = 0; + + // printf("lora.recv() got message: SNR = %d, data = ", lora.lastSNR()); + // for(uint8_t i = 0; i < len; ++i) printf("%s0x%02X", (i==0) ? "" : " ", buf[i]); + // printf("\r\n"); + printf("lora.recv() got message: SNR = %d, data = %s\r\n", lora.lastSNR(), buf); + led2_off(); +} + +void send_lora_msg() +{ + uint8_t data[] = "spreadspace.org/avr-utils usb-lora test using radiohead library"; + printf("lora: sending message with %d bytes\r\n", sizeof(data)); + + led_on(); + if(!lora.send(data, sizeof(data))) { + printf("lora.send(): failed\r\n"); + led_off(); + return; + } + lora.waitPacketSent(); + printf("lora.send(): success\r\n"); + led_off(); +} + +void handle_cmd(uint8_t cmd) +{ + switch(cmd) { + case 's': send_lora_msg(); break; + default: printf("error\r\n"); return; + } +} + +int main(void) +{ + MCUSR &= ~(1 << WDRF); + wdt_disable(); + + cpu_init(); + led_init(); + usbio_init(); + arduino_init(); + sei(); + + lora.init(); + lora.setFrequency(868.0); + + for(;;) { + if(lora.available()) { + recv_lora_msg(); + } + + int16_t BytesReceived = usbio_bytes_received(); + while(BytesReceived > 0) { + int ReceivedByte = fgetc(stdin); + if(ReceivedByte != EOF) { + handle_cmd(ReceivedByte); + } + BytesReceived--; + } + + usbio_task(); + } +} |