├── .gitattributes ├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── README.md ├── ci └── Dockerfile ├── lmic ├── aes.c ├── aes.h ├── debug.c ├── debug.h ├── hal.h ├── lce.c ├── lce.h ├── lmic.c ├── lmic.h ├── lorabase.h ├── oslmic.c ├── oslmic.h ├── peripherals.h ├── radio-sx126x.c ├── radio-sx127x.c ├── radio.c └── region.h ├── projects ├── .gitignore ├── ex-join │ ├── Makefile │ ├── app.svc │ ├── main.c │ └── test.py ├── fam-devboards.mk ├── fam-simul.mk ├── platform.mk ├── projects.gmk └── variants.mk ├── services ├── appstart.svc ├── appstart │ └── main.c ├── eefs.svc ├── eefs │ ├── eefs.c │ ├── eefs.h │ ├── picofs.c │ ├── picofs.h │ └── ufid.py ├── frag.svc ├── fuota.svc ├── fuota │ ├── .gitignore │ ├── Makefile │ ├── frag.c │ ├── frag.h │ ├── frag.py │ ├── fuota.c │ ├── fuota.h │ ├── fuota_hal.h │ ├── fuota_hal_x86_64.h │ ├── fwman.c │ ├── test.c │ ├── test.py │ ├── test.up │ └── testkey.pem ├── fwman.svc ├── lwmux.svc ├── lwmux │ ├── lwmux.c │ └── lwmux.h ├── lwtest.svc ├── lwtest │ └── testmode.c ├── pwrman.svc ├── pwrman │ ├── pwrman.c │ └── pwrman.h ├── uexti.svc └── uexti │ ├── uexti.c │ └── uexti.h ├── stm32 ├── CMSIS │ ├── Device │ │ └── ST │ │ │ └── STM32L0xx │ │ │ └── Include │ │ │ ├── stm32l051xx.h │ │ │ ├── stm32l052xx.h │ │ │ ├── stm32l053xx.h │ │ │ ├── stm32l061xx.h │ │ │ ├── stm32l062xx.h │ │ │ ├── stm32l063xx.h │ │ │ ├── stm32l071xx.h │ │ │ ├── stm32l072xx.h │ │ │ ├── stm32l073xx.h │ │ │ ├── stm32l081xx.h │ │ │ ├── stm32l082xx.h │ │ │ ├── stm32l083xx.h │ │ │ ├── stm32l0xx.h │ │ │ └── system_stm32l0xx.h │ └── Include │ │ ├── arm_common_tables.h │ │ ├── arm_const_structs.h │ │ ├── arm_math.h │ │ ├── core_cm0.h │ │ ├── core_cm0plus.h │ │ ├── core_cm3.h │ │ ├── core_cm4.h │ │ ├── core_cm7.h │ │ ├── core_cmFunc.h │ │ ├── core_cmInstr.h │ │ ├── core_cmSimd.h │ │ ├── core_sc000.h │ │ └── core_sc300.h ├── adc.c ├── board.h ├── brd_devboards.h ├── eeprom.c ├── fw.ld ├── gpio.c ├── hal.c ├── hal_stm32.h ├── hw.h ├── i2c.c ├── leds.c ├── persodata.c ├── sleep.S ├── startup.c ├── trng.c └── usart.c ├── tools ├── arch.gmk ├── openocd │ ├── flash.cfg │ ├── nucleo-l0.cfg │ └── stlink-rules.tgz ├── pylora │ ├── loracrypto.py │ ├── loradefs.py │ ├── loramsg.py │ ├── loraopts.py │ └── rtlib │ │ ├── __init__.py │ │ ├── eui.py │ │ └── types.py └── svctool │ ├── cc.py │ └── svctool.py └── unicorn ├── board.h ├── fw.ld ├── hal.c ├── hal_unicorn.h ├── hw.h ├── persodata.c ├── simul ├── devsimul.py ├── devtest.py ├── fuotatest.py ├── lwtest.py ├── peripherals.py └── vtimeloop.py └── startup.c /.gitattributes: -------------------------------------------------------------------------------- 1 | *.hex -diff 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .*.swp 3 | *.pyc 4 | __pycache__ 5 | .mypy_cache 6 | /TAGS 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "basicloader"] 2 | path = basicloader 3 | url = ../basicloader.git 4 | [submodule "services/fuota/micro-ecc"] 5 | path = services/fuota/micro-ecc 6 | url = https://github.com/kmackay/micro-ecc.git 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: minimal 2 | 3 | services: docker 4 | 5 | env: Dimg=lorabasics/basicmac-test; Dname=basicmac-test 6 | 7 | before_install: 8 | - sudo chown -R 1000:1000 $TRAVIS_BUILD_DIR 9 | - docker run -d --name $Dname -v $TRAVIS_BUILD_DIR:/home/nonprivuser/travis -w /home/nonprivuser/travis $Dimg tail -f /dev/null 10 | - export Dcmd="docker exec -t $Dname bash -c" 11 | 12 | install: 13 | # nothing to install 14 | 15 | script: 16 | # all commands must be quoted 17 | - $Dcmd "make -C basicloader && cd projects/ex-join && make && VARIANT=simul make test" 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | --- Revised 3-Clause BSD License --- 2 | Copyright (C) 2016-2019, SEMTECH (International) AG. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL SEMTECH BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basic MAC 2 | 3 | Basic MAC is a portable implementation of the LoRa™ Alliance's LoRaWAN™ 4 | specification in the C programming language. It is a fork of IBM's LMiC 5 | library, and supports multiple regions, which are selectable at compile and/or 6 | run time. It can handle Class A, Class B, and Class C devices. 7 | 8 | #### Release 2.2 9 | 10 | This is the final official release of the stack, as Semtech is ending support 11 | for LoRa Basics™ MAC. For new designs, we recommend using LoRaMAC-Node. 12 | 13 | #### Status 14 | Branch | Travis CI 15 | -------|---------- 16 | [`master`](https://github.com/lorabasics/basicmac/tree/master) | [![Build Status](https://travis-ci.com/lorabasics/basicmac.svg?token=arWXaZpgdXnmsbdFHygd&branch=master)](https://travis-ci.com/lorabasics/basicmac) 17 | 18 | #### Documentation 19 | 20 | The full documentation is available at 21 | [https://doc.sm.tc/mac](https://doc.sm.tc/mac). 22 | 23 | #### Forum 24 | 25 | For questions and support, please visit the 26 | [LoRa Developer Portal Forum](https://lora-developers.semtech.com/knowledge-base/forum/viewforum/33/). 27 | -------------------------------------------------------------------------------- /ci/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:19.04 2 | 3 | ENV container=docker TERM=xterm LC_ALL=en_US LANGUAGE=en_US LANG=en_US.UTF-8 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | 6 | # locale 7 | RUN apt-get update -q > /dev/null && \ 8 | apt-get install --no-install-recommends -yq apt-utils locales language-pack-en dialog \ 9 | > /dev/null && \ 10 | locale-gen $LANGUAGE $LANG 11 | 12 | # sudo commmand 13 | RUN apt-get -yq install sudo > /dev/null 14 | 15 | # non-privileged user 16 | RUN echo "nonprivuser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers 17 | RUN useradd --no-log-init --home-dir /home/nonprivuser --create-home --shell /bin/bash -u 1000 \ 18 | nonprivuser && adduser nonprivuser sudo 19 | USER nonprivuser 20 | WORKDIR /home/nonprivuser 21 | 22 | # system packages 23 | RUN sudo apt-get install --no-install-recommends -yq \ 24 | git make gcc-arm-none-eabi libnewlib-arm-none-eabi \ 25 | python3 python3-pip python3-setuptools python3-wheel \ 26 | > /dev/null && \ 27 | sudo apt-get clean -q 28 | 29 | # python packages 30 | RUN pip3 install \ 31 | setuptools \ 32 | Click intelhex PyYAML \ 33 | colorama intervaltree lz4 numpy pycryptodome unicorn 34 | 35 | -------------------------------------------------------------------------------- /lmic/aes.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _aes_h_ 7 | #define _aes_h_ 8 | 9 | #include "oslmic.h" 10 | 11 | // ====================================================================== 12 | // AES support 13 | // !!Keep in sync with lorabase.hpp!! 14 | // !!Keep in sync with bootloader/aes.c!! 15 | 16 | #ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too 17 | #define AES_ENC 0x00 18 | #define AES_DEC 0x01 19 | #define AES_MIC 0x02 20 | #define AES_CTR 0x04 21 | #define AES_MICNOAUX 0x08 22 | #endif 23 | #ifndef AESkey // if AESkey is defined as macro all other values must be too 24 | extern u1_t* AESkey; 25 | extern u1_t* AESaux; 26 | #endif 27 | #ifndef os_aes 28 | u4_t os_aes (u1_t mode, u1_t* buf, u2_t len); 29 | #endif 30 | 31 | #endif // _aes_h_ 32 | -------------------------------------------------------------------------------- /lmic/debug.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // Copyright (C) 2014-2016 IBM Corporation. All rights reserved. 3 | // 4 | // This file is subject to the terms and conditions defined in file 'LICENSE', 5 | // which is part of this source code package. 6 | 7 | #ifdef CFG_DEBUG 8 | 9 | #include 10 | 11 | #include "lmic.h" 12 | 13 | void debug_led (int val) { 14 | hal_debug_led(val); 15 | } 16 | 17 | void debug_str (const char* str) { 18 | hal_debug_str(str); 19 | } 20 | 21 | static int itoa (char* buf, unsigned int val, int base, int mindigits, int exp, int prec, char sign) { 22 | char num[33], *p = num, *b = buf; 23 | if (sign) { 24 | if ((int) val < 0) { 25 | val = -val; 26 | *b++ = '-'; 27 | } else if (sign != '-') { 28 | *b++ = sign; // space or plus 29 | } 30 | } 31 | if (mindigits > 32) { 32 | mindigits = 32; 33 | } 34 | do { 35 | int m = val % base; 36 | *p++ = (m <= 9) ? m + '0' : m - 10 + 'A'; 37 | if (p - num == exp) *p++ = '.'; 38 | } while ( (val /= base) || p - num < mindigits ); 39 | do { 40 | *b++ = *--p; 41 | } while (p > num + exp - prec); 42 | *b = 0; 43 | return b - buf; 44 | } 45 | 46 | static int strpad (char *buf, int size, const char *str, int len, int width, int leftalign, char pad) { 47 | if (len > width) { 48 | width = len; 49 | } 50 | if (width > size) { 51 | width = size; 52 | } 53 | for (int i = 0, npad = width - len; i < width; i++) { 54 | buf[i] = (leftalign) ? ((i < len) ? str[i] : pad) : ((i < npad) ? pad : str[i - npad]); 55 | } 56 | return width; 57 | } 58 | 59 | static const char* const evnames[] = { 60 | [EV_SCAN_TIMEOUT] = "SCAN_TIMEOUT", 61 | [EV_BEACON_FOUND] = "BEACON_FOUND", 62 | [EV_BEACON_MISSED] = "BEACON_MISSED", 63 | [EV_BEACON_TRACKED] = "BEACON_TRACKED", 64 | [EV_JOINING] = "JOINING", 65 | [EV_JOINED] = "JOINED", 66 | [EV_RFU1] = "RFU1", 67 | [EV_JOIN_FAILED] = "JOIN_FAILED", 68 | [EV_REJOIN_FAILED] = "REJOIN_FAILED", 69 | [EV_TXCOMPLETE] = "TXCOMPLETE", 70 | [EV_LOST_TSYNC] = "LOST_TSYNC", 71 | [EV_RESET] = "RESET", 72 | [EV_RXCOMPLETE] = "RXCOMPLETE", 73 | [EV_ADR_BACKOFF] = "ADR_BACKOFF", 74 | [EV_LINK_DEAD] = "LINK_DEAD", 75 | [EV_LINK_ALIVE] = "LINK_ALIVE", 76 | [EV_SCAN_FOUND] = "SCAN_FOUND", 77 | [EV_TXSTART] = "TXSTART", 78 | [EV_TXDONE] = "TXDONE", 79 | [EV_DATARATE] = "DATARATE", 80 | [EV_START_SCAN] = "START_SCAN", 81 | [EV_ADR_BACKOFF] = "ADR_BACKOFF", 82 | }; 83 | 84 | static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) { 85 | char c, *dst = str, *end = str + size - 1; 86 | int width, left, base, zero, space, plus, prec, sign; 87 | 88 | while ( (c = *format++) && dst < end ) { 89 | if (c != '%') { 90 | *dst++ = c; 91 | } else { 92 | // flags 93 | width = prec = left = zero = sign = space = plus = 0; 94 | while ( (c = *format++) ) { 95 | if (c == '-') left = 1; 96 | else if (c == ' ') space = 1; 97 | else if (c == '+') plus = 1; 98 | else if (c == '0') zero = 1; 99 | else break; 100 | } 101 | // width 102 | if (c == '*') { 103 | width = va_arg(arg, int); 104 | c = *format++; 105 | } else { 106 | while (c >= '0' && c <= '9') { 107 | width = width * 10 + c - '0'; 108 | c = *format++; 109 | } 110 | } 111 | // precision 112 | if (c == '.') { 113 | c = *format++; 114 | if (c == '*') { 115 | prec = va_arg(arg, int); 116 | c = *format++; 117 | } else { 118 | while (c >= '0' && c <= '9') { 119 | prec = prec * 10 + c - '0'; 120 | c = *format++; 121 | } 122 | } 123 | } 124 | // conversion specifiers 125 | switch (c) { 126 | case 'c': // character 127 | c = va_arg(arg, int); 128 | // fallthrough 129 | case '%': // percent literal 130 | *dst++ = c; 131 | break; 132 | case 's': { // nul-terminated string 133 | char *s = va_arg(arg, char *); 134 | int l = strlen(s); 135 | if(prec && l > prec) { 136 | l = prec; 137 | } 138 | dst += strpad(dst, end - dst, s, l, width, left, ' '); 139 | break; 140 | } 141 | case 'd': // signed integer as decimal 142 | sign = (plus) ? '+' : (space) ? ' ' : '-'; 143 | // fallthrough 144 | case 'u': // unsigned integer as decimal 145 | base = 10; 146 | goto numeric; 147 | case 'x': 148 | case 'X': // unsigned integer as hex 149 | base = 16; 150 | goto numeric; 151 | case 'b': // unsigned integer as binary 152 | base = 2; 153 | numeric: { 154 | char num[33], pad = ' '; 155 | if (zero && left == 0 && prec == 0) { 156 | prec = width - 1; // have itoa() do the leading zero-padding for correct placement of sign 157 | pad = '0'; 158 | } 159 | int len = itoa(num, va_arg(arg, int), base, prec, 0, 0, sign); 160 | dst += strpad(dst, end - dst, num, len, width, left, pad); 161 | break; 162 | } 163 | case 'F': { // signed integer and exponent as fixed-point decimal 164 | char num[33], pad = (zero && left == 0) ? '0' : ' '; 165 | int val = va_arg(arg, int); 166 | int exp = va_arg(arg, int); 167 | int len = itoa(num, val, 10, exp + 2, exp, (prec) ? prec : exp, (plus) ? '+' : (space) ? ' ' : '-'); 168 | dst += strpad(dst, end - dst, num, len, width, left, pad); 169 | break; 170 | } 171 | case 'e': { // LMIC event name 172 | int ev = va_arg(arg, int); 173 | const char *evn = (ev < sizeof(evnames) / sizeof(evnames[0]) && evnames[ev]) ? evnames[ev] : "UNKNOWN"; 174 | dst += strpad(dst, end - dst, evn, strlen(evn), width, left, ' '); 175 | break; 176 | } 177 | case 'E': { // EUI64, lsbf (xx-xx-xx-xx-xx-xx-xx-xx) 178 | char buf[23], *p = buf; 179 | unsigned char *eui = va_arg(arg, unsigned char *); 180 | for (int i = 7; i >= 0; i--) { 181 | p += itoa(p, eui[i], 16, 2, 0, 0, 0); 182 | if (i) *p++ = '-'; 183 | } 184 | dst += strpad(dst, end - dst, buf, 23, width, left, ' '); 185 | break; 186 | } 187 | case 't': // ostime_t (hh:mm:ss.mmm) 188 | case 'T': { // osxtime_t (ddd.hh:mm:ss) 189 | char buf[12], *p = buf; 190 | uint64_t t = ((c == 'T') ? va_arg(arg, uint64_t) : va_arg(arg, uint32_t)) * 1000 / OSTICKS_PER_SEC; 191 | int ms = t % 1000; 192 | t /= 1000; 193 | int sec = t % 60; 194 | t /= 60; 195 | int min = t % 60; 196 | t /= 60; 197 | int hr = t % 24; 198 | t /= 24; 199 | int day = t; 200 | if (c == 'T') { 201 | p += itoa(p, day, 10, 3, 0, 0, 0); 202 | *p++ = '.'; 203 | } 204 | p += itoa(p, hr, 10, 2, 0, 0, 0); 205 | *p++ = ':'; 206 | p += itoa(p, min, 10, 2, 0, 0, 0); 207 | *p++ = ':'; 208 | p += itoa(p, sec, 10, 2, 0, 0, 0); 209 | if (c == 't') { 210 | *p++ = '.'; 211 | p += itoa(p, ms, 10, 3, 0, 0, 0); 212 | } 213 | dst += strpad(dst, end - dst, buf, 12, width, left, ' '); 214 | break; 215 | } 216 | case 'h': { // buffer+length as hex dump (no field padding, but precision/maxsize truncation) 217 | unsigned char *buf = va_arg(arg, unsigned char *); 218 | int len = va_arg(arg, int); 219 | char *top = (prec == 0 || dst + prec > end) ? end : dst + prec; 220 | while (len--) { 221 | if ((len == 0 && top - dst >= 2) || top - dst >= 2 + space + 2) { 222 | dst += itoa(dst, *buf++, 16, 2, 0, 0, 0); 223 | if(space && len && dst < top) *dst++ = ' '; 224 | } else { 225 | while (dst < top) *dst++ = '.'; 226 | } 227 | } 228 | break; 229 | } 230 | default: // (also catch '\0') 231 | goto stop; 232 | } 233 | } 234 | } 235 | stop: 236 | *dst++ = 0; 237 | return dst - str - 1; 238 | } 239 | 240 | int debug_snprintf (char *str, int size, const char *format, ...) { 241 | va_list arg; 242 | int length; 243 | 244 | va_start(arg, format); 245 | length = debug_vsnprintf(str, size, format, arg); 246 | va_end(arg); 247 | return length; 248 | } 249 | 250 | void debug_printf (char const *format, ...) { 251 | char buf[256]; 252 | va_list arg; 253 | 254 | va_start(arg, format); 255 | debug_vsnprintf(buf, sizeof(buf), format, arg); 256 | va_end(arg); 257 | debug_str(buf); 258 | } 259 | 260 | #endif 261 | -------------------------------------------------------------------------------- /lmic/debug.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // Copyright (C) 2014-2016 IBM Corporation. All rights reserved. 3 | // 4 | // This file is subject to the terms and conditions defined in file 'LICENSE', 5 | // which is part of this source code package. 6 | 7 | #ifndef _debug_h_ 8 | #define _debug_h_ 9 | 10 | #ifndef CFG_DEBUG 11 | 12 | #define debug_snprintf(s,n,f,...) do { } while (0) 13 | #define debug_printf(f,...) do { } while (0) 14 | #define debug_str(s) do { } while (0) 15 | #define debug_led(val) do { } while (0) 16 | 17 | #else 18 | 19 | // write formatted string to buffer 20 | int debug_snprintf (char *str, int size, const char *format, ...); 21 | 22 | // write formatted string to USART 23 | void debug_printf (char const *format, ...); 24 | 25 | // write nul-terminated string to USART 26 | void debug_str (const char* str); 27 | 28 | // set LED state 29 | void debug_led (int val); 30 | 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /lmic/hal.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // Copyright (C) 2014-2016 IBM Corporation. All rights reserved. 3 | // 4 | // This file is subject to the terms and conditions defined in file 'LICENSE', 5 | // which is part of this source code package. 6 | 7 | #ifndef _hal_hpp_ 8 | #define _hal_hpp_ 9 | 10 | #ifdef HAL_IMPL_INC 11 | #include HAL_IMPL_INC 12 | #endif 13 | 14 | /* 15 | * initialize hardware (IO, SPI, TIMER, IRQ). 16 | */ 17 | void hal_init (void* bootarg); 18 | 19 | /* 20 | * set watchdog counter (in 2s units) 21 | */ 22 | void hal_watchcount (int cnt); 23 | 24 | /* 25 | * drive antenna switch (and account power consumption) 26 | */ 27 | #define HAL_ANTSW_OFF 0 28 | #define HAL_ANTSW_RX 1 29 | #define HAL_ANTSW_TX 2 30 | #define HAL_ANTSW_TX2 3 31 | void hal_ant_switch (u1_t val); 32 | 33 | /* 34 | * control radio TCXO power (0=off, 1=on) 35 | * (return if TCXO is present and in use) 36 | */ 37 | bool hal_pin_tcxo (u1_t val); 38 | 39 | /* 40 | * control radio RST pin (0=low, 1=high, 2=floating) 41 | */ 42 | void hal_pin_rst (u1_t val); 43 | 44 | /* 45 | * wait until radio BUSY pin is low 46 | */ 47 | void hal_pin_busy_wait (void); 48 | 49 | /* 50 | * set DIO0/1/2/3 interrupt mask 51 | */ 52 | #define HAL_IRQMASK_DIO0 (1<<0) 53 | #define HAL_IRQMASK_DIO1 (1<<1) 54 | #define HAL_IRQMASK_DIO2 (1<<2) 55 | #define HAL_IRQMASK_DIO3 (1<<3) 56 | void hal_irqmask_set (int mask); 57 | 58 | /* 59 | * drive radio NSS pin (on=low, off=high). 60 | */ 61 | void hal_spi_select (int on); 62 | 63 | /* 64 | * perform 8-bit SPI transaction with radio. 65 | * - write given byte 'outval' 66 | * - read byte and return value 67 | */ 68 | u1_t hal_spi (u1_t outval); 69 | 70 | /* 71 | * disable all CPU interrupts. 72 | * - might be invoked nested 73 | * - will be followed by matching call to hal_enableIRQs() 74 | */ 75 | void hal_disableIRQs (void); 76 | 77 | /* 78 | * enable CPU interrupts. 79 | */ 80 | void hal_enableIRQs (void); 81 | 82 | /* 83 | * put system and CPU in low-power mode, sleep until target time / interrupt. 84 | * - return 0 if target time is close 85 | * - otherwise sleep until target time / interrupt and return non-zero 86 | */ 87 | #define HAL_SLEEP_EXACT 0 88 | #define HAL_SLEEP_APPROX 1 89 | #define HAL_SLEEP_FOREVER 2 90 | u1_t hal_sleep (u1_t type, u4_t targettime); 91 | 92 | /* 93 | * return 32-bit system time in ticks. 94 | */ 95 | u4_t hal_ticks (void); 96 | 97 | /* 98 | * return 64-bit system time in ticks. 99 | */ 100 | u8_t hal_xticks (void); 101 | 102 | /* 103 | * return subticks (1/1024th tick) 104 | */ 105 | s2_t hal_subticks (void); 106 | 107 | /* 108 | * busy-wait until specified timestamp (in ticks) is reached. 109 | */ 110 | void hal_waitUntil (u4_t time); 111 | 112 | /* 113 | * get current battery level 114 | */ 115 | u1_t hal_getBattLevel (void); 116 | 117 | /* 118 | * set current battery level 119 | */ 120 | void hal_setBattLevel (u1_t level); 121 | 122 | /* 123 | * perform fatal failure action. 124 | * - called by assertions 125 | * - action could be HALT or reboot 126 | */ 127 | void hal_failed (void); 128 | 129 | #ifdef CFG_DEBUG 130 | 131 | void hal_debug_str (const char* str); 132 | void hal_debug_led (int val); 133 | 134 | #endif 135 | 136 | typedef struct { 137 | uint32_t blversion; 138 | uint32_t version; 139 | uint32_t crc; 140 | uint32_t flashsz; 141 | } hal_fwi; 142 | 143 | void hal_fwinfo (hal_fwi* fwi); 144 | 145 | u1_t* hal_joineui (void); 146 | u1_t* hal_deveui (void); 147 | u1_t* hal_nwkkey (void); 148 | u1_t* hal_appkey (void); 149 | u1_t* hal_serial (void); 150 | u4_t hal_region (void); 151 | u4_t hal_hwid (void); 152 | u4_t hal_unique (void); 153 | 154 | u4_t hal_dnonce_next (void); 155 | 156 | void hal_reboot (void); 157 | bool hal_set_update (void* ptr); 158 | 159 | void hal_logEv (uint8_t evcat, uint8_t evid, uint32_t evparam); 160 | 161 | #endif // _hal_hpp_ 162 | -------------------------------------------------------------------------------- /lmic/lce.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "oslmic.h" 7 | #include "aes.h" 8 | #include "lce.h" 9 | #include "lmic.h" 10 | 11 | 12 | bool lce_processJoinAccept (u1_t* jacc, u1_t jacclen, u2_t devnonce) { 13 | if( (jacc[0] & HDR_FTYPE) != HDR_FTYPE_JACC || (jacclen != LEN_JA && jacclen != LEN_JAEXT) ) { 14 | return 0; 15 | } 16 | os_getNwkKey(AESkey); 17 | os_aes(AES_ENC, jacc+1, jacclen-1); 18 | 19 | jacclen -= 4; 20 | u4_t mic1 = os_rmsbf4(jacc+jacclen); 21 | #if defined(CFG_lorawan11) 22 | u1_t optneg = jacc[OFF_JA_DLSET] & JA_DLS_OPTNEG; 23 | if( optneg ) { 24 | os_moveMem(jacc+OFF_JA_JOINNONCE+2, jacc+OFF_JA_JOINNONCE, jacclen-OFF_JA_JOINNONCE); 25 | os_wlsbf2(jacc+OFF_JA_JOINNONCE, devnonce); 26 | jacclen += 2; 27 | } 28 | #endif 29 | os_getNwkKey(AESkey); 30 | u4_t mic2 = os_aes(AES_MIC|AES_MICNOAUX, jacc, jacclen); 31 | #if defined(CFG_lorawan11) 32 | if( optneg ) { // Restore orig frame 33 | jacclen -= 2; 34 | os_moveMem(jacc+OFF_JA_JOINNONCE, jacc+OFF_JA_JOINNONCE+2, jacclen-OFF_JA_JOINNONCE); 35 | os_wlsbf4(jacc+jacclen, mic1); 36 | } 37 | #endif 38 | if( mic1 != mic2 ) { 39 | return 0; 40 | } 41 | u1_t* nwkskey = LMIC.lceCtx.nwkSKey; 42 | os_clearMem(nwkskey, 16); 43 | nwkskey[0] = 0x01; 44 | os_copyMem(nwkskey+1, &jacc[OFF_JA_JOINNONCE], LEN_JOINNONCE+LEN_NETID); 45 | os_wlsbf2(nwkskey+1+LEN_JOINNONCE+LEN_NETID, devnonce); 46 | os_copyMem(LMIC.lceCtx.appSKey, nwkskey, 16); 47 | LMIC.lceCtx.appSKey[0] = 0x02; 48 | #if defined(CFG_lorawan11) 49 | os_copyMem(LMIC.lceCtx.nwkSKeyDn, nwkskey, 16); 50 | LMIC.lceCtx.nwkSKeyDn[0] = 0x03; 51 | #endif 52 | 53 | os_getNwkKey(AESkey); 54 | os_aes(AES_ENC, nwkskey, 16); 55 | #if defined(CFG_lorawan11) 56 | if( optneg ) { 57 | os_getNwkKey(AESkey); 58 | os_aes(AES_ENC, LMIC.lceCtx.nwkSKeyDn, 16); 59 | os_getAppKey(AESkey); 60 | } else { 61 | os_copyMem(LMIC.lceCtx.nwkSKeyDn, nwkskey, 16); 62 | os_getNwkKey(AESkey); 63 | } 64 | #else 65 | os_getNwkKey(AESkey); 66 | #endif 67 | os_aes(AES_ENC, LMIC.lceCtx.appSKey, 16); 68 | return 1; 69 | } 70 | 71 | 72 | void lce_addMicJoinReq (u1_t* pdu, int len) { 73 | os_getNwkKey(AESkey); 74 | os_wmsbf4(pdu+len, os_aes(AES_MIC|AES_MICNOAUX, pdu, len)); // MSB because of internal structure of AES 75 | } 76 | 77 | void lce_encKey0 (u1_t* buf) { 78 | os_clearMem(AESkey,16); 79 | os_aes(AES_ENC,buf,16); 80 | } 81 | 82 | static void micB0 (u4_t devaddr, u4_t seqno, u1_t cat, int len) { 83 | os_clearMem(AESaux,16); 84 | AESaux[0] = 0x49; 85 | AESaux[5] = cat; 86 | AESaux[15] = len; 87 | os_wlsbf4(AESaux+ 6,devaddr); 88 | os_wlsbf4(AESaux+10,seqno); 89 | } 90 | 91 | bool lce_verifyMic (s1_t keyid, u4_t devaddr, u4_t seqno, u1_t* pdu, int len) { 92 | micB0(devaddr, seqno, 1, len); 93 | const u1_t* key; 94 | if( keyid == LCE_NWKSKEY ) { 95 | #if defined(CFG_lorawan11) 96 | key = LMIC.lceCtx.nwkSKeyDn; 97 | #else 98 | key = LMIC.lceCtx.nwkSKey; 99 | #endif 100 | } 101 | else if( keyid >= LCE_MCGRP_0 && keyid < LCE_MCGRP_0+LCE_MCGRP_MAX ) { 102 | key = LMIC.lceCtx.mcgroup[keyid - LCE_MCGRP_0].nwkSKeyDn; 103 | } 104 | else { 105 | // Illegal key index 106 | return 0; 107 | } 108 | os_copyMem(AESkey,key,16); 109 | return os_aes(AES_MIC, pdu, len) == os_rmsbf4(pdu+len); 110 | } 111 | 112 | void lce_addMic (s1_t keyid, u4_t devaddr, u4_t seqno, u1_t* pdu, int len) { 113 | if( keyid != LCE_NWKSKEY ) { 114 | return; // Illegal key index 115 | } 116 | micB0(devaddr, seqno, 0, len); 117 | const u1_t* key = LMIC.lceCtx.nwkSKey; 118 | os_copyMem(AESkey,key,16); 119 | // MSB because of internal structure of AES 120 | os_wmsbf4(pdu+len, os_aes(AES_MIC, pdu, len)); 121 | } 122 | 123 | u4_t lce_micKey0 (u4_t devaddr, u4_t seqno, u1_t* pdu, int len) { 124 | micB0(devaddr, seqno, 0, len); 125 | os_clearMem(AESkey,16); 126 | // MSB because of internal structure of AES 127 | u1_t mic[4]; 128 | os_wmsbf4(mic, os_aes(AES_MIC, pdu, len)); 129 | return os_rlsbf4(mic); 130 | } 131 | 132 | void lce_cipher (s1_t keyid, u4_t devaddr, u4_t seqno, int cat, u1_t* payload, int len) { 133 | if(len <= 0 || (cat==LCE_SCC_UP && (LMIC.opmode & OP_NOCRYPT)) ) { 134 | return; 135 | } 136 | const u1_t* key; 137 | if( keyid == LCE_NWKSKEY ) { 138 | #if defined(CFG_lorawan11) 139 | key = cat==LCE_SCC_DN ? LMIC.lceCtx.nwkSKeyDn : LMIC.lceCtx.nwkSKey; 140 | #else 141 | key = LMIC.lceCtx.nwkSKey; 142 | #endif 143 | } 144 | else if( keyid == LCE_APPSKEY ) { 145 | key = LMIC.lceCtx.appSKey; 146 | } 147 | else if( keyid >= LCE_MCGRP_0 && keyid < LCE_MCGRP_0+LCE_MCGRP_MAX ) { 148 | key = LMIC.lceCtx.mcgroup[keyid - LCE_MCGRP_0].appSKey; 149 | cat = LCE_SCC_DN; 150 | } 151 | else { 152 | // Illegal key index 153 | os_clearMem(payload,len); 154 | return; 155 | } 156 | micB0(devaddr, seqno, cat, 1); 157 | AESaux[0] = 0x01; 158 | os_copyMem(AESkey,key,16); 159 | os_aes(AES_CTR, payload, len); 160 | } 161 | 162 | 163 | #if defined(CFG_lorawan11) 164 | void lce_loadSessionKeys (const u1_t* nwkSKey, const u1_t* nwkSKeyDn, const u1_t* appSKey) 165 | #else 166 | void lce_loadSessionKeys (const u1_t* nwkSKey, const u1_t* appSKey) 167 | #endif 168 | { 169 | if( nwkSKey != (u1_t*)0 ) 170 | os_copyMem(LMIC.lceCtx.nwkSKey, nwkSKey, 16); 171 | #if defined(CFG_lorawan11) 172 | if( nwkSKeyDn != (u1_t*)0 ) 173 | os_copyMem(LMIC.lceCtx.nwkSKeyDn, nwkSKeyDn, 16); 174 | #endif 175 | if( appSKey != (u1_t*)0 ) 176 | os_copyMem(LMIC.lceCtx.appSKey, appSKey, 16); 177 | } 178 | 179 | 180 | void lce_init (void) { 181 | os_clearMem(&LMIC.lceCtx, sizeof(LMIC.lceCtx)); 182 | } 183 | -------------------------------------------------------------------------------- /lmic/lce.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _lce_h_ 7 | #define _lce_h_ 8 | 9 | #include "oslmic.h" 10 | 11 | // Some keyids: 12 | #define LCE_APPSKEY (-2) 13 | #define LCE_NWKSKEY (-1) 14 | #define LCE_MCGRP_0 ( 0) 15 | #define LCE_MCGRP_MAX ( 2) 16 | 17 | // Stream cipher categories (lce_cipher(..,cat,..): 18 | // Distinct use of the AppSKey must use different key classes 19 | // or plain text will leak: 20 | enum { 21 | LCE_SCC_UP = 0, // std LoRaWAN uplink frame 22 | LCE_SCC_DN = 1, // std LoRaWAN downlink frame 23 | LCE_SCC_FUP = 0x40, // file upload 24 | LCE_SCC_DSE = 0x41, // data streaming engine 25 | LCE_SCC_ROSE = 0x42, // reliable octet streaming engine 26 | }; 27 | 28 | void lce_encKey0 (u1_t* buf); 29 | u4_t lce_micKey0 (u4_t devaddr, u4_t seqno, u1_t* pdu, int len); 30 | bool lce_processJoinAccept (u1_t* jacc, u1_t jacclen, u2_t devnonce); 31 | void lce_addMicJoinReq (u1_t* pdu, int len); 32 | bool lce_verifyMic (s1_t keyid, u4_t devaddr, u4_t seqno, u1_t* pdu, int len); 33 | void lce_addMic (s1_t keyid, u4_t devaddr, u4_t seqno, u1_t* pdu, int len); 34 | void lce_cipher (s1_t keyid, u4_t devaddr, u4_t seqno, int cat, u1_t* payload, int len); 35 | #if defined(CFG_lorawan11) 36 | void lce_loadSessionKeys (const u1_t* nwkSKey, const u1_t* nwkSKeyDn, const u1_t* appSKey); 37 | #else 38 | void lce_loadSessionKeys (const u1_t* nwkSKey, const u1_t* appSKey); 39 | #endif 40 | void lce_init (void); 41 | 42 | 43 | typedef struct lce_ctx_mcgrp { 44 | u1_t nwkSKeyDn[16]; // network session key for down-link 45 | u1_t appSKey[16]; // application session key 46 | } lce_ctx_mcgrp_t; 47 | 48 | typedef struct lce_ctx { 49 | u1_t nwkSKey[16]; // network session key (LoRaWAN1.1: up-link only) 50 | #if defined(CFG_lorawan11) 51 | u1_t nwkSKeyDn[16]; // network session key for down-link 52 | #endif 53 | u1_t appSKey[16]; // application session key 54 | lce_ctx_mcgrp_t mcgroup[LCE_MCGRP_MAX]; 55 | } lce_ctx_t; 56 | 57 | 58 | #endif // _lce_h_ 59 | -------------------------------------------------------------------------------- /lmic/oslmic.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // Copyright (C) 2014-2016 IBM Corporation. All rights reserved. 3 | // 4 | // This file is subject to the terms and conditions defined in file 'LICENSE', 5 | // which is part of this source code package. 6 | 7 | #include "lmic.h" 8 | #include "aes.h" 9 | #include "peripherals.h" 10 | 11 | // RUNTIME STATE 12 | static struct { 13 | osjob_t* scheduledjobs; 14 | unsigned int exact; 15 | union { 16 | u4_t randwrds[4]; 17 | u1_t randbuf[16]; 18 | } /* anonymous */; 19 | } OS; 20 | 21 | void os_init (void* bootarg) { 22 | memset(&OS, 0x00, sizeof(OS)); 23 | hal_init(bootarg); 24 | #ifndef CFG_noradio 25 | radio_init(false); 26 | #endif 27 | LMIC_init(); 28 | } 29 | 30 | // return next random byte derived from seed buffer 31 | // (buf[0] holds index of next byte to be returned 1-16) 32 | 33 | void rng_init (void) { 34 | #ifdef PERIPH_TRNG 35 | trng_next(OS.randwrds, 4); 36 | #else 37 | memcpy(OS.randbuf, __TIME__, 8); 38 | os_getDevEui(OS.randbuf + 8); 39 | #endif 40 | } 41 | 42 | u1_t os_getRndU1 (void) { 43 | u1_t i = OS.randbuf[0]; 44 | switch( i ) { 45 | case 0: 46 | rng_init(); // lazy initialization 47 | // fall-thru 48 | case 16: 49 | os_aes(AES_ENC, OS.randbuf, 16); // encrypt seed with any key 50 | i = 0; 51 | } 52 | u1_t v = OS.randbuf[i++]; 53 | OS.randbuf[0] = i; 54 | return v; 55 | } 56 | 57 | bit_t os_cca (u2_t rps, u4_t freq) { //XXX:this belongs into os_radio module 58 | return 0; // never grant access 59 | } 60 | 61 | u1_t os_getBattLevel (void) { 62 | return hal_getBattLevel(); 63 | } 64 | 65 | ostime_t os_getTime () { 66 | return hal_ticks(); 67 | } 68 | 69 | osxtime_t os_getXTime () { 70 | return hal_xticks(); 71 | } 72 | 73 | osxtime_t os_time2XTime (ostime_t t, osxtime_t context) { 74 | return context + ((t - (ostime_t) context)); 75 | } 76 | 77 | // unlink job from queue, return 1 if removed 78 | static int unlinkjob (osjob_t** pnext, osjob_t* job) { 79 | for( ; *pnext; pnext = &((*pnext)->next)) { 80 | if(*pnext == job) { // unlink 81 | *pnext = job->next; 82 | if ((job->flags & OSJOB_FLAG_APPROX) == 0) { 83 | OS.exact -= 1; 84 | } 85 | return 1; 86 | } 87 | } 88 | return 0; 89 | } 90 | 91 | // NOTE: since the job queue might begin with jobs which already have a shortly expired deadline, we cannot use 92 | // the maximum span of ostime to schedule the next job (otherwise it would be queued in first)! 93 | #define XJOBTIME_MAX_DIFF (OSTIME_MAX_DIFF / 2) 94 | 95 | // update schedule of extended job 96 | static void extendedjobcb (osxjob_t* xjob) { 97 | hal_disableIRQs(); 98 | osxtime_t now = os_getXTime(); 99 | if (xjob->deadline - now > XJOBTIME_MAX_DIFF) { 100 | // schedule intermediate callback 101 | os_setTimedCallbackEx((osjob_t*) xjob, (ostime_t) (now + XJOBTIME_MAX_DIFF), (osjobcb_t) extendedjobcb, OSJOB_FLAG_APPROX); 102 | } else { 103 | // schedule final callback 104 | os_setTimedCallbackEx((osjob_t*) xjob, (ostime_t) xjob->deadline, xjob->func, OSJOB_FLAG_APPROX); 105 | } 106 | hal_enableIRQs(); 107 | } 108 | 109 | // schedule job far in the future (deadline may exceed max delta of ostime_t 2^31-1 ticks = 65535.99s = 18.2h) 110 | void os_setExtendedTimedCallback (osxjob_t* xjob, osxtime_t xtime, osjobcb_t cb) { 111 | hal_disableIRQs(); 112 | unlinkjob(&OS.scheduledjobs, (osjob_t*) xjob); 113 | xjob->func = cb; 114 | xjob->deadline = xtime; 115 | extendedjobcb(xjob); 116 | hal_enableIRQs(); 117 | } 118 | 119 | // clear scheduled job, return 1 if job was removed 120 | int os_clearCallback (osjob_t* job) { 121 | hal_disableIRQs(); 122 | int r = unlinkjob(&OS.scheduledjobs, job); 123 | hal_enableIRQs(); 124 | return r; 125 | } 126 | 127 | // schedule timed job 128 | void os_setTimedCallbackEx (osjob_t* job, ostime_t time, osjobcb_t cb, unsigned int flags) { 129 | osjob_t** pnext; 130 | hal_disableIRQs(); 131 | // remove if job was already queued 132 | unlinkjob(&OS.scheduledjobs, job); 133 | // fill-in job 134 | ostime_t now = os_getTime(); 135 | if( flags & OSJOB_FLAG_NOW ) { 136 | time = now; 137 | } else if ( time - now <= 0 ) { 138 | flags |= OSJOB_FLAG_NOW; 139 | } 140 | job->deadline = time; 141 | job->func = cb; 142 | job->next = NULL; 143 | job->flags = flags; 144 | if ((flags & OSJOB_FLAG_APPROX) == 0) { 145 | OS.exact += 1; 146 | } 147 | // insert into schedule 148 | for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) { 149 | if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!) 150 | // enqueue before next element and stop 151 | job->next = *pnext; 152 | break; 153 | } 154 | } 155 | *pnext = job; 156 | hal_enableIRQs(); 157 | } 158 | 159 | // execute 1 job from timer or run queue, or sleep if nothing is pending 160 | void os_runstep (void) { 161 | osjob_t* j = NULL; 162 | hal_disableIRQs(); 163 | // check for runnable jobs 164 | if (OS.scheduledjobs) { 165 | if (hal_sleep(OS.exact ? HAL_SLEEP_EXACT : HAL_SLEEP_APPROX, OS.scheduledjobs->deadline) == 0) { 166 | j = OS.scheduledjobs; 167 | OS.scheduledjobs = j->next; 168 | if ((j->flags & OSJOB_FLAG_APPROX) == 0) { 169 | OS.exact -= 1; 170 | } 171 | } 172 | } else { // nothing pending 173 | hal_sleep(HAL_SLEEP_FOREVER, 0); 174 | } 175 | if( j == NULL || (j->flags & OSJOB_FLAG_IRQDISABLED) == 0) { 176 | hal_enableIRQs(); 177 | } 178 | if (j) { // run job callback 179 | #ifdef CFG_warnjobs 180 | // warn about late execution of precisely timed jobs 181 | if ( (j->flags & (OSJOB_FLAG_NOW | OSJOB_FLAG_APPROX) ) == 0) { 182 | ostime_t delta = os_getTime() - j->deadline; 183 | if ( delta > 1 ) { 184 | debug_printf("WARNING: job 0x%08x (func 0x%08x) executed %d ticks late\r\n", j, j->func, delta); 185 | } 186 | } 187 | #endif 188 | hal_watchcount(30); // max 60 sec 189 | j->func(j); 190 | hal_watchcount(0); 191 | } 192 | } 193 | 194 | // execute jobs from timer and from run queue 195 | void os_runloop (void) { 196 | while (1) { 197 | os_runstep(); 198 | } 199 | } 200 | 201 | static u1_t evcatEn = 0xFF; 202 | 203 | void os_logEv (uint8_t evcat, uint8_t evid, uint32_t evparam) { 204 | if( evcat >= EVCAT_MAX && evcat < sizeof(evcatEn)*8 && (evcatEn & (1<data = (unsigned char*) "hello"; 20 | txinfo->dlen = 5; 21 | txinfo->port = 15; 22 | txinfo->txcomplete = txc; 23 | return true; 24 | } 25 | 26 | static void next (osjob_t* job) { 27 | lwm_request_send(&lj, 0, tx); 28 | } 29 | 30 | void app_dl (int port, unsigned char* data, int dlen, unsigned int flags) { 31 | debug_printf("DL[%d]: %h\r\n", port, data, dlen); 32 | } 33 | 34 | void app_main (osjob_t* job) { 35 | debug_printf("Hello World!\r\n"); 36 | 37 | // join network 38 | lwm_setmode(LWM_MODE_NORMAL); 39 | 40 | // re-use current job 41 | mainjob = job; 42 | 43 | // initiate first uplink 44 | next(mainjob); 45 | } 46 | -------------------------------------------------------------------------------- /projects/ex-join/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 4 | # 5 | # This file is subject to the terms and conditions defined in file 'LICENSE', 6 | # which is part of this source code package. 7 | 8 | import argparse 9 | import asyncio 10 | import sys 11 | 12 | from devtest import DeviceTest, LoRaWANTest 13 | from vtimeloop import VirtualTimeLoop 14 | 15 | class ExJoinTest(LoRaWANTest): 16 | @DeviceTest.test() 17 | async def join(self) -> bool: 18 | await self.lw_join() 19 | await self.lw_uplink() 20 | return True 21 | 22 | @DeviceTest.test() 23 | async def uplink(self) -> bool: 24 | await self.lw_join() 25 | t1 = None 26 | for _ in range(5): 27 | m = await self.lw_uplink() 28 | self.assert_eq(m['FRMPayload'], b'hello') 29 | t0 = t1 30 | t1 = asyncio.get_event_loop().time() 31 | if t0 is not None: 32 | self.assert_range((t1 - t0), 5, 10) 33 | return True 34 | 35 | @DeviceTest.test() 36 | async def dnlink(self) -> bool: 37 | await self.lw_join() 38 | m = await self.lw_uplink() 39 | self.lw_dnlink(m, port=15, payload=b'hi there!') 40 | await asyncio.sleep(5) 41 | return True 42 | 43 | 44 | 45 | if __name__ == '__main__': 46 | p = argparse.ArgumentParser() 47 | LoRaWANTest.stdargs(p) 48 | args = p.parse_args() 49 | 50 | if args.virtual_time: 51 | asyncio.set_event_loop(VirtualTimeLoop()) # type: ignore 52 | 53 | test = ExJoinTest(args) 54 | 55 | if not asyncio.get_event_loop().run_until_complete(test.run()): 56 | sys.exit(1) 57 | -------------------------------------------------------------------------------- /projects/fam-devboards.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | # ------------------------------------------------ 7 | # Family: Development Boards 8 | 9 | ifneq (,$(filter nucleo_l073rz,$(FAMILIES))) 10 | MCU := stm32l0 11 | LD_SCRIPTS += $(BL)/src/arm/stm32lx/ld/STM32L0xxZ.ld 12 | DEFS += -DSTM32L0 -DSTM32L073xx 13 | DEFS += -DCFG_nucleo_board 14 | DEFS += -DBRD_IMPL_INC='"brd_devboards.h"' 15 | OOCFGS += $(TOOLSDIR)/openocd/nucleo-l0.cfg 16 | BL_BRD := NUCLEO-L073RZ 17 | endif 18 | 19 | ifneq (,$(filter nucleo_l053r8,$(FAMILIES))) 20 | MCU := stm32l0 21 | LD_SCRIPTS += $(BL)/src/arm/stm32lx/ld/STM32L0xx8.ld 22 | DEFS += -DSTM32L0 -DSTM32L053xx 23 | DEFS += -DCFG_nucleo_board 24 | DEFS += -DBRD_IMPL_INC='"brd_devboards.h"' 25 | OOCFGS += $(TOOLSDIR)/openocd/nucleo-l0.cfg 26 | BL_BRD := NUCLEO-L053R8 27 | endif 28 | 29 | ifneq (,$(filter sx1272mbed,$(FAMILIES))) 30 | DEFS += -DCFG_sx1272mbed 31 | endif 32 | 33 | ifneq (,$(filter sx1276mb1las,$(FAMILIES))) 34 | DEFS += -DCFG_sx1276mb1las 35 | endif 36 | 37 | ifneq (,$(filter sx1276mb1mas,$(FAMILIES))) 38 | DEFS += -DCFG_sx1276mb1mas 39 | endif 40 | 41 | ifneq (,$(filter sx1261mbed,$(FAMILIES))) 42 | DEFS += -DCFG_sx1261mbed 43 | endif 44 | 45 | ifneq (,$(filter sx1262mbed,$(FAMILIES))) 46 | DEFS += -DCFG_sx1262mbed 47 | endif 48 | 49 | ifneq (,$(filter b_l072z_lrwan1,$(FAMILIES))) 50 | MCU := stm32l0 51 | LD_SCRIPTS += $(BL)/src/arm/stm32lx/ld/STM32L0xxZ.ld 52 | DEFS += -DSTM32L0 -DSTM32L072xx 53 | DEFS += -DCFG_b_l072Z_lrwan1_board 54 | DEFS += -DBRD_IMPL_INC='"brd_devboards.h"' 55 | OOCFGS += $(TOOLSDIR)/openocd/nucleo-l0.cfg 56 | BL_BRD := B-L072Z-LRWAN1 57 | endif 58 | -------------------------------------------------------------------------------- /projects/fam-simul.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | # ------------------------------------------------ 7 | # Family: Simulation 8 | 9 | ifneq (,$(filter unicorn,$(FAMILIES))) 10 | MCU := unicorn 11 | LD_SCRIPTS += $(BL)/src/arm/unicorn/ld/mem.ld 12 | CFLAGS += -Dunicorn 13 | BL_BRD := simul-unicorn 14 | endif 15 | -------------------------------------------------------------------------------- /projects/platform.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | # ------------------------------------------------ 7 | # Target 8 | # (hyphen-concatenated list of families) 9 | 10 | FAMILIES := $(subst -, ,$(TARGET)) 11 | 12 | 13 | # ------------------------------------------------ 14 | # Families 15 | 16 | FAM_MAKEFILES += $(wildcard $(TOPDIR)/projects/fam-*.mk) 17 | -include $(FAM_MAKEFILES) 18 | 19 | 20 | # ------------------------------------------------ 21 | # MCUs 22 | 23 | ifeq ($(MCU:stm32%=stm32),stm32) 24 | TOOLCHAIN := gcc 25 | CROSS_COMPILE:=arm-none-eabi- 26 | CFLAGS += -fno-common -fno-builtin -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer 27 | HALDIR := $(TOPDIR)/stm32 28 | CMSIS := $(TOPDIR)/stm32/CMSIS 29 | CFLAGS += -I$(CMSIS)/Include 30 | CFLAGS += -I$(BL)/src/common 31 | CFLAGS += -I$(BL)/src/arm/stm32lx 32 | CFLAGS += -DHAL_IMPL_INC=\"hal_stm32.h\" 33 | LDFLAGS += -nostartfiles 34 | LDFLAGS += $(addprefix -T,$(LD_SCRIPTS)) 35 | OOCFGS += $(TOOLSDIR)/openocd/flash.cfg 36 | ifeq ($(MCU),stm32l0) 37 | FLAGS += -mcpu=cortex-m0plus -mthumb 38 | CFLAGS += -I$(CMSIS)/Device/ST/STM32L0xx/Include/ 39 | LD_SCRIPTS += $(HALDIR)/fw.ld 40 | OBJS_BLACKLIST += blipper.o 41 | OBJS_BLACKLIST += spi.o # these do not build yet for L0 42 | endif 43 | ifeq ($(MCU),stm32l1) 44 | FLAGS += -mcpu=cortex-m3 -mthumb 45 | CFLAGS += -I$(CMSIS)/Device/ST/STM32L1xx/Include/ 46 | LD_SCRIPTS += STM32L1.ld 47 | OBJS_BLACKLIST += i2c.o adc.o # these do not build yet for L1 48 | endif 49 | ALL += $(BUILDDIR)/$(PROJECT).hex 50 | ALL += $(BUILDDIR)/$(PROJECT).bin 51 | ALL += $(BUILDDIR)/$(PROJECT).zfw 52 | LOAD = loadhex 53 | endif 54 | 55 | ifeq ($(MCU),unicorn) 56 | TOOLCHAIN := gcc 57 | CROSS_COMPILE:=arm-none-eabi- 58 | HALDIR := $(TOPDIR)/unicorn 59 | FLAGS += -mcpu=cortex-m0plus -mthumb 60 | CFLAGS += -fno-common -fno-builtin -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer 61 | CFLAGS += -I$(BL)/src/common 62 | CFLAGS += -I$(BL)/src/arm/unicorn 63 | CFLAGS += -DHAL_IMPL_INC=\"hal_unicorn.h\" 64 | LDFLAGS += -nostartfiles 65 | LDFLAGS += $(addprefix -T,$(LD_SCRIPTS)) 66 | LD_SCRIPTS += $(HALDIR)/fw.ld 67 | ALL += $(BUILDDIR)/$(PROJECT).hex 68 | LOAD = dummy 69 | OBJS_BLACKLIST += radio.o 70 | endif 71 | 72 | 73 | # ------------------------------------------------ 74 | # Build tools 75 | 76 | BL ?= $(TOPDIR)/basicloader 77 | BL_BRD ?= $(error "No basic loader board set") 78 | BL_BUILD ?= $(BL)/build/boards/$(BL_BRD) 79 | 80 | ifneq (ok,$(shell python3 -c 'import sys; print("ok" if sys.hexversion >= 0x03060000 else "")')) 81 | $(error "Python 3.6 or newer required") 82 | endif 83 | 84 | SVCTOOL = $(TOOLSDIR)/svctool/svctool.py 85 | 86 | FWTOOL = $(BL)/tools/fwtool/fwtool.py 87 | ZFWTOOL = $(BL)/tools/fwtool/zfwtool.py 88 | 89 | 90 | # ------------------------------------------------ 91 | # Miscellaneous settings 92 | 93 | ifeq ($(TOOLCHAIN),gcc) 94 | CFLAGS += -MMD -MP -std=gnu11 95 | LDFLAGS += -Wl,--gc-sections -Wl,-Map,$(basename $@).map 96 | CC := $(CROSS_COMPILE)gcc 97 | AS := $(CROSS_COMPILE)as 98 | LD := $(CROSS_COMPILE)gcc 99 | HEX := $(CROSS_COMPILE)objcopy -O ihex 100 | BIN := $(CROSS_COMPILE)objcopy -O binary 101 | GDB := $(CROSS_COMPILE)gdb 102 | AR := $(CROSS_COMPILE)ar 103 | OPENOCD ?= openocd 104 | endif 105 | 106 | 107 | # ------------------------------------------------ 108 | # vim: filetype=make 109 | -------------------------------------------------------------------------------- /projects/projects.gmk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | -include Makefile.local 7 | -include ../Makefile.local 8 | 9 | TOPDIR ?= ../.. 10 | 11 | include $(TOPDIR)/projects/variants.mk 12 | include $(TOPDIR)/projects/platform.mk 13 | 14 | #TARGET ?= $(error "TARGET not specified (See platform.gmk for possible settings)") 15 | 16 | LMICDIR := $(TOPDIR)/lmic 17 | TOOLSDIR := $(TOPDIR)/tools 18 | COMMONDIR := $(TOPDIR)/common 19 | SVCSDIR := $(TOPDIR)/services 20 | BUILDDIR_PFX := build- 21 | 22 | PROJECT ?= $(notdir $(lastword $(CURDIR))) 23 | 24 | CFLAGS += $(FLAGS) 25 | LDFLAGS += $(FLAGS) 26 | ASFLAGS += $(FLAGS) 27 | 28 | DEFS += -DVARIANT="\"$(VARIANT)\"" 29 | DEFS += -DPROJECT="\"$(PROJECT)\"" 30 | DEFS += $(addprefix -DCFG_,$(REGIONS)) 31 | DEFS += $(addprefix -DCFG_,$(LMICCFG)) 32 | 33 | PDEFS += $(filter-out $(UNDEFS),$(DEFS)) 34 | CFLAGS += $(PDEFS) 35 | ASDEFS += $(PDEFS) 36 | 37 | CFLAGS += -I$(LMICDIR) -I$(HALDIR) 38 | CFLAGS += -I$(COMMONDIR) 39 | 40 | BUILDTIME := $(shell date -u +'%FT%TZ') 41 | 42 | PATCHFLAGS += $(addprefix --target=,$(HWID.$(VARIANT))) 43 | ifneq ($(RELEASE),) 44 | PATCHFLAGS += $(addprefix --version=,$(RELEASE)) 45 | else 46 | PATCHFLAGS += -v 47 | endif 48 | 49 | SRCS += $(notdir $(wildcard $(LMICDIR)/*.c $(HALDIR)/*.c *.c $(HALDIR)/*.S)) 50 | 51 | VPATH += $(LMICDIR) $(HALDIR) 52 | 53 | ifneq ($(COMMON),) 54 | SRCS += $(COMMON) 55 | VPATH += $(COMMONDIR) 56 | DEFS += $(addprefix -DCOMMON_,$(basename $(notdir $(COMMON)))) 57 | endif 58 | 59 | SVCTOOLARGS += -p $(SVCSDIR) -p . $(SVCS) 60 | SVCCHECK := $(shell $(SVCTOOL) check $(SVCTOOLARGS)) 61 | ifneq ($(SVCCHECK),) 62 | $(error $(SVCCHECK)) 63 | endif 64 | 65 | SVCDEFS_H = $(BUILDDIR)/svcdefs.h 66 | ifneq ($(SVCS),) 67 | VPATH += $(SVCSDIR) 68 | SVCSRCS := $(shell $(SVCTOOL) sources $(SVCTOOLARGS)) # only once 69 | SRCS += $(SVCSRCS) 70 | SVC_DEPS += $(SVCDEFS_H) 71 | CFLAGS += -I$(BUILDDIR) -I$(SVCSDIR) 72 | SVCSDEFS := $(shell $(SVCTOOL) defines $(SVCTOOLARGS)) # only once 73 | DEFS += $(addprefix -D,$(SVCSDEFS)) 74 | endif 75 | 76 | OBJS = $(filter-out $(addprefix $(BUILDDIR)/,$(OBJS_BLACKLIST)),$(patsubst %,$(BUILDDIR)/%.o,$(basename $(SRCS)))) 77 | 78 | ALL ?= error_all 79 | LOAD ?= error_load 80 | 81 | OOFLAGS += $(addprefix -f ,$(OOCFGS)) 82 | 83 | BUILDDIRS = $(sort $(dir $(OBJS))) 84 | 85 | MAKE_DEPS := $(MAKEFILE_LIST) # before we include all the *.d files... 86 | 87 | default: variant 88 | 89 | variant: $(VTARGET) 90 | 91 | variants: $(VTARGETS) 92 | 93 | variant-%: 94 | $(MAKE) -j 6 variant VARIANT=$* 95 | 96 | all: $(ALL) 97 | 98 | error_all: 99 | $(error No output targets collected for this build) 100 | 101 | error_load: 102 | $(error No load target collected for this build) 103 | 104 | $(OBJS): $(MAKE_DEPS) 105 | 106 | $(OBJS): | $(BUILDDIRS) $(SVC_DEPS) 107 | 108 | $(BUILDDIR)/%.o: %.c 109 | $(CC) -c $(CFLAGS) $< -o $@ 110 | 111 | $(BUILDDIR)/%.o: %.s 112 | $(AS) $(ASFLAGS) $< -o $@ 113 | 114 | $(BUILDDIR)/%.o: %.S 115 | $(CC) -c $(ASFLAGS) $(ASDEFS) $< -o $@ 116 | 117 | $(BUILDDIR)/%.out: $(OBJS) 118 | $(LD) $(LDFLAGS) $^ $(LDLIBS) -o $@ 119 | 120 | $(BUILDDIR)/%.a: $(OBJS) 121 | $(AR) rcs $@ $^ -o $@ 122 | 123 | $(BUILDDIR)/%.unpatched.hex: $(BUILDDIR)/%.out 124 | $(HEX) $< $@ 125 | 126 | $(BUILDDIR)/%.zfw: $(BUILDDIR)/%.unpatched.hex 127 | $(ZFWTOOL) create --patch \ 128 | --meta project "$(PROJECT)" \ 129 | --meta variant "$(VARIANT)" \ 130 | --meta build_time "$(BUILDTIME)" \ 131 | $< $@ 132 | $(ZFWTOOL) info $@ 133 | 134 | $(BUILDDIR)/%.hex: $(BUILDDIR)/%.zfw 135 | $(ZFWTOOL) export $< $@ 136 | 137 | $(BUILDDIR)/%.bin: $(BUILDDIR)/%.zfw 138 | $(ZFWTOOL) export $< $@ 139 | 140 | $(SVCDEFS_H): $(MAKE_DEPS) | $(BUILDDIRS) 141 | $(SVCTOOL) svcdefs -o $@ -d $(SVCTOOLARGS) 142 | 143 | clean: 144 | rm -rf build/ $(BUILDDIR_PFX)* 145 | 146 | load: $(LOAD) 147 | 148 | loadhex: $(BUILDDIR)/$(PROJECT).hex 149 | $(OPENOCD) $(OOFLAGS) -c "flash_ihex $<" 150 | 151 | loadbl: 152 | $(OPENOCD) $(OOFLAGS) -c "flash_ihex $(BL_BUILD)/bootloader.hex" 153 | 154 | debug: $(BUILDDIR)/$(PROJECT).out 155 | $(GDB) $< -ex 'target remote | $(OPENOCD) $(OOFLAGS) -c "gdb_port pipe;"' -ex "monitor reset halt" 156 | 157 | $(BUILDDIRS): 158 | mkdir -p $@ 159 | 160 | .PHONY: default all clean load loadhex loadbin loadfw loadup loadbl loadosbl debug variant variants 161 | 162 | .SECONDARY: 163 | 164 | .DELETE_ON_ERROR: 165 | 166 | -include $(OBJS:.o=.d) $(SVCDEFS_H:.h=.d) 167 | 168 | # vim: filetype=make 169 | -------------------------------------------------------------------------------- /projects/variants.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | VARIANTS ?= eu868 7 | 8 | RVARIANT := $(VARIANT) 9 | 10 | ifeq ($(VARIANT),) 11 | VTARGET := variants 12 | VARIANT := $(firstword $(DEFAULT_VARIANT) $(VARIANTS)) 13 | else 14 | VTARGET := all 15 | endif 16 | 17 | BUILDDIR = $(BUILDDIR_PFX)$(VARIANT) 18 | VTARGETS := $(addprefix variant-,$(VARIANTS)) 19 | 20 | # settings for "well-known" variants 21 | 22 | REGIONS.eu868 := eu868 23 | REGIONS.us915 := us915 24 | REGIONS.in865 := in865 25 | 26 | REGIONS.hybrid ?= eu868 us915 27 | 28 | ifneq (,$(REGIONS.$(VARIANT))) 29 | REGIONS = $(REGIONS.$(VARIANT)) 30 | endif 31 | 32 | ifneq (,$(TARGET.$(VARIANT))) 33 | TARGET = $(TARGET.$(VARIANT)) 34 | endif 35 | -------------------------------------------------------------------------------- /services/appstart.svc: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | 7 | src: 8 | - appstart/main.c 9 | 10 | hooks: 11 | - void appstart (osjob_t*) 12 | 13 | 14 | # vim: syntax=yaml 15 | -------------------------------------------------------------------------------- /services/appstart/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "lmic.h" 7 | 8 | #include "svcdefs.h" 9 | 10 | #ifdef SVC_backtrace 11 | #include "backtrace/backtrace.h" 12 | #endif 13 | 14 | static void initfunc (osjob_t* job) { 15 | #if defined(CFG_DEBUG) && CFG_DEBUG != 0 16 | unsigned char eui[8]; 17 | hal_fwi fwi; 18 | 19 | os_getDevEui(eui); 20 | hal_fwinfo(&fwi); 21 | 22 | debug_printf("id: %E | sn: %.16s | hw: 0x%03x | flash: %dK\r\n", 23 | eui, hal_serial(), hal_hwid(), fwi.flashsz >> 10); 24 | debug_printf("bl: v%d | fw: %s %s 0x%08x 0x%08x | boot: %s\r\n", 25 | fwi.blversion, 26 | PROJECT, VARIANT, fwi.version, fwi.crc, 27 | #if 0 28 | (BOOT_DEVINFO->bootmode == TABS_BOOT_UFT) ? "uft" : 29 | (BOOT_DEVINFO->bootmode == TABS_BOOT_SHIPPING) ? "ship" : 30 | (BOOT_DEVINFO->bootmode == TABS_BOOT_FLIGHT) ? "flight" : 31 | #endif 32 | "normal"); 33 | #endif 34 | 35 | #ifdef SVC_backtrace 36 | bt_print(); 37 | #endif 38 | 39 | // Application start hook 40 | SVCHOOK_appstart(job); 41 | } 42 | 43 | int main (void* bootarg) { 44 | osjob_t initjob; 45 | 46 | os_init(bootarg); 47 | os_setCallback(&initjob, initfunc); 48 | os_runloop(); 49 | 50 | // (not reached) 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /services/eefs.svc: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | 7 | src: 8 | - eefs/eefs.c 9 | - eefs/picofs.c 10 | 11 | hooks: 12 | - void eefs_init (void) 13 | - void eefs_gc (const char* fn, int* pkeep) 14 | - const char* eefs_fn (const uint8_t* ufid) 15 | 16 | 17 | # vim: syntax=yaml 18 | -------------------------------------------------------------------------------- /services/eefs/eefs.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "lmic.h" 7 | #include "peripherals.h" 8 | 9 | #include "picofs.h" 10 | #include "eefs.h" 11 | 12 | #include "svcdefs.h" 13 | 14 | static struct { 15 | bool initialized; 16 | pfs fs; 17 | 18 | } state; 19 | 20 | #if defined(CFG_DEBUG) && CFG_DEBUG != 0 21 | static const char* fn (const uint8_t* ufid) { 22 | const char* name = SVCHOOK_eefs_fn(ufid); 23 | return name ?: "unknown"; 24 | } 25 | 26 | static void cb_debug_ls (int fh, const uint8_t* ufid, void* ctx) { 27 | unsigned int q1 = os_rlsbf4(ufid); 28 | unsigned int q2 = os_rlsbf4(ufid + 4); 29 | unsigned int i = os_rlsbf4(ufid + 8); 30 | debug_printf("eefs: %02x %4dB %08x%08x-%08x (%s)\r\n", 31 | fh, pfs_read_fh(&state.fs, fh, NULL, 0), q2, q1, i, fn(ufid)); 32 | } 33 | #endif 34 | 35 | void eefs_init (void* begin, unsigned int size) { 36 | pfs_init(&state.fs, begin, size / PFS_BLOCKSZ); 37 | state.initialized = true; 38 | #if defined(CFG_DEBUG) && CFG_DEBUG != 0 39 | pfs_ls(&state.fs, cb_debug_ls, NULL); 40 | #endif 41 | SVCHOOK_eefs_init(); 42 | } 43 | 44 | int eefs_read (const uint8_t* ufid, void* data, int sz) { 45 | ASSERT(state.initialized); 46 | return pfs_read(&state.fs, ufid, data, sz); 47 | } 48 | 49 | int eefs_save (const uint8_t* ufid, void* data, int sz) { 50 | ASSERT(state.initialized); 51 | int fh = pfs_save(&state.fs, ufid, data, sz); 52 | if( fh < 0 ) { 53 | // TODO: garbage collect 54 | fh = pfs_save(&state.fs, ufid, data, sz); 55 | } 56 | return fh; 57 | } 58 | 59 | bool eefs_rm (const uint8_t* ufid) { 60 | ASSERT(state.initialized); 61 | int fh = pfs_find(&state.fs, ufid); 62 | if( fh < 0 ) { 63 | return false; 64 | } else { 65 | pfs_rm_fh(&state.fs, fh); 66 | return true; 67 | } 68 | } 69 | 70 | // FNV32 hash (1a) 71 | void pfs_crc32 (uint32_t* fnv, unsigned char* buf, uint32_t len) { 72 | if( buf ) { 73 | uint32_t h = *fnv; 74 | while (len-- > 0) { 75 | h ^= *buf++; 76 | h += ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)); 77 | } 78 | *fnv = h; 79 | } else { 80 | *fnv = 0x811c9dc5; // initialization value 81 | } 82 | } 83 | 84 | void pfs_write_block (void* dst, void* src, int nwords) { 85 | eeprom_copy(dst, src, nwords << 2); 86 | } 87 | 88 | uint8_t pfs_rnd_block (uint8_t nblks) { 89 | uint32_t rnd = (os_getRndU1() << 24) | (os_getRndU1() << 16) 90 | | (os_getRndU1() << 8) | os_getRndU1(); 91 | return rnd % nblks; 92 | } 93 | -------------------------------------------------------------------------------- /services/eefs/eefs.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _eefs_h_ 7 | #define _eefs_h_ 8 | 9 | #include 10 | #include 11 | 12 | void eefs_init (void* begin, unsigned int size); 13 | int eefs_read (const uint8_t* ufid, void* data, int sz); 14 | int eefs_save (const uint8_t* ufid, void* data, int sz); 15 | bool eefs_rm (const uint8_t* ufid); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /services/eefs/picofs.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _picofs_h_ 7 | #define _picofs_h_ 8 | 9 | #include 10 | #include 11 | 12 | union _pfs_block; 13 | typedef union _pfs_block pfs_block; 14 | 15 | enum { 16 | PFS_BLOCKSZ = 32, 17 | }; 18 | 19 | // block allocation map 20 | typedef struct { 21 | uint32_t map[8]; // 32 B - block allocation map 22 | } pfs_alloc; 23 | 24 | // file system state 25 | typedef struct { 26 | pfs_block* bb; // base block pointer 27 | int nblks; // number of blocks 28 | int next; // next alloc search start 29 | pfs_alloc alloc; // allocation bitmap 30 | } pfs; 31 | 32 | // glue functions 33 | extern void pfs_write_block (void* dst, void* src, int nwords); 34 | extern void pfs_crc32 (uint32_t* pcrc, unsigned char* buf, uint32_t len); 35 | extern uint8_t pfs_rnd_block (uint8_t nblks); 36 | 37 | // public API 38 | void pfs_init (pfs* s, void* p, int nblks); 39 | int pfs_find (pfs* s, const uint8_t* ufid); 40 | int pfs_read (pfs* s, const uint8_t* ufid, void* data, int sz); 41 | int pfs_save (pfs* s, const uint8_t* ufid, void* data, int sz); 42 | void pfs_ls (pfs* s, void (*cb) (int fh, const uint8_t* ufid, void* ctx), void* ctx); 43 | 44 | void pfs_rm_fh (pfs* s, int fh); 45 | int pfs_read_fh (pfs* s, int fh, void* data, int sz); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /services/eefs/ufid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 4 | # 5 | # This file is subject to the terms and conditions defined in file 'LICENSE', 6 | # which is part of this source code package. 7 | 8 | import random 9 | import time 10 | import struct 11 | import sys 12 | 13 | # create unique file identifiers (UFIDs) 14 | 15 | class UFID: 16 | def __init__(self, q:int, i:int) -> None: 17 | self.q = q 18 | self.i = i 19 | 20 | def version(self) -> int: 21 | return self.q & 0xf 22 | 23 | def __str__(self) -> str: 24 | return '%016x-%08x' % (self.q, self.i) 25 | 26 | def to_bytes(self) -> bytes: 27 | return struct.pack(' 'UFID': 31 | q, i = struct.unpack(' 'UFID': 36 | # Version 0: 37 | # - Q: time (ms, 44b), random_1 (16b), version (4b) 38 | # - I: random_2 (32b) 39 | 40 | t = int(time.time() * 1000) 41 | r1 = random.getrandbits(16) 42 | v = 0 43 | r2 = random.getrandbits(32) 44 | 45 | q = (t << 20) | (r1 << 4) | v 46 | i = r2 47 | 48 | return UFID(q, i) 49 | 50 | 51 | if __name__ == '__main__': 52 | for name in sys.argv[1:]: 53 | u = UFID.generate() 54 | print('// %s\nstatic const uint8_t UFID_%s[12] = { %s };' % (u, name, 55 | ', '.join([('0x%02x' % x) for x in u.to_bytes()]))) 56 | -------------------------------------------------------------------------------- /services/frag.svc: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | 7 | src: 8 | - fuota/frag.c 9 | 10 | require: 11 | - fuota 12 | - lwmux 13 | - eefs 14 | 15 | hook.lwm_downlink: _frag_dl 16 | hook.eefs_init: _frag_restore 17 | hook.eefs_fn: _frag_eefs_fn 18 | 19 | # vim: syntax=yaml 20 | -------------------------------------------------------------------------------- /services/fuota.svc: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | 7 | src: 8 | - fuota/fuota.c 9 | 10 | # vim: syntax=yaml 11 | -------------------------------------------------------------------------------- /services/fuota/.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | *.d 3 | *.o 4 | -------------------------------------------------------------------------------- /services/fuota/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -Wall -g 2 | CFLAGS += -std=gnu11 3 | CFLAGS += -MMD -MP -std=gnu11 4 | 5 | CFLAGS += -DFUOTA_HAL_IMPL='"fuota_hal_x86_64.h"' 6 | CFLAGS += -DFUOTA_GENERATOR 7 | 8 | OBJS := test.o fuota.o 9 | 10 | test: $(OBJS) 11 | 12 | clean: 13 | rm -f *.o *.d test 14 | 15 | .PHONY: clean 16 | 17 | -include $(OBJS:.o=.d) 18 | -------------------------------------------------------------------------------- /services/fuota/frag.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _frag_h_ 7 | #define _frag_h_ 8 | 9 | void _frag_init (int nsessions, void** sbeg, void** send); 10 | 11 | int frag_get (int idx, void** pdata); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /services/fuota/frag.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (C) 2016-2020 Semtech (International) AG. All rights reserved. 4 | # 5 | # This file is subject to the terms and conditions defined in file 'LICENSE', 6 | # which is part of this source code package. 7 | 8 | from typing import BinaryIO,Callable,List,Optional,Union 9 | 10 | import random 11 | import struct 12 | from bitarray import bitarray 13 | 14 | def bitarrayfrombytes(buf:bytes) -> bitarray: 15 | b = bitarray(endian='little') 16 | b.frombytes(buf) 17 | return b 18 | 19 | class CBGenerator: 20 | def generate(self, cct:int, cid:int) -> bitarray: 21 | raise NotImplementedError() 22 | 23 | class TrackNetGenerator(CBGenerator): 24 | @staticmethod 25 | def tn_avalanche(x:int) -> int: 26 | x = x & 0xffffffff 27 | x = (((x >> 16) ^ x) * 0x45d9f3b) & 0xffffffff 28 | x = (((x >> 16) ^ x) * 0x45d9f3b) & 0xffffffff 29 | x = (x >> 16) ^ x 30 | return x 31 | 32 | def generate(self, cct:int, cid:int) -> bitarray: 33 | nw = (cct + 31) // 32 34 | b = bitarrayfrombytes(struct.pack('<%dI' % nw, *(self.tn_avalanche((cid * nw) + i) for i in range(nw)))) 35 | return b[:cct] 36 | 37 | class FragCarousel: 38 | def __init__(self, data:bytes, csz:int, cbg:CBGenerator, pad:int=0) -> None: 39 | cct,rem = divmod(len(data), csz) 40 | if rem: 41 | raise ValueError('data length must be a multiple of chunk size') 42 | self.data = data 43 | self.csz = csz 44 | self.cct = cct 45 | self.cbg = cbg 46 | self.pad = pad 47 | self.blocks = [bitarrayfrombytes(data[b*csz:(b+1)*csz]) for b in range(cct)] 48 | 49 | def chunk(self, cid:int) -> bytes: 50 | cb = self.cbg.generate(self.cct, cid) 51 | chunk = bitarray(self.csz * 8) 52 | chunk.setall(0) 53 | for c in range(self.cct): 54 | if cb[c]: 55 | chunk ^= self.blocks[c] 56 | return chunk.tobytes() 57 | 58 | @staticmethod 59 | def fromfile(upf:Union[bytes,str,BinaryIO], csz:int=200, 60 | cbg:CBGenerator=TrackNetGenerator(), pad:bytes=b'*') -> 'FragCarousel': 61 | if isinstance(upf, str): 62 | with open(upf, 'rb') as f: 63 | upd = bytes(f.read()) 64 | elif isinstance(upf, bytes): 65 | upd = upf 66 | else: 67 | upd = upf.read() 68 | 69 | _,rem = divmod(len(upd), csz) 70 | padlen = (csz - rem) if rem else 0 71 | upd += padlen * pad 72 | return FragCarousel(upd, csz, cbg, padlen) 73 | 74 | class DefragSession: 75 | def __init__(self, cct:int, csz:int, cbg:CBGenerator) -> None: 76 | self.cct = cct 77 | self.csz = csz 78 | self.cbg = cbg 79 | self.rows:List[Optional[bitarray]] = [None for _ in range(cct)] 80 | self.dct = 0 81 | self.unp:Optional[bytes] = None 82 | 83 | def process(self, cid:int, data:bytes) -> bool: 84 | if self.csz != len(data): 85 | raise ValueError() 86 | bits = self.cbg.generate(self.cct, cid) + bitarrayfrombytes(data) 87 | for c in reversed(range(self.cct)): 88 | if bits[c]: 89 | if self.rows[c]: 90 | bits ^= self.rows[c] 91 | else: 92 | self.rows[c] = bits 93 | self.dct += 1 94 | return True 95 | return False 96 | 97 | def complete(self) -> bool: 98 | return self.dct == self.cct 99 | 100 | def unpack(self) -> bytes: 101 | if not self.complete(): 102 | raise RuntimeError() 103 | if not self.unp: 104 | data = b'' 105 | for c in range(self.cct): 106 | for i in range(c): 107 | if self.rows[c][i]: 108 | self.rows[c] ^= self.rows[i] 109 | data += self.rows[c][self.cct:].tobytes() 110 | self.unp = data 111 | return self.unp 112 | 113 | if __name__ == '__main__': 114 | csz = 8 115 | cct = 1024-8 116 | 117 | data = bytes(random.randrange(0,256) for _ in range(cct*csz)) 118 | cbg = TrackNetGenerator() 119 | fc = FragCarousel(data, csz, cbg) 120 | ds = DefragSession(fc.cct, fc.csz, cbg) 121 | 122 | print('cct=%d' % fc.cct) 123 | 124 | cid = random.randint(0, 100000) 125 | while not ds.complete(): 126 | s = ds.process(cid, fc.chunk(cid)) 127 | print('.' if s else 'X', end='') 128 | cid += 1 129 | print() 130 | 131 | assert data == ds.unpack() 132 | -------------------------------------------------------------------------------- /services/fuota/fuota.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _fuota_h_ 7 | #define _fuota_h_ 8 | 9 | #include 10 | #include 11 | 12 | enum { 13 | FUOTA_MORE = 0, 14 | FUOTA_COMPLETE = 1, 15 | FUOTA_UNPACKED = 2, 16 | FUOTA_ERROR = -1, 17 | }; 18 | 19 | struct _fuota_session; 20 | typedef struct _fuota_session fuota_session; 21 | 22 | // return the required matrix size 23 | // - chunk_ct: chunk count 24 | // - chunk_nw: number of 4-byte words per chunk 25 | size_t fuota_matrix_size (uint32_t chunk_ct, uint32_t chunk_nw); 26 | 27 | // initialize a session 28 | // - session: pointer to a single flash page that will hold the session state 29 | // - matrix: page-aligned pointer to flash area large enough to 30 | // hold matrix; use fuota_matrix_size() to determine min. size 31 | // - data: page-aligned pointer to flash area large enough to 32 | // hold chunk data; at least chunk_ct*chunk_nw*4 33 | // - sid: application specific session identifier 34 | // - chunk_ct: chunk count 35 | // - chunk_nw: number of 4-byte words per chunk 36 | // NOTE: All flash areas passed to this function (session, matrix, data) must 37 | // be in erased, unwritten condition. 38 | void fuota_init (void* session, void* matrix, void* data, uint32_t sid, 39 | uint32_t chunk_ct, uint32_t chunk_nw); 40 | 41 | // process a chunk 42 | // - session: pointer to session 43 | // - chunk_id: chunk identifier 44 | // - chunk_buf: buffer to chunk data (does not need to be aligned) 45 | int fuota_process (fuota_session* session, uint32_t chunk_id, 46 | unsigned char* chunk_buf); 47 | 48 | // get current session state 49 | int fuota_state (fuota_session* session, uint32_t* sid, 50 | uint32_t* chunk_ct, uint32_t* chunk_nw, uint32_t* complete_ct); 51 | 52 | // convenience function to check if a session has the expected parameters 53 | int fuota_check_state (fuota_session* session, uint32_t sid, 54 | uint32_t chunk_ct, uint32_t chunk_nw); 55 | 56 | // unpack the received chunks and recover the original file 57 | // - session: pointer to session 58 | void* fuota_unpack (fuota_session* session); 59 | 60 | #ifdef FUOTA_GENERATOR 61 | void fuota_gen_chunk (uint32_t* dst, uint32_t* src, uint32_t chunk_id, 62 | uint32_t chunk_ct, uint32_t chunk_nw); 63 | #endif 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /services/fuota/fuota_hal.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _fuota_hal_h_ 7 | #define _fuota_hal_h_ 8 | 9 | // The functions below must be provided by the HAL. They can be implemented as 10 | // macros. 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef FUOTA_HAL_IMPL 17 | #include FUOTA_HAL_IMPL 18 | #else 19 | #include "lmic.h" 20 | #endif 21 | 22 | 23 | // ------------------------------------------------ 24 | // Flash access 25 | 26 | // Get flash default state 27 | #ifndef fuota_flash_bitdefault 28 | #error "Macro 'fuota_flash_bitdefault' must be defined by HAL" 29 | #else 30 | #if ((fuota_flash_bitdefault & ~1) != 0) 31 | #error "Macro 'fuota_flash_bitdefault' must be set to 1 or 0" 32 | #endif 33 | #endif 34 | 35 | // Get flash page size in bytes 36 | #ifndef fuota_flash_pagesz 37 | #error "Macro 'fuota_flash_pagesz' must be defined by HAL" 38 | #endif 39 | 40 | // Write words from RAM to flash memory (aligned-to-aligned) 41 | // If erase is true, erase the page when reaching a page boundary before writing 42 | #ifndef fuota_flash_write 43 | void fuota_flash_write (void* dst, void* src, uint32_t nwords, bool erase); 44 | #endif 45 | 46 | // Read words from flash memory to RAM (aligned-to-aligned) 47 | #ifndef fuota_flash_read 48 | void fuota_flash_read (void* dst, void* src, uint32_t nwords); 49 | #endif 50 | 51 | // Read a uint32_t from flash memory 52 | #ifndef fuota_flash_rd_u4 53 | uint32_t fuota_flash_rd_u4 (void* addr); 54 | #endif 55 | 56 | // Read a void* from flash memory 57 | #ifndef fuota_flash_rd_ptr 58 | void* fuota_flash_rd_ptr (void* addr); 59 | #endif 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /services/fuota/fuota_hal_x86_64.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _fuota_hal_x86_64_h_ 7 | #define _fuota_hal_x86_64_h_ 8 | 9 | #define fuota_flash_pagesz 128 10 | #define fuota_flash_bitdefault 0 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /services/fuota/fwman.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | // This service is an implementation of the LoRaWAN™ Firmware Management 7 | // Protocol Specification Release Candidate 2 8 | 9 | #include 10 | #include 11 | 12 | #include "bootloader.h" 13 | 14 | #include "lwmux/lwmux.h" 15 | #include "fuota/micro-ecc/uECC.h" 16 | 17 | #include "frag.h" 18 | 19 | #ifndef SVC_FWMAN_PORT 20 | #define SVC_FWMAN_PORT 203 21 | #endif 22 | 23 | #ifndef SVC_FWMAN_UPDATE_FRAG_IDX 24 | #define SVC_FWMAN_UPDATE_FRAG_IDX 0 25 | #endif 26 | 27 | #ifdef SVC_FWMAN_PUBKEY 28 | extern const unsigned char* SVC_FWMAN_PUBKEY (void); 29 | #else 30 | #error "SVC_FWMAN_PUBKEY function (trusted FUOTA public key) must be defined" 31 | #endif 32 | 33 | #ifdef SVC_FWMAN_CURVE 34 | extern uECC_Curve SVC_FWMAN_CURVE (void); 35 | #else 36 | #error "SVC_FWMAN_CURVE function (ECC curve used trusted FUOTA public key) must be defined" 37 | #endif 38 | 39 | enum { 40 | PKG_ID = 4, 41 | PKG_VERSION = 1, 42 | }; 43 | 44 | enum { 45 | PKG_VERSION_REQ = 0x00, 46 | PKG_VERSION_ANS = 0x00, 47 | DEV_VERSION_REQ = 0x01, 48 | DEV_VERSION_ANS = 0x01, 49 | DEV_REBOOT_TIME_REQ = 0x02, 50 | DEV_REBOOT_TIME_ANS = 0x02, 51 | DEV_REBOOT_CTDN_REQ = 0x03, 52 | DEV_REBOOT_CTDN_ANS = 0x03, 53 | DEV_UPGRADE_IMG_REQ = 0x04, 54 | DEV_UPGRADE_IMG_ANS = 0x04, 55 | DEV_DELETE_IMG_REQ = 0x05, 56 | DEV_DELETE_IMG_ANS = 0x05, 57 | }; 58 | 59 | static const char ANS_LENS[] = { 60 | [PKG_VERSION_ANS] = 3, 61 | [DEV_VERSION_ANS] = 9, 62 | [DEV_REBOOT_TIME_ANS] = 5, 63 | [DEV_REBOOT_CTDN_ANS] = 4, 64 | [DEV_UPGRADE_IMG_ANS] = 2, // +4 (conditional) 65 | [DEV_DELETE_IMG_ANS] = 2, 66 | }; 67 | 68 | enum { 69 | DUI_STAT_NONE = 0, // no image present 70 | DUI_STAT_INVALID = 1, // image is corrupt 71 | DUI_STAT_MISMATCH = 2, // image is not compatible 72 | DUI_STAT_VALID = 3, // image is valid and can be installed 73 | }; 74 | 75 | static struct { 76 | unsigned char resp[64]; // response buffer 77 | int rlen; // response length 78 | 79 | lwm_job lwmjob; // uplink job 80 | osjob_t rebootjob; // reboot job 81 | } state; 82 | 83 | // ensure that there is at least n bytes available in the 84 | // response buffer -- if not, drop older responses. 85 | static void resp_makeroom (int n) { 86 | int skip = 0; 87 | int rlen = state.rlen; 88 | while( sizeof(state.resp) - rlen < n ) { 89 | int cmd = state.resp[skip]; 90 | ASSERT(cmd < sizeof(ANS_LENS)); 91 | int csz = ANS_LENS[cmd]; 92 | if( cmd == DEV_UPGRADE_IMG_ANS && state.resp[skip+1] == DUI_STAT_VALID ) { 93 | csz += 4; 94 | } 95 | skip += csz; 96 | rlen -= csz; 97 | } 98 | if( skip ) { 99 | memmove(state.resp, state.resp + skip, rlen); 100 | state.rlen = rlen; 101 | } 102 | } 103 | 104 | static bool txfunc (lwm_txinfo* txi) { 105 | txi->port = SVC_FWMAN_PORT; 106 | txi->data = state.resp; 107 | txi->dlen = state.rlen; 108 | state.rlen = 0; 109 | return true; 110 | } 111 | 112 | static int pkg_version_req (void) { 113 | resp_makeroom(ANS_LENS[PKG_VERSION_ANS]); 114 | state.resp[state.rlen++] = PKG_VERSION_ANS; 115 | state.resp[state.rlen++] = PKG_ID; 116 | state.resp[state.rlen++] = PKG_VERSION; 117 | return 1; 118 | } 119 | 120 | static int dev_version_req (void) { 121 | hal_fwi fwi; 122 | hal_fwinfo(&fwi); 123 | 124 | resp_makeroom(ANS_LENS[DEV_VERSION_ANS]); 125 | state.resp[state.rlen++] = DEV_VERSION_ANS; 126 | os_wlsbf4(state.resp + state.rlen, fwi.crc); 127 | state.rlen += 4; 128 | os_wlsbf4(state.resp + state.rlen, hal_hwid()); 129 | state.rlen += 4; 130 | 131 | return 1; 132 | } 133 | 134 | static bool check_sig (void* ptr, int len) { 135 | const unsigned char* pubkey = SVC_FWMAN_PUBKEY(); 136 | uECC_Curve curve = SVC_FWMAN_CURVE(); 137 | int sigsize = uECC_curve_private_key_size(curve) << 1; 138 | 139 | boot_uphdr* up = ptr; 140 | unsigned char* sig = ptr; 141 | uint32_t hash[8]; 142 | sha256(hash, ptr, up->size); 143 | 144 | sig += up->size; 145 | len -= up->size; 146 | while( len >= sigsize ) { 147 | if( uECC_verify(pubkey, (unsigned char*) hash, 32, sig, curve) == 1 ) { 148 | debug_str("fwman: signature verified\r\n"); 149 | return true; 150 | } else { 151 | debug_str("fwman: signature invalid\r\n"); 152 | } 153 | len -= sigsize; 154 | sig += sigsize; 155 | } 156 | return false; 157 | } 158 | 159 | static int check_img (uint32_t* pcrc, void** pdata) { 160 | void* ptr; 161 | int len; 162 | if( (len = frag_get(SVC_FWMAN_UPDATE_FRAG_IDX, &ptr)) < 0 ) { 163 | return DUI_STAT_NONE; 164 | } else { 165 | // check format, length, crc, and signatures 166 | boot_uphdr* up = ptr; 167 | if( len < sizeof(boot_uphdr) 168 | || (up->size & 3) != 0 169 | || len < up->size 170 | || crc32((unsigned char*) ptr + 8, (up->size - 8) >> 2) != up->crc 171 | || !check_sig(ptr, len) ) { 172 | return DUI_STAT_INVALID; 173 | } else { 174 | if( pcrc ) { 175 | *pcrc = up->fwcrc; 176 | } 177 | if( pdata ) { 178 | *pdata = ptr; 179 | } 180 | return DUI_STAT_VALID; 181 | } 182 | } 183 | } 184 | 185 | static void reboot (osjob_t* j) { 186 | void* ptr; 187 | if( check_img(NULL, &ptr) == DUI_STAT_VALID ) { 188 | if( hal_set_update(ptr) ) { 189 | debug_str("fwman: update registered\r\n"); 190 | } else { 191 | debug_str("fwman: update registration failed\r\n"); 192 | } 193 | } 194 | debug_str("fwman: rebooting...\r\n"); 195 | hal_reboot(); 196 | } 197 | 198 | static int dev_reboot_time_req (unsigned char* data, int dlen) { 199 | if( dlen < 5 ) { 200 | return -1; 201 | } 202 | uint32_t t = os_rlsbf4(data + 1); 203 | 204 | if( t == 0 ) { 205 | // reboot now (don't send answer) 206 | os_setCallback(&state.rebootjob, reboot); 207 | } else { 208 | if ( t == 0xffffffff ) { 209 | // cancel any pending reboot 210 | os_clearCallback(&state.rebootjob); 211 | } else { 212 | // exact time reboot not supported for now 213 | t = 0; 214 | } 215 | resp_makeroom(ANS_LENS[DEV_REBOOT_TIME_ANS]); 216 | state.resp[state.rlen++] = DEV_REBOOT_TIME_ANS; 217 | os_wlsbf4(state.resp + state.rlen, t); 218 | state.rlen += 4; 219 | } 220 | 221 | return 5; 222 | } 223 | 224 | static int dev_upgrade_img_req (void) { 225 | uint32_t crc; 226 | 227 | int status = check_img(&crc, NULL); 228 | 229 | resp_makeroom(ANS_LENS[DEV_UPGRADE_IMG_ANS] + ((status == DUI_STAT_VALID) ? 4 : 0)); 230 | state.resp[state.rlen++] = DEV_UPGRADE_IMG_ANS; 231 | state.resp[state.rlen++] = status; 232 | if( status == DUI_STAT_VALID ) { 233 | os_wlsbf4(state.resp + state.rlen, crc); 234 | state.rlen += 4; 235 | } 236 | return 1; 237 | } 238 | 239 | void fwman_dl (int port, unsigned char* data, int dlen, unsigned int flags) { 240 | // TODO - only accept if unicast 241 | if( port == SVC_FWMAN_PORT ) { 242 | while( dlen ) { 243 | int n; 244 | switch( *data ) { 245 | case PKG_VERSION_REQ: 246 | n = pkg_version_req(); 247 | break; 248 | 249 | case DEV_VERSION_REQ: 250 | n = dev_version_req(); 251 | break; 252 | 253 | case DEV_REBOOT_TIME_REQ: 254 | n = dev_reboot_time_req(data, dlen); 255 | break; 256 | 257 | case DEV_UPGRADE_IMG_REQ: 258 | n = dev_upgrade_img_req(); 259 | break; 260 | 261 | default: 262 | // unknown command -- abort processing 263 | goto done; 264 | } 265 | data += n; 266 | dlen -= n; 267 | } 268 | done: 269 | if( state.rlen ) { 270 | lwm_request_send(&state.lwmjob, 0, txfunc); 271 | } 272 | } 273 | } 274 | 275 | #ifdef SVC_FWMAN_TEST 276 | const unsigned char* fwman_testkey (void) { 277 | static const unsigned char pubkey[64] = { 278 | 0xec, 0x70, 0x36, 0xe8, 0xf1, 0xa8, 0xd5, 0x74, 0x4c, 0x9f, 0xd9, 0xfc, 0x34, 0xdf, 0x43, 0xd8, 279 | 0xff, 0x0b, 0xf0, 0x5b, 0xc0, 0xe6, 0x8e, 0xf9, 0x31, 0x40, 0xe8, 0x01, 0x72, 0xfd, 0x06, 0x8e, 280 | 0x36, 0x86, 0x7c, 0x09, 0xa9, 0x28, 0x5e, 0xca, 0x0e, 0x88, 0x67, 0x4a, 0x28, 0x77, 0x34, 0xdc, 281 | 0x04, 0x2e, 0x24, 0x42, 0x02, 0x8a, 0xc8, 0x3a, 0xb3, 0xd1, 0x5d, 0xaf, 0x3d, 0x2f, 0x0f, 0x07, 282 | }; 283 | return pubkey; 284 | } 285 | #endif 286 | -------------------------------------------------------------------------------- /services/fuota/test.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "fuota.h" 7 | #include "fuota_hal.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #ifndef __x86_64__ 17 | #error "Simulation requires 64-bit platform" 18 | #endif 19 | 20 | 21 | // ------------------------------------------------ 22 | // Flash simulation 23 | 24 | #define FLASH_SZ (128 * 1024) // 128K 25 | #define FLASH_PAGE_SZ fuota_flash_pagesz 26 | 27 | #define FLASH_WORD_CT (FLASH_SZ >> 2) 28 | #define FLASH_PAGE_CT (FLASH_SZ / FLASH_PAGE_SZ) 29 | #define FLASH_END (FLASH.W + (FLASH_SZ / 4)) 30 | 31 | static union { 32 | uint32_t W[FLASH_SZ / 4]; 33 | uint32_t P[FLASH_PAGE_CT][FLASH_PAGE_SZ / 4]; 34 | } FLASH; 35 | 36 | // Fake flash addresses are non-canonical, i.e. they are not valid 37 | // in amd64 virtual address space. 38 | static uint32_t addr2word (void* ptr) { 39 | uintptr_t addr = (uintptr_t) ptr; 40 | assert((addr >> 32) == 0xdeadbeef); 41 | assert((addr & 3) == 0); 42 | return (addr & 0xffffffff) >> 2; 43 | } 44 | 45 | static void* word2addr (uint32_t word) { 46 | assert(word <= FLASH_WORD_CT); 47 | return (void*) ((0xdeadbeefULL << 32) | (word << 2)); 48 | } 49 | 50 | void fuota_flash_write (void* _dst, void* _src, uint32_t nwords, bool erase) { 51 | assert((((uintptr_t) _src) & 3) == 0); 52 | uint32_t* src = _src; 53 | uint32_t w = addr2word(_dst); 54 | assert((w + nwords) <= FLASH_WORD_CT); 55 | uint32_t* dst = FLASH.W + w; 56 | int i; 57 | for (i = 0; i < nwords; i++) { 58 | if (((w++ << 2) & (FLASH_PAGE_SZ-1)) == 0 && erase) { 59 | memset(dst + i, (fuota_flash_bitdefault) ? 0xff : 0x00, FLASH_PAGE_SZ); 60 | } 61 | assert(dst[i] == ((fuota_flash_bitdefault) ? ~0 : 0)); 62 | dst[i] = src[i]; 63 | } 64 | } 65 | 66 | void fuota_flash_read (void* dst, void* src, uint32_t nwords) { 67 | uint32_t w = addr2word(src); 68 | assert((w + nwords) <= FLASH_WORD_CT); 69 | memcpy(dst, FLASH.W + w, nwords << 2); 70 | } 71 | 72 | uint32_t fuota_flash_rd_u4 (void* addr) { 73 | uint32_t w = addr2word(addr); 74 | assert(w < FLASH_WORD_CT); 75 | return(FLASH.W[w]); 76 | } 77 | 78 | void* fuota_flash_rd_ptr (void* addr) { 79 | uint32_t w = addr2word(addr); 80 | assert(w < FLASH_WORD_CT); 81 | return *((void**) (FLASH.W + w)); 82 | } 83 | 84 | 85 | // ------------------------------------------------ 86 | // Testing 87 | 88 | static uint32_t readfile (unsigned char** pbuf, const char* fn, uint32_t chunk_nw) { 89 | FILE* f = fopen(fn, "rb"); 90 | if (f == NULL) { 91 | perror("Error opening file"); 92 | exit(1); 93 | } 94 | fseek(f, 0, SEEK_END); 95 | long fsize = ftell(f); 96 | fseek(f, 0, SEEK_SET); 97 | uint32_t chunk_sz = chunk_nw << 2; 98 | uint32_t msize = ((fsize + (chunk_sz - 1)) / (chunk_sz)) * chunk_sz; 99 | *pbuf = malloc(msize); 100 | assert(*pbuf); 101 | size_t br = fread(*pbuf, 1, fsize, f); 102 | assert(br == fsize); 103 | fclose(f); 104 | if (msize > fsize) { 105 | printf("Note: padding input file with %d bytes to full chunk size\n", 106 | (int) (msize - fsize)); 107 | memset(*pbuf + fsize, 0, msize - fsize); 108 | } 109 | return msize >> 2; 110 | } 111 | 112 | int main (int argc, char** argv) { 113 | if (argc != 2) { 114 | printf("usage: %s \n", argv[0]); 115 | return 1; 116 | } 117 | 118 | memset(&FLASH, 0xa5, sizeof(FLASH)); 119 | 120 | uint32_t chunk_nw = 60; // 60 words = 240 bytes 121 | 122 | unsigned char* inbuf; 123 | uint32_t data_nw = readfile(&inbuf, argv[1], chunk_nw); 124 | uint32_t chunk_ct = data_nw / chunk_nw; 125 | 126 | printf("chunk size: %6d words (%d bytes)\n", chunk_nw, chunk_nw << 2); 127 | printf("chunk count: %6d\n", chunk_ct); 128 | printf("file size: %6d bytes\n", (chunk_ct * chunk_nw) << 2); 129 | uint32_t dnp = (((chunk_ct * chunk_nw) << 2) + (FLASH_PAGE_SZ-1)) / FLASH_PAGE_SZ; 130 | printf(" %6d pages (%d bytes)\n", dnp, dnp * FLASH_PAGE_SZ); 131 | 132 | size_t ms = fuota_matrix_size(chunk_ct, chunk_nw); 133 | printf("matrix size: %d bytes\n", (uint32_t) ms); 134 | uint32_t mnp = (ms + (FLASH_PAGE_SZ-1)) / FLASH_PAGE_SZ; 135 | printf(" %6d pages (%d bytes)\n", mnp, mnp * FLASH_PAGE_SZ); 136 | 137 | size_t ss = FLASH_PAGE_SZ; 138 | printf("session size: %d bytes\n", (uint32_t) ss); 139 | uint32_t snp = (ss + (FLASH_PAGE_SZ-1)) / FLASH_PAGE_SZ; 140 | printf(" %6d pages (%d bytes)\n", snp, snp * FLASH_PAGE_SZ); 141 | 142 | assert((mnp + dnp + snp) < FLASH_PAGE_CT); 143 | // Flash: |........| 144 | uint32_t mw = (FLASH_PAGE_CT - (mnp + dnp + snp)) * (FLASH_PAGE_SZ >> 2); 145 | uint32_t dw = (FLASH_PAGE_CT - (dnp + snp)) * (FLASH_PAGE_SZ >> 2); 146 | uint32_t sw = (FLASH_PAGE_CT - (snp)) * (FLASH_PAGE_SZ >> 2); 147 | 148 | // erase session pages 149 | memset(FLASH.W + mw, (fuota_flash_bitdefault) ? 0xff : 0x00, mnp * FLASH_PAGE_SZ); 150 | memset(FLASH.W + dw, (fuota_flash_bitdefault) ? 0xff : 0x00, dnp * FLASH_PAGE_SZ); 151 | memset(FLASH.W + sw, (fuota_flash_bitdefault) ? 0xff : 0x00, snp * FLASH_PAGE_SZ); 152 | 153 | void* matrix = word2addr(mw); 154 | void* data = word2addr(dw); 155 | void* session = word2addr(sw); 156 | printf("matrix addr: %p\n", matrix); 157 | printf("data addr: %p\n", data); 158 | printf("session addr: %p\n", session); 159 | 160 | fuota_init(session, matrix, data, 0x123, chunk_ct, chunk_nw); 161 | fuota_session* s = session; 162 | 163 | srand(time(NULL)); 164 | 165 | uint32_t chunk_id = rand(); 166 | uint32_t total = 0; 167 | uint32_t cc = 0; 168 | while (1) { 169 | uint32_t chunk[chunk_nw]; 170 | // skip random number of chunks (simulate packet loss) 171 | chunk_id += (rand() % 10) + 1; 172 | // generate a new chunk 173 | fuota_gen_chunk(chunk, (uint32_t*) inbuf, chunk_id, chunk_ct, chunk_nw); 174 | // process chunk 175 | int rv = fuota_process(s, chunk_id, (unsigned char*) chunk); 176 | assert(rv != FUOTA_ERROR); 177 | total += 1; 178 | // get complete count 179 | uint32_t cc2; 180 | int rv2 = fuota_state(s, NULL, NULL, NULL, &cc2); 181 | assert(rv2 == rv); 182 | printf("processed: 0x%08x: %3d/%d%s\n", chunk_id, cc2, chunk_ct, 183 | (cc2 == cc) ? " *" : ""); 184 | cc = cc2; 185 | if (rv == FUOTA_COMPLETE) { 186 | assert(cc == chunk_ct); 187 | printf("complete, %d chunks processed (%d useless)\n", total, total - chunk_ct); 188 | break; 189 | } 190 | assert(rv == FUOTA_MORE); 191 | assert(cc != chunk_ct); 192 | } 193 | 194 | void* outbuf = fuota_unpack(s); 195 | assert(outbuf); 196 | assert(outbuf == data); 197 | 198 | int diff = memcmp(inbuf, FLASH.W + addr2word(outbuf), chunk_ct * chunk_nw * 4); 199 | assert(!diff); 200 | 201 | printf("all done!\n"); 202 | 203 | return 0; 204 | } 205 | -------------------------------------------------------------------------------- /services/fuota/test.up: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorabasics/basicmac/2c2678eab23bf2d13b659611ff9cd4ad57ef2f6b/services/fuota/test.up -------------------------------------------------------------------------------- /services/fuota/testkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIFDihCr1+guDkHjv5LO5GvOUxebr3cATnJGn0l+obJfYoAoGCCqGSM49 3 | AwEHoUQDQgAE7HA26PGo1XRMn9n8NN9D2P8L8FvA5o75MUDoAXL9Bo42hnwJqShe 4 | yg6IZ0oodzTcBC4kQgKKyDqz0V2vPS8PBw== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /services/fwman.svc: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | 7 | src: 8 | - fuota/fwman.c 9 | - fuota/micro-ecc/uECC.c 10 | 11 | require: 12 | - frag 13 | - lwmux 14 | 15 | hook.lwm_downlink: fwman_dl 16 | 17 | # vim: syntax=yaml 18 | -------------------------------------------------------------------------------- /services/lwmux.svc: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | 7 | src: 8 | - lwmux/lwmux.c 9 | 10 | hooks: 11 | - void lwm_event (ev_t) 12 | - void lwm_downlink (int port, unsigned char* data, int dlen, unsigned int txrxFlags) 13 | 14 | 15 | # vim: syntax=yaml 16 | -------------------------------------------------------------------------------- /services/lwmux/lwmux.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _lwmux_h_ 7 | #define _lwmux_h_ 8 | 9 | #include "lmic.h" 10 | 11 | typedef void (*lwm_complete) (void); 12 | typedef int (*lwm_jit_cb) (unsigned char* data, int dlen); 13 | 14 | typedef struct { 15 | unsigned char* data; 16 | int dlen; 17 | int port; 18 | int confirmed; 19 | lwm_complete txcomplete; 20 | lwm_jit_cb jit_cb; 21 | } lwm_txinfo; 22 | 23 | typedef bool (*lwm_tx) (lwm_txinfo*); 24 | 25 | typedef struct _lwm_job { 26 | unsigned int prio; 27 | lwm_tx txfunc; 28 | lwm_complete completefunc; 29 | struct _lwm_job* next; 30 | } lwm_job; 31 | 32 | 33 | enum { 34 | LWM_MODE_SHUTDOWN, 35 | LWM_MODE_NORMAL, 36 | #ifdef LWM_SLOTTED 37 | LWM_MODE_SLOTTED, 38 | #endif 39 | }; 40 | 41 | #define LWM_PRIO_MIN 0 42 | #define LWM_PRIO_MAX ~0 43 | 44 | int lwm_getmode (); 45 | void lwm_setmode (int mode); 46 | unsigned int lwm_setpriority (unsigned int priority); 47 | 48 | void lwm_request_send (lwm_job* job, unsigned int priority, lwm_tx txfunc); 49 | bool lwm_clear_send (lwm_job* job); 50 | 51 | void lwm_setadrprofile (int txPowAdj, const unsigned char* drlist, int n); 52 | 53 | #ifdef LWM_SLOTTED 54 | void lwm_slotparams (u4_t freq, dr_t dr, ostime_t interval, int slotsz, int missed_max, int timeouts_max); 55 | 56 | extern unsigned int lwm_slot (void); 57 | extern void lwm_bcn_setup (void); 58 | #endif 59 | 60 | enum { 61 | LWM_FLAG_ACK = TXRX_ACK, 62 | LWM_FLAG_NAK = TXRX_NACK, 63 | LWM_FLAG_DNW1 = TXRX_DNW1, 64 | LWM_FLAG_DNW2 = TXRX_DNW2, 65 | LWM_FLAG_PING = TXRX_PING, 66 | }; 67 | #define LWM_FLAG_MASK (LWM_FLAG_ACK | LWM_FLAG_NAK | LWM_FLAG_DNW1 | LWM_FLAG_DNW2 | LWM_FLAG_PING) 68 | typedef void (*lwm_downlink) (int port, unsigned char* data, int dlen, unsigned int flags); 69 | void lwm_process_dl (lwm_downlink dlfunc); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /services/lwtest.svc: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | 7 | src: 8 | - lwtest/testmode.c 9 | 10 | require: 11 | - lwmux 12 | 13 | hook.lwm_event: testmode_handleEvent 14 | 15 | # vim: syntax=yaml 16 | 17 | 18 | -------------------------------------------------------------------------------- /services/lwtest/testmode.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "lmic.h" 7 | 8 | #include "lwmux/lwmux.h" 9 | 10 | #define TESTMODE_PORT 224 11 | 12 | #define TESTMODE_MAXCNT 192 13 | #define TESTMODE_TIMEOUT (30*60) // (sec) 14 | #define TESTMODE_INTERVAL 2 // (sec) 15 | 16 | // commands 17 | #define TESTCMD_STOP 0x00 18 | #define TESTCMD_START 0x01 19 | #define TESTCMD_CONFIRMED 0x02 20 | #define TESTCMD_UNCONFIRMED 0x03 21 | #define TESTCMD_ECHO 0x04 22 | #define TESTCMD_LINKCHECK 0x05 23 | #define TESTCMD_JOIN 0x06 24 | #define TESTCMD_CW 0x07 25 | #define TESTCMD_RFU 0x08 // 0x08-0x7F 26 | #define TESTCMD_VENDOR 0x80 // 0x80-0xFF 27 | 28 | static struct { 29 | uint8_t active; // test mode active 30 | uint8_t confirmed; // request confirmation for uplinks 31 | uint16_t retrans; // confirmed frame retransmission counter 32 | uint16_t dncnt; // downlink counter (to be echoed) 33 | uint16_t upcnt; // number of uplinks w/o downlink (max 192) 34 | uint32_t uptotal; // total number of up messages since start of test mode 35 | uint32_t lastdf; // last down fcnt 36 | ostime_t dntime; // time of last downlink (max 30 minutes) 37 | osjob_t timer; // cw timeout or uplink timer 38 | lwm_job lwmjob; // uplink job 39 | } testmode; 40 | 41 | static void starttestmode (void) { 42 | debug_printf("TEST MODE START\r\n"); 43 | testmode.active = 1; 44 | testmode.uptotal = 0; 45 | testmode.dncnt = 0; 46 | testmode.retrans = 0; 47 | testmode.lastdf = 0; 48 | // disable duty cycle 49 | LMIC_enableFastJoin(); 50 | LMIC_disableDC(); 51 | // disable ADR profiles 52 | lwm_setadrprofile(0, NULL, 0); 53 | // don't send MAC options immediately in empty frame, wait for next uplink 54 | LMIC.polltimeout = sec2osticks(30); 55 | // disable application messages 56 | lwm_setpriority(LWM_PRIO_MAX - 2); 57 | } 58 | 59 | static void stoptestmode (void) { 60 | debug_printf("TEST MODE END\r\n"); 61 | testmode.active = 0; 62 | // stop uplinks 63 | os_clearCallback(&testmode.timer); 64 | // reset duty cycle limitations (XXX) 65 | #if defined(CFG_eu868) 66 | LMIC.noDC = 0; 67 | #endif // defined(CFG_eu868) 68 | LMIC.opmode &= ~OP_TESTMODE; 69 | // send MAC options immediately 70 | LMIC.polltimeout = 0; 71 | // enable application traffic 72 | lwm_setpriority(LWM_PRIO_MIN); 73 | } 74 | 75 | static bool txfunc (lwm_txinfo* txi) { 76 | if (testmode.confirmed && (LMIC.txrxFlags & TXRX_NACK) && testmode.retrans < 8) { 77 | // no ACK received - retransmit last uplink data in LMIC.pendTxData with same seqnoUp 78 | txi->dlen = LMIC.pendTxLen; 79 | LMIC.seqnoUp -= 1; 80 | testmode.retrans += 1; 81 | } else if (LMIC.frame[LMIC.dataBeg] == TESTCMD_ECHO) { 82 | txi->data[0] = TESTCMD_ECHO; 83 | for( int i = 1; i < LMIC.dataLen; i++ ) { 84 | txi->data[i] = LMIC.frame[LMIC.dataBeg + i] + 1; 85 | } 86 | txi->dlen = LMIC.dataLen; 87 | testmode.retrans = 0; 88 | } else { 89 | txi->data[0] = testmode.dncnt >> 8; // fill in downlink_counter 90 | txi->data[1] = testmode.dncnt; // (2 bytes, big endian) 91 | txi->dlen = 2; 92 | testmode.retrans = 0; 93 | } 94 | txi->port = TESTMODE_PORT; 95 | txi->confirmed = testmode.confirmed; 96 | LMIC_setAdrMode(1); 97 | debug_printf("TESTMODE UPLINK #%d (%sconfirmed, seq=%d, len=%d): %h\r\n", 98 | testmode.uptotal++, (testmode.confirmed) ? "" : "un", LMIC.seqnoUp, txi->dlen, txi->data, txi->dlen); 99 | return true; 100 | } 101 | 102 | static void uplink (osjob_t* job) { 103 | lwm_request_send(&testmode.lwmjob, LWM_PRIO_MAX - 2, txfunc); 104 | } 105 | 106 | static void stopcw (osjob_t* job) { 107 | // stop continuous wave 108 | os_radio(RADIO_STOP); 109 | // continue frame reporting... 110 | uplink(job); 111 | } 112 | 113 | // referenced by tabs / rm_event() 114 | void testmode_handleEvent (ev_t ev) { 115 | switch (ev) { 116 | case EV_TXCOMPLETE: { 117 | // check for downlink 118 | if (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2)) { 119 | unsigned char *buf = LMIC.frame + LMIC.dataBeg; 120 | int port = -1; 121 | if (LMIC.txrxFlags & TXRX_PORT) { 122 | port = LMIC.frame[LMIC.dataBeg - 1]; 123 | } 124 | 125 | // save timestamp of last downlink 126 | testmode.dntime = os_getTime(); 127 | 128 | // reset uplink-without-downlink counter 129 | testmode.upcnt = 0; 130 | 131 | if (testmode.active) { 132 | debug_printf("TESTMODE DOWNLINK (seq=%d, port=%d, len=%d%s%s%s%s): %h\r\n", 133 | LMIC.seqnoDn, port, LMIC.dataLen, 134 | (LMIC.txrxFlags & TXRX_DNW1) ? ", RX1" : "", 135 | (LMIC.txrxFlags & TXRX_DNW2) ? ", RX2" : "", 136 | (LMIC.txrxFlags & TXRX_ACK) ? ", ACK" : "", 137 | (LMIC.txrxFlags & TXRX_NACK) ? ", NACK" : "", 138 | buf, LMIC.dataLen); 139 | 140 | // update downlink counter 141 | if( testmode.lastdf != LMIC.seqnoDn) { 142 | testmode.lastdf = LMIC.seqnoDn; 143 | testmode.dncnt += 1; 144 | } 145 | 146 | if (port == TESTMODE_PORT && LMIC.dataLen > 0) { 147 | 148 | // dispatch test commands 149 | switch (buf[0]) { 150 | 151 | case TESTCMD_STOP: // deactivate test mode 152 | stoptestmode(); 153 | break; 154 | 155 | case TESTCMD_CONFIRMED: // activate confirmations 156 | testmode.confirmed = 1; 157 | break; 158 | 159 | case TESTCMD_UNCONFIRMED: // deactivate confirmations 160 | testmode.confirmed = 0; 161 | break; 162 | 163 | case TESTCMD_LINKCHECK: // XXX undocumented?!? 164 | // XXX 165 | break; 166 | 167 | case TESTCMD_JOIN: // trigger join request 168 | stoptestmode(); // (activation command will be resent after join) 169 | lwm_setmode(LWM_MODE_SHUTDOWN); 170 | lwm_setmode(LWM_MODE_NORMAL); 171 | break; 172 | 173 | case TESTCMD_ECHO: // modify and echo frame 174 | LMIC.pendTxData[0] = buf[0]; 175 | for (int i = 1; i < LMIC.dataLen; i++) { 176 | LMIC.pendTxData[i] = buf[i] + 1; 177 | } 178 | LMIC.pendTxLen = LMIC.dataLen; 179 | break; 180 | 181 | case TESTCMD_CW: // continous wave 182 | // set timeout and parameters 183 | os_setApproxTimedCallback(&testmode.timer, os_getTime() + sec2osticks((buf[1] << 8) | buf[2]), stopcw); // duration [s] 184 | LMIC.freq = ((buf[3] << 16) | (buf[4] << 8) | buf[5]) * 100; // [Hz] 185 | LMIC.txpow = buf[6]; // dBm 186 | // start continuous wave 187 | os_radio(RADIO_TXCW); 188 | return; // no uplink now 189 | } 190 | } 191 | } else { // test mode not active 192 | if (port == TESTMODE_PORT && LMIC.dataLen == 4 && os_rlsbf4(buf) == 0x01010101) { 193 | // activate test mode 194 | starttestmode(); 195 | } 196 | } 197 | } else { // no downlink 198 | if (testmode.active && 199 | (++testmode.upcnt > TESTMODE_MAXCNT || 200 | (os_getTime() - testmode.dntime) > sec2osticks(TESTMODE_TIMEOUT))) { 201 | // test mode timed out 202 | debug_printf("TEST MODE TIMEOUT\r\n"); 203 | stoptestmode(); 204 | } 205 | } 206 | 207 | if (testmode.active) { 208 | // schedule next uplink 209 | os_setApproxTimedCallback(&testmode.timer, os_getTime() + sec2osticks(TESTMODE_INTERVAL), uplink); 210 | } 211 | } 212 | 213 | default: // ignore other events 214 | break; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /services/pwrman.svc: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | 7 | src: 8 | - pwrman/pwrman.c 9 | 10 | require: 11 | - eefs 12 | 13 | hook.eefs_init: _pwrman_init 14 | hook.eefs_fn: _pwrman_eefs_fn 15 | 16 | # vim: syntax=yaml 17 | -------------------------------------------------------------------------------- /services/pwrman/pwrman.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include 7 | 8 | #include "lmic.h" 9 | 10 | #include "eefs/eefs.h" 11 | #include "pwrman.h" 12 | 13 | #include "svcdefs.h" // for type-checking hook functions 14 | 15 | // our basic unit is the micro-ampere hour -- 2^32 uAh = 4295 Ah 16 | 17 | // 16bc26f9da64e290-05b08ee7 18 | static const uint8_t UFID_PWRMAN_STATS[12] = { 0x90, 0xe2, 0x64, 0xda, 0xf9, 0x26, 0xbc, 0x16, 0xe7, 0x8e, 0xb0, 0x05 }; 19 | 20 | const char* _pwrman_eefs_fn (const uint8_t* ufid) { 21 | if( memcmp(ufid, UFID_PWRMAN_STATS, sizeof(UFID_PWRMAN_STATS)) == 0 ) { 22 | return "com.semtech.svc.pwrman.stats"; 23 | } 24 | return NULL; 25 | } 26 | 27 | typedef struct { 28 | uint32_t hr; 29 | uint32_t ticks; 30 | } ptime; 31 | 32 | // Volatile state 33 | static struct { 34 | ptime accu; // accumulator 35 | ptime stats[PWRMAN_C_MAX]; // consumption statistics 36 | } state; 37 | 38 | // Persistent state (eefs) 39 | typedef struct { 40 | uint32_t uah_accu; // accumulator 41 | uint32_t uah_stats[PWRMAN_C_MAX]; // total micro-amp hours used 42 | } pwrman_pstate; 43 | 44 | static void update_rtstats (void); // fwd decl 45 | 46 | static void add (ptime* ppt, uint64_t uaticks) { 47 | uaticks += ppt->ticks; 48 | uint32_t uah = uaticks / (OSTICKS_PER_SEC * 60 * 60); 49 | if( uah != 0 ) { 50 | ppt->hr += uah; 51 | uaticks -= ((int64_t) uah * (OSTICKS_PER_SEC * 60 * 60)); 52 | } 53 | ppt->ticks = uaticks; 54 | } 55 | 56 | void pwrman_consume (int ctype, uint32_t ticks, uint32_t ua) { 57 | ASSERT(ctype < PWRMAN_C_MAX); 58 | uint64_t uaticks = (uint64_t) ticks * ua; 59 | #ifdef CFG_DEBUG_pwrman 60 | debug_printf("pwrman: adding %u ticks to accu (%d/%d", 61 | (uint32_t) uaticks, 62 | state.accu.hr, state.accu.ticks); 63 | #endif 64 | add(&state.accu, uaticks); 65 | #ifdef CFG_DEBUG_pwrman 66 | debug_printf(" --> %d/%d)\r\n", 67 | state.accu.hr, state.accu.ticks); 68 | #endif 69 | add(&state.stats[ctype], uaticks); 70 | } 71 | 72 | uint32_t pwrman_accu_uah (void) { 73 | update_rtstats(); 74 | return state.accu.hr; 75 | } 76 | 77 | void pwrman_commit (void) { 78 | update_rtstats(); 79 | pwrman_pstate ps; 80 | ps.uah_accu = state.accu.hr; 81 | for( int i = 0; i < PWRMAN_C_MAX; i++ ) { 82 | ps.uah_stats[i] = state.stats[i].hr; 83 | } 84 | eefs_save(UFID_PWRMAN_STATS, &ps, sizeof(ps)); 85 | } 86 | 87 | void pwrman_reset (void) { 88 | memset(&state, 0x00, sizeof(state)); 89 | pwrman_pstate ps; 90 | memset(&ps, 0x00, sizeof(ps)); 91 | eefs_save(UFID_PWRMAN_STATS, &ps, sizeof(ps)); 92 | } 93 | 94 | void _pwrman_init (void) { 95 | pwrman_pstate ps; 96 | if( eefs_read(UFID_PWRMAN_STATS, &ps, sizeof(ps)) == sizeof(ps) ) { 97 | state.accu.hr = ps.uah_accu; 98 | for( int i = 0; i < PWRMAN_C_MAX; i++ ) { 99 | state.stats[i].hr = ps.uah_stats[i]; 100 | } 101 | } 102 | } 103 | 104 | static void update_rtstats (void) { 105 | #if defined(STM32L0) && defined(CFG_rtstats) 106 | hal_rtstats stats; 107 | hal_rtstats_collect(&stats); 108 | pwrman_consume(PWRMAN_C_RUN, stats.run_ticks, BRD_PWR_RUN_UA); 109 | pwrman_consume(PWRMAN_C_SLEEP, stats.sleep_ticks[HAL_SLEEP_S0], BRD_PWR_S0_UA); 110 | pwrman_consume(PWRMAN_C_SLEEP, stats.sleep_ticks[HAL_SLEEP_S1], BRD_PWR_S1_UA); 111 | pwrman_consume(PWRMAN_C_SLEEP, stats.sleep_ticks[HAL_SLEEP_S2], BRD_PWR_S2_UA); 112 | #endif 113 | } 114 | -------------------------------------------------------------------------------- /services/pwrman/pwrman.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _pwrman_h_ 7 | #define _pwrman_h_ 8 | 9 | enum { 10 | PWRMAN_C_RUN, // MCU run 11 | PWRMAN_C_SLEEP, // MCU sleep 12 | PWRMAN_C_RX, // radio RX 13 | PWRMAN_C_TX, // radio TX 14 | PWRMAN_C_APP1, // application-specific 1 15 | PWRMAN_C_APP2, // application-specific 2 16 | PWRMAN_C_APP3, // application-specific 3 17 | PWRMAN_C_APP4, // application-specific 4 18 | 19 | PWRMAN_C_MAX 20 | }; 21 | 22 | void pwrman_consume (int ctype, uint32_t ticks, uint32_t ua); 23 | void pwrman_commit (void); 24 | void pwrman_reset (void); 25 | 26 | uint32_t pwrman_accu_uah (void); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /services/uexti.svc: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | 7 | src: 8 | - uexti/uexti.c 9 | 10 | hooks: 11 | - void uexti_irq (unsigned int* mask) 12 | 13 | 14 | # vim: syntax=yaml 15 | -------------------------------------------------------------------------------- /services/uexti/uexti.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "lmic.h" 7 | 8 | #include "svcdefs.h" 9 | #include "uexti.h" 10 | 11 | #include "peripherals.h" 12 | 13 | // ------------------------------------------------ 14 | // Unified External Interrupt Handler 15 | 16 | static void hook_uexti_irq (unsigned int* mask) { 17 | SVCHOOK_uexti_irq(mask); 18 | } 19 | 20 | #if defined(STM32L0) 21 | 22 | #include "board.h" 23 | 24 | #define uexti_handler 1 25 | #if (CFG_EXTI_IRQ_HANDLER != uexti_handler) 26 | #error "UEXTI requires 'uexti_handler' to be set as EXTI_IRQ_HANDLER!" 27 | #endif 28 | #undef uexti_handler 29 | 30 | void uexti_handler (void) { 31 | unsigned int m0, m1; 32 | m0 = m1 = EXTI->PR; // get pending interrupts 33 | hook_uexti_irq(&m1); // call hooks 34 | EXTI->PR = (m0 ^ m1) & m0; // clear handled interrupts 35 | } 36 | 37 | void uexti_config (unsigned int gpio, bool rising, bool falling) { 38 | gpio_cfg_extirq_ex(BRD_PORT(gpio), BRD_PIN(gpio), rising, falling); 39 | } 40 | 41 | void uexti_enable (unsigned int gpio, bool enable) { 42 | gpio_set_extirq(BRD_PIN(gpio), enable); 43 | } 44 | 45 | #elif defined(unicorn) 46 | 47 | void uexti_handler (void) { 48 | unsigned int mask = pio_irq_get(); 49 | unsigned int m0 = mask; 50 | hook_uexti_irq(&mask); // call hooks 51 | pio_irq_clear(mask ^ m0); 52 | } 53 | 54 | void uexti_config (unsigned int gpio, bool rising, bool falling) { 55 | pio_irq_config(gpio, rising, falling); 56 | } 57 | 58 | void uexti_enable (unsigned int gpio, bool enable) { 59 | pio_irq_enable(gpio, enable); 60 | } 61 | 62 | #else 63 | #error "Platform not supported by UEXTI service module" 64 | #endif 65 | -------------------------------------------------------------------------------- /services/uexti/uexti.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _uexti_h_ 7 | #define _uexti_h_ 8 | 9 | #include 10 | 11 | void uexti_config (unsigned int gpio, bool rising, bool falling); 12 | void uexti_enable (unsigned int gpio, bool enable); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /stm32/CMSIS/Device/ST/STM32L0xx/Include/stm32l0xx.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file stm32l0xx.h 4 | * @author MCD Application Team 5 | * @version V1.2.0 6 | * @date 06-February-2015 7 | * @brief CMSIS Cortex-M0+ Device Peripheral Access Layer Header File. 8 | * This file contains all the peripheral register's definitions, bits 9 | * definitions and memory mapping for STM32L0xx devices. 10 | * 11 | * The file is the unique include file that the application programmer 12 | * is using in the C source code, usually in main.c. This file contains: 13 | * - Configuration section that allows to select: 14 | * - The device used in the target application 15 | * - To use or not the peripheral’s drivers in application code(i.e. 16 | * code will be based on direct access to peripheral’s registers 17 | * rather than drivers API), this option is controlled by 18 | * "#define USE_HAL_DRIVER" 19 | * 20 | ****************************************************************************** 21 | * @attention 22 | * 23 | *

© COPYRIGHT(c) 2015 STMicroelectronics

24 | * 25 | * Redistribution and use in source and binary forms, with or without modification, 26 | * are permitted provided that the following conditions are met: 27 | * 1. Redistributions of source code must retain the above copyright notice, 28 | * this list of conditions and the following disclaimer. 29 | * 2. Redistributions in binary form must reproduce the above copyright notice, 30 | * this list of conditions and the following disclaimer in the documentation 31 | * and/or other materials provided with the distribution. 32 | * 3. Neither the name of STMicroelectronics nor the names of its contributors 33 | * may be used to endorse or promote products derived from this software 34 | * without specific prior written permission. 35 | * 36 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 37 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 38 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 39 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 40 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 41 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 42 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 43 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 44 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 46 | * 47 | ****************************************************************************** 48 | */ 49 | 50 | /** @addtogroup CMSIS 51 | * @{ 52 | */ 53 | 54 | /** @addtogroup stm32l0xx 55 | * @{ 56 | */ 57 | 58 | #ifndef __STM32L0xx_H 59 | #define __STM32L0xx_H 60 | 61 | #ifdef __cplusplus 62 | extern "C" { 63 | #endif /* __cplusplus */ 64 | 65 | /** @addtogroup Library_configuration_section 66 | * @{ 67 | */ 68 | 69 | /** 70 | * @brief STM32 Family 71 | */ 72 | #if !defined (STM32L0) 73 | #define STM32L0 74 | #endif /* STM32L0 */ 75 | 76 | /* Uncomment the line below according to the target STM32 device used in your 77 | application 78 | */ 79 | 80 | #if !defined (STM32L051xx) && !defined (STM32L052xx) && !defined (STM32L053xx) && !defined (STM32L061xx) && !defined (STM32L062xx) && !defined (STM32L063xx) \ 81 | && !defined (STM32L071xx) && !defined (STM32L072xx) && !defined (STM32L073xx) && !defined (STM32L081xx) && !defined (STM32L082xx) && !defined (STM32L083xx) 82 | /* #define STM32L051xx */ /*!< STM32L051K8, STM32L051C6, STM32L051C8, STM32L051R6, STM32L051R8 Devices */ 83 | /* #define STM32L052xx */ /*!< STM32L052K6, STM32L052K8, STM32L052C6, STM32L052C8, STM32L052R6, STM32L052R8 Devices */ 84 | /* #define STM32L053xx */ /*!< STM32L053C6, STM32L053C8, STM32L053R6, STM32L053R8 Devices */ 85 | /* #define STM32L061xx */ /*!< */ 86 | /* #define STM32L062xx */ /*!< STM32L062K8 */ 87 | /* #define STM32L063xx */ /*!< STM32L063C8, STM32L063R8 */ 88 | /* #define STM32L071xx */ /*!< */ 89 | /* #define STM32L072xx */ /*!< */ 90 | /* #define STM32L073xx */ /*!< STM32L073V8, STM32L073VB, STM32L073RB, STM32L073VZ, STM32L073RZ Devices */ 91 | /* #define STM32L081xx */ /*!< */ 92 | /* #define STM32L082xx */ /*!< */ 93 | /* #define STM32L083xx */ /*!< */ 94 | #endif 95 | 96 | /* Tip: To avoid modifying this file each time you need to switch between these 97 | devices, you can define the device in your toolchain compiler preprocessor. 98 | */ 99 | #if !defined (USE_HAL_DRIVER) 100 | /** 101 | * @brief Comment the line below if you will not use the peripherals drivers. 102 | In this case, these drivers will not be included and the application code will 103 | be based on direct access to peripherals registers 104 | */ 105 | /*#define USE_HAL_DRIVER */ 106 | #endif /* USE_HAL_DRIVER */ 107 | 108 | /** 109 | * @brief CMSIS Device version number V1.2.0RC1 110 | */ 111 | #define __STM32L0xx_CMSIS_DEVICE_VERSION_MAIN (0x01) /*!< [31:24] main version */ 112 | #define __STM32L0xx_CMSIS_DEVICE_VERSION_SUB1 (0x02) /*!< [23:16] sub1 version */ 113 | #define __STM32L0xx_CMSIS_DEVICE_VERSION_SUB2 (0x00) /*!< [15:8] sub2 version */ 114 | #define __STM32L0xx_CMSIS_DEVICE_VERSION_RC (0x00) /*!< [7:0] release candidate */ 115 | #define __STM32L0xx_CMSIS_DEVICE_VERSION ((__CMSIS_DEVICE_VERSION_MAIN << 24)\ 116 | |(__CMSIS_DEVICE_HAL_VERSION_SUB1 << 16)\ 117 | |(__CMSIS_DEVICE_HAL_VERSION_SUB2 << 8 )\ 118 | |(__CMSIS_DEVICE_HAL_VERSION_RC)) 119 | 120 | /** 121 | * @} 122 | */ 123 | 124 | /** @addtogroup Device_Included 125 | * @{ 126 | */ 127 | 128 | #if defined(STM32L031xx) 129 | #include "stm32l031xx.h" 130 | #elif defined(STM32L041xx) 131 | #include "stm32l041xx.h" 132 | #elif defined(STM32L051xx) 133 | #include "stm32l051xx.h" 134 | #elif defined(STM32L052xx) 135 | #include "stm32l052xx.h" 136 | #elif defined(STM32L053xx) 137 | #include "stm32l053xx.h" 138 | #elif defined(STM32L062xx) 139 | #include "stm32l062xx.h" 140 | #elif defined(STM32L063xx) 141 | #include "stm32l063xx.h" 142 | #elif defined(STM32L061xx) 143 | #include "stm32l061xx.h" 144 | #elif defined(STM32L071xx) 145 | #include "stm32l071xx.h" 146 | #elif defined(STM32L072xx) 147 | #include "stm32l072xx.h" 148 | #elif defined(STM32L073xx) 149 | #include "stm32l073xx.h" 150 | #elif defined(STM32L082xx) 151 | #include "stm32l082xx.h" 152 | #elif defined(STM32L083xx) 153 | #include "stm32l083xx.h" 154 | #elif defined(STM32L081xx) 155 | #include "stm32l081xx.h" 156 | #else 157 | #error "Please select first the target STM32L0xx device used in your application (in stm32l0xx.h file)" 158 | #endif 159 | 160 | /** 161 | * @} 162 | */ 163 | 164 | /** @addtogroup Exported_types 165 | * @{ 166 | */ 167 | typedef enum 168 | { 169 | RESET = 0, 170 | SET = !RESET 171 | } FlagStatus, ITStatus; 172 | 173 | typedef enum 174 | { 175 | DISABLE = 0, 176 | ENABLE = !DISABLE 177 | } FunctionalState; 178 | #define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE)) 179 | 180 | typedef enum 181 | { 182 | ERROR = 0, 183 | SUCCESS = !ERROR 184 | } ErrorStatus; 185 | 186 | /** 187 | * @} 188 | */ 189 | 190 | 191 | /** @addtogroup Exported_macro 192 | * @{ 193 | */ 194 | #define SET_BIT(REG, BIT) ((REG) |= (BIT)) 195 | 196 | #define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) 197 | 198 | #define READ_BIT(REG, BIT) ((REG) & (BIT)) 199 | 200 | #define CLEAR_REG(REG) ((REG) = (0x0)) 201 | 202 | #define WRITE_REG(REG, VAL) ((REG) = (VAL)) 203 | 204 | #define READ_REG(REG) ((REG)) 205 | 206 | #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) 207 | 208 | 209 | /** 210 | * @} 211 | */ 212 | 213 | #if defined (USE_HAL_DRIVER) 214 | #include "stm32l0xx_hal.h" 215 | #endif /* USE_HAL_DRIVER */ 216 | 217 | #ifdef __cplusplus 218 | } 219 | #endif /* __cplusplus */ 220 | 221 | #endif /* __STM32L0xx_H */ 222 | /** 223 | * @} 224 | */ 225 | 226 | /** 227 | * @} 228 | */ 229 | 230 | 231 | 232 | 233 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 234 | -------------------------------------------------------------------------------- /stm32/CMSIS/Device/ST/STM32L0xx/Include/system_stm32l0xx.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file system_stm32l0xx.h 4 | * @author MCD Application Team 5 | * @version V1.2.0 6 | * @date 06-February-2015 7 | * @brief CMSIS Cortex-M0+ Device Peripheral Access Layer System Header File. 8 | ****************************************************************************** 9 | * @attention 10 | * 11 | *

© COPYRIGHT(c) 2015 STMicroelectronics

12 | * 13 | * Redistribution and use in source and binary forms, with or without modification, 14 | * are permitted provided that the following conditions are met: 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 2. Redistributions in binary form must reproduce the above copyright notice, 18 | * this list of conditions and the following disclaimer in the documentation 19 | * and/or other materials provided with the distribution. 20 | * 3. Neither the name of STMicroelectronics nor the names of its contributors 21 | * may be used to endorse or promote products derived from this software 22 | * without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | ****************************************************************************** 36 | */ 37 | 38 | /** @addtogroup CMSIS 39 | * @{ 40 | */ 41 | 42 | /** @addtogroup stm32l0xx_system 43 | * @{ 44 | */ 45 | 46 | /** 47 | * @brief Define to prevent recursive inclusion 48 | */ 49 | #ifndef __SYSTEM_STM32L0XX_H 50 | #define __SYSTEM_STM32L0XX_H 51 | 52 | #ifdef __cplusplus 53 | extern "C" { 54 | #endif 55 | 56 | /** @addtogroup STM32L0xx_System_Includes 57 | * @{ 58 | */ 59 | 60 | /** 61 | * @} 62 | */ 63 | 64 | 65 | /** @addtogroup STM32L0xx_System_Exported_types 66 | * @{ 67 | */ 68 | /* This variable is updated in three ways: 69 | 1) by calling CMSIS function SystemCoreClockUpdate() 70 | 2) by calling HAL API function HAL_RCC_GetSysClockFreq() 71 | 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency 72 | Note: If you use this function to configure the system clock; then there 73 | is no need to call the 2 first functions listed above, since SystemCoreClock 74 | variable is updated automatically. 75 | */ 76 | extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ 77 | 78 | /** 79 | * @} 80 | */ 81 | 82 | /** @addtogroup STM32L0xx_System_Exported_Constants 83 | * @{ 84 | */ 85 | 86 | /** 87 | * @} 88 | */ 89 | 90 | /** @addtogroup STM32L0xx_System_Exported_Macros 91 | * @{ 92 | */ 93 | 94 | /** 95 | * @} 96 | */ 97 | 98 | /** @addtogroup STM32L0xx_System_Exported_Functions 99 | * @{ 100 | */ 101 | 102 | extern void SystemInit(void); 103 | extern void SystemCoreClockUpdate(void); 104 | /** 105 | * @} 106 | */ 107 | 108 | #ifdef __cplusplus 109 | } 110 | #endif 111 | 112 | #endif /*__SYSTEM_STM32L0XX_H */ 113 | 114 | /** 115 | * @} 116 | */ 117 | 118 | /** 119 | * @} 120 | */ 121 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 122 | -------------------------------------------------------------------------------- /stm32/CMSIS/Include/arm_common_tables.h: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------- 2 | * Copyright (C) 2010-2014 ARM Limited. All rights reserved. 3 | * 4 | * $Date: 31. July 2014 5 | * $Revision: V1.4.4 6 | * 7 | * Project: CMSIS DSP Library 8 | * Title: arm_common_tables.h 9 | * 10 | * Description: This file has extern declaration for common tables like Bitreverse, reciprocal etc which are used across different functions 11 | * 12 | * Target Processor: Cortex-M4/Cortex-M3 13 | * 14 | * Redistribution and use in source and binary forms, with or without 15 | * modification, are permitted provided that the following conditions 16 | * are met: 17 | * - Redistributions of source code must retain the above copyright 18 | * notice, this list of conditions and the following disclaimer. 19 | * - Redistributions in binary form must reproduce the above copyright 20 | * notice, this list of conditions and the following disclaimer in 21 | * the documentation and/or other materials provided with the 22 | * distribution. 23 | * - Neither the name of ARM LIMITED nor the names of its contributors 24 | * may be used to endorse or promote products derived from this 25 | * software without specific prior written permission. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 29 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 30 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 31 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 32 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 33 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 34 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 35 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 37 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 | * POSSIBILITY OF SUCH DAMAGE. 39 | * -------------------------------------------------------------------- */ 40 | 41 | #ifndef _ARM_COMMON_TABLES_H 42 | #define _ARM_COMMON_TABLES_H 43 | 44 | #include "arm_math.h" 45 | 46 | extern const uint16_t armBitRevTable[1024]; 47 | extern const q15_t armRecipTableQ15[64]; 48 | extern const q31_t armRecipTableQ31[64]; 49 | //extern const q31_t realCoefAQ31[1024]; 50 | //extern const q31_t realCoefBQ31[1024]; 51 | extern const float32_t twiddleCoef_16[32]; 52 | extern const float32_t twiddleCoef_32[64]; 53 | extern const float32_t twiddleCoef_64[128]; 54 | extern const float32_t twiddleCoef_128[256]; 55 | extern const float32_t twiddleCoef_256[512]; 56 | extern const float32_t twiddleCoef_512[1024]; 57 | extern const float32_t twiddleCoef_1024[2048]; 58 | extern const float32_t twiddleCoef_2048[4096]; 59 | extern const float32_t twiddleCoef_4096[8192]; 60 | #define twiddleCoef twiddleCoef_4096 61 | extern const q31_t twiddleCoef_16_q31[24]; 62 | extern const q31_t twiddleCoef_32_q31[48]; 63 | extern const q31_t twiddleCoef_64_q31[96]; 64 | extern const q31_t twiddleCoef_128_q31[192]; 65 | extern const q31_t twiddleCoef_256_q31[384]; 66 | extern const q31_t twiddleCoef_512_q31[768]; 67 | extern const q31_t twiddleCoef_1024_q31[1536]; 68 | extern const q31_t twiddleCoef_2048_q31[3072]; 69 | extern const q31_t twiddleCoef_4096_q31[6144]; 70 | extern const q15_t twiddleCoef_16_q15[24]; 71 | extern const q15_t twiddleCoef_32_q15[48]; 72 | extern const q15_t twiddleCoef_64_q15[96]; 73 | extern const q15_t twiddleCoef_128_q15[192]; 74 | extern const q15_t twiddleCoef_256_q15[384]; 75 | extern const q15_t twiddleCoef_512_q15[768]; 76 | extern const q15_t twiddleCoef_1024_q15[1536]; 77 | extern const q15_t twiddleCoef_2048_q15[3072]; 78 | extern const q15_t twiddleCoef_4096_q15[6144]; 79 | extern const float32_t twiddleCoef_rfft_32[32]; 80 | extern const float32_t twiddleCoef_rfft_64[64]; 81 | extern const float32_t twiddleCoef_rfft_128[128]; 82 | extern const float32_t twiddleCoef_rfft_256[256]; 83 | extern const float32_t twiddleCoef_rfft_512[512]; 84 | extern const float32_t twiddleCoef_rfft_1024[1024]; 85 | extern const float32_t twiddleCoef_rfft_2048[2048]; 86 | extern const float32_t twiddleCoef_rfft_4096[4096]; 87 | 88 | 89 | /* floating-point bit reversal tables */ 90 | #define ARMBITREVINDEXTABLE__16_TABLE_LENGTH ((uint16_t)20 ) 91 | #define ARMBITREVINDEXTABLE__32_TABLE_LENGTH ((uint16_t)48 ) 92 | #define ARMBITREVINDEXTABLE__64_TABLE_LENGTH ((uint16_t)56 ) 93 | #define ARMBITREVINDEXTABLE_128_TABLE_LENGTH ((uint16_t)208 ) 94 | #define ARMBITREVINDEXTABLE_256_TABLE_LENGTH ((uint16_t)440 ) 95 | #define ARMBITREVINDEXTABLE_512_TABLE_LENGTH ((uint16_t)448 ) 96 | #define ARMBITREVINDEXTABLE1024_TABLE_LENGTH ((uint16_t)1800) 97 | #define ARMBITREVINDEXTABLE2048_TABLE_LENGTH ((uint16_t)3808) 98 | #define ARMBITREVINDEXTABLE4096_TABLE_LENGTH ((uint16_t)4032) 99 | 100 | extern const uint16_t armBitRevIndexTable16[ARMBITREVINDEXTABLE__16_TABLE_LENGTH]; 101 | extern const uint16_t armBitRevIndexTable32[ARMBITREVINDEXTABLE__32_TABLE_LENGTH]; 102 | extern const uint16_t armBitRevIndexTable64[ARMBITREVINDEXTABLE__64_TABLE_LENGTH]; 103 | extern const uint16_t armBitRevIndexTable128[ARMBITREVINDEXTABLE_128_TABLE_LENGTH]; 104 | extern const uint16_t armBitRevIndexTable256[ARMBITREVINDEXTABLE_256_TABLE_LENGTH]; 105 | extern const uint16_t armBitRevIndexTable512[ARMBITREVINDEXTABLE_512_TABLE_LENGTH]; 106 | extern const uint16_t armBitRevIndexTable1024[ARMBITREVINDEXTABLE1024_TABLE_LENGTH]; 107 | extern const uint16_t armBitRevIndexTable2048[ARMBITREVINDEXTABLE2048_TABLE_LENGTH]; 108 | extern const uint16_t armBitRevIndexTable4096[ARMBITREVINDEXTABLE4096_TABLE_LENGTH]; 109 | 110 | /* fixed-point bit reversal tables */ 111 | #define ARMBITREVINDEXTABLE_FIXED___16_TABLE_LENGTH ((uint16_t)12 ) 112 | #define ARMBITREVINDEXTABLE_FIXED___32_TABLE_LENGTH ((uint16_t)24 ) 113 | #define ARMBITREVINDEXTABLE_FIXED___64_TABLE_LENGTH ((uint16_t)56 ) 114 | #define ARMBITREVINDEXTABLE_FIXED__128_TABLE_LENGTH ((uint16_t)112 ) 115 | #define ARMBITREVINDEXTABLE_FIXED__256_TABLE_LENGTH ((uint16_t)240 ) 116 | #define ARMBITREVINDEXTABLE_FIXED__512_TABLE_LENGTH ((uint16_t)480 ) 117 | #define ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH ((uint16_t)992 ) 118 | #define ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH ((uint16_t)1984) 119 | #define ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH ((uint16_t)4032) 120 | 121 | extern const uint16_t armBitRevIndexTable_fixed_16[ARMBITREVINDEXTABLE_FIXED___16_TABLE_LENGTH]; 122 | extern const uint16_t armBitRevIndexTable_fixed_32[ARMBITREVINDEXTABLE_FIXED___32_TABLE_LENGTH]; 123 | extern const uint16_t armBitRevIndexTable_fixed_64[ARMBITREVINDEXTABLE_FIXED___64_TABLE_LENGTH]; 124 | extern const uint16_t armBitRevIndexTable_fixed_128[ARMBITREVINDEXTABLE_FIXED__128_TABLE_LENGTH]; 125 | extern const uint16_t armBitRevIndexTable_fixed_256[ARMBITREVINDEXTABLE_FIXED__256_TABLE_LENGTH]; 126 | extern const uint16_t armBitRevIndexTable_fixed_512[ARMBITREVINDEXTABLE_FIXED__512_TABLE_LENGTH]; 127 | extern const uint16_t armBitRevIndexTable_fixed_1024[ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH]; 128 | extern const uint16_t armBitRevIndexTable_fixed_2048[ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH]; 129 | extern const uint16_t armBitRevIndexTable_fixed_4096[ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH]; 130 | 131 | /* Tables for Fast Math Sine and Cosine */ 132 | extern const float32_t sinTable_f32[FAST_MATH_TABLE_SIZE + 1]; 133 | extern const q31_t sinTable_q31[FAST_MATH_TABLE_SIZE + 1]; 134 | extern const q15_t sinTable_q15[FAST_MATH_TABLE_SIZE + 1]; 135 | 136 | #endif /* ARM_COMMON_TABLES_H */ 137 | -------------------------------------------------------------------------------- /stm32/CMSIS/Include/arm_const_structs.h: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------- 2 | * Copyright (C) 2010-2014 ARM Limited. All rights reserved. 3 | * 4 | * $Date: 31. July 2014 5 | * $Revision: V1.4.4 6 | * 7 | * Project: CMSIS DSP Library 8 | * Title: arm_const_structs.h 9 | * 10 | * Description: This file has constant structs that are initialized for 11 | * user convenience. For example, some can be given as 12 | * arguments to the arm_cfft_f32() function. 13 | * 14 | * Target Processor: Cortex-M4/Cortex-M3 15 | * 16 | * Redistribution and use in source and binary forms, with or without 17 | * modification, are permitted provided that the following conditions 18 | * are met: 19 | * - Redistributions of source code must retain the above copyright 20 | * notice, this list of conditions and the following disclaimer. 21 | * - Redistributions in binary form must reproduce the above copyright 22 | * notice, this list of conditions and the following disclaimer in 23 | * the documentation and/or other materials provided with the 24 | * distribution. 25 | * - Neither the name of ARM LIMITED nor the names of its contributors 26 | * may be used to endorse or promote products derived from this 27 | * software without specific prior written permission. 28 | * 29 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 32 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 33 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 34 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 35 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 36 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 37 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 39 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 40 | * POSSIBILITY OF SUCH DAMAGE. 41 | * -------------------------------------------------------------------- */ 42 | 43 | #ifndef _ARM_CONST_STRUCTS_H 44 | #define _ARM_CONST_STRUCTS_H 45 | 46 | #include "arm_math.h" 47 | #include "arm_common_tables.h" 48 | 49 | extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len16; 50 | extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len32; 51 | extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len64; 52 | extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len128; 53 | extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len256; 54 | extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len512; 55 | extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len1024; 56 | extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len2048; 57 | extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len4096; 58 | 59 | extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len16; 60 | extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len32; 61 | extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len64; 62 | extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len128; 63 | extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len256; 64 | extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len512; 65 | extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len1024; 66 | extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len2048; 67 | extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096; 68 | 69 | extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len16; 70 | extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len32; 71 | extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len64; 72 | extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len128; 73 | extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len256; 74 | extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len512; 75 | extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len1024; 76 | extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len2048; 77 | extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len4096; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /stm32/adc.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // Copyright (C) 2014-2016 IBM Corporation. All rights reserved. 3 | // 4 | // This file is subject to the terms and conditions defined in file 'LICENSE', 5 | // which is part of this source code package. 6 | 7 | #include "lmic.h" 8 | #include "peripherals.h" 9 | 10 | static void adc_on (void) { 11 | #if defined(STM32L0) 12 | RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // enable peripheral clock 13 | ADC1->CR |= ADC_CR_ADCAL; // start calibration 14 | while( (ADC1->CR & ADC_CR_ADCAL) != 0 ); // wait for it 15 | ADC1->ISR = ADC_ISR_ADRDY; // clear ready bit (rc_w1) 16 | ADC1->CR |= ADC_CR_ADEN; // switch on the ADC 17 | while( (ADC1->ISR & ADC_ISR_ADRDY) == 0 ); // wait until ADC is ready 18 | #elif defined(STM32L1) 19 | RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // make sure the ADC is clocked 20 | ADC1->CR2 |= ADC_CR2_ADON; // switch on the ADC 21 | #endif 22 | } 23 | 24 | static void adc_off (void) { 25 | #if defined(STM32L0) 26 | ADC1->CR |= ADC_CR_ADDIS; // switch off the ADC 27 | while( (ADC1->CR & ADC_CR_ADEN) != 0 ); // wait for it 28 | ADC1->CR &= ~ADC_CR_ADVREGEN; // switch off regulator 29 | #elif defined(STM32L1) 30 | #warning "adc_off() not yet implemented for STM32L0" 31 | // TODO - implement 32 | #endif 33 | } 34 | 35 | unsigned int adc_read (unsigned int chnl, unsigned int rate) { 36 | adc_on(); 37 | #if defined(STM32L0) 38 | if( chnl == VREFINT_ADC_CH ) { 39 | ADC->CCR |= ADC_CCR_VREFEN; // internal voltage reference on channel 17 40 | } else if( chnl == TEMPINT_ADC_CH ) { 41 | ADC->CCR |= ADC_CCR_TSEN; // internal temperature on channel 18 42 | } 43 | ADC1->CHSELR = (1 << chnl); // select channel 44 | ADC1->SMPR = rate & 0x7; // sample rate 45 | ADC1->CR |= ADC_CR_ADSTART; // start conversion 46 | while( (ADC1->ISR & ADC_ISR_EOC) == 0 ); // wait for it 47 | if( chnl == VREFINT_ADC_CH ) { 48 | ADC->CCR &= ~ADC_CCR_VREFEN; 49 | } else if( chnl == TEMPINT_ADC_CH ) { 50 | ADC->CCR &= ~ADC_CCR_TSEN; 51 | } 52 | #elif defined(STM32L1) 53 | ADC1->SQR5 = chnl; // select the channel for the 1st conversion 54 | ADC1->CR2 |= ADC_CR2_SWSTART; // start the conversion 55 | while( (ADC1->SR & ADC_SR_EOC) == 0 ); // wait for it 56 | #endif 57 | u2_t v = ADC1->DR; 58 | adc_off(); 59 | return v; 60 | } 61 | -------------------------------------------------------------------------------- /stm32/board.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _board_h_ 7 | #define _board_h_ 8 | 9 | // GPIO definitions 10 | // 33222222 22221111 11111100 00000000 11 | // 10987654 32109876 54321098 76543210 12 | // ________ _____fff ccccaaaa PPPPpppp 13 | 14 | #define BRD_GPIO(port,pin) (((port) << 4) | (pin)) 15 | #define BRD_GPIO_EX(port,pin,ex) (((port) << 4) | (pin) | (ex)) 16 | #define BRD_GPIO_AF(port,pin,af) (((af) << 8) | ((port) << 4) | (pin)) 17 | #define BRD_GPIO_AF_EX(port,pin,af,ex) (((af) << 8) | ((port) << 4) | (pin) | (ex)) 18 | #define BRD_PIN(gpio) ((gpio) & 0x0f) 19 | #define BRD_PORT(gpio) (((gpio) >> 4) & 0x0f) 20 | #define BRD_AF(gpio) (((gpio) >> 8) & 0x0f) 21 | 22 | // alternate function configuratons (c) 23 | #define BRD_GPIO_CHAN(ch) ((ch) << 12) 24 | #define BRD_GPIO_GET_CHAN(gpio) (((gpio) >> 12) & 0x07) 25 | 26 | // flags (f) 27 | #define BRD_GPIO_EXT_PULLUP (1 << 16) 28 | #define BRD_GPIO_EXT_PULLDN (1 << 17) 29 | #define BRD_GPIO_ACTIVE_LOW (1 << 18) 30 | 31 | // special values for low-power UART 32 | #define BRD_LPUART(x) ((x) | (1 << 8)) 33 | 34 | #define PORT_A 0 35 | #define PORT_B 1 36 | #define PORT_C 2 37 | 38 | 39 | #ifdef BRD_IMPL_INC 40 | #include BRD_IMPL_INC 41 | #else 42 | #error "Missing board implementation include file" 43 | #endif 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /stm32/brd_devboards.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | // to be included from board.h 7 | 8 | // ------------------------------------------- 9 | #if defined(CFG_nucleo_board) 10 | 11 | #define GPIO_RST BRD_GPIO(PORT_A, 0) 12 | 13 | #if defined(CFG_sx1272mbed) 14 | #define BRD_sx1272_radio 15 | #define GPIO_DIO0 BRD_GPIO(PORT_A, 10) 16 | #define GPIO_DIO1 BRD_GPIO(PORT_B, 3) 17 | #define GPIO_DIO2 BRD_GPIO(PORT_B, 5) 18 | #define GPIO_NSS BRD_GPIO(PORT_B, 6) 19 | 20 | #elif defined(CFG_sx1276mb1mas) || defined(CFG_sx1276mb1las) 21 | 22 | #define GPIO_DIO0 BRD_GPIO(PORT_A, 10) 23 | #define GPIO_DIO1 BRD_GPIO(PORT_B, 3) 24 | #define GPIO_DIO2 BRD_GPIO(PORT_B, 5) 25 | #define GPIO_NSS BRD_GPIO(PORT_B, 6) 26 | #define GPIO_TX BRD_GPIO(PORT_C, 1) 27 | 28 | #define BRD_sx1276_radio 29 | #if defined(CFG_sx1276mb1las) 30 | #define BRD_PABOOSTSEL(f,p) true 31 | #else 32 | #define BRD_PABOOSTSEL(f,p) false 33 | #endif 34 | 35 | #elif defined(CFG_sx1261mbed) || defined(CFG_sx1262mbed) 36 | 37 | #if defined(CFG_sx1261mbed) 38 | #define BRD_sx1261_radio 39 | #elif defined(CFG_sx1262mbed) 40 | #define BRD_sx1262_radio 41 | #endif 42 | #define GPIO_DIO1 BRD_GPIO_AF_EX(PORT_B, 4, 4, BRD_GPIO_CHAN(1)) 43 | #define GPIO_BUSY BRD_GPIO(PORT_B, 3) 44 | #define GPIO_NSS BRD_GPIO(PORT_A, 8) 45 | #define GPIO_TXRX_EN BRD_GPIO(PORT_A, 9) 46 | 47 | #else 48 | #error "Missing radio configuration" 49 | #endif 50 | 51 | #define BRD_RADIO_SPI 1 52 | #define GPIO_SCK BRD_GPIO_AF(PORT_A, 5, 0) 53 | #define GPIO_MISO BRD_GPIO_AF(PORT_A, 6, 0) 54 | #define GPIO_MOSI BRD_GPIO_AF(PORT_A, 7, 0) 55 | 56 | #define GPIO_BOOT_LED BRD_GPIO(PORT_A, 5) // -- LED is shared with SCK!! 57 | //#define GPIO_DBG_LED BRD_GPIO(PORT_A, 5) // -- LED is shared with SCK!! 58 | #define GPIO_DBG_TX BRD_GPIO_AF(PORT_A, 2, 4) 59 | #define GPIO_DBG_RX BRD_GPIO_AF(PORT_A, 3, 4) 60 | #define BRD_DBG_UART 2 61 | 62 | #define GPIO_PERSO_TX BRD_GPIO_AF(PORT_A, 2, 4) 63 | #define GPIO_PERSO_RX BRD_GPIO_AF(PORT_A, 3, 4) 64 | #define BRD_PERSO_UART 2 65 | 66 | #define BRD_USART BRD_LPUART(1) 67 | #define GPIO_USART_TX BRD_GPIO_AF(PORT_C, 4, 2) 68 | #define GPIO_USART_RX BRD_GPIO_AF(PORT_C, 5, 2) 69 | 70 | // power consumption 71 | 72 | #ifndef BRD_PWR_RUN_UA 73 | #define BRD_PWR_RUN_UA 6000 74 | #endif 75 | 76 | #ifndef BRD_PWR_S0_UA 77 | #define BRD_PWR_S0_UA 2000 78 | #endif 79 | 80 | #ifndef BRD_PWR_S1_UA 81 | #define BRD_PWR_S1_UA 12 82 | #endif 83 | 84 | #ifndef BRD_PWR_S2_UA 85 | #define BRD_PWR_S2_UA 5 86 | #endif 87 | 88 | 89 | // ------------------------------------------- 90 | #elif defined(CFG_b_l072Z_lrwan1_board) 91 | 92 | #define GPIO_RST BRD_GPIO(PORT_C, 0) 93 | #define GPIO_DIO0 BRD_GPIO_AF_EX(PORT_B, 4, 4, BRD_GPIO_CHAN(1)) 94 | #define GPIO_DIO1 BRD_GPIO(PORT_B, 1) 95 | #define GPIO_DIO2 BRD_GPIO(PORT_B, 0) 96 | #define GPIO_DIO3 BRD_GPIO(PORT_C, 13) 97 | #define GPIO_DIO4 BRD_GPIO(PORT_A, 5) 98 | #define GPIO_DIO5 BRD_GPIO(PORT_A, 4) 99 | 100 | #define GPIO_TCXO_PWR BRD_GPIO(PORT_A, 12) 101 | #define GPIO_RX BRD_GPIO(PORT_A, 1) // PA_RFI 102 | #define GPIO_TX BRD_GPIO(PORT_C, 1) // PA_BOOST 103 | #define GPIO_TX2 BRD_GPIO(PORT_C, 2) // PA_RFO 104 | 105 | #define GPIO_LED1 BRD_GPIO(PORT_B, 5) // grn 106 | #define GPIO_LED2 BRD_GPIO(PORT_A, 5) // red -- used by bootloader 107 | #define GPIO_LED3 BRD_GPIO(PORT_B, 6) // blu 108 | #define GPIO_LED4 BRD_GPIO(PORT_B, 7) // red 109 | 110 | // button PB2 111 | 112 | #define BRD_sx1276_radio 113 | #define BRD_PABOOSTSEL(f,p) ((p) > 15) 114 | #define BRD_TXANTSWSEL(f,p) ((BRD_PABOOSTSEL(f,p)) ? HAL_ANTSW_TX : HAL_ANTSW_TX2) 115 | 116 | #define BRD_RADIO_SPI 1 117 | #define GPIO_NSS BRD_GPIO(PORT_A, 15) 118 | #define GPIO_SCK BRD_GPIO_AF(PORT_B, 3, 0) 119 | #define GPIO_MISO BRD_GPIO_AF(PORT_A, 6, 0) 120 | #define GPIO_MOSI BRD_GPIO_AF(PORT_A, 7, 0) 121 | 122 | #define GPIO_DBG_LED GPIO_LED4 123 | #define GPIO_DBG_TX BRD_GPIO_AF(PORT_A, 2, 4) 124 | #define GPIO_DBG_RX BRD_GPIO_AF(PORT_A, 3, 4) 125 | #define BRD_DBG_UART 2 126 | 127 | #define BRD_USART 1 128 | #define GPIO_USART_TX BRD_GPIO_AF(PORT_A, 9, 4) 129 | #define GPIO_USART_RX BRD_GPIO_AF(PORT_A, 10, 4) 130 | 131 | // power consumption 132 | 133 | #ifndef BRD_PWR_RUN_UA 134 | #define BRD_PWR_RUN_UA 6000 135 | #endif 136 | 137 | #ifndef BRD_PWR_S0_UA 138 | #define BRD_PWR_S0_UA 2000 139 | #endif 140 | 141 | #ifndef BRD_PWR_S1_UA 142 | #define BRD_PWR_S1_UA 12 143 | #endif 144 | 145 | #ifndef BRD_PWR_S2_UA 146 | #define BRD_PWR_S2_UA 5 147 | #endif 148 | 149 | // brown-out 150 | #define BRD_borlevel 9 // RM0376, pg 116: BOR level 2, around 2.0 V 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /stm32/eeprom.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // Copyright (C) 2014-2016 IBM Corporation. All rights reserved. 3 | // 4 | // This file is subject to the terms and conditions defined in file 'LICENSE', 5 | // which is part of this source code package. 6 | 7 | #include "peripherals.h" 8 | 9 | // write 32-bit word to EEPROM memory 10 | void eeprom_write (void* dest, unsigned int val) { 11 | u4_t* addr = dest; 12 | // check previous value 13 | if( *addr != val ) { 14 | // unlock data eeprom memory and registers 15 | FLASH->PEKEYR = 0x89ABCDEF; // FLASH_PEKEY1 16 | FLASH->PEKEYR = 0x02030405; // FLASH_PEKEY2 17 | 18 | // only auto-erase if neccessary (when content is non-zero) 19 | #if defined(STM32L0) 20 | FLASH->PECR &= ~FLASH_PECR_FIX; // clear FIX 21 | #elif defined(STM32L1) 22 | FLASH->PECR &= ~FLASH_PECR_FTDW; // clear FTDW 23 | #endif 24 | 25 | // write value 26 | *addr = val; 27 | 28 | // check for end of programming 29 | while( FLASH->SR & FLASH_SR_BSY ); // loop while busy 30 | 31 | // lock data eeprom memory and registers 32 | FLASH->PECR |= FLASH_PECR_PELOCK; 33 | 34 | // verify value 35 | ASSERT( *((volatile u4_t*) addr) == val ); 36 | } 37 | } 38 | 39 | void eeprom_copy (void* dest, const void* src, int len) { 40 | ASSERT( (((u4_t) dest | (u4_t) src | len) & 3) == 0 ); 41 | u4_t* d = (u4_t*) dest; 42 | u4_t* s = (u4_t*) src; 43 | len >>= 2; 44 | 45 | while( len-- ) { 46 | eeprom_write(d++, *s++); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /stm32/fw.ld: -------------------------------------------------------------------------------- 1 | SECTIONS { 2 | .text : { 3 | . = ALIGN(4); 4 | KEEP(*(.fwhdr)) 5 | . = ALIGN(4); 6 | *(.text) 7 | *(.text*) 8 | . = ALIGN(4); 9 | } >FWFLASH 10 | 11 | .data : { 12 | . = ALIGN(4); 13 | _sdata = .; 14 | *(.fastcode) 15 | *(.fastcode*) 16 | . = ALIGN(4); 17 | *(.data) 18 | *(.data*) 19 | . = ALIGN(4); 20 | _edata = .; 21 | } >RAM AT>FWFLASH 22 | 23 | _sidata = LOADADDR(.data); 24 | 25 | .bss : { 26 | . = ALIGN(4); 27 | _sbss = .; 28 | *(.bss) 29 | *(.bss*) 30 | *(COMMON) 31 | . = ALIGN(4); 32 | _ebss = .; 33 | } >RAM 34 | 35 | .rodata : { 36 | . = ALIGN(4); 37 | *(.rodata) 38 | *(.rodata*) 39 | . = ALIGN(4); 40 | 41 | /* make sure flash image is a multiple of page size */ 42 | FILL(0x00000000) 43 | . = ALIGN(128); 44 | __fw_end__ = .; 45 | } >FWFLASH 46 | 47 | /DISCARD/ : { 48 | *(.ARM) 49 | *(.ARM*) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /stm32/gpio.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "hw.h" 7 | 8 | static inline void gpio_begin (int port) { 9 | unsigned int enr = GPIO_RCC_ENR; 10 | if ((enr & GPIO_EN(port)) == 0) { 11 | GPIO_RCC_ENR = enr | GPIO_EN(port); 12 | // dummy read as per errata 13 | (void) GPIOx(port)->IDR; 14 | } 15 | } 16 | 17 | static inline void gpio_end (int port) { 18 | GPIO_RCC_ENR &= ~GPIO_EN(port); 19 | } 20 | 21 | void gpio_cfg_pin (int port, int pin, int gpiocfg) { 22 | gpio_begin(port); 23 | HW_CFG_PIN(GPIOx(port), pin, gpiocfg); 24 | gpio_end(port); 25 | } 26 | 27 | void gpio_set_pin (int port, int pin, int state) { 28 | gpio_begin(port); 29 | HW_SET_PIN(GPIOx(port), pin, state); 30 | gpio_end(port); 31 | } 32 | 33 | void gpio_cfg_set_pin (int port, int pin, int gpiocfg, int state) { 34 | gpio_begin(port); 35 | HW_SET_PIN(GPIOx(port), pin, state); 36 | HW_CFG_PIN(GPIOx(port), pin, gpiocfg); 37 | gpio_end(port); 38 | } 39 | 40 | int gpio_get_pin (int port, int pin) { 41 | int val; 42 | gpio_begin(port); 43 | val = HW_GET_PIN(GPIOx(port), pin); 44 | gpio_end(port); 45 | return val; 46 | } 47 | 48 | int gpio_transition (int port, int pin, int type, int duration, unsigned int config) { 49 | int val; 50 | gpio_begin(port); 51 | val = HW_GET_PIN(GPIOx(port), pin); 52 | HW_SET_PIN(GPIOx(port), pin, type); 53 | HW_CFG_PIN(GPIOx(port), pin, GPIOCFG_MODE_OUT | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); 54 | for (int i = 0; i < duration; i++) __NOP(); 55 | HW_SET_PIN(GPIOx(port), pin, type ^ 1); 56 | for (int i = 0; i < duration; i++) __NOP(); 57 | HW_CFG_PIN(GPIOx(port), pin, config); 58 | gpio_end(port); 59 | return val; 60 | } 61 | 62 | void gpio_cfg_extirq_ex (int port, int pin, bool rising, bool falling) { 63 | RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // enable system configuration controller 64 | 65 | // configure external interrupt (every irq line 0-15 can be configured with a 4-bit port index A-G) 66 | u4_t tmp1 = (pin & 0x3) << 2; 67 | u4_t tmp2 = 0x0F << tmp1; 68 | SYSCFG->EXTICR[pin >> 2] = (SYSCFG->EXTICR[pin >> 2] & ~tmp2) | (port << tmp1); 69 | 70 | RCC->APB2ENR &= ~RCC_APB2ENR_SYSCFGEN; // disable system configuration controller 71 | 72 | // configure trigger and enable irq 73 | u4_t mask = 1 << pin; 74 | EXTI->RTSR &= ~mask; // clear trigger 75 | EXTI->FTSR &= ~mask; // clear trigger 76 | if( rising ) { 77 | EXTI->RTSR |= mask; 78 | } 79 | if( falling ) { 80 | EXTI->FTSR |= mask; 81 | } 82 | 83 | // configure the NVIC 84 | #if defined(STM32L0) 85 | u1_t channel = (pin < 2) ? EXTI0_1_IRQn : (pin < 4) ? EXTI2_3_IRQn : EXTI4_15_IRQn; 86 | #elif defined(STM32L1) 87 | u1_t channel = (pin < 5) ? (EXTI0_IRQn + pin) : ((pin < 10) ? EXTI9_5_IRQn : EXTI15_10_IRQn); 88 | #endif 89 | NVIC->IP[channel] = 0x70; // interrupt priority 90 | NVIC->ISER[channel>>5] = 1 << (channel & 0x1F); // set enable IRQ 91 | } 92 | 93 | void gpio_cfg_extirq (int port, int pin, int irqcfg) { 94 | gpio_cfg_extirq_ex(port, pin, 95 | (irqcfg == GPIO_IRQ_CHANGE || irqcfg == GPIO_IRQ_RISING), 96 | (irqcfg == GPIO_IRQ_CHANGE || irqcfg == GPIO_IRQ_FALLING)); 97 | } 98 | 99 | 100 | void gpio_set_extirq (int pin, int on) { 101 | if (on) { 102 | EXTI->PR = (1 << pin); 103 | EXTI->IMR |= (1 << pin); 104 | } else { 105 | EXTI->IMR &= ~(1 << pin); 106 | } 107 | } 108 | 109 | void pio_set (unsigned int pin, int value) { 110 | if (value < 0) { 111 | gpio_cfg_pin(BRD_PORT(pin), BRD_PIN(pin), 0 112 | | (((value & 1) == 0 ? GPIOCFG_PUPD_PUP : 0)) 113 | | (((value & 2) == 0 ? GPIOCFG_PUPD_PDN : 0)) 114 | | (((value & 4) == 0 ? GPIOCFG_MODE_ANA : GPIOCFG_MODE_INP))); 115 | } else { 116 | gpio_cfg_set_pin(BRD_PORT(pin), BRD_PIN(pin), 117 | GPIOCFG_MODE_OUT | GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE, 118 | value); 119 | } 120 | } 121 | 122 | int pio_get (unsigned int pin) { 123 | return gpio_get_pin(BRD_PORT(pin), BRD_PIN(pin)); 124 | } 125 | -------------------------------------------------------------------------------- /stm32/hal_stm32.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _hal_stm32_h_ 7 | #define _hal_stm32_h_ 8 | 9 | #include "hw.h" 10 | #include "boottab.h" 11 | 12 | // Get current PC 13 | __attribute__((always_inline)) static inline uint32_t hal_getpc (void) { 14 | uint32_t addr; 15 | __asm__ volatile ("mov %[addr], pc" : [addr]"=r" (addr) : : ); 16 | return addr; 17 | } 18 | 19 | // Macro to place code in RAM 20 | #define __fastcode __attribute__((noinline,section(".fastcode"))) 21 | 22 | // Sleep modes 23 | enum { 24 | HAL_SLEEP_S0, // sleep, full speed clock 25 | HAL_SLEEP_S1, // sleep, reduced speed clock 26 | HAL_SLEEP_S2, // stop mode 27 | 28 | HAL_SLEEP_CNT // number of sleep states 29 | }; 30 | 31 | void hal_setMaxSleep (unsigned int level); 32 | void hal_clearMaxSleep (unsigned int level); 33 | 34 | #ifdef CFG_rtstats 35 | typedef struct { 36 | uint32_t run_ticks; 37 | uint32_t sleep_ticks[HAL_SLEEP_CNT]; 38 | } hal_rtstats; 39 | 40 | void hal_rtstats_collect (hal_rtstats* stats); 41 | #endif 42 | 43 | 44 | // NVIC interrupt definition 45 | typedef struct { 46 | uint32_t num; // NVIC interrupt number 47 | void* handler; // Pointer to handler function 48 | } irqdef; 49 | 50 | extern const irqdef HAL_irqdefs[]; 51 | 52 | // Firmware header -- do not modify (append only) 53 | typedef struct { 54 | boot_fwhdr boot; 55 | 56 | uint32_t version; 57 | } hal_fwhdr; 58 | 59 | // Personalization data (persodata.c) 60 | void pd_init (void); 61 | bool pd_verify (void); 62 | 63 | void usart_init (void); 64 | void usart_irq (void); 65 | 66 | void i2c_irq (void); 67 | 68 | #if defined(SVC_fuota) 69 | // Glue for FUOTA (fountain code) service 70 | 71 | #include "peripherals.h" 72 | 73 | #define fuota_flash_pagesz FLASH_PAGE_SZ 74 | #define fuota_flash_bitdefault 0 75 | 76 | #define fuota_flash_write(dst,src,nwords,erase) \ 77 | flash_write((uint32_t*) (dst), (uint32_t*) (src), nwords, erase) 78 | 79 | #define fuota_flash_read(dst,src,nwords) \ 80 | memcpy(dst, src, (nwords) << 2) 81 | 82 | #define fuota_flash_rd_u4(addr) \ 83 | (*((uint32_t*) (addr))) 84 | 85 | #define fuota_flash_rd_ptr(addr) \ 86 | (*((void**) (addr))) 87 | 88 | #endif 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /stm32/i2c.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "peripherals.h" 7 | 8 | #ifdef BRD_I2C 9 | 10 | #if BRD_I2C == 1 11 | #define I2Cx I2C1 12 | #define I2Cx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; } while (0) 13 | #define I2Cx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_I2C1EN; } while (0) 14 | #define I2Cx_IRQn I2C1_IRQn 15 | #else 16 | #error "Unsupported I2C peripheral" 17 | #endif 18 | 19 | static struct { 20 | unsigned int wlen; 21 | unsigned char* wptr; 22 | unsigned int rlen; 23 | unsigned char* rptr; 24 | osjob_t* job; 25 | osjobcb_t cb; 26 | int* pstatus; 27 | } xfr; 28 | 29 | static struct { 30 | int status; 31 | osjob_t job; 32 | i2c_cb cb; 33 | } xfr2; 34 | 35 | static void i2c_stop (int status) { 36 | // generate stop condition 37 | I2Cx->CR2 |= I2C_CR2_STOP; 38 | // disable interrupts in NVIC 39 | NVIC_DisableIRQ(I2Cx_IRQn); 40 | // disable interrupts/peripheral 41 | I2Cx->CR1 = 0; 42 | // reconfigure GPIOs 43 | CFG_PIN_DEFAULT(GPIO_I2C_SCL); 44 | CFG_PIN_DEFAULT(GPIO_I2C_SDA); 45 | // disable peripheral clock 46 | I2Cx_disable(); 47 | // schedule callback 48 | *(xfr.pstatus) = status; 49 | if (xfr.job != NULL) { 50 | os_setCallback(xfr.job, xfr.cb); 51 | } else { 52 | xfr.cb(NULL); 53 | } 54 | // re-enable sleep 55 | hal_clearMaxSleep(HAL_SLEEP_S0); 56 | } 57 | 58 | static void i2c_start (int addr) { 59 | // enable peripheral clock 60 | I2Cx_enable(); 61 | // set timing 62 | I2Cx->TIMINGR = 0x40101A22; // from CubeMX tool; t_rise=t_fall=50ns, 100kHz 63 | // start I2C 64 | I2Cx->CR1 |= I2C_CR1_PE; 65 | // setup slave address 66 | I2Cx->CR2 = (I2Cx->CR2 & ~I2C_CR2_SADD) | (addr & I2C_CR2_SADD); 67 | // setup GPIOs 68 | CFG_PIN_AF(GPIO_I2C_SCL, GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE); 69 | CFG_PIN_AF(GPIO_I2C_SDA, GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE); 70 | // disable sleep (keep clock at full speed during transfer 71 | hal_setMaxSleep(HAL_SLEEP_S0); 72 | // enable interrupts in NVIC 73 | NVIC_EnableIRQ(I2Cx_IRQn); 74 | } 75 | 76 | static void i2c_cont (void) { 77 | if (xfr.wlen) { 78 | // calculate length; TODO: handle >255 79 | int n = xfr.wlen & 0xff; 80 | xfr.wlen -= n; 81 | // set direction & number of bytes 82 | I2Cx->CR2 = (I2Cx->CR2 & ~(I2C_CR2_RD_WRN | I2C_CR2_NBYTES)) | (n << 16); 83 | // enable interrupts 84 | I2Cx->CR1 = (I2Cx->CR1 & ~0xfe) | I2C_CR1_TXIE | I2C_CR1_TCIE | I2C_CR1_NACKIE | I2C_CR1_ERRIE; 85 | // start TX 86 | I2Cx->CR2 |= I2C_CR2_START; 87 | } else if (xfr.rlen) { 88 | // calculate length; TODO: handle >255 89 | int n = xfr.rlen & 0xff; 90 | xfr.rlen -= n; 91 | // set direction & number of bytes 92 | I2Cx->CR2 = (I2Cx->CR2 & ~(I2C_CR2_RD_WRN | I2C_CR2_NBYTES)) | I2C_CR2_RD_WRN | (n << 16); 93 | // enable interrupts 94 | I2Cx->CR1 = (I2Cx->CR1 & ~0xfe) | I2C_CR1_RXIE | I2C_CR1_TCIE | I2C_CR1_NACKIE | I2C_CR1_ERRIE; 95 | // start RX 96 | I2Cx->CR2 |= I2C_CR2_START; 97 | } else { 98 | // done 99 | i2c_stop(I2C_OK); 100 | } 101 | } 102 | 103 | void i2c_irq (void) { 104 | unsigned int isr = I2Cx->ISR; 105 | if (isr & I2C_ISR_NACKF) { 106 | // NACK detected, transfer failed! 107 | i2c_stop(I2C_NAK); 108 | } else if (isr & I2C_ISR_TC) { 109 | // transfer complete, move on 110 | i2c_cont(); 111 | } else if (isr & I2C_ISR_TXIS) { 112 | // write next byte 113 | I2Cx->TXDR = *xfr.wptr++; 114 | } else if (isr & I2C_ISR_RXNE) { 115 | // next byte received 116 | *xfr.rptr++ = I2Cx->RXDR; 117 | } else { 118 | hal_failed(); // XXX 119 | } 120 | } 121 | 122 | static void i2c_timeout (osjob_t* job) { 123 | i2c_abort(); 124 | } 125 | 126 | void i2c_xfer_ex (unsigned int addr, unsigned char* buf, unsigned int wlen, unsigned int rlen, ostime_t timeout, 127 | osjob_t* job, osjobcb_t cb, int* pstatus) { 128 | // setup xfr structure 129 | xfr.wlen = wlen; 130 | xfr.rlen = rlen; 131 | xfr.wptr = xfr.rptr = buf; 132 | xfr.job = job; 133 | xfr.cb = cb; 134 | xfr.pstatus = pstatus; 135 | *xfr.pstatus = I2C_BUSY; 136 | // set timeout 137 | if (timeout) { 138 | os_setTimedCallback(job, os_getTime() + timeout, i2c_timeout); 139 | } 140 | // prepare peripheral 141 | i2c_start(addr); 142 | // start actual transfer 143 | i2c_cont(); 144 | } 145 | 146 | static void i2cfunc (osjob_t* j) { 147 | xfr2.cb(xfr2.status); 148 | } 149 | 150 | void i2c_xfer (unsigned int addr, unsigned char* buf, unsigned int wlen, unsigned int rlen, i2c_cb cb, ostime_t timeout) { 151 | xfr2.cb = cb; 152 | i2c_xfer_ex(addr, buf, wlen, rlen, timeout, &xfr2.job, i2cfunc, &xfr2.status); 153 | } 154 | 155 | void i2c_abort (void) { 156 | hal_disableIRQs(); 157 | i2c_stop(I2C_ABORT); 158 | hal_enableIRQs(); 159 | } 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /stm32/leds.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "hw.h" 7 | 8 | #if defined(BRD_LED_TIM) 9 | 10 | #if BRD_LED_TIM == 2 11 | #define TIMx TIM2 12 | #define TIMx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } while (0) 13 | #define TIMx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN; } while (0) 14 | #define TIMx_IRQn TIM2_IRQn 15 | #else 16 | #error "Unsupported timer" 17 | #endif 18 | 19 | static struct { 20 | unsigned int state; 21 | struct { 22 | int step; 23 | unsigned int n; 24 | unsigned int delay; 25 | unsigned int min; 26 | unsigned int max; 27 | } pulse[4]; 28 | } pwm; 29 | 30 | #endif 31 | 32 | void leds_init (void) { 33 | #if defined(BRD_LED_TIM) 34 | TIMx_enable(); 35 | TIMx->PSC = 4; 36 | TIMx->ARR = 0xffff; 37 | TIMx->CCMR1 = 38 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE | 39 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE; 40 | TIMx->CCMR2 = 41 | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE | 42 | TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE; 43 | TIMx_disable(); 44 | #endif 45 | } 46 | 47 | #if defined(BRD_LED_TIM) 48 | static void pwm_set_gpio (unsigned int gpio, bool enable, bool pulse, unsigned int ccr) { 49 | unsigned int ch = BRD_GPIO_GET_CHAN(gpio) - 1; 50 | ASSERT(ch < 4); 51 | 52 | unsigned int state0 = pwm.state; 53 | unsigned int state1 = state0; 54 | 55 | if (enable) { 56 | state1 |= (0x01 << ch); 57 | if (pulse) { 58 | state1 |= (0x10 << ch); 59 | } 60 | } else { 61 | state1 &= ~(0x11 << ch); 62 | } 63 | 64 | if (state0 == state1) { 65 | return; 66 | } 67 | 68 | hal_disableIRQs(); 69 | 70 | if (state1) { 71 | if (state0 == 0) { 72 | TIMx_enable(); // enable peripheral clock 73 | hal_setMaxSleep(HAL_SLEEP_S0); // disable sleep (keep clock at full speed) 74 | TIMx->CR1 |= TIM_CR1_CEN; // enable timer peripheral 75 | TIMx->EGR |= TIM_EGR_UG; // start pwm 76 | } 77 | if (state1 & 0xf0) { 78 | if ((state0 & 0xf0) == 0) { 79 | TIMx->DIER |= TIM_DIER_UIE; // enable update interrupt 80 | NVIC_EnableIRQ(TIMx_IRQn); // enable interrupt in NVIC 81 | } 82 | } else { 83 | if ((state0 & 0xf0) == 0) { 84 | TIMx->DIER &= ~TIM_DIER_UIE; // disable update interrupt 85 | NVIC_DisableIRQ(TIMx_IRQn); // disable interrupt in NVIC 86 | } 87 | } 88 | } else if (state0) { 89 | TIMx->CR1 &= ~TIM_CR1_CEN; // disable timer 90 | TIMx->DIER &= ~TIM_DIER_UIE; // disable update interrupt 91 | TIMx_disable(); // disable peripheral clock 92 | hal_clearMaxSleep(HAL_SLEEP_S0); // re-enable sleep 93 | NVIC_DisableIRQ(TIMx_IRQn); // disable interrupt in NVIC 94 | } 95 | 96 | if (enable) { 97 | *((&(TIMx->CCR1)) + ch) = ccr; // set initial CCR value 98 | if ((state0 & (1 << ch)) == 0) { 99 | unsigned int ccer = TIM_CCER_CC1E; 100 | if (gpio & BRD_GPIO_ACTIVE_LOW) { 101 | ccer |= TIM_CCER_CC1P; 102 | } 103 | TIMx->CCER |= (ccer << (4 * ch)); // enable channel 104 | CFG_PIN_AF(gpio, GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); 105 | } 106 | } 107 | pwm.state = state1; 108 | 109 | hal_enableIRQs(); 110 | } 111 | #endif 112 | 113 | void leds_pwm (unsigned int gpio, int dc) { 114 | #if defined(BRD_LED_TIM) 115 | pwm_set_gpio(gpio, true, false, dc); 116 | #endif 117 | } 118 | 119 | void leds_pulse (unsigned int gpio, unsigned int min, unsigned int max, int step, unsigned int delay) { 120 | #if defined(BRD_LED_TIM) 121 | unsigned int ch = BRD_GPIO_GET_CHAN(gpio) - 1; 122 | ASSERT(ch < 4); 123 | pwm.pulse[ch].n = 0; 124 | pwm.pulse[ch].min = min; 125 | pwm.pulse[ch].max = max; 126 | pwm.pulse[ch].step = step; 127 | pwm.pulse[ch].delay = delay; 128 | pwm_set_gpio(gpio, true, true, (step < 0) ? max : min); 129 | #endif 130 | } 131 | 132 | void leds_set (unsigned int gpio, int state) { 133 | if (state) { 134 | if (gpio & BRD_GPIO_ACTIVE_LOW) { 135 | SET_PIN(gpio, 0); 136 | } else { 137 | SET_PIN(gpio, 1); 138 | } 139 | CFG_PIN(gpio, GPIOCFG_MODE_OUT | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); 140 | } else { 141 | CFG_PIN(gpio, GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE); 142 | } 143 | #if defined(BRD_LED_TIM) 144 | pwm_set_gpio(gpio, false, false, 0); 145 | #endif 146 | } 147 | 148 | #if defined(BRD_LED_TIM) 149 | void leds_pwm_irq (void) { 150 | if (TIMx->SR & TIM_SR_UIF) { // update event 151 | TIMx->SR = ~TIM_SR_UIF; // clear flag 152 | unsigned int ps = pwm.state & 0x0f; 153 | while (ps) { 154 | unsigned int ch = __builtin_ctz(ps); 155 | if (pwm.pulse[ch].step) { 156 | if (pwm.pulse[ch].n < pwm.pulse[ch].delay) { 157 | pwm.pulse[ch].n += 1; 158 | } else { 159 | pwm.pulse[ch].n = 0; 160 | int ccr = *((&(TIMx->CCR1)) + ch); 161 | ccr += pwm.pulse[ch].step; 162 | if (ccr <= pwm.pulse[ch].min) { 163 | ccr = pwm.pulse[ch].min; 164 | pwm.pulse[ch].step = -pwm.pulse[ch].step; 165 | } else if (ccr >= pwm.pulse[ch].max) { 166 | ccr = pwm.pulse[ch].max; 167 | pwm.pulse[ch].step = -pwm.pulse[ch].step; 168 | } 169 | *((&(TIMx->CCR1)) + ch) = ccr; 170 | } 171 | } 172 | ps &= ~(1 << ch); 173 | } 174 | } 175 | } 176 | #endif 177 | -------------------------------------------------------------------------------- /stm32/persodata.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "lmic.h" 7 | #include "peripherals.h" 8 | 9 | #define PERSODATA_MAGIC_V1 0xb2dc4db2 /* openssl rand */ 10 | 11 | typedef struct { 12 | uint32_t magic; // 0x00 magic 13 | uint32_t hwid; // 0x04 hardware ID 14 | uint32_t region; // 0x08 region ID 15 | uint32_t reserved; // 0x0c (reserved, set to 0) 16 | uint8_t serial[16]; // 0x10 production serial number 17 | uint8_t deveui[8]; // 0x20 device EUI 18 | uint8_t joineui[8]; // 0x28 join EUI 19 | uint8_t nwkkey[16]; // 0x30 network key 20 | uint8_t appkey[16]; // 0x40 application key 21 | uint32_t hash[8]; // 0x50 hash 22 | } persodata_v1; 23 | 24 | _Static_assert(sizeof(persodata_v1) <= PERSODATA_SZ, "persodata struct too large"); 25 | 26 | persodata_v1 pd; 27 | 28 | static persodata_v1* pd_check_v1 (void* ptr) { 29 | persodata_v1* ppd = ptr; 30 | if( ppd->magic == PERSODATA_MAGIC_V1 ) { 31 | uint32_t hash[8]; 32 | sha256(hash, ptr, sizeof(persodata_v1) - 32); 33 | if( memcmp(hash, ppd->hash, 32) != 0 ) { 34 | return NULL; 35 | } 36 | return ppd; 37 | } 38 | return NULL; 39 | } 40 | 41 | void pd_init (void) { 42 | persodata_v1* ppd = pd_check_v1((void*) PERSODATA_BASE); 43 | if( ppd ) { 44 | pd = *ppd; 45 | } else { // TODO - check TrackNet legacy 46 | // fill defaults 47 | uint64_t eui; 48 | 49 | eui = 0xffffffaa00000000ULL | hal_unique(); 50 | memcpy(pd.deveui, &eui, 8); 51 | eui = 0xffffffbb00000000ULL; 52 | memcpy(pd.joineui, &eui, 8); 53 | memcpy(pd.nwkkey, "@ABCDEFGHIJKLMNO", 16); 54 | memcpy(pd.appkey, "`abcdefghijklmno", 16); 55 | } 56 | } 57 | 58 | // private API to verify EEPROM data was valid 59 | bool pd_verify (void) { 60 | persodata_v1* ppd = (void*) PERSODATA_BASE; 61 | if( ppd->magic == PERSODATA_MAGIC_V1 ) { 62 | uint32_t hash[8]; 63 | sha256(hash, (uint8_t*) ppd, sizeof(persodata_v1) - 32); 64 | if( memcmp(hash, ppd->hash, 32) == 0 ) { 65 | return true; 66 | } 67 | } 68 | return false; 69 | } 70 | 71 | u1_t* hal_joineui (void) { 72 | return pd.joineui; 73 | } 74 | 75 | u1_t* hal_deveui (void) { 76 | return pd.deveui; 77 | } 78 | 79 | u1_t* hal_nwkkey (void) { 80 | return pd.nwkkey; 81 | } 82 | 83 | u1_t* hal_appkey (void) { 84 | return pd.appkey; 85 | } 86 | 87 | u1_t* hal_serial (void) { 88 | return pd.serial; 89 | } 90 | 91 | u4_t hal_region (void) { 92 | return pd.region; 93 | } 94 | 95 | u4_t hal_hwid (void) { 96 | return pd.hwid; 97 | } 98 | 99 | #ifdef CFG_eeprom_keys 100 | // provide region code 101 | u1_t os_getRegion (void) { 102 | return hal_region(); 103 | } 104 | 105 | // provide device ID (8 bytes, LSBF) 106 | void os_getDevEui (u1_t* buf) { 107 | memcpy(buf, hal_deveui(), 8); 108 | } 109 | 110 | // provide join ID (8 bytes, LSBF) 111 | void os_getJoinEui (u1_t* buf) { 112 | memcpy(buf, hal_joineui(), 8); 113 | } 114 | 115 | // provide device network key (16 bytes) 116 | void os_getNwkKey (u1_t* buf) { 117 | memcpy(buf, hal_nwkkey(), 16); 118 | } 119 | 120 | // provide device application key (16 bytes) 121 | void os_getAppKey (u1_t* buf) { 122 | memcpy(buf, hal_appkey(), 16); 123 | } 124 | #endif 125 | -------------------------------------------------------------------------------- /stm32/sleep.S: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | //#include "bootloader.h" 7 | 8 | #define LPTIM_ISR 0x00 9 | #define LPTIM_ICR 0x04 10 | 11 | #define LPTIM_ARRM_BIT 1 12 | 13 | #define LPTIM1_IRQn_BIT 13 14 | 15 | #define IWDG_KR 0 16 | 17 | #define MASK(n) (1 << (n ## _BIT)) // bit mask 18 | 19 | 20 | // -------------------------------------------- 21 | // assembler settings 22 | .syntax unified 23 | .thumb 24 | 25 | 26 | // -------------------------------------------- 27 | // u4_t sleep_htt (u4_t hticks, u4_t htt); 28 | // 29 | //.section .fastcode.tim22_sync,"ax",%progbits 30 | .section .fastcode.sleep_htt,"ax",%progbits 31 | .thumb_func 32 | sleep_htt: 33 | #if CFG_watchdog 34 | #define REGLIST r4-r7 35 | #else 36 | #define REGLIST r4-r6 37 | #endif 38 | push {REGLIST,lr} 39 | 40 | // r0 - hticks 41 | // r1 - htt 42 | // r2 - LPTIM1 base 43 | ldr r2, .L_LPTIM1_BASE 44 | // r3 - ARRM mask 45 | movs r3, #MASK(LPTIM_ARRM) 46 | // r4 - NVIC ICPR 47 | ldr r4, .L_NVIC_ICPR 48 | // r5 - LPTIM1_IRQn mask 49 | movs r5, #1 50 | lsls r5, #LPTIM1_IRQn_BIT 51 | // r6 - scratch 52 | #if CFG_watchdog 53 | // r7 - IWDG base 54 | ldr r7, .L_IWDG_BASE 55 | #endif 56 | 57 | b 3f 58 | 59 | // zzz 60 | 1: wfi 61 | 62 | // read and check ISR 63 | ldr r6, [r2, #LPTIM_ISR] 64 | tst r6, r3 65 | beq 4f 66 | 67 | // clear ISR 68 | str r3, [r2, #LPTIM_ICR] 69 | 2: ldr r6, [r2, #LPTIM_ISR] 70 | tst r6, r3 71 | bne 2b 72 | 73 | // clear NVIC 74 | str r5, [r4, #0] 75 | 76 | // increment hticks 77 | adds r0, #1 78 | 79 | #if CFG_watchdog 80 | // refresh watchdog 81 | ldr r6, .L_IWDG_REFRESH 82 | str r6, [r7, #IWDG_KR] 83 | #endif 84 | 85 | // compare hticks to htt 86 | 3: cmp r0, r1 87 | bmi 1b 88 | 89 | 4: pop {REGLIST,pc} 90 | 91 | .p2align(2) 92 | .L_LPTIM1_BASE: 93 | .word 0x40007c00 94 | .L_NVIC_ICPR: 95 | .word 0xE000E100+0x180 96 | #if CFG_watchdog 97 | .L_IWDG_BASE: 98 | .word 0x40003000 99 | .L_IWDG_REFRESH: 100 | .word 0xAAAA 101 | #endif 102 | #undef REGLIST 103 | 104 | .size sleep_htt, .-sleep_htt 105 | .global sleep_htt 106 | -------------------------------------------------------------------------------- /stm32/startup.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "lmic.h" 7 | #include "hw.h" 8 | 9 | #if defined(STM32L0) 10 | #define MAX_IRQn 32 /* see PM0223, 2.3.4, pg. 29 */ 11 | #elif defined(STM32L1) 12 | #define MAX_IRQn 68 /* see PM0056, 2.3.4, pg. 36 */ 13 | #else 14 | #error "Unsupported MCU" 15 | #endif 16 | 17 | __attribute__((aligned(512))) 18 | static uint32_t irqvector[16 + MAX_IRQn]; 19 | 20 | void _start (boot_boottab* boottab) { 21 | // symbols provided by linker script 22 | extern uint32_t _sidata, _sdata, _edata, _sbss, _ebss; 23 | 24 | // initialize data 25 | uint32_t* src = &_sidata; 26 | uint32_t* dst = &_sdata; 27 | while( dst < &_edata ) { 28 | *dst++ = *src++; 29 | } 30 | 31 | // initialize bss 32 | dst = &_sbss; 33 | while( dst < &_ebss ) { 34 | *dst++ = 0; 35 | } 36 | 37 | // copy current Cortex M IRQ + NVIC vector to RAM 38 | src = (uint32_t*) 0; 39 | dst = irqvector; 40 | for( int i = 0; i < (16 + MAX_IRQn); i++ ) { 41 | *dst++ = *src++; 42 | } 43 | // fix-up NVIC vector with handlers from firmware 44 | for( const irqdef* id = HAL_irqdefs; id->handler; id++ ) { 45 | irqvector[16 + id->num] = (uint32_t) id->handler; 46 | } 47 | // re-map interrupt vector 48 | SCB->VTOR = (uint32_t) irqvector; 49 | 50 | // call main function 51 | extern void main (boot_boottab* boottab); 52 | main(boottab); 53 | } 54 | 55 | // Firmware header 56 | __attribute__((section(".fwhdr"))) 57 | const volatile hal_fwhdr fwhdr = { 58 | // CRC and size will be patched by external tool 59 | .boot.crc = 0, 60 | .boot.size = BOOT_MAGIC_SIZE, 61 | .boot.entrypoint = (uint32_t) _start, 62 | 63 | .version = 0, 64 | }; 65 | -------------------------------------------------------------------------------- /stm32/trng.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "peripherals.h" 7 | 8 | #if defined(STM32L072xx) 9 | 10 | void trng_next (uint32_t* dest, int count) { 11 | RCC->AHBENR |= RCC_AHBENR_RNGEN; 12 | RNG->CR |= RNG_CR_RNGEN; 13 | while( count-- > 0 ) { 14 | while( (RNG->SR & RNG_SR_DRDY) == 0 ); 15 | *dest++ = RNG->DR; 16 | } 17 | RNG->CR &= ~RNG_CR_RNGEN; 18 | RCC->AHBENR &= ~RCC_AHBENR_RNGEN; 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /stm32/usart.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "peripherals.h" 7 | 8 | #ifdef BRD_USART 9 | 10 | #if BRD_USART == 1 11 | #define USARTx USART1 12 | #define USARTx_enable() do { RCC->APB2ENR |= RCC_APB2ENR_USART1EN; } while (0) 13 | #define USARTx_disable() do { RCC->APB2ENR &= ~RCC_APB2ENR_USART1EN; } while (0) 14 | #define USARTx_IRQn USART1_IRQn 15 | #elif BRD_USART == BRD_LPUART(1) 16 | #define USARTx LPUART1 17 | #define USARTx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_LPUART1EN; } while (0) 18 | #define USARTx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_LPUART1EN; } while (0) 19 | #define USARTx_IRQn LPUART1_IRQn 20 | #endif 21 | 22 | enum { 23 | RX_ON = (1 << 0), 24 | TX_ON = (1 << 1), 25 | }; 26 | 27 | static struct { 28 | unsigned int on; 29 | 30 | unsigned int br; 31 | 32 | usart_rx_func rx; 33 | void* rxarg; 34 | 35 | usart_tx_func tx; 36 | void* txarg; 37 | } usart; 38 | 39 | static void usart_on (unsigned int flag) { 40 | hal_disableIRQs(); 41 | if (usart.on == 0) { 42 | // disable sleep (keep clock at full speed during transfer 43 | hal_setMaxSleep(HAL_SLEEP_S0); 44 | // enable peripheral clock 45 | USARTx_enable(); 46 | // set baudrate 47 | USARTx->BRR = usart.br; 48 | // usart enable 49 | USARTx->CR1 = USART_CR1_UE; 50 | // enable interrupts in NVIC 51 | NVIC_EnableIRQ(USARTx_IRQn); 52 | } 53 | usart.on |= flag; 54 | hal_enableIRQs(); 55 | } 56 | 57 | static void usart_off (unsigned int flag) { 58 | hal_disableIRQs(); 59 | usart.on &= ~flag; 60 | if (usart.on == 0) { 61 | // disable USART 62 | USARTx->CR1 = 0; 63 | // disable peripheral clock 64 | USARTx_disable(); 65 | // disable interrupts in NVIC 66 | NVIC_DisableIRQ(USARTx_IRQn); 67 | // re-enable sleep 68 | hal_clearMaxSleep(HAL_SLEEP_S0); 69 | } 70 | hal_enableIRQs(); 71 | } 72 | 73 | static void rx_on (unsigned int noirq) { 74 | // turn on usart 75 | usart_on(RX_ON); 76 | // enable receiver 77 | USARTx->CR1 |= USART_CR1_RE; 78 | // setup I/O line 79 | CFG_PIN_AF(GPIO_USART_RX, GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); 80 | if (noirq == 0) { 81 | // flush data, clear ORE and enable receive interrupt 82 | USARTx->RQR |= USART_RQR_RXFRQ; 83 | USARTx->ICR |= USART_ISR_ORE; 84 | USARTx->CR1 |= USART_CR1_RXNEIE; 85 | } 86 | } 87 | 88 | static void rx_off (void) { 89 | // deconfigure I/O line 90 | CFG_PIN_DEFAULT(GPIO_USART_RX); 91 | // disable receiver and interrupts 92 | USARTx->CR1 &= ~(USART_CR1_RE | USART_CR1_RXNEIE); 93 | // turn off usart 94 | usart_off(RX_ON); 95 | } 96 | 97 | static void tx_on (void) { 98 | // turn on usart 99 | usart_on(TX_ON); 100 | // enable transmitter 101 | USARTx->CR1 |= USART_CR1_TE; 102 | // setup I/O line 103 | CFG_PIN_AF(GPIO_USART_TX, GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); 104 | // enable interrupt 105 | USARTx->CR1 |= USART_CR1_TXEIE; 106 | } 107 | 108 | static void tx_off (void) { 109 | // deconfigure I/O line, activate pullup 110 | CFG_PIN(GPIO_USART_TX, GPIOCFG_MODE_INP | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_PUP); 111 | // disable receiver and interrupts 112 | USARTx->CR1 &= ~(USART_CR1_TE | USART_CR1_TXEIE); 113 | // turn off usart 114 | usart_off(TX_ON); 115 | } 116 | 117 | void usart_init (void) { 118 | usart.on = 0; 119 | // activate pullup on tx line 120 | CFG_PIN(GPIO_USART_TX, GPIOCFG_MODE_INP | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_PUP); 121 | } 122 | 123 | void usart_cfg (unsigned int br) { 124 | usart.br = br; 125 | } 126 | 127 | void usart_recv (usart_rx_func rx, void* arg) { 128 | usart.rx = rx; 129 | usart.rxarg = arg; 130 | rx_on(0); 131 | } 132 | 133 | void usart_abort_recv (void) { 134 | hal_disableIRQs(); 135 | if (usart.on & RX_ON) { 136 | rx_off(); 137 | usart.rx(USART_ERROR, usart.rxarg); 138 | } 139 | hal_enableIRQs(); 140 | } 141 | 142 | void usart_send (usart_tx_func tx, void* arg) { 143 | usart.tx = tx; 144 | usart.txarg = arg; 145 | tx_on(); 146 | } 147 | 148 | void usart_irq (void) { 149 | unsigned int isr = USARTx->ISR; 150 | unsigned int cr1 = USARTx->CR1; 151 | if (cr1 & USART_CR1_RXNEIE) { 152 | if (isr & USART_ISR_ORE) { 153 | USARTx->ICR |= USART_ISR_ORE; 154 | rx_off(); 155 | usart.rx(USART_ERROR, usart.rxarg); 156 | } else if (isr & USART_ISR_RXNE) { 157 | if (usart.rx(USARTx->RDR, usart.rxarg) != USART_CONTINUE) { // done 158 | rx_off(); 159 | } 160 | } 161 | } 162 | if ((cr1 & USART_CR1_TXEIE) && (isr & USART_ISR_TXE)) { 163 | int ch; 164 | if ((ch = usart.tx(USART_CONTINUE, usart.txarg)) < 0) { // done 165 | unsigned int cr1 = USARTx->CR1; 166 | cr1 = (cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE; 167 | USARTx->CR1 = cr1; 168 | } else { 169 | USARTx->TDR = ch; 170 | } 171 | } 172 | if ((cr1 & USART_CR1_TCIE) && (isr & USART_ISR_TC)) { 173 | USARTx->CR1 &= ~USART_CR1_TCIE; 174 | tx_off(); 175 | usart.tx(USART_DONE, usart.txarg); 176 | } 177 | } 178 | 179 | int usart_wait_silence (int silence_ticks, int timeout_ticks) { 180 | ostime_t deadline = os_getTime() + timeout_ticks; 181 | ostime_t threshold = os_getTime() + silence_ticks; 182 | int retval = 0; 183 | rx_on(1); 184 | while (1) { 185 | if (USARTx->ISR & USART_ISR_BUSY) { 186 | threshold = os_getTime() + silence_ticks; 187 | } 188 | if ((deadline - os_getTime()) < 0) { 189 | retval = -1; 190 | break; 191 | } 192 | if ((threshold - os_getTime()) < 0) { 193 | retval = 0; 194 | break; 195 | } 196 | } 197 | rx_off(); 198 | return retval; 199 | } 200 | 201 | #endif 202 | -------------------------------------------------------------------------------- /tools/arch.gmk: -------------------------------------------------------------------------------- 1 | ARCH := $(shell gcc -dumpmachine) 2 | 3 | ifeq (,$(ARCH)) 4 | $(error Could not determine build platform architecture) 5 | endif 6 | 7 | 8 | # vim: filetype=make 9 | -------------------------------------------------------------------------------- /tools/openocd/flash.cfg: -------------------------------------------------------------------------------- 1 | proc flash_binary { filename } { 2 | init 3 | reset init 4 | halt 5 | flash write_image erase unlock $filename 0x08000000 6 | sleep 100 7 | reset run 8 | shutdown 9 | } 10 | 11 | proc flash_fw { firmware } { 12 | init 13 | reset init 14 | halt 15 | flash write_image erase unlock $firmware 0x08003000 16 | sleep 100 17 | reset run 18 | shutdown 19 | } 20 | 21 | proc flash_up { update } { 22 | set size [file size $update] 23 | set addr [expr [expr 0x08020000 - $size] & 0xFFFFFF80] 24 | init 25 | reset init 26 | halt 27 | flash write_image erase unlock $update $addr 28 | puts "update start address: 0x[format %08X $addr]" 29 | puts "gdb: call set_update(0x[format %08X $addr])" 30 | sleep 100 31 | reset run 32 | shutdown 33 | } 34 | 35 | proc flash_ihex { ihex } { 36 | init 37 | reset init 38 | halt 39 | flash write_image erase unlock $ihex 40 | sleep 100 41 | reset run 42 | shutdown 43 | } 44 | 45 | proc flash_info { } { 46 | init 47 | reset init 48 | halt 49 | flash info 0 50 | shutdown 51 | } 52 | 53 | proc restart_target { } { 54 | init 55 | reset init 56 | halt 57 | sleep 10 58 | reset run 59 | shutdown 60 | } 61 | 62 | proc memdump { addr len } { 63 | init 64 | reset init 65 | halt 66 | mdb $addr $len 67 | #mem2array m 8 $addr $len 68 | shutdown 69 | } 70 | 71 | proc option_bytes { } { 72 | init 73 | reset init 74 | halt 75 | read_option_bytes 0 76 | shutdown 77 | } 78 | 79 | proc flash_lock { } { 80 | init 81 | reset init 82 | halt 83 | stm32lx lock 0 84 | shutdown 85 | } 86 | 87 | proc flash_unlock { } { 88 | init 89 | reset init 90 | halt 91 | stm32lx unlock 0 92 | shutdown 93 | } 94 | -------------------------------------------------------------------------------- /tools/openocd/nucleo-l0.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/stlink-v2-1.cfg] 2 | transport select hla_swd 3 | source [find target/stm32l0.cfg] 4 | reset_config srst_only srst_nogate connect_assert_srst 5 | gdb_memory_map disable 6 | -------------------------------------------------------------------------------- /tools/openocd/stlink-rules.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorabasics/basicmac/2c2678eab23bf2d13b659611ff9cd4ad57ef2f6b/tools/openocd/stlink-rules.tgz -------------------------------------------------------------------------------- /tools/pylora/loracrypto.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | import struct 7 | 8 | from Crypto.Hash import CMAC 9 | from Crypto.Cipher import AES 10 | 11 | class crypto: 12 | @staticmethod 13 | def encrypt (devkey:bytes, pdu:bytes) -> bytes: 14 | aes = AES.new(devkey, AES.MODE_ECB) 15 | return aes.encrypt(pdu) 16 | 17 | @staticmethod 18 | def decrypt (devkey:bytes, pdu:bytes) -> bytes: 19 | aes = AES.new(devkey, AES.MODE_ECB) 20 | return aes.decrypt(pdu) 21 | 22 | @staticmethod 23 | def calcMicJoin (key:bytes, pdu:bytes) -> int: 24 | pdu = pdu[:-4] 25 | cmac = CMAC.new(key, ciphermod=AES) 26 | cmac.update(pdu) 27 | mic, = struct.unpack_from(' int: 32 | pdu = pdu[:-4] 33 | b0 = struct.pack(' bytes: 42 | a0 = struct.pack(' bytes: 48 | d = (bytes([keytype]) 49 | + struct.pack(' str: 12 | if f==0: return '0' 13 | s = str(int(f)) 14 | l = len(s) 15 | m = re.search(r'0*$', s) 16 | z = len(m.group(0) if m else '') 17 | g = (l-1)//3 18 | u = ['Hz','kHz','MHz','GHz'][g] 19 | a = g*3 20 | if z < a: 21 | return s[:l-a]+'.'+s[l-a:l-z]+u 22 | return s[:l-a]+u 23 | -------------------------------------------------------------------------------- /tools/pylora/rtlib/eui.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | import functools 7 | import re 8 | import struct 9 | from base64 import b16decode as _b16decode 10 | 11 | 12 | __all__ = ['Eui'] 13 | 14 | 15 | @functools.total_ordering 16 | class Eui(object): 17 | """Class to represent EUIs. The normal representation is an string with 23 characters 18 | and a format of HH-...-HH. This matches the JSON and SQL representation. 19 | There are methods to convert to binary string or 64bit integer. 20 | """ 21 | 22 | HEX_REGEX = re.compile("^[0-9A-Fa-f]{16}$") 23 | """Pure hexadecimal.""" 24 | NUM_REGEX = re.compile("^(\-?\d+|0[xX][0-9A-Fa-f]+)$") 25 | """Standard decimal or hexadecimal number""" 26 | REGEX = re.compile("^([0-9a-fA-F]){2}(-([0-9a-fA-F]){2}){7}$") 27 | """Regular expression matching a well formed EUI""" 28 | REGEX2 = re.compile("^([0-9a-fA-F]){2}(:([0-9a-fA-F]){2}){7}$") 29 | """Regular expression matching a well formed EUI using colon instead of dashes""" 30 | 31 | 32 | @staticmethod 33 | def int2str (num:int) -> str: 34 | return "-".join(["{0:02X}".format((num>>(i*8)&0xFF)) for i in range(7,-1,-1)]) 35 | 36 | @staticmethod 37 | def str2int (s:str) -> int: 38 | if Eui.HEX_REGEX.match(s): 39 | return int(s,16) 40 | if Eui.NUM_REGEX.match(s): 41 | return int(s,0) 42 | if Eui.REGEX.match(s): 43 | b = _b16decode(s.replace("-","").encode('ascii')) 44 | elif Eui.REGEX2.match(s): 45 | b = _b16decode(s.replace(":","").encode('ascii')) 46 | else: 47 | raise ValueError("Illegal Eui: {}".format(s)) 48 | return struct.unpack_from('>q',b)[0] 49 | 50 | @staticmethod 51 | def from_str(euistr:str) -> 'Eui': 52 | return Eui(euistr) 53 | 54 | @staticmethod 55 | def from_int(euinum:int) -> 'Eui': 56 | return Eui(euinum) 57 | 58 | @staticmethod 59 | def from_bytes(euibytes:bytes) -> 'Eui': 60 | return Eui(euibytes) 61 | 62 | def __bool__(self) -> bool: 63 | # Bool value is not dependent on EUI's int value 64 | return True 65 | 66 | def __index__(self) -> int: 67 | return struct.unpack_from('>q',self.as_bytes(),0)[0] 68 | 69 | def as_int(self) -> int: 70 | return self.__index__() 71 | 72 | def as_bytes(self) -> bytes: 73 | return _b16decode(self.euistr.replace("-","").encode('ascii')) 74 | 75 | def __str__(self): 76 | return self.euistr 77 | 78 | def __repr__(self) -> str: 79 | return "<{}: {}>".format(self.__class__.__name__, str(self)) 80 | 81 | def __hash__(self): 82 | return hash(self.euistr) 83 | 84 | def __lt__(self, other): 85 | return self.euistr < other.euistr 86 | 87 | def __eq__(self, other): 88 | return True if isinstance(other,Eui) and self.euistr==other.euistr else False 89 | 90 | def __init__(self, euispec) -> None: 91 | if isinstance(euispec,Eui): 92 | self.euistr = euispec.euistr # type: str 93 | elif isinstance(euispec,str): 94 | if Eui.HEX_REGEX.match(euispec): 95 | self.euistr = Eui.int2str(int(euispec,16)) 96 | elif Eui.NUM_REGEX.match(euispec): 97 | self.euistr = Eui.int2str(int(euispec,0)) 98 | else: 99 | if Eui.REGEX.match(euispec): 100 | self.euistr = euispec.upper() 101 | elif Eui.REGEX2.match(euispec): 102 | self.euistr = euispec.replace(':','-').upper() 103 | else: 104 | raise ValueError("Illegal EUI: {}".format(euispec)) 105 | elif isinstance(euispec,bytes): 106 | if len(euispec)!=8: 107 | raise ValueError("Illegal EUI length: {}".format(len(euispec))) 108 | self.euistr = "-".join([ "{:02X}".format(euispec[i]) for i in range(8) ]) 109 | elif isinstance(euispec,int): 110 | self.euistr = Eui.int2str(euispec) 111 | else: 112 | raise ValueError("Illegal EUI type: "+str(type(euispec))) 113 | 114 | 115 | -------------------------------------------------------------------------------- /tools/pylora/rtlib/types.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | from typing import Any,Mapping,MutableMapping 7 | 8 | __all__ = ['Conf', 'Msg', 'State' ] 9 | 10 | Conf = Mapping[str,Any] 11 | Msg = MutableMapping[str,Any] 12 | State = MutableMapping[str,Any] 13 | -------------------------------------------------------------------------------- /tools/svctool/cc.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | import argparse 7 | 8 | from typing import Any,Callable,List,Optional,Union 9 | from argparse import Namespace as NS, ArgumentParser as AP # type aliases 10 | 11 | class CommandCollection: 12 | parser = argparse.ArgumentParser() 13 | subs = parser.add_subparsers(dest='command') 14 | subs.required = True # type: ignore 15 | sub = None 16 | 17 | @staticmethod 18 | def cmd(name:Optional[str]=None, **kwargs:Any) -> Callable: 19 | def cmd_decorator(f:Callable) -> Callable: 20 | p = CommandCollection.subs.add_parser(name if name else f.__name__, **kwargs) 21 | def cmd_do(obj:Any, args:NS) -> None: 22 | CommandCollection.sub = p 23 | f(obj, args) 24 | p.set_defaults(func=cmd_do) 25 | cmd_do.parser = p # type: ignore 26 | return cmd_do 27 | return cmd_decorator 28 | 29 | @staticmethod 30 | def arg(*args:Any, **kwargs:Any) -> Callable: 31 | def arg_decorator(f:Callable) -> Callable: 32 | p:AP = f.parser # type: ignore 33 | p.add_argument(*args, **kwargs) 34 | return f 35 | return arg_decorator 36 | 37 | @staticmethod 38 | def argf(af:Callable, **kwargs:Any) -> Callable: 39 | def arg_decorator(f:Callable) -> Callable: 40 | p:AP = f.parser # type: ignore 41 | af(p, **kwargs) 42 | return f 43 | return arg_decorator 44 | 45 | @staticmethod 46 | def run(obj:Any) -> None: 47 | args = CommandCollection.parser.parse_args() 48 | args.func(obj, args) 49 | 50 | @staticmethod 51 | def error(message:str) -> None: 52 | (CommandCollection.sub or CommandCollection.parser).error(message) 53 | -------------------------------------------------------------------------------- /unicorn/board.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | // This page intentionally left blank. 18 | -------------------------------------------------------------------------------- /unicorn/fw.ld: -------------------------------------------------------------------------------- 1 | SECTIONS { 2 | .text : { 3 | . = ALIGN(4); 4 | KEEP(*(.fwhdr)) 5 | . = ALIGN(4); 6 | *(.text) 7 | *(.text*) 8 | . = ALIGN(4); 9 | } >FWFLASH 10 | 11 | .data : { 12 | . = ALIGN(4); 13 | _sdata = .; 14 | *(.data) 15 | *(.data*) 16 | . = ALIGN(4); 17 | _edata = .; 18 | } >RAM AT>FWFLASH 19 | 20 | _sidata = LOADADDR(.data); 21 | 22 | .bss : { 23 | . = ALIGN(4); 24 | _sbss = .; 25 | *(.bss) 26 | *(.bss*) 27 | *(COMMON) 28 | . = ALIGN(4); 29 | _ebss = .; 30 | } >RAM 31 | 32 | .rodata : { 33 | . = ALIGN(4); 34 | *(.rodata) 35 | *(.rodata*) 36 | . = ALIGN(4); 37 | 38 | /* make sure flash image is a multiple of page size */ 39 | FILL(0x00000000) 40 | . = ALIGN(128); 41 | __fw_end__ = .; 42 | } >FWFLASH 43 | 44 | /DISCARD/ : { 45 | *(.ARM) 46 | *(.ARM*) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /unicorn/hal_unicorn.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _hal_unicorn_h_ 7 | #define _hal_unicorn_h_ 8 | 9 | #include "hw.h" 10 | #include "boottab.h" 11 | 12 | uint32_t pio_irq_get (void); 13 | void pio_irq_clear (uint32_t mask); 14 | void pio_irq_enable (unsigned int gpio, bool enable); 15 | void pio_irq_config (unsigned int pin, bool rising, bool falling); 16 | 17 | // Personalization data (persodata.c) 18 | void pd_init (void); 19 | bool pd_verify (void); 20 | 21 | #if defined(SVC_fuota) 22 | // Glue for FUOTA (fountain code) service 23 | 24 | #include "peripherals.h" 25 | 26 | #define fuota_flash_pagesz FLASH_PAGE_SZ 27 | #define fuota_flash_bitdefault 0 28 | 29 | #define fuota_flash_write(dst,src,nwords,erase) \ 30 | flash_write((uint32_t*) (dst), (uint32_t*) (src), nwords, erase) 31 | 32 | #define fuota_flash_read(dst,src,nwords) \ 33 | memcpy(dst, src, (nwords) << 2) 34 | 35 | #define fuota_flash_rd_u4(addr) \ 36 | (*((uint32_t*) (addr))) 37 | 38 | #define fuota_flash_rd_ptr(addr) \ 39 | (*((void**) (addr))) 40 | 41 | #endif 42 | 43 | // Firmware header -- do not modify (append only) 44 | typedef struct { 45 | boot_fwhdr boot; 46 | 47 | uint32_t version; 48 | } hal_fwhdr; 49 | 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /unicorn/hw.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #ifndef _hw_h_ 7 | #define _hw_h_ 8 | 9 | #define PERIPH_EEPROM 10 | 11 | #define EEPROM_BASE 0x30000000 12 | #define EEPROM_SZ (8 * 1024) 13 | #define EEPROM_END (EEPROM_BASE + EEPROM_SZ) 14 | 15 | // 0x0000-0x003f 64 B : reserved for bootloader 16 | // 0x0040-0x005f 32 B : reserved for persistent stack data 17 | // 0x0060-0x00ff 160 B : reserved for personalization data 18 | // 0x0100-...... : reserved for application 19 | 20 | #define STACKDATA_BASE (EEPROM_BASE + 0x0040) 21 | #define PERSODATA_BASE (EEPROM_BASE + 0x0060) 22 | #define APPDATA_BASE (EEPROM_BASE + 0x0100) 23 | 24 | #define STACKDATA_SZ (PERSODATA_BASE - STACKDATA_BASE) 25 | #define PERSODATA_SZ (APPDATA_BASE - PERSODATA_BASE) 26 | #define APPDATA_SZ (EEPROM_END - APPDATA_BASE) 27 | 28 | #define PERIPH_FLASH 29 | #define FLASH_BASE 0x20000000 30 | #define FLASH_SZ (128 * 1024) 31 | #define FLASH_END (FLASH_BASE + FLASH_SZ) 32 | #define FLASH_PAGE_SZ 128 33 | #define FLASH_PAGE_NW (FLASH_PAGE_SZ >> 2) 34 | 35 | #define PERIPH_USART 36 | #define USART_BR_9600 9600 37 | #define USART_BR_115200 115200 38 | 39 | #define PERIPH_PIO 40 | #define PIO_IRQ_LINE(gpio) (gpio) 41 | 42 | #define PERIPH_CRC 43 | #define PERIPH_SHA256 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /unicorn/persodata.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "lmic.h" 7 | #include "peripherals.h" 8 | 9 | #define PERSODATA_MAGIC_V1 0xb2dc4db2 /* openssl rand */ 10 | 11 | typedef struct { 12 | uint32_t magic; // 0x00 magic 13 | uint32_t hwid; // 0x04 hardware ID 14 | uint32_t region; // 0x08 region ID 15 | uint32_t reserved; // 0x0c (reserved, set to 0) 16 | uint8_t serial[16]; // 0x10 production serial number 17 | uint8_t deveui[8]; // 0x20 device EUI 18 | uint8_t joineui[8]; // 0x28 join EUI 19 | uint8_t nwkkey[16]; // 0x30 network key 20 | uint8_t appkey[16]; // 0x40 application key 21 | uint32_t hash[8]; // 0x50 hash 22 | } persodata_v1; 23 | 24 | _Static_assert(sizeof(persodata_v1) <= PERSODATA_SZ, "persodata struct too large"); 25 | 26 | persodata_v1 pd; 27 | 28 | static persodata_v1* pd_check_v1 (void* ptr) { 29 | persodata_v1* ppd = ptr; 30 | if( ppd->magic == PERSODATA_MAGIC_V1 ) { 31 | uint32_t hash[8]; 32 | sha256(hash, ptr, sizeof(persodata_v1) - 32); 33 | if( memcmp(hash, ppd->hash, 32) != 0 ) { 34 | return NULL; 35 | } 36 | return ppd; 37 | } 38 | return NULL; 39 | } 40 | 41 | void pd_init (void) { 42 | persodata_v1* ppd = pd_check_v1((void*) PERSODATA_BASE); 43 | if( ppd ) { 44 | pd = *ppd; 45 | } else { // TODO - check TrackNet legacy 46 | // fill defaults 47 | uint64_t eui; 48 | 49 | eui = 0xffffffaa00000000ULL | hal_unique(); 50 | memcpy(pd.deveui, &eui, 8); 51 | eui = 0xffffffbb00000000ULL; 52 | memcpy(pd.joineui, &eui, 8); 53 | memcpy(pd.nwkkey, "@ABCDEFGHIJKLMNO", 16); 54 | memcpy(pd.appkey, "`abcdefghijklmno", 16); 55 | } 56 | } 57 | 58 | // private API to verify EEPROM data was valid 59 | bool pd_verify (void) { 60 | persodata_v1* ppd = (void*) PERSODATA_BASE; 61 | if( ppd->magic == PERSODATA_MAGIC_V1 ) { 62 | uint32_t hash[8]; 63 | sha256(hash, (uint8_t*) ppd, sizeof(persodata_v1) - 32); 64 | if( memcmp(hash, ppd->hash, 32) == 0 ) { 65 | return true; 66 | } 67 | } 68 | return false; 69 | } 70 | 71 | u1_t* hal_joineui (void) { 72 | return pd.joineui; 73 | } 74 | 75 | u1_t* hal_deveui (void) { 76 | return pd.deveui; 77 | } 78 | 79 | u1_t* hal_nwkkey (void) { 80 | return pd.nwkkey; 81 | } 82 | 83 | u1_t* hal_appkey (void) { 84 | return pd.appkey; 85 | } 86 | 87 | u1_t* hal_serial (void) { 88 | return pd.serial; 89 | } 90 | 91 | u4_t hal_region (void) { 92 | return pd.region; 93 | } 94 | 95 | u4_t hal_hwid (void) { 96 | return pd.hwid; 97 | } 98 | 99 | #ifdef CFG_eeprom_keys 100 | // provide region code 101 | u1_t os_getRegion (void) { 102 | return hal_region(); 103 | } 104 | 105 | // provide device ID (8 bytes, LSBF) 106 | void os_getDevEui (u1_t* buf) { 107 | memcpy(buf, hal_deveui(), 8); 108 | } 109 | 110 | // provide join ID (8 bytes, LSBF) 111 | void os_getJoinEui (u1_t* buf) { 112 | memcpy(buf, hal_joineui(), 8); 113 | } 114 | 115 | // provide device network key (16 bytes) 116 | void os_getNwkKey (u1_t* buf) { 117 | memcpy(buf, hal_nwkkey(), 16); 118 | } 119 | 120 | // provide device application key (16 bytes) 121 | void os_getAppKey (u1_t* buf) { 122 | memcpy(buf, hal_appkey(), 16); 123 | } 124 | #endif 125 | -------------------------------------------------------------------------------- /unicorn/simul/fuotatest.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | from typing import Optional,Tuple 7 | 8 | import argparse 9 | import asyncio 10 | import struct 11 | import sys 12 | 13 | from devsimul import LoraMsg, Simulation 14 | from devtest import DeviceTest, LoRaWANTest 15 | from colorama import init as colorama_init 16 | from vtimeloop import VirtualTimeLoop 17 | 18 | class FWMan: 19 | PORT = 203 20 | PKG_ID = 4 21 | PKG_VERSION = 0x00 22 | DEV_VERSION = 0x01 23 | 24 | class Frag: 25 | PORT = 201 26 | PKG_ID = 3 27 | PKG_VERSION = 0x00 28 | FRAG_STATUS = 0x01 29 | FRAG_SESS_SETUP = 0x02 30 | FRAG_SESS_DEL = 0x03 31 | DATA_FRAGMENT = 0x08 32 | 33 | 34 | class FuotaTest(LoRaWANTest): 35 | @DeviceTest.test(reboot=False) 36 | async def join(self) -> bool: 37 | msg = await self.upmsg() 38 | self.process_join(msg) 39 | 40 | # expect any message 41 | msg = await self.upmsg() 42 | m = self.verify(msg) 43 | 44 | return True 45 | 46 | @DeviceTest.test(reboot=False) 47 | async def fwman_pkgversion(self) -> bool: 48 | msg = await self.upmsg() 49 | self.dl(msg, FWMan.PORT, struct.pack('B', FWMan.PKG_VERSION)) 50 | 51 | msg, m = await self.ul(FWMan.PORT) 52 | c, p, v = struct.unpack('BBB', m['FRMPayload']) 53 | assert c == FWMan.PKG_VERSION and p == FWMan.PKG_ID and v == 1 54 | 55 | return True 56 | 57 | @DeviceTest.test(reboot=False) 58 | async def fwman_devversion(self) -> bool: 59 | msg = await self.upmsg() 60 | self.dl(msg, FWMan.PORT, struct.pack('B', FWMan.DEV_VERSION)) 61 | 62 | msg, m = await self.ul(FWMan.PORT) 63 | c, f, h = struct.unpack(' bool: 71 | msg = await self.upmsg() 72 | self.dl(msg, Frag.PORT, struct.pack('B', Frag.PKG_VERSION)) 73 | 74 | msg, m = await self.ul(Frag.PORT) 75 | c, p, v = struct.unpack('BBB', m['FRMPayload']) 76 | assert c == Frag.PKG_VERSION and p == Frag.PKG_ID and v == 1 77 | 78 | return True 79 | 80 | 81 | if __name__ == '__main__': 82 | p = argparse.ArgumentParser() 83 | LoRaWANTest.stdargs(p) 84 | args = p.parse_args() 85 | 86 | colorama_init() 87 | 88 | if args.virtual_time: 89 | asyncio.set_event_loop(VirtualTimeLoop()) # type: ignore 90 | 91 | sim = Simulation(args.hexfiles, args.debug, args.traffic) 92 | test = FuotaTest(sim, args) 93 | 94 | if not asyncio.get_event_loop().run_until_complete(test.run()): 95 | sys.exit(1) 96 | -------------------------------------------------------------------------------- /unicorn/simul/peripherals.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | from typing import Optional,Set 7 | 8 | import asyncio 9 | import itertools 10 | import random 11 | import struct 12 | 13 | class PeripheralsUtil: 14 | @staticmethod 15 | def setbits(v:int, mask:int, on:bool=True) -> int: 16 | return v | mask if on else v & ~mask 17 | 18 | class InterruptController: 19 | def __init__(self, ev:'SimEvent') -> None: 20 | self.ev = ev 21 | self.irq = 0 22 | 23 | def set(self, line:int) -> None: 24 | self.irq |= (1 << line) 25 | if self.irq: 26 | self.ev.set('irq') 27 | 28 | def clear(self, line:int) -> None: 29 | self.irq &= ~(1 << line) 30 | if self.irq == 0: 31 | self.ev.clear('irq') 32 | 33 | def next(self) -> int: 34 | if self.irq: 35 | for i in itertools.count(): 36 | if (self.irq & (1 << i)): 37 | return i 38 | raise RuntimeError('No IRQ lines asserted') 39 | 40 | 41 | class GPIO: 42 | def __init__(self, ic:InterruptController, line:int) -> None: 43 | self.ic = ic 44 | self.line = line 45 | self.reset() 46 | self.watchers:Set[asyncio.Event] = set() 47 | 48 | def reset(self) -> None: 49 | # high low inp ana 50 | self.cfg1 = 0 # 1 1 0 0 51 | self.cfg2 = 0 # 1 0 1 0 52 | self.pup = 0 # pull-up 53 | self.pdn = 0 # pull-down 54 | self.rise = 0 # rising edge irq 55 | self.fall = 0 # falling edge irq 56 | 57 | self.inpm = 0 # connected input 58 | self.inpv = 0 # input value 59 | 60 | self.val = 0 # calculated value 61 | self.irq = 0 # pending irq 62 | 63 | self.epup = 0 # external pull-up 64 | self.epdn = 0 # external pull-down 65 | 66 | self.ic.clear(self.line) 67 | 68 | def read_irq(self) -> int: 69 | return self.irq 70 | 71 | def clear_irq(self, mask:int) -> None: 72 | self.irq &= ~mask 73 | if self.irq == 0: 74 | self.ic.clear(self.line) 75 | 76 | def read(self) -> int: 77 | return self.val 78 | 79 | def __getitem__(self, pio:int) -> bool: 80 | return bool(self.val & (1 << pio)) 81 | 82 | def config(self, config:bytes) -> None: 83 | (self.cfg1, self.cfg2, self.pup, self.pdn, 84 | self.rise, self.fall) = struct.unpack(' None: 88 | mask = (1 << pio) 89 | if value is None: 90 | self.inpm &= ~mask 91 | else: 92 | if value: 93 | self.inpv |= mask 94 | else: 95 | self.inpv &= ~mask 96 | self.inpm |= mask 97 | self.update() 98 | 99 | def extconfig(self, pio:int, epup:bool=False, epdn:bool=False) -> None: 100 | mask = (1 << pio) 101 | if epup: 102 | self.epup |= mask 103 | else: 104 | self.epup &= ~mask 105 | if epdn: 106 | self.epdn |= mask 107 | else: 108 | self.epdn &= ~mask 109 | 110 | async def waitfor(self, line:int, lvl:bool) -> None: 111 | mask = 1 << line 112 | if (not not (self.val & mask)) != lvl: 113 | ev = asyncio.Event() 114 | self.watchers.add(ev) 115 | while (not not (self.val & mask)) != lvl: 116 | await ev.wait() 117 | ev.clear() 118 | self.watchers.remove(ev) 119 | 120 | def update(self) -> None: 121 | if self.cfg1 & self.inpm: 122 | raise RuntimeError('GPIO short circuit!') 123 | pval = self.val 124 | self.val = ((self.cfg1 & self.cfg2) 125 | | ((~self.cfg1 & self.cfg2) 126 | & ((self.inpm & self.inpv) | (~self.inpm & (self.pup | self.epup)) | 127 | ((~self.inpm & ~self.pdn & ~self.epdn) & random.getrandbits(32))))) 128 | cval = pval ^ self.val 129 | if cval: 130 | self.irq |= ((self.rise & cval & self.val) 131 | | (self.fall & cval & ~self.val)) 132 | if self.irq: 133 | self.ic.set(self.line) 134 | for e in self.watchers: 135 | e.set() 136 | 137 | class UART: 138 | def __init__(self, ic:InterruptController, line:int) -> None: 139 | self.ic = ic 140 | self.line = line 141 | self.buf = asyncio.Queue() # type: asyncio.Queue[int] 142 | self.reset() 143 | 144 | SR_RXE = (1 << 0) 145 | SR_TXE = (1 << 1) 146 | SR_RXR = (1 << 2) 147 | SR_TXR = (1 << 3) 148 | SR_OVR = (1 << 4) 149 | 150 | def reset(self) -> None: 151 | self.sr = 0 152 | self.rx = 0 153 | self.tx = 0 154 | self.config() 155 | self.irq_update() 156 | 157 | def setrx(self, on:bool) -> None: 158 | self.sr = PeripheralsUtil.setbits(self.sr, UART.SR_RXE, on) 159 | self.irq_update() 160 | 161 | def settx(self, on:bool) -> None: 162 | self.sr = PeripheralsUtil.setbits(self.sr, UART.SR_TXE | UART.SR_TXR, on) 163 | self.irq_update() 164 | 165 | def config(self, br:int=9600, db:int=8, sb:int=1, par:int=0) -> None: 166 | self.ctime = (1 + db + sb + (not not par)) / br 167 | 168 | def irq_update(self) -> None: 169 | mask = UART.SR_RXR 170 | if self.sr & UART.SR_TXE: 171 | mask |= UART.SR_TXR 172 | if self.sr & mask: 173 | self.ic.set(self.line) 174 | else: 175 | self.ic.clear(self.line) 176 | 177 | def rx_char(self, ch:int) -> None: 178 | if self.sr & self.SR_RXR: 179 | self.sr |= UART.SR_OVR 180 | else: 181 | self.sr |= UART.SR_RXR 182 | self.rx = ch 183 | self.irq_update() 184 | 185 | def read_rx(self) -> int: 186 | ch = self.rx 187 | self.sr &= ~(UART.SR_RXR | UART.SR_OVR) 188 | self.irq_update() 189 | return ch 190 | 191 | async def _tx_char(self) -> None: 192 | await asyncio.sleep(self.ctime) 193 | sr = self.sr 194 | if sr & UART.SR_TXE: 195 | await self.buf.put(self.tx) 196 | self.sr = sr | UART.SR_TXR 197 | self.irq_update() 198 | 199 | def tx_char(self, ch:int) -> None: 200 | sr = self.sr 201 | if sr & UART.SR_TXE: 202 | if sr & UART.SR_TXR: 203 | self.sr = sr & ~UART.SR_TXR 204 | self.tx = ch 205 | else: 206 | self.sr = sr | UART.SR_OVR 207 | self.tx = random.getrandbits(8) 208 | self.irq_update() 209 | asyncio.ensure_future(self._tx_char()) 210 | 211 | async def xfr_todevice(self, data:bytes) -> None: 212 | for ch in data: 213 | sr0 = self.sr 214 | await asyncio.sleep(self.ctime) 215 | if (self.sr & sr0) & UART.SR_RXE: 216 | self.rx_char(ch) 217 | 218 | async def xfr_fromdevice(self, n:int) -> bytes: 219 | ba = bytearray(n) 220 | for i in range(n): 221 | ba[i] = await self.buf.get() 222 | return ba 223 | -------------------------------------------------------------------------------- /unicorn/simul/vtimeloop.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | # 3 | # This file is subject to the terms and conditions defined in file 'LICENSE', 4 | # which is part of this source code package. 5 | 6 | from typing import Any, Awaitable, Callable, Dict, Generator, List, \ 7 | Optional, TypeVar, Union 8 | from typing import cast 9 | 10 | import asyncio 11 | import heapq 12 | 13 | T = TypeVar('T') 14 | 15 | class VirtualTimeLoop(asyncio.AbstractEventLoop): 16 | def __init__(self) -> None: 17 | self._time:float = 0 18 | self._tasks:List[asyncio.TimerHandle] = list() 19 | self._ex:Optional[BaseException] = None 20 | 21 | def get_debug(self) -> bool: 22 | return False 23 | 24 | def time(self) -> float: 25 | return self._time 26 | 27 | def call_exception_handler(self, context:Dict[str,Any]) -> None: 28 | self._ex = context.get('exception', None) 29 | 30 | def _run(self, future:Optional[asyncio.Future]) -> None: 31 | try: 32 | asyncio.events._set_running_loop(self) 33 | while len(self._tasks) and (future is None or not future.done()): 34 | th = heapq.heappop(self._tasks) 35 | self._time = th.when() 36 | if not th.cancelled(): 37 | th._run() 38 | if self._ex is not None: 39 | raise self._ex 40 | finally: 41 | asyncio.events._set_running_loop(None) 42 | 43 | def run_until_complete(self, 44 | future:Union[Generator[Any,None,T],Awaitable[T]]) -> T: 45 | f = asyncio.ensure_future(future, loop=self) 46 | self._run(f) 47 | return f.result() 48 | 49 | def create_task(self, coro): # type: ignore 50 | async def wrap_for_ex() -> Any: 51 | try: 52 | return await coro 53 | except asyncio.CancelledError: 54 | pass 55 | except BaseException as e: 56 | self._ex = e 57 | return asyncio.Task(wrap_for_ex(), loop=self) 58 | 59 | def create_future(self) -> asyncio.Future: 60 | return asyncio.Future(loop=self) 61 | 62 | def call_at(self, when:float, callback:Callable, 63 | *args:Any, **kwargs:Any) -> asyncio.TimerHandle: 64 | th = asyncio.TimerHandle(when, callback, list(args), loop=self, **kwargs) 65 | heapq.heappush(self._tasks, th) 66 | return th 67 | 68 | def call_later(self, delay:float, callback:Callable, 69 | *args:Any, **kwargs:Any) -> asyncio.TimerHandle: 70 | return self.call_at(self._time + delay, callback, *args, **kwargs) 71 | 72 | def call_soon(self, callback:Callable, *args, **kwargs): 73 | return self.call_later(0, callback, *args, **kwargs) 74 | 75 | def _timer_handle_cancelled(self, handle:asyncio.TimerHandle) -> None: 76 | pass 77 | -------------------------------------------------------------------------------- /unicorn/startup.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016-2019 Semtech (International) AG. All rights reserved. 2 | // 3 | // This file is subject to the terms and conditions defined in file 'LICENSE', 4 | // which is part of this source code package. 5 | 6 | #include "lmic.h" 7 | 8 | #include "boottab.h" 9 | 10 | void _start (boot_boottab* boottab) { 11 | // symbols provided by linker script 12 | extern uint32_t _sidata, _sdata, _edata, _sbss, _ebss; 13 | 14 | // initialize data 15 | uint32_t* src = &_sidata; 16 | uint32_t* dst = &_sdata; 17 | while( dst < &_edata ) { 18 | *dst++ = *src++; 19 | } 20 | 21 | // initialize bss 22 | dst = &_sbss; 23 | while( dst < &_ebss ) { 24 | *dst++ = 0; 25 | } 26 | 27 | // call main function 28 | extern void main (boot_boottab* boottab); 29 | main(boottab); 30 | } 31 | 32 | // Firmware header 33 | __attribute__((section(".fwhdr"))) 34 | const volatile hal_fwhdr fwhdr = { 35 | // CRC and size will be patched by external tool 36 | .boot.crc = 0, 37 | .boot.size = BOOT_MAGIC_SIZE, 38 | .boot.entrypoint = (uint32_t) _start, 39 | 40 | .version = 0, 41 | }; 42 | --------------------------------------------------------------------------------