├── CMakeLists.txt
├── Kconfig
├── README.md
├── component.mk
├── include
└── lmic.h
└── src
├── aes
├── ideetron
│ └── AES-128_V10.cpp
├── lmic.c
└── other.c
├── hal
├── hal.c
└── hal.h
└── lmic
├── config.h
├── hal.h
├── lmic.c
├── lmic.h
├── lorabase.h
├── oslmic.c
├── oslmic.h
└── radio.c
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | set(COMPONENT_SRCDIRS "src/aes src/hal src/lmic")
2 | set(COMPONENT_ADD_INCLUDEDIRS "include")
3 | register_component()
--------------------------------------------------------------------------------
/Kconfig:
--------------------------------------------------------------------------------
1 | menu "LMIC"
2 |
3 | choice LMIC_DEFAULT_FREQ_BAND
4 | prompt "Default frequency band"
5 |
6 | config LMIC_FREQ_USE_EU_868
7 | bool "868 MHz EU"
8 |
9 | config LMIC_FREQ_USE_US_915
10 | bool "915 MHz US"
11 |
12 | endchoice
13 |
14 | choice LMIC_SX_CHIP
15 | prompt "LoRa chip"
16 |
17 | config LMIC_USE_SX1276
18 | bool "SX1276"
19 |
20 | config LMIC_USE_SX1272
21 | bool "SX1272"
22 |
23 | endchoice
24 |
25 | choice LMIC_SPI
26 | prompt "SPI Host interface"
27 |
28 | config LMIC_SPI_HSPI
29 | bool "HSPI"
30 |
31 | config LMIC_SPI_VSPI
32 | bool "VSPI"
33 |
34 | endchoice
35 |
36 | config LMIC_DEBUG_LEVEL
37 | int "LMIC debug level"
38 | default 0
39 |
40 | menu "Features"
41 |
42 | config LMIC_FEATURE_JOIN
43 | bool "Join"
44 | help
45 | Enable code required for OTA activation
46 | default y
47 |
48 | config LMIC_FEATURE_PING
49 | bool "Ping"
50 | help
51 | Enable code required for pinging
52 | default y
53 |
54 | config LMIC_FEATURE_BEACONS
55 | bool "Beacons"
56 | help
57 | Enable code required for beacon tracking
58 | default y
59 |
60 | endmenu
61 |
62 | endmenu
63 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ESP-IDF LMIC library
2 | ====================
3 | This repository contains the IBM LMIC (LoraMAC-in-C) library, slightly
4 | modified to run in the ESP-IDF environment, allowing using the SX1272,
5 | SX1276 tranceivers and compatible modules (such as some HopeRF RFM9x
6 | modules).
7 |
8 | This port is based on the Arduino port of IBM LMIC by Matthijs Kooijman
9 | (https://github.com/matthijskooijman/arduino-lmic) and exposes the same
10 | API. The following documentation was created by Matthijs Kooijman and
11 | only slightly adjusted by me, too.
12 |
13 | This library requires ESP-IDF version 3.1 or above.
14 |
15 | Installing
16 | ----------
17 | To install this library:
18 | - clone this repo into the `components` directory of your project
19 | - download this repo as a zip file and unpack it into the `components`
20 | directory of your project
21 |
22 | Features
23 | --------
24 | The LMIC library provides a fairly complete LoRaWAN Class A and Class B
25 | implementation, supporting the EU-868 and US-915 bands. Only a limited
26 | number of features was tested using this port on Arduino hardware, so be
27 | careful when using any of the untested features.
28 |
29 | What certainly works:
30 | - Sending packets uplink, taking into account duty cycling.
31 | - Encryption and message integrity checking.
32 | - Receiving downlink packets in the RX2 window.
33 | - Custom frequencies and datarate settings.
34 | - Over-the-air activation (OTAA / joining).
35 |
36 | What has not been tested:
37 | - Receiving downlink packets in the RX1 window.
38 | - Receiving and processing MAC commands.
39 | - Class B operation.
40 |
41 | If you try one of these untested features and it works, be sure to let
42 | us know (creating a github issue is probably the best way for that).
43 |
44 | Configuration
45 | -------------
46 | Most configuration can be done through ESP-IDFs integrated menuconfig
47 | under `Component config -> LMIC`. Just type `make menuconfig` to start
48 | interactive configuration.
49 |
50 | More knobs are available through `src/lmic/config.h`.
51 |
52 | Supported hardware
53 | ------------------
54 | This library is intended to be used with plain LoRa transceivers,
55 | connecting to them using SPI. In particular, the SX1272 and SX1276
56 | families are supported (which should include SX1273, SX1277, SX1278 and
57 | SX1279 which only differ in the available frequencies, bandwidths and
58 | spreading factors). It has been tested with both SX1272 and SX1276
59 | chips, using the Semtech SX1272 evaluation board and the HopeRF RFM92
60 | and RFM95 boards (which supposedly contain an SX1272 and SX1276 chip
61 | respectively).
62 |
63 | This library contains a full LoRaWAN stack and is intended to drive
64 | these Transceivers directly. It is *not* intended to be used with
65 | full-stack devices like the Microchip RN2483 and the Embit LR1272E.
66 | These contain a transceiver and microcontroller that implements the
67 | LoRaWAN stack and exposes a high-level serial interface instead of the
68 | low-level SPI transceiver interface.
69 |
70 | This library is intended to be used inside the ESP-IDF environment. It
71 | should be mostly architecture-independent, so it should run on both
72 | ESP8266 and ESP32.
73 |
74 | Connections
75 | -----------
76 | To make this library work, your ESP should be connected to the
77 | transceiver. The exact connections are a dependent on the transceiver
78 | board and ESP board used, so this section tries to explain what each
79 | connection is for and in what cases it is (not) required.
80 |
81 | Note that the SX127x module runs at 3.3V and likely does not like 5V on
82 | its pins (though the datasheet is not say anything about this, and my
83 | transceiver did not obviously break after accidentally using 5V I/O for
84 | a few hours). To be safe, make sure to use a level shifter if you are
85 | using 5V IOs. The Semtech evaluation board has 100 ohm resistors in
86 | series with all data lines that might prevent damage, but I would not
87 | count on that.
88 |
89 | ### Power
90 | The SX127x transceivers need a supply voltage between 1.8V and 3.9V.
91 | Using a 3.3V supply is typical. Some modules have a single power pin
92 | (like the HopeRF modules, labeled 3.3V) but others expose multiple power
93 | pins for different parts (like the Semtech evaluation board that has
94 | `VDD_RF`, `VDD_ANA` and `VDD_FEM`), which can all be connected together.
95 | Any *GND* pins need to be connected to the Arduino *GND* pin(s).
96 |
97 | ### SPI
98 | The primary way of communicating with the transceiver is through SPI
99 | (Serial Peripheral Interface). This uses four pins: MOSI, MISO, SCK and
100 | SS. The former three need to be directly connected: so MOSI to MOSI,
101 | MISO to MISO, SCK to SCK. Where these pins are located on your ESP
102 | board varies, the most common design is the ESP32 devkit. A quick
103 | search for "ESP32 devkit pinout" on your favourite serach engine will
104 | provide you with a pinout for this design.
105 |
106 | The SS (slave select) connection is a bit more flexible. On the SPI
107 | slave side (the transceiver), this must be connect to the pin
108 | (typically) labeled *NSS*. On the SPI master (ESP) side, this pin
109 | can connect to any I/O pin that is not input only.
110 |
111 | ### DIO pins
112 | The DIO (digitial I/O) pins on the transceiver board can be configured
113 | for various functions. The LMIC library uses them to get instant status
114 | information from the transceiver. For example, when a LoRa transmission
115 | starts, the DIO0 pin is configured as a TxDone output. When the
116 | transmission is complete, the DIO0 pin is pulled high by the transceiver,
117 | which can be detected by the LMIC library.
118 |
119 | The LMIC library needs only access to DIO0, DIO1 and DIO2, the other
120 | DIOx pins can be left disconnected. On the Arduino side, they can
121 | connect to any I/O pin, since the current implementation does not use
122 | interrupts or other special hardware features (though this might be
123 | added in the future, see also the "Timing" section).
124 |
125 | In LoRa mode the DIO pins are used as follows:
126 | * DIO0: TxDone and RxDone
127 | * DIO1: RxTimeout
128 |
129 | In FSK mode they are used as follows::
130 | * DIO0: PayloadReady and PacketSent
131 | * DIO2: TimeOut
132 |
133 | Both modes need only 2 pins, but the tranceiver does not allow mapping
134 | them in such a way that all needed interrupts map to the same 2 pins.
135 | So, if both LoRa and FSK modes are used, all three pins must be
136 | connected.
137 |
138 | The pins used on the ESP side should be configured in the pin
139 | mapping in your sketch (see below).
140 |
141 | ### Reset
142 | The transceiver has a reset pin that can be used to explicitely reset
143 | it. The LMIC library uses this to ensure the chip is in a consistent
144 | state at startup. In practice, this pin can be left disconnected, since
145 | the transceiver will already be in a sane state on power-on, but
146 | connecting it might prevent problems in some cases.
147 |
148 | On the ESP side, any I/O pin can be used. The pin number used must
149 | be configured in the pin mapping (see below).
150 |
151 | ### RXTX
152 | The transceiver contains two separate antenna connections: One for RX
153 | and one for TX. A typical transceiver board contains an antenna switch
154 | chip, that allows switching a single antenna between these RX and TX
155 | connections. Such a antenna switcher can typically be told what
156 | position it should be through an input pin, often labeled *RXTX*.
157 |
158 | The easiest way to control the antenna switch is to use the *RXTX* pin
159 | on the SX127x transceiver. This pin is automatically set high during TX
160 | and low during RX. For example, the HopeRF boards seem to have this
161 | connection in place, so they do not expose any *RXTX* pins and the pin
162 | can be marked as unused in the pin mapping.
163 |
164 | Some boards do expose the antenna switcher pin, and sometimes also the
165 | SX127x *RXTX* pin. For example, the SX1272 evaluation board calls the
166 | former *FEM_CTX* and the latter *RXTX*. Again, simply connecting these
167 | together with a jumper wire is the easiest solution.
168 |
169 | Alternatively, or if the SX127x *RXTX* pin is not available, LMIC can be
170 | configured to control the antenna switch. Connect the antenna switch
171 | control pin (e.g. *FEM_CTX* on the Semtech evaluation board) to any I/O
172 | pin on the ESP side, and configure the pin used in the pin map (see
173 | below). It is not entirely clear why would *not* want the transceiver to
174 | control the antenna directly, though.
175 |
176 | ### Pin mapping
177 | As described above, most connections can use arbitrary I/O pins on the
178 | ESP side. To tell the LMIC library about these, a pin mapping struct
179 | is used.
180 |
181 | For example, this could look like this (Heltec Wifi LoRa 32):
182 |
183 | ```c
184 | const lmic_pinmap lmic_pins = {
185 | .nss = 18,
186 | .rst = 14,
187 | .dio = { 26, 34, 35 },
188 | .spi = { /* MISO = */ 19, /* MOSI = */ 27, /* SCK = */ 5 },
189 | .rxtx = LMIC_UNUSED_PIN,
190 | };
191 | ```
192 | The names refer to the pins on the transceiver side, the numbers refer
193 | to the ESP GPIO numbers.
194 | For the DIO pins, the three numbers refer to DIO0, DIO1 and DIO2
195 | respectively. Any pins that are not needed should be specified as
196 | `LMIC_UNUSED_PIN`. The nss, DIO0 and SPI pins are required, the others
197 | can potentially be left out (depending on the environments and
198 | requirements, see the notes above for when a pin can or cannot be left
199 | out).
200 |
201 | The name of this struct must always be `lmic_pins`, which is a special name
202 | recognized by the library.
203 |
204 | #### Heltec Wifi LoRa 32
205 | This board uses the following pin mapping:
206 |
207 | ```c
208 | const lmic_pinmap lmic_pins = {
209 | .nss = 18,
210 | .rst = 14,
211 | .dio = { 26, 34, 35 },
212 | .spi = { /* MISO = */ 19, /* MOSI = */ 27, /* SCK = */ 5 },
213 | .rxtx = LMIC_UNUSED_PIN,
214 | };
215 | ```
216 |
217 | Examples
218 | --------
219 | This library currently provides three examples:
220 |
221 | - `ttn-abp.ino` shows a basic transmission of a "Hello, world!" message
222 | using the LoRaWAN protocol. It contains some frequency settings and
223 | encryption keys intended for use with The Things Network, but these
224 | also correspond to the default settings of most gateways, so it
225 | should work with other networks and gateways as well. This example
226 | uses activation-by-personalization (ABP, preconfiguring a device
227 | address and encryption keys), and does not employ over-the-air
228 | activation.
229 |
230 | Reception of packets (in response to transmission, using the RX1 and
231 | RX2 receive windows is also supported).
232 |
233 | - `ttn-otaa.ino` also sends a "Hello, world!" message, but uses over
234 | the air activation (OTAA) to first join a network to establish a
235 | session and security keys. This was tested with The Things Network,
236 | but should also work (perhaps with some changes) for other networks.
237 |
238 | - `raw.ino` shows how to access the radio on a somewhat low level,
239 | and allows to send raw (non-LoRaWAN) packets between nodes directly.
240 | This is useful to verify basic connectivity, and when no gateway is
241 | available, but this example also bypasses duty cycle checks, so be
242 | careful when changing the settings.
243 |
244 | Timing
245 | ------
246 | Unfortunately, the SX127x tranceivers do not support accurate
247 | timekeeping themselves (there is a sequencer that is *almost* sufficient
248 | for timing the RX1 and RX2 downlink windows, but that is only available
249 | in FSK mode, not in LoRa mode). This means that the microcontroller is
250 | responsible for keeping track of time. In particular, it should note
251 | when a packet finished transmitting, so it can open up the RX1 and RX2
252 | receive windows at a fixed time after the end of transmission.
253 |
254 | This timing uses one of the ESP32s builtin timers, which is set up for a
255 | granularity of 20μs and is based on the 80 MHz peripheral clock. For timing
256 | events, the tranceiver uses its DIOx pins as interrupt outputs. In the
257 | current implementation, these pins are not handled by an actual
258 | interrupt handler, but they are just polled once every LMIC loop,
259 | resulting in a bit inaccuracy in the timestamping. Also, running
260 | scheduled jobs (such as opening up the receive windows) is done using a
261 | polling approach, which might also result in further delays.
262 |
263 | Fortunately, LoRa is a fairly slow protocol and the timing of the
264 | receive windows is not super critical. To synchronize transmitter and
265 | receiver, a preamble is first transmitted. Using LoRaWAN, this preamble
266 | consists of 8 symbols, of which the receiver needs to see 4 symbols to
267 | lock on. The current implementation tries to enable the receiver for 5
268 | symbol times at 1.5 symbol after the start of the receive window,
269 | meaning that a inacurracy of plus or minus 2.5 symbol times should be
270 | acceptable.
271 |
272 | At the fastest LoRa setting supported by the tranceiver (SF5BW500) a
273 | single preamble symbol takes 64μs, so the receive window timing should
274 | be accurate within 160μs (for LoRaWAN this is SF7BW250, needing accuracy
275 | within 1280μs). This accuracy should also be
276 | feasible with the polling approach used, provided that the LMIC loop is
277 | run often enough.
278 |
279 | It would be good to properly review this code at some point, since it
280 | seems that in some places some offsets and corrections are applied that
281 | might not be appropriate for the ESP environment. So if reception is
282 | not working, the timing is something to have a closer look at.
283 |
284 | The LMIC library was intended to connect the DIO pins to interrupt
285 | lines and run code inside the interrupt handler. One simpler
286 | alternative could be to use an interrupt handler to just store a
287 | timestamp, and then do the actual handling in the main loop (this
288 | requires modifications of the library to pass a timestamp to the LMIC
289 | `radio_irq_handler()` function).
290 |
291 | An even more accurate solution could be to use a dedicated timer with an
292 | input capture unit, that can store the timestamp of a change on the DIO0
293 | pin (the only one that is timing-critical) entirely in hardware.
294 |
295 | Downlink datarate
296 | -----------------
297 | Note that the datarate used for downlink packets in the RX2 window
298 | defaults to SF12BW125 according to the specification, but some networks
299 | use different values (iot.semtech.com and The Things Network both use
300 | SF9BW). When using personalized activate (ABP), it is your
301 | responsibility to set the right settings, e.g. by adding this to your
302 | code:
303 |
304 | LMIC.dn2Dr = DR_SF9;
305 |
306 | When using OTAA, the network communicates the RX2 settings in the
307 | join accept message, but the LMIC library does not currently process
308 | these settings. Until that is solved you should
309 | manually set the RX2 rate, *after* joining.
310 |
311 | License
312 | -------
313 | Most source files in this repository are made available under the
314 | Eclipse Public License v1.0. The examples which use a more liberal
315 | license. Some of the AES code is available under the LGPL. Refer to each
316 | individual source file for more details.
317 |
--------------------------------------------------------------------------------
/component.mk:
--------------------------------------------------------------------------------
1 | #COMPONENT_OWNBUILDTARGET = 1
2 | COMPONENT_SRCDIRS := src/aes src/hal src/lmic
3 | COMPONENT_ADD_INCLUDEDIRS := include
4 |
5 | #AES_OBJS = src/aes/lmic.o src/aes/other.o
6 | #HAL_OBJS = src/hal/hal.o
7 | #LMIC_OBJS = $(patsubst %.c,%.o,$(wildcard src/lmic/*.c))
8 |
9 | #%.o : %.c
10 | # $(CC) -c $(CCFLAGS) $(CFLAGS) $< -o $@
11 |
12 | #aes: $(AES_OBJS)
13 |
14 | #hal: $(HAL_OBJS)
15 |
16 | #lmic: $(LMIC_OBJS)
17 |
18 | #build: aes hal lmic
19 | # $(CC) $(CCFLAGS) $(CFLAGS) $(AES_OBJS) $(HAL_OBJS) $(LMIC_OBJS) -o $(COMPONENT_LIBRARY)
20 |
21 | #clean:
22 | # rm -f $(AES_OBJS) $(HAL_OBJS) $(LMIC_OBJS)
--------------------------------------------------------------------------------
/include/lmic.h:
--------------------------------------------------------------------------------
1 | #ifdef __cplusplus
2 | extern "C"{
3 | #endif
4 |
5 | #include "../src/lmic/lmic.h"
6 |
7 | #ifdef __cplusplus
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/src/aes/ideetron/AES-128_V10.cpp:
--------------------------------------------------------------------------------
1 | /******************************************************************************************
2 | #if defined(USE_IDEETRON_AES)
3 | * Copyright 2015, 2016 Ideetron B.V.
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | ******************************************************************************************/
18 | /******************************************************************************************
19 | *
20 | * File: AES-128_V10.cpp
21 | * Author: Gerben den Hartog
22 | * Compagny: Ideetron B.V.
23 | * Website: http://www.ideetron.nl/LoRa
24 | * E-mail: info@ideetron.nl
25 | ******************************************************************************************/
26 | /****************************************************************************************
27 | *
28 | * Created on: 20-10-2015
29 | * Supported Hardware: ID150119-02 Nexus board with RFM95
30 | *
31 | * Firmware Version 1.0
32 | * First version
33 | ****************************************************************************************/
34 |
35 | // This file was taken from
36 | // https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for
37 | // use with LMIC. It was only cosmetically modified:
38 | // - AES_Encrypt was renamed to lmic_aes_encrypt.
39 | // - All other functions and variables were made static
40 | // - Tabs were converted to 2 spaces
41 | // - An #include and #if guard was added
42 | // - S_Table is now stored in PROGMEM
43 |
44 | #include "../../lmic/oslmic.h"
45 |
46 | #if defined(USE_IDEETRON_AES)
47 |
48 | /*
49 | ********************************************************************************************
50 | * Global Variables
51 | ********************************************************************************************
52 | */
53 |
54 | static unsigned char State[4][4];
55 |
56 | static CONST_TABLE(unsigned char, S_Table)[16][16] = {
57 | {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76},
58 | {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0},
59 | {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15},
60 | {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75},
61 | {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84},
62 | {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF},
63 | {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8},
64 | {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2},
65 | {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73},
66 | {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB},
67 | {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79},
68 | {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08},
69 | {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A},
70 | {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E},
71 | {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF},
72 | {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16}
73 | };
74 |
75 | extern "C" void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key);
76 | static void AES_Add_Round_Key(unsigned char *Round_Key);
77 | static unsigned char AES_Sub_Byte(unsigned char Byte);
78 | static void AES_Shift_Rows();
79 | static void AES_Mix_Collums();
80 | static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key);
81 | static void Send_State();
82 |
83 | /*
84 | *****************************************************************************************
85 | * Description : Function for encrypting data using AES-128
86 | *
87 | * Arguments : *Data Data to encrypt is a 16 byte long arry
88 | * *Key Key to encrypt data with is a 16 byte long arry
89 | *****************************************************************************************
90 | */
91 | void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key)
92 | {
93 | unsigned char i;
94 | unsigned char Row,Collum;
95 | unsigned char Round = 0x00;
96 | unsigned char Round_Key[16];
97 |
98 | //Copy input to State arry
99 | for(Collum = 0; Collum < 4; Collum++)
100 | {
101 | for(Row = 0; Row < 4; Row++)
102 | {
103 | State[Row][Collum] = Data[Row + (4*Collum)];
104 | }
105 | }
106 |
107 | //Copy key to round key
108 | for(i = 0; i < 16; i++)
109 | {
110 | Round_Key[i] = Key[i];
111 | }
112 |
113 | //Add round key
114 | AES_Add_Round_Key(Round_Key);
115 |
116 | //Preform 9 full rounds
117 | for(Round = 1; Round < 10; Round++)
118 | {
119 | //Preform Byte substitution with S table
120 | for(Collum = 0; Collum < 4; Collum++)
121 | {
122 | for(Row = 0; Row < 4; Row++)
123 | {
124 | State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]);
125 | }
126 | }
127 |
128 | //Preform Row Shift
129 | AES_Shift_Rows();
130 |
131 | //Mix Collums
132 | AES_Mix_Collums();
133 |
134 | //Calculate new round key
135 | AES_Calculate_Round_Key(Round,Round_Key);
136 |
137 | //Add round key
138 | AES_Add_Round_Key(Round_Key);
139 | }
140 |
141 | //Last round whitout mix collums
142 | //Preform Byte substitution with S table
143 | for(Collum = 0; Collum < 4; Collum++)
144 | {
145 | for(Row = 0; Row < 4; Row++)
146 | {
147 | State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]);
148 | }
149 | }
150 |
151 | //Shift rows
152 | AES_Shift_Rows();
153 |
154 | //Calculate new round key
155 | AES_Calculate_Round_Key(Round,Round_Key);
156 |
157 | //Add round Key
158 | AES_Add_Round_Key(Round_Key);
159 |
160 | //Copy the State into the data array
161 | for(Collum = 0; Collum < 4; Collum++)
162 | {
163 | for(Row = 0; Row < 4; Row++)
164 | {
165 | Data[Row + (4*Collum)] = State[Row][Collum];
166 | }
167 | }
168 |
169 | }
170 |
171 | /*
172 | *****************************************************************************************
173 | * Description : Function that add's the round key for the current round
174 | *
175 | * Arguments : *Round_Key 16 byte long array holding the Round Key
176 | *****************************************************************************************
177 | */
178 | static void AES_Add_Round_Key(unsigned char *Round_Key)
179 | {
180 | unsigned char Row,Collum;
181 |
182 | for(Collum = 0; Collum < 4; Collum++)
183 | {
184 | for(Row = 0; Row < 4; Row++)
185 | {
186 | State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)];
187 | }
188 | }
189 | }
190 |
191 | /*
192 | *****************************************************************************************
193 | * Description : Function that substitutes a byte with a byte from the S_Table
194 | *
195 | * Arguments : Byte The byte that will be substituted
196 | *
197 | * Return : The return is the found byte in the S_Table
198 | *****************************************************************************************
199 | */
200 | static unsigned char AES_Sub_Byte(unsigned char Byte)
201 | {
202 | unsigned char S_Row,S_Collum;
203 | unsigned char S_Byte;
204 |
205 | //Split byte up in Row and Collum
206 | S_Row = ((Byte >> 4) & 0x0F);
207 | S_Collum = (Byte & 0x0F);
208 |
209 | //Find the correct byte in the S_Table
210 | S_Byte = TABLE_GET_U1_TWODIM(S_Table, S_Row, S_Collum);
211 |
212 | return S_Byte;
213 | }
214 |
215 | /*
216 | *****************************************************************************************
217 | * Description : Function that preforms the shift row operation described in the AES standard
218 | *****************************************************************************************
219 | */
220 | static void AES_Shift_Rows()
221 | {
222 | unsigned char Buffer;
223 |
224 | //Row 0 doesn't change
225 |
226 | //Shift Row 1 one left
227 | //Store firt byte in buffer
228 | Buffer = State[1][0];
229 | //Shift all bytes
230 | State[1][0] = State[1][1];
231 | State[1][1] = State[1][2];
232 | State[1][2] = State[1][3];
233 | State[1][3] = Buffer;
234 |
235 | //Shift row 2 two left
236 | Buffer = State[2][0];
237 | State[2][0] = State[2][2];
238 | State[2][2] = Buffer;
239 | Buffer = State[2][1];
240 | State[2][1] = State[2][3];
241 | State[2][3] = Buffer;
242 |
243 | //Shift row 3 three left
244 | Buffer = State[3][3];
245 | State[3][3] = State[3][2];
246 | State[3][2] = State[3][1];
247 | State[3][1] = State[3][0];
248 | State[3][0] = Buffer;
249 | }
250 |
251 | /*
252 | *****************************************************************************************
253 | * Description : Function that preforms the Mix Collums operation described in the AES standard
254 | *****************************************************************************************
255 | */
256 | static void AES_Mix_Collums()
257 | {
258 | unsigned char Row,Collum;
259 | unsigned char a[4], b[4];
260 | for(Collum = 0; Collum < 4; Collum++)
261 | {
262 | for(Row = 0; Row < 4; Row++)
263 | {
264 | a[Row] = State[Row][Collum];
265 | b[Row] = (State[Row][Collum] << 1);
266 |
267 | if((State[Row][Collum] & 0x80) == 0x80)
268 | {
269 | b[Row] = b[Row] ^ 0x1B;
270 | }
271 | }
272 | State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3];
273 | State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3];
274 | State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3];
275 | State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3];
276 | }
277 | }
278 |
279 | /*
280 | *****************************************************************************************
281 | * Description : Function that calculaties the round key for the current round
282 | *
283 | * Arguments : Round Number of current Round
284 | * *Round_Key 16 byte long array holding the Round Key
285 | *****************************************************************************************
286 | */
287 | static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key)
288 | {
289 | unsigned char i,j;
290 | unsigned char b;
291 | unsigned char Temp[4];
292 | unsigned char Buffer;
293 | unsigned char Rcon;
294 |
295 | //Calculate first Temp
296 | //Copy laste byte from previous key
297 | for(i = 0; i < 4; i++)
298 | {
299 | Temp[i] = Round_Key[i+12];
300 | }
301 |
302 | //Rotate Temp
303 | Buffer = Temp[0];
304 | Temp[0] = Temp[1];
305 | Temp[1] = Temp[2];
306 | Temp[2] = Temp[3];
307 | Temp[3] = Buffer;
308 |
309 | //Substitute Temp
310 | for(i = 0; i < 4; i++)
311 | {
312 | Temp[i] = AES_Sub_Byte(Temp[i]);
313 | }
314 |
315 | //Calculate Rcon
316 | Rcon = 0x01;
317 | while(Round != 1)
318 | {
319 | b = Rcon & 0x80;
320 | Rcon = Rcon << 1;
321 | if(b == 0x80)
322 | {
323 | Rcon = Rcon ^ 0x1b;
324 | }
325 | Round--;
326 | }
327 |
328 | //XOR Rcon
329 | Temp[0] = Temp[0] ^ Rcon;
330 |
331 | //Calculate new key
332 | for(i = 0; i < 4; i++)
333 | {
334 | for(j = 0; j < 4; j++)
335 | {
336 | Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j];
337 | Temp[j] = Round_Key[j + (4*i)];
338 | }
339 | }
340 | }
341 |
342 | #endif // defined(USE_IDEETRON_AES)
343 |
--------------------------------------------------------------------------------
/src/aes/lmic.c:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2014-2015 IBM Corporation.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Eclipse Public License v1.0
5 | * which accompanies this distribution, and is available at
6 | * http://www.eclipse.org/legal/epl-v10.html
7 | *
8 | * Contributors:
9 | * IBM Zurich Research Lab - initial API, implementation and documentation
10 | *******************************************************************************/
11 |
12 | #include "../lmic/oslmic.h"
13 |
14 | #if defined(USE_ORIGINAL_AES)
15 |
16 | #pragma GCC diagnostic push
17 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
18 |
19 | #define AES_MICSUB 0x30 // internal use only
20 |
21 | static CONST_TABLE(u4_t, AES_RCON)[10] = {
22 | 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
23 | 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000
24 | };
25 |
26 | static CONST_TABLE(u1_t, AES_S)[256] = {
27 | 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
28 | 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
29 | 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
30 | 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
31 | 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
32 | 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
33 | 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
34 | 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
35 | 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
36 | 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
37 | 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
38 | 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
39 | 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
40 | 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
41 | 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
42 | 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
43 | };
44 |
45 | static CONST_TABLE(u4_t, AES_E1)[256] = {
46 | 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554,
47 | 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A,
48 | 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B,
49 | 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B,
50 | 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F,
51 | 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F,
52 | 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5,
53 | 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F,
54 | 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB,
55 | 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497,
56 | 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED,
57 | 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A,
58 | 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594,
59 | 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3,
60 | 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504,
61 | 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D,
62 | 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739,
63 | 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395,
64 | 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883,
65 | 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76,
66 | 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4,
67 | 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B,
68 | 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0,
69 | 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818,
70 | 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651,
71 | 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85,
72 | 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12,
73 | 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9,
74 | 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7,
75 | 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A,
76 | 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8,
77 | 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A,
78 | };
79 |
80 | static CONST_TABLE(u4_t, AES_E2)[256] = {
81 | 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5,
82 | 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676,
83 | 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0,
84 | 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0,
85 | 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC,
86 | 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515,
87 | 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A,
88 | 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575,
89 | 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0,
90 | 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484,
91 | 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B,
92 | 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF,
93 | 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585,
94 | 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8,
95 | 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5,
96 | 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2,
97 | 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717,
98 | 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373,
99 | 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888,
100 | 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB,
101 | 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C,
102 | 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979,
103 | 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9,
104 | 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808,
105 | 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6,
106 | 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A,
107 | 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E,
108 | 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E,
109 | 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494,
110 | 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF,
111 | 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868,
112 | 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616,
113 | };
114 |
115 | static CONST_TABLE(u4_t, AES_E3)[256] = {
116 | 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5,
117 | 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76,
118 | 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0,
119 | 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0,
120 | 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC,
121 | 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15,
122 | 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A,
123 | 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75,
124 | 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0,
125 | 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384,
126 | 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B,
127 | 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF,
128 | 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185,
129 | 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8,
130 | 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5,
131 | 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2,
132 | 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17,
133 | 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673,
134 | 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88,
135 | 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB,
136 | 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C,
137 | 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279,
138 | 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9,
139 | 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008,
140 | 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6,
141 | 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A,
142 | 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E,
143 | 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E,
144 | 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394,
145 | 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF,
146 | 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068,
147 | 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16,
148 | };
149 |
150 | static CONST_TABLE(u4_t, AES_E4)[256] = {
151 | 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
152 | 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
153 | 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
154 | 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
155 | 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
156 | 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
157 | 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
158 | 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
159 | 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
160 | 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
161 | 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
162 | 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
163 | 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
164 | 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
165 | 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
166 | 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
167 | 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
168 | 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
169 | 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
170 | 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
171 | 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
172 | 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
173 | 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
174 | 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
175 | 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
176 | 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
177 | 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
178 | 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
179 | 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
180 | 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
181 | 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
182 | 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C,
183 | };
184 |
185 | #define msbf4_read(p) ((p)[0]<<24 | (p)[1]<<16 | (p)[2]<<8 | (p)[3])
186 | #define msbf4_write(p,v) (p)[0]=(v)>>24,(p)[1]=(v)>>16,(p)[2]=(v)>>8,(p)[3]=(v)
187 | #define swapmsbf(x) ( (x&0xFF)<<24 | (x&0xFF00)<<8 | (x&0xFF0000)>>8 | (x>>24) )
188 |
189 | #define u1(v) ((u1_t)(v))
190 |
191 | #define AES_key4(r1,r2,r3,r0,i) r1 = ki[i+1]; \
192 | r2 = ki[i+2]; \
193 | r3 = ki[i+3]; \
194 | r0 = ki[i]
195 |
196 | #define AES_expr4(r1,r2,r3,r0,i) r1 ^= TABLE_GET_U4(AES_E4, u1(i)); \
197 | r2 ^= TABLE_GET_U4(AES_E3, u1(i>>8)); \
198 | r3 ^= TABLE_GET_U4(AES_E2, u1(i>>16)); \
199 | r0 ^= TABLE_GET_U4(AES_E1, (i>>24))
200 |
201 | #define AES_expr(a,r0,r1,r2,r3,i) a = ki[i]; \
202 | a ^= ((u4_t)TABLE_GET_U1(AES_S, r0>>24 )<<24); \
203 | a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r1>>16))<<16); \
204 | a ^= ((u4_t)TABLE_GET_U1(AES_S, u1(r2>> 8))<< 8); \
205 | a ^= (u4_t)TABLE_GET_U1(AES_S, u1(r3) )
206 |
207 | // global area for passing parameters (aux, key) and for storing round keys
208 | u4_t AESAUX[16/sizeof(u4_t)];
209 | u4_t AESKEY[11*16/sizeof(u4_t)];
210 |
211 | // generate 1+10 roundkeys for encryption with 128-bit key
212 | // read 128-bit key from AESKEY in MSBF, generate roundkey words in place
213 | static void aesroundkeys () {
214 | int i;
215 | u4_t b;
216 |
217 | for( i=0; i<4; i++) {
218 | AESKEY[i] = swapmsbf(AESKEY[i]);
219 | }
220 |
221 | b = AESKEY[3];
222 | for( ; i<44; i++ ) {
223 | if( i%4==0 ) {
224 | // b = SubWord(RotWord(b)) xor Rcon[i/4]
225 | b = ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 16)) << 24) ^
226 | ((u4_t)TABLE_GET_U1(AES_S, u1(b >> 8)) << 16) ^
227 | ((u4_t)TABLE_GET_U1(AES_S, u1(b) ) << 8) ^
228 | ((u4_t)TABLE_GET_U1(AES_S, b >> 24 ) ) ^
229 | TABLE_GET_U4(AES_RCON, (i-4)/4);
230 | }
231 | AESKEY[i] = b ^= AESKEY[i-4];
232 | }
233 | }
234 |
235 | u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) {
236 |
237 | aesroundkeys();
238 |
239 | if( mode & AES_MICNOAUX ) {
240 | AESAUX[0] = AESAUX[1] = AESAUX[2] = AESAUX[3] = 0;
241 | } else {
242 | AESAUX[0] = swapmsbf(AESAUX[0]);
243 | AESAUX[1] = swapmsbf(AESAUX[1]);
244 | AESAUX[2] = swapmsbf(AESAUX[2]);
245 | AESAUX[3] = swapmsbf(AESAUX[3]);
246 | }
247 |
248 | while( (signed char)len > 0 ) {
249 | u4_t a0, a1, a2, a3;
250 | u4_t t0, t1, t2, t3;
251 | u4_t *ki, *ke;
252 |
253 | // load input block
254 | if( (mode & AES_CTR) || ((mode & AES_MIC) && (mode & AES_MICNOAUX)==0) ) { // load CTR block or first MIC block
255 | a0 = AESAUX[0];
256 | a1 = AESAUX[1];
257 | a2 = AESAUX[2];
258 | a3 = AESAUX[3];
259 | }
260 | else if( (mode & AES_MIC) && len <= 16 ) { // last MIC block
261 | a0 = a1 = a2 = a3 = 0; // load null block
262 | mode |= ((len == 16) ? 1 : 2) << 4; // set MICSUB: CMAC subkey K1 or K2
263 | } else
264 | LOADDATA: { // load data block (partially)
265 | for(t0=0; t0<16; t0++) {
266 | t1 = (t1<<8) | ((t0> 4) != 0 ) { // last block
317 | do {
318 | // compute CMAC subkey K1 and K2
319 | t0 = a0 >> 31; // save MSB
320 | a0 = (a0 << 1) | (a1 >> 31);
321 | a1 = (a1 << 1) | (a2 >> 31);
322 | a2 = (a2 << 1) | (a3 >> 31);
323 | a3 = (a3 << 1);
324 | if( t0 ) a3 ^= 0x87;
325 | } while( --t1 );
326 |
327 | AESAUX[0] ^= a0;
328 | AESAUX[1] ^= a1;
329 | AESAUX[2] ^= a2;
330 | AESAUX[3] ^= a3;
331 | mode &= ~AES_MICSUB;
332 | goto LOADDATA;
333 | } else {
334 | // save cipher block as new iv
335 | AESAUX[0] = a0;
336 | AESAUX[1] = a1;
337 | AESAUX[2] = a2;
338 | AESAUX[3] = a3;
339 | }
340 | } else { // CIPHER
341 | if( mode & AES_CTR ) { // xor block (partially)
342 | t0 = (len > 16) ? 16: len;
343 | for(t1=0; t1>24);
345 | a0 <<= 8;
346 | if((t1&3)==3) {
347 | a0 = a1;
348 | a1 = a2;
349 | a2 = a3;
350 | }
351 | }
352 | // update counter
353 | AESAUX[3]++;
354 | } else { // ECB
355 | // store block
356 | msbf4_write(buf+0, a0);
357 | msbf4_write(buf+4, a1);
358 | msbf4_write(buf+8, a2);
359 | msbf4_write(buf+12, a3);
360 | }
361 | }
362 |
363 | // update block state
364 | if( (mode & AES_MIC)==0 || (mode & AES_MICNOAUX) ) {
365 | buf += 16;
366 | len -= 16;
367 | }
368 | mode |= AES_MICNOAUX;
369 | }
370 | return AESAUX[0];
371 | }
372 |
373 | #pragma GCC diagnostic pop
374 |
375 | #endif
376 |
--------------------------------------------------------------------------------
/src/aes/other.c:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2016 Matthijs Kooijman
3 | *
4 | * LICENSE
5 | *
6 | * Permission is hereby granted, free of charge, to anyone
7 | * obtaining a copy of this document and accompanying files,
8 | * to do whatever they want with them without any restriction,
9 | * including, but not limited to, copying, modification and
10 | * redistribution.
11 | *
12 | * NO WARRANTY OF ANY KIND IS PROVIDED.
13 | *******************************************************************************/
14 |
15 | /*
16 | * The original LMIC AES implementation integrates raw AES encryption
17 | * with CMAC and AES-CTR in a single piece of code. Most other AES
18 | * implementations (only) offer raw single block AES encryption, so this
19 | * file contains an implementation of CMAC and AES-CTR, and offers the
20 | * same API through the os_aes() function as the original AES
21 | * implementation. This file assumes that there is an encryption
22 | * function available with this signature:
23 | *
24 | * extern "C" void lmic_aes_encrypt(u1_t *data, u1_t *key);
25 | *
26 | * That takes a single 16-byte buffer and encrypts it wit the given
27 | * 16-byte key.
28 | */
29 |
30 | #include "../lmic/oslmic.h"
31 |
32 | #if !defined(USE_ORIGINAL_AES)
33 |
34 | // This should be defined elsewhere
35 | void lmic_aes_encrypt(u1_t *data, u1_t *key);
36 |
37 | // global area for passing parameters (aux, key)
38 | u4_t AESAUX[16/sizeof(u4_t)];
39 | u4_t AESKEY[16/sizeof(u4_t)];
40 |
41 | // Shift the given buffer left one bit
42 | static void shift_left(xref2u1_t buf, u1_t len) {
43 | while (len--) {
44 | u1_t next = len ? buf[1] : 0;
45 |
46 | u1_t val = (*buf << 1);
47 | if (next & 0x80)
48 | val |= 1;
49 | *buf++ = val;
50 | }
51 | }
52 |
53 | // Apply RFC4493 CMAC, using AESKEY as the key. If prepend_aux is true,
54 | // AESAUX is prepended to the message. AESAUX is used as working memory
55 | // in any case. The CMAC result is returned in AESAUX as well.
56 | static void os_aes_cmac(xref2u1_t buf, u2_t len, u1_t prepend_aux) {
57 | if (prepend_aux)
58 | lmic_aes_encrypt(AESaux, AESkey);
59 | else
60 | memset (AESaux, 0, 16);
61 |
62 | while (len > 0) {
63 | u1_t need_padding = 0;
64 | for (u1_t i = 0; i < 16; ++i, ++buf, --len) {
65 | if (len == 0) {
66 | // The message is padded with 0x80 and then zeroes.
67 | // Since zeroes are no-op for xor, we can just skip them
68 | // and leave AESAUX unchanged for them.
69 | AESaux[i] ^= 0x80;
70 | need_padding = 1;
71 | break;
72 | }
73 | AESaux[i] ^= *buf;
74 | }
75 |
76 | if (len == 0) {
77 | // Final block, xor with K1 or K2. K1 and K2 are calculated
78 | // by encrypting the all-zeroes block and then applying some
79 | // shifts and xor on that.
80 | u1_t final_key[16];
81 | memset(final_key, 0, sizeof(final_key));
82 | lmic_aes_encrypt(final_key, AESkey);
83 |
84 | // Calculate K1
85 | u1_t msb = final_key[0] & 0x80;
86 | shift_left(final_key, sizeof(final_key));
87 | if (msb)
88 | final_key[sizeof(final_key)-1] ^= 0x87;
89 |
90 | // If the final block was not complete, calculate K2 from K1
91 | if (need_padding) {
92 | msb = final_key[0] & 0x80;
93 | shift_left(final_key, sizeof(final_key));
94 | if (msb)
95 | final_key[sizeof(final_key)-1] ^= 0x87;
96 | }
97 |
98 | // Xor with K1 or K2
99 | for (u1_t i = 0; i < sizeof(final_key); ++i)
100 | AESaux[i] ^= final_key[i];
101 | }
102 |
103 | lmic_aes_encrypt(AESaux, AESkey);
104 | }
105 | }
106 |
107 | // Run AES-CTR using the key in AESKEY and using AESAUX as the
108 | // counter block. The last byte of the counter block will be incremented
109 | // for every block. The given buffer will be encrypted in place.
110 | static void os_aes_ctr (xref2u1_t buf, u2_t len) {
111 | u1_t ctr[16];
112 | while (len) {
113 | // Encrypt the counter block with the selected key
114 | memcpy(ctr, AESaux, sizeof(ctr));
115 | lmic_aes_encrypt(ctr, AESkey);
116 |
117 | // Xor the payload with the resulting ciphertext
118 | for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++)
119 | *buf ^= ctr[i];
120 |
121 | // Increment the block index byte
122 | AESaux[15]++;
123 | }
124 | }
125 |
126 | u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len) {
127 | switch (mode & ~AES_MICNOAUX) {
128 | case AES_MIC:
129 | os_aes_cmac(buf, len, /* prepend_aux */ !(mode & AES_MICNOAUX));
130 | return os_rmsbf4(AESaux);
131 |
132 | case AES_ENC:
133 | // TODO: Check / handle when len is not a multiple of 16
134 | for (u1_t i = 0; i < len; i += 16)
135 | lmic_aes_encrypt(buf+i, AESkey);
136 | break;
137 |
138 | case AES_CTR:
139 | os_aes_ctr(buf, len);
140 | break;
141 | }
142 | return 0;
143 | }
144 |
145 | #endif // !defined(USE_ORIGINAL_AES)
146 |
--------------------------------------------------------------------------------
/src/hal/hal.c:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2015 Matthijs Kooijman
3 | * Copyright (c) 2019 Tobias Schramm
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * which accompanies this distribution, and is available at
7 | * http://www.eclipse.org/legal/epl-v10.html
8 | *
9 | * Thi is the HAL to run LMIC on top of the ESP-IDF environment
10 | *******************************************************************************/
11 |
12 | #include "../include/lmic.h"
13 | #include "hal.h"
14 |
15 | #include "freertos/FreeRTOS.h"
16 | #include "freertos/task.h"
17 | #include "driver/gpio.h"
18 | #include "driver/spi_master.h"
19 | #include "driver/timer.h"
20 | #include "esp_log.h"
21 |
22 | #define TAG "lmic"
23 |
24 | // Declared here, to be defined an initialized by the application
25 | extern const lmic_pinmap lmic_pins;
26 |
27 | // -----------------------------------------------------------------------------
28 | // I/O
29 |
30 | static void hal_io_init () {
31 | int i;
32 | ESP_LOGI(TAG, "Starting IO initialization");
33 |
34 | gpio_config_t io_conf;
35 | io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
36 | io_conf.mode = GPIO_MODE_OUTPUT;
37 | io_conf.pin_bit_mask = 1< tx 1
58 | void hal_pin_rxtx (u1_t val) {
59 | if (lmic_pins.rxtx != LMIC_UNUSED_PIN)
60 | gpio_set_level(lmic_pins.rxtx, val);
61 | }
62 |
63 | // set radio NSS pin to given value
64 | void hal_pin_nss (u1_t val) {
65 | gpio_set_level(lmic_pins.nss, val);
66 | }
67 |
68 | // set radio RST pin to given value (or keep floating!)
69 | void hal_pin_rst (u1_t val) {
70 | gpio_config_t io_conf;
71 | io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
72 | io_conf.pin_bit_mask = (1< 2000){
199 | vTaskDelay(1 / portTICK_PERIOD_MS);
200 | delta -= 1000;
201 | } if(delta > 0){
202 | vTaskDelay(delta / portTICK_PERIOD_MS);
203 | }
204 | ESP_LOGI(TAG, "Done waiting until");
205 | }
206 |
207 | // check and rewind for target time
208 | u1_t hal_checkTimer (u4_t time) {
209 | return delta_time(time) <= 0;
210 | }
211 |
212 | // -----------------------------------------------------------------------------
213 | // IRQ
214 |
215 | int x_irq_level = 0;
216 |
217 | void hal_disableIRQs () {
218 | //ESP_LOGD(TAG, "Disabling interrupts");
219 | if(x_irq_level < 1){
220 | //taskDISABLE_INTERRUPTS();
221 | }
222 | x_irq_level++;
223 | }
224 |
225 | void hal_enableIRQs () {
226 | //ESP_LOGD(TAG, "Enable interrupts");
227 | if(--x_irq_level == 0){
228 | //taskENABLE_INTERRUPTS();
229 | hal_io_check();
230 | }
231 | }
232 |
233 | void hal_sleep () {
234 | // unused
235 | }
236 |
237 | // -----------------------------------------------------------------------------
238 |
239 | #if defined(LMIC_PRINTF_TO)
240 | static int uart_putchar (char c, FILE *)
241 | {
242 | putc(c);
243 | // printf("%c", c);
244 | return 0 ;
245 | }
246 |
247 | void hal_printf_init() {
248 | // nop, init done
249 | }
250 | #endif // defined(LMIC_PRINTF_TO)
251 |
252 | void hal_init() {
253 | // configure radio I/O and interrupt handler
254 | hal_io_init();
255 | // configure radio SPI
256 | hal_spi_init();
257 | // configure timer and interrupt handler
258 | hal_time_init();
259 | }
260 |
261 | void hal_failed(const char *file, u2_t line) {
262 | // HALT...
263 | ESP_LOGE(TAG, "LMIC HAL failed (%s:%u)", file, line);
264 | hal_disableIRQs();
265 | hal_sleep();
266 | while(1);
267 | }
268 |
--------------------------------------------------------------------------------
/src/hal/hal.h:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2015 Matthijs Kooijman
3 | * Copyright (c) 2019 Tobias Schramm
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * which accompanies this distribution, and is available at
7 | * http://www.eclipse.org/legal/epl-v10.html
8 | *
9 | * This is the HAL to run LMIC on top of the esp-idf environment
10 | *******************************************************************************/
11 | #ifndef _hal_hal_h_
12 | #define _hal_hal_h_
13 |
14 | #define NUM_DIO 3
15 |
16 | struct lmic_pinmap {
17 | u1_t nss;
18 | u1_t rxtx;
19 | u1_t rst;
20 | u1_t dio[NUM_DIO];
21 | u1_t spi[3]; // MISO, MOSI, SCK
22 | };
23 |
24 | typedef struct lmic_pinmap lmic_pinmap;
25 |
26 | // Use this for any unused pins.
27 | #define LMIC_UNUSED_PIN 0xFF
28 |
29 | #endif // _hal_hal_h_
30 |
--------------------------------------------------------------------------------
/src/lmic/config.h:
--------------------------------------------------------------------------------
1 | #ifndef _lmic_config_h_
2 | #define _lmic_config_h_
3 |
4 | #include "sdkconfig.h"
5 |
6 | // In the original LMIC code, these config values were defined on the
7 | // gcc commandline. Since Arduino does not allow easily modifying the
8 | // compiler commandline, use this file instead.
9 |
10 | #ifdef CONFIG_LMIC_FREQ_USE_EU_868
11 | #define CFG_eu868 1
12 | #endif
13 |
14 | #ifdef CONFIG_LMIC_FREQ_USE_US_915
15 | #define CFG_us915 1
16 | #endif
17 | // This is the SX1272/SX1273 radio, which is also used on the HopeRF
18 | // RFM92 boards.
19 | #ifdef CONFIG_LMIC_USE_SX1272
20 | #define CFG_sx1272_radio 1
21 | #endif
22 | // This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on
23 | // the HopeRF RFM95 boards.
24 | #ifdef CONFIG_LMIC_USE_SX1276
25 | #define CFG_sx1276_radio 1
26 | #endif
27 |
28 | // 20 μs per tick
29 | // LMIC requires ticks to be 15.5μs - 100 μs long
30 | #define OSTICKS_PER_SEC 50000
31 |
32 | // SPI interface used by LMIC
33 | #ifdef CONFIG_LMIC_SPI_HSPI
34 | #define LMIC_SPI HSPI_HOST
35 | #endif
36 |
37 | #ifdef CONFIG_LMIC_SPI_VSPI
38 | #define LMIC_SPI VSPI_HOST
39 | #endif
40 |
41 | // Set this to 1 to enable some basic debug output (using printf) about
42 | // RF settings used during transmission and reception. Set to 2 to
43 | // enable more verbose output. Make sure that printf is actually
44 | // configured (e.g. on AVR it is not by default), otherwise using it can
45 | // cause crashing.
46 | #define LMIC_DEBUG_LEVEL CONFIG_LMIC_DEBUG_LEVEL
47 |
48 | // Uncomment this to disable all code related to joining
49 | #ifndef CONFIG_LMIC_FEATURE_JOIN
50 | #define DISABLE_JOIN
51 | #endif
52 | // Uncomment this to disable all code related to ping
53 | #ifndef CONFIG_LMIC_FEATURE_PING
54 | #define DISABLE_PING
55 | #endif
56 | // Uncomment this to disable all code related to beacon tracking.
57 | // Requires ping to be disabled too
58 | #ifndef CONFIG_LMIC_FEATURE_BEACONS
59 | #define DISABLE_BEACONS
60 | #endif
61 |
62 | // Uncomment these to disable the corresponding MAC commands.
63 | // Class A
64 | //#define DISABLE_MCMD_DCAP_REQ // duty cycle cap
65 | //#define DISABLE_MCMD_DN2P_SET // 2nd DN window param
66 | //#define DISABLE_MCMD_SNCH_REQ // set new channel
67 | // Class B
68 | //#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING
69 | //#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical disabled by DISABLE_BEACON
70 |
71 | // In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the
72 | // same on RX. This ensures that gateways can talk to nodes and vice
73 | // versa, but gateways will not hear other gateways and nodes will not
74 | // hear other nodes. By uncommenting this macro, this inversion is
75 | // disabled and this node can hear other nodes. If two nodes both have
76 | // this macro set, they can talk to each other (but they can no longer
77 | // hear gateways). This should probably only be used when debugging
78 | // and/or when talking to the radio directly (e.g. like in the "raw"
79 | // example).
80 | //#define DISABLE_INVERT_IQ_ON_RX
81 |
82 | // This allows choosing between multiple included AES implementations.
83 | // Make sure exactly one of these is uncommented.
84 | //
85 | // This selects the original AES implementation included LMIC. This
86 | // implementation is optimized for speed on 32-bit processors using
87 | // fairly big lookup tables, but it takes up big amounts of flash on the
88 | // AVR architecture.
89 | #define USE_ORIGINAL_AES
90 | //
91 | // This selects the AES implementation written by Ideetroon for their
92 | // own LoRaWAN library. It also uses lookup tables, but smaller
93 | // byte-oriented ones, making it use a lot less flash space (but it is
94 | // also about twice as slow as the original).
95 | // #define USE_IDEETRON_AES
96 |
97 | #endif // _lmic_config_h_
98 |
--------------------------------------------------------------------------------
/src/lmic/hal.h:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2014-2015 IBM Corporation.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Eclipse Public License v1.0
5 | * which accompanies this distribution, and is available at
6 | * http://www.eclipse.org/legal/epl-v10.html
7 | *
8 | * Contributors:
9 | * IBM Zurich Research Lab - initial API, implementation and documentation
10 | *******************************************************************************/
11 |
12 | #ifndef _hal_hpp_
13 | #define _hal_hpp_
14 |
15 | #ifdef __cplusplus
16 | extern "C"{
17 | #endif
18 |
19 | /*
20 | * initialize hardware (IO, SPI, TIMER, IRQ).
21 | */
22 | void hal_init (void);
23 |
24 | /*
25 | * drive radio NSS pin (0=low, 1=high).
26 | */
27 | void hal_pin_nss (u1_t val);
28 |
29 | /*
30 | * drive radio RX/TX pins (0=rx, 1=tx).
31 | */
32 | void hal_pin_rxtx (u1_t val);
33 |
34 | /*
35 | * control radio RST pin (0=low, 1=high, 2=floating)
36 | */
37 | void hal_pin_rst (u1_t val);
38 |
39 | /*
40 | * perform 8-bit SPI transaction with radio.
41 | * - write given byte 'outval'
42 | * - read byte and return value
43 | */
44 | u1_t hal_spi (u1_t outval);
45 |
46 | /*
47 | * disable all CPU interrupts.
48 | * - might be invoked nested
49 | * - will be followed by matching call to hal_enableIRQs()
50 | */
51 | void hal_disableIRQs (void);
52 |
53 | /*
54 | * enable CPU interrupts.
55 | */
56 | void hal_enableIRQs (void);
57 |
58 | /*
59 | * put system and CPU in low-power mode, sleep until interrupt.
60 | */
61 | void hal_sleep (void);
62 |
63 | /*
64 | * return 32-bit system time in ticks.
65 | */
66 | u4_t hal_ticks (void);
67 |
68 | /*
69 | * busy-wait until specified timestamp (in ticks) is reached.
70 | */
71 | void hal_waitUntil (u4_t time);
72 |
73 | /*
74 | * check and rewind timer for target time.
75 | * - return 1 if target time is close
76 | * - otherwise rewind timer for target time or full period and return 0
77 | */
78 | u1_t hal_checkTimer (u4_t targettime);
79 |
80 | /*
81 | * perform fatal failure action.
82 | * - called by assertions
83 | * - action could be HALT or reboot
84 | */
85 | void hal_failed (const char *file, u2_t line);
86 |
87 | #ifdef __cplusplus
88 | } // extern "C"
89 | #endif
90 |
91 | #endif // _hal_hpp_
92 |
--------------------------------------------------------------------------------
/src/lmic/lmic.h:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2014-2015 IBM Corporation.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Eclipse Public License v1.0
5 | * which accompanies this distribution, and is available at
6 | * http://www.eclipse.org/legal/epl-v10.html
7 | *
8 | * Contributors:
9 | * IBM Zurich Research Lab - initial API, implementation and documentation
10 | *******************************************************************************/
11 |
12 | //! @file
13 | //! @brief LMIC API
14 |
15 | #ifndef _lmic_h_
16 | #define _lmic_h_
17 |
18 | #include "oslmic.h"
19 | #include "lorabase.h"
20 |
21 | #include "../hal/hal.h"
22 |
23 | #ifdef __cplusplus
24 | extern "C"{
25 | #endif
26 |
27 | // LMIC version
28 | #define LMIC_VERSION_MAJOR 1
29 | #define LMIC_VERSION_MINOR 5
30 | #define LMIC_VERSION_BUILD 1431528305
31 |
32 | enum { MAX_FRAME_LEN = 64 }; //!< Library cap on max frame length
33 | enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames
34 | enum { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests
35 | enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this
36 |
37 | enum { LINK_CHECK_CONT = 12 , // continue with this after reported dead link
38 | LINK_CHECK_DEAD = 24 , // after this UP frames and no response from NWK assume link is dead
39 | LINK_CHECK_INIT = -12 , // UP frame count until we inc datarate
40 | LINK_CHECK_OFF =-128 }; // link check disabled
41 |
42 | enum { TIME_RESYNC = 6*128 }; // secs
43 | enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon
44 | enum { JOIN_GUARD_ms = 9000 }; // msecs - don't start Join Req/Acc transaction before beacon
45 | enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time
46 | enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send
47 |
48 | #if defined(CFG_eu868) // EU868 spectrum ====================================================
49 |
50 | enum { MAX_CHANNELS = 16 }; //!< Max supported channels
51 | enum { MAX_BANDS = 4 };
52 |
53 | enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels
54 | //! \internal
55 | struct band_t {
56 | u2_t txcap; // duty cycle limitation: 1/txcap
57 | s1_t txpow; // maximum TX power
58 | u1_t lastchnl; // last used channel
59 | ostime_t avail; // channel is blocked until this time
60 | };
61 | TYPEDEF_xref2band_t; //!< \internal
62 |
63 | #elif defined(CFG_us915) // US915 spectrum =================================================
64 |
65 | enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable
66 | enum { MAX_TXPOW_125kHz = 30 };
67 |
68 | #endif // ==========================================================================
69 |
70 | // Keep in sync with evdefs.hpp::drChange
71 | enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD };
72 | enum { KEEP_TXPOW = -128 };
73 |
74 |
75 | #if !defined(DISABLE_PING)
76 | //! \internal
77 | struct rxsched_t {
78 | u1_t dr;
79 | u1_t intvExp; // 0..7
80 | u1_t slot; // runs from 0 to 128
81 | u1_t rxsyms;
82 | ostime_t rxbase;
83 | ostime_t rxtime; // start of next spot
84 | u4_t freq;
85 | };
86 | TYPEDEF_xref2rxsched_t; //!< \internal
87 | #endif // !DISABLE_PING
88 |
89 |
90 | #if !defined(DISABLE_BEACONS)
91 | //! Parsing and tracking states of beacons.
92 | enum { BCN_NONE = 0x00, //!< No beacon received
93 | BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous)
94 | BCN_FULL = 0x02, //!< Full beacon decoded
95 | BCN_NODRIFT = 0x04, //!< No drift value measured yet
96 | BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet
97 | //! Information about the last and previous beacons.
98 | struct bcninfo_t {
99 | ostime_t txtime; //!< Time when the beacon was sent
100 | s1_t rssi; //!< Adjusted RSSI value of last received beacon
101 | s1_t snr; //!< Scaled SNR value of last received beacon
102 | u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values.
103 | u4_t time; //!< GPS time in seconds of last beacon (received or surrogate)
104 | //
105 | u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set)
106 | s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set)
107 | s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set)
108 | };
109 | #endif // !DISABLE_BEACONS
110 |
111 | // purpose of receive window - lmic_t.rxState
112 | enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 };
113 | // Netid values / lmic_t.netid
114 | enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF };
115 | // MAC operation modes (lmic_t.opmode).
116 | enum { OP_NONE = 0x0000,
117 | OP_SCAN = 0x0001, // radio scan to find a beacon
118 | OP_TRACK = 0x0002, // track my networks beacon (netid)
119 | OP_JOINING = 0x0004, // device joining in progress (blocks other activities)
120 | OP_TXDATA = 0x0008, // TX user data (buffered in pendTxData)
121 | OP_POLL = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data
122 | OP_REJOIN = 0x0020, // occasionally send JOIN REQUEST
123 | OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything
124 | OP_TXRXPEND = 0x0080, // TX/RX transaction pending
125 | OP_RNDTX = 0x0100, // prevent TX lining up after a beacon
126 | OP_PINGINI = 0x0200, // pingable is initialized and scheduling active
127 | OP_PINGABLE = 0x0400, // we're pingable
128 | OP_NEXTCHNL = 0x0800, // find a new channel
129 | OP_LINKDEAD = 0x1000, // link was reported as dead
130 | OP_TESTMODE = 0x2000, // developer test mode
131 | };
132 | // TX-RX transaction flags - report back to user
133 | enum { TXRX_ACK = 0x80, // confirmed UP frame was acked
134 | TXRX_NACK = 0x40, // confirmed UP frame was not acked
135 | TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port
136 | TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port
137 | TXRX_DNW1 = 0x01, // received in 1st DN slot
138 | TXRX_DNW2 = 0x02, // received in 2dn DN slot
139 | TXRX_PING = 0x04 }; // received in a scheduled RX slot
140 | // Event types for event callback
141 | enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND,
142 | EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING,
143 | EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED,
144 | EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET,
145 | EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE };
146 | typedef enum _ev_t ev_t;
147 |
148 | enum {
149 | // This value represents 100% error in LMIC.clockError
150 | MAX_CLOCK_ERROR = 65536,
151 | };
152 |
153 | struct lmic_t {
154 | // Radio settings TX/RX (also accessed by HAL)
155 | ostime_t txend;
156 | ostime_t rxtime;
157 | u4_t freq;
158 | s1_t rssi;
159 | s1_t snr;
160 | rps_t rps;
161 | u1_t rxsyms;
162 | u1_t dndr;
163 | s1_t txpow; // dBm
164 |
165 | osjob_t osjob;
166 |
167 | // Channel scheduling
168 | #if defined(CFG_eu868)
169 | band_t bands[MAX_BANDS];
170 | u4_t channelFreq[MAX_CHANNELS];
171 | u2_t channelDrMap[MAX_CHANNELS];
172 | u2_t channelMap;
173 | #elif defined(CFG_us915)
174 | u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater)
175 | u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto
176 | u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits
177 | u2_t chRnd; // channel randomizer
178 | #endif
179 | u1_t txChnl; // channel for next TX
180 | u1_t globalDutyRate; // max rate: 1/2^k
181 | ostime_t globalDutyAvail; // time device can send again
182 |
183 | u4_t netid; // current network id (~0 - none)
184 | u2_t opmode;
185 | u1_t upRepeat; // configured up repeat
186 | s1_t adrTxPow; // ADR adjusted TX power
187 | u1_t datarate; // current data rate
188 | u1_t errcr; // error coding rate (used for TX only)
189 | u1_t rejoinCnt; // adjustment for rejoin datarate
190 | #if !defined(DISABLE_BEACONS)
191 | s2_t drift; // last measured drift
192 | s2_t lastDriftDiff;
193 | s2_t maxDriftDiff;
194 | #endif
195 |
196 | u2_t clockError; // Inaccuracy in the clock. CLOCK_ERROR_MAX
197 | // represents +/-100% error
198 |
199 | u1_t pendTxPort;
200 | u1_t pendTxConf; // confirmed data
201 | u1_t pendTxLen; // +0x80 = confirmed
202 | u1_t pendTxData[MAX_LEN_PAYLOAD];
203 |
204 | u2_t devNonce; // last generated nonce
205 | u1_t nwkKey[16]; // network session key
206 | u1_t artKey[16]; // application router session key
207 | devaddr_t devaddr;
208 | u4_t seqnoDn; // device level down stream seqno
209 | u4_t seqnoUp;
210 |
211 | u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0
212 | s1_t adrAckReq; // counter until we reset data rate (0=off)
213 | u1_t adrChanged;
214 |
215 | u1_t rxDelay; // Rx delay after TX
216 |
217 | u1_t margin;
218 | bit_t ladrAns; // link adr adapt answer pending
219 | bit_t devsAns; // device status answer pending
220 | u1_t adrEnabled;
221 | u1_t moreData; // NWK has more data pending
222 | #if !defined(DISABLE_MCMD_DCAP_REQ)
223 | bit_t dutyCapAns; // have to ACK duty cycle settings
224 | #endif
225 | #if !defined(DISABLE_MCMD_SNCH_REQ)
226 | u1_t snchAns; // answer set new channel
227 | #endif
228 | // 2nd RX window (after up stream)
229 | u1_t dn2Dr;
230 | u4_t dn2Freq;
231 | #if !defined(DISABLE_MCMD_DN2P_SET)
232 | u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs
233 | #endif
234 |
235 | // Class B state
236 | #if !defined(DISABLE_BEACONS)
237 | u1_t missedBcns; // unable to track last N beacons
238 | u1_t bcninfoTries; // how often to try (scan mode only)
239 | #endif
240 | #if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING)
241 | u1_t pingSetAns; // answer set cmd and ACK bits
242 | #endif
243 | #if !defined(DISABLE_PING)
244 | rxsched_t ping; // pingable setup
245 | #endif
246 |
247 | // Public part of MAC state
248 | u1_t txCnt;
249 | u1_t txrxFlags; // transaction flags (TX-RX combo)
250 | u1_t dataBeg; // 0 or start of data (dataBeg-1 is port)
251 | u1_t dataLen; // 0 no data or zero length data, >0 byte count of data
252 | u1_t frame[MAX_LEN_FRAME];
253 |
254 | #if !defined(DISABLE_BEACONS)
255 | u1_t bcnChnl;
256 | u1_t bcnRxsyms; //
257 | ostime_t bcnRxtime;
258 | bcninfo_t bcninfo; // Last received beacon info
259 | #endif
260 | };
261 | //! \var struct lmic_t LMIC
262 | //! The state of LMIC MAC layer is encapsulated in this variable.
263 | DECLARE_LMIC; //!< \internal
264 |
265 | //! Construct a bit map of allowed datarates from drlo to drhi (both included).
266 | #define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi))))
267 | #if defined(CFG_eu868)
268 | enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2, BAND_AUX=3 };
269 | bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap);
270 | #endif
271 | bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band);
272 | void LMIC_disableChannel (u1_t channel);
273 | #if defined(CFG_us915)
274 | void LMIC_enableChannel (u1_t channel);
275 | void LMIC_enableSubBand (u1_t band);
276 | void LMIC_disableSubBand (u1_t band);
277 | void LMIC_selectSubBand (u1_t band);
278 | #endif
279 |
280 | void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow
281 | void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off)
282 | #if !defined(DISABLE_JOIN)
283 | bit_t LMIC_startJoining (void);
284 | #endif
285 |
286 | void LMIC_shutdown (void);
287 | void LMIC_init (void);
288 | void LMIC_reset (void);
289 | void LMIC_clrTxData (void);
290 | void LMIC_setTxData (void);
291 | int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
292 | void LMIC_sendAlive (void);
293 |
294 | #if !defined(DISABLE_BEACONS)
295 | bit_t LMIC_enableTracking (u1_t tryBcnInfo);
296 | void LMIC_disableTracking (void);
297 | #endif
298 |
299 | #if !defined(DISABLE_PING)
300 | void LMIC_stopPingable (void);
301 | void LMIC_setPingable (u1_t intvExp);
302 | #endif
303 | #if !defined(DISABLE_JOIN)
304 | void LMIC_tryRejoin (void);
305 | #endif
306 |
307 | void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
308 | void LMIC_setLinkCheckMode (bit_t enabled);
309 | void LMIC_setClockError(u2_t error);
310 |
311 | // Declare onEvent() function, to make sure any definition will have the
312 | // C conventions, even when in a C++ file.
313 | DECL_ON_LMIC_EVENT;
314 |
315 | // Special APIs - for development or testing
316 | // !!!See implementation for caveats!!!
317 |
318 | #ifdef __cplusplus
319 | } // extern "C"
320 | #endif
321 |
322 | #endif // _lmic_h_
323 |
--------------------------------------------------------------------------------
/src/lmic/lorabase.h:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2014-2015 IBM Corporation.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Eclipse Public License v1.0
5 | * which accompanies this distribution, and is available at
6 | * http://www.eclipse.org/legal/epl-v10.html
7 | *
8 | * Contributors:
9 | * IBM Zurich Research Lab - initial API, implementation and documentation
10 | *******************************************************************************/
11 |
12 | #ifndef _lorabase_h_
13 | #define _lorabase_h_
14 |
15 | #ifdef __cplusplus
16 | extern "C"{
17 | #endif
18 |
19 | // ================================================================================
20 | // BEG: Keep in sync with lorabase.hpp
21 | //
22 |
23 | enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 };
24 | enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu };
25 | enum _bw_t { BW125=0, BW250, BW500, BWrfu };
26 | typedef u1_t cr_t;
27 | typedef u1_t sf_t;
28 | typedef u1_t bw_t;
29 | typedef u1_t dr_t;
30 | // Radio parameter set (encodes SF/BW/CR/IH/NOCRC)
31 | typedef u2_t rps_t;
32 | TYPEDEF_xref2rps_t;
33 |
34 | enum { ILLEGAL_RPS = 0xFF };
35 | enum { DR_PAGE_EU868 = 0x00 };
36 | enum { DR_PAGE_US915 = 0x10 };
37 |
38 | // Global maximum frame length
39 | enum { STD_PREAMBLE_LEN = 8 };
40 | enum { MAX_LEN_FRAME = 64 };
41 | enum { LEN_DEVNONCE = 2 };
42 | enum { LEN_ARTNONCE = 3 };
43 | enum { LEN_NETID = 3 };
44 | enum { DELAY_JACC1 = 5 }; // in secs
45 | enum { DELAY_DNW1 = 1 }; // in secs down window #1
46 | enum { DELAY_EXTDNW2 = 1 }; // in secs
47 | enum { DELAY_JACC2 = DELAY_JACC1+(int)DELAY_EXTDNW2 }; // in secs
48 | enum { DELAY_DNW2 = DELAY_DNW1 +(int)DELAY_EXTDNW2 }; // in secs down window #1
49 | enum { BCN_INTV_exp = 7 };
50 | enum { BCN_INTV_sec = 1<> 3) & 0x3); }
350 | inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); }
351 | inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); }
352 | inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); }
353 | inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); }
354 | inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); }
355 | inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); }
356 | inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); }
357 | inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) {
358 | return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8);
359 | }
360 | #define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8)))
361 | // Two frames with params r1/r2 would interfere on air: same SFx + BWx
362 | inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; }
363 |
364 | extern CONST_TABLE(u1_t, _DR2RPS_CRC)[];
365 | inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); }
366 | inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); }
367 | inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; }
368 | inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; }
369 | inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate
370 | inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate
371 | inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? DR_DFLTMIN : dr; } // force into a valid DR
372 | inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range
373 | inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps
374 |
375 | //
376 | // BEG: Keep in sync with lorabase.hpp
377 | // ================================================================================
378 |
379 |
380 | // Convert between dBm values and power codes (MCMD_LADR_XdBm)
381 | s1_t pow2dBm (u1_t mcmd_ladr_p1);
382 | // Calculate airtime
383 | ostime_t calcAirTime (rps_t rps, u1_t plen);
384 | // Sensitivity at given SF/BW
385 | int getSensitivity (rps_t rps);
386 |
387 | #ifdef __cplusplus
388 | } // extern "C"
389 | #endif
390 |
391 | #endif // _lorabase_h_
392 |
--------------------------------------------------------------------------------
/src/lmic/oslmic.c:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2014-2015 IBM Corporation.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Eclipse Public License v1.0
5 | * which accompanies this distribution, and is available at
6 | * http://www.eclipse.org/legal/epl-v10.html
7 | *
8 | * Contributors:
9 | * IBM Zurich Research Lab - initial API, implementation and documentation
10 | *******************************************************************************/
11 |
12 | #include "lmic.h"
13 | #include
14 |
15 | // RUNTIME STATE
16 | static struct {
17 | osjob_t* scheduledjobs;
18 | osjob_t* runnablejobs;
19 | } OS;
20 |
21 | void os_init () {
22 | memset(&OS, 0x00, sizeof(OS));
23 | hal_init();
24 | radio_init();
25 | LMIC_init();
26 | }
27 |
28 | ostime_t os_getTime () {
29 | return hal_ticks();
30 | }
31 |
32 | static u1_t unlinkjob (osjob_t** pnext, osjob_t* job) {
33 | for( ; *pnext; pnext = &((*pnext)->next)) {
34 | if(*pnext == job) { // unlink
35 | *pnext = job->next;
36 | return 1;
37 | }
38 | }
39 | return 0;
40 | }
41 |
42 | // clear scheduled job
43 | void os_clearCallback (osjob_t* job) {
44 | hal_disableIRQs();
45 | u1_t res = unlinkjob(&OS.scheduledjobs, job) || unlinkjob(&OS.runnablejobs, job);
46 | hal_enableIRQs();
47 | #if LMIC_DEBUG_LEVEL > 1
48 | if (res)
49 | lmic_printf("%u: Cleared job %p\n", os_getTime(), job);
50 | #endif
51 | }
52 |
53 | // schedule immediately runnable job
54 | void os_setCallback (osjob_t* job, osjobcb_t cb) {
55 | osjob_t** pnext;
56 | hal_disableIRQs();
57 | // remove if job was already queued
58 | os_clearCallback(job);
59 | // fill-in job
60 | job->func = cb;
61 | job->next = NULL;
62 | // add to end of run queue
63 | for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next));
64 | *pnext = job;
65 | hal_enableIRQs();
66 | #if LMIC_DEBUG_LEVEL > 1
67 | lmic_printf("%u: Scheduled job %p, cb %p ASAP\n", os_getTime(), job, cb);
68 | #endif
69 | }
70 |
71 | // schedule timed job
72 | void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) {
73 | osjob_t** pnext;
74 | hal_disableIRQs();
75 | // remove if job was already queued
76 | os_clearCallback(job);
77 | // fill-in job
78 | job->deadline = time;
79 | job->func = cb;
80 | job->next = NULL;
81 | // insert into schedule
82 | for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) {
83 | if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!)
84 | // enqueue before next element and stop
85 | job->next = *pnext;
86 | break;
87 | }
88 | }
89 | *pnext = job;
90 | hal_enableIRQs();
91 | #if LMIC_DEBUG_LEVEL > 1
92 | lmic_printf("%u: Scheduled job %p, cb %p at %u\n", os_getTime(), job, cb, time);
93 | #endif
94 | }
95 |
96 | // execute jobs from timer and from run queue
97 | void os_runloop () {
98 | while(1) {
99 | os_runloop_once();
100 | }
101 | }
102 |
103 | void os_runloop_once() {
104 | #if LMIC_DEBUG_LEVEL > 1
105 | bool has_deadline = false;
106 | #endif
107 | osjob_t* j = NULL;
108 | hal_disableIRQs();
109 | // check for runnable jobs
110 | if(OS.runnablejobs) {
111 | j = OS.runnablejobs;
112 | OS.runnablejobs = j->next;
113 | } else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs
114 | j = OS.scheduledjobs;
115 | OS.scheduledjobs = j->next;
116 | #if LMIC_DEBUG_LEVEL > 1
117 | has_deadline = true;
118 | #endif
119 | } else { // nothing pending
120 | hal_sleep(); // wake by irq (timer already restarted)
121 | }
122 | hal_enableIRQs();
123 | if(j) { // run job callback
124 | #if LMIC_DEBUG_LEVEL > 1
125 | lmic_printf("%u: Running job %p, cb %p, deadline %u\n", os_getTime(), j, j->func, has_deadline ? j->deadline : 0);
126 | #endif
127 | j->func(j);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/lmic/oslmic.h:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2014-2015 IBM Corporation.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Eclipse Public License v1.0
5 | * which accompanies this distribution, and is available at
6 | * http://www.eclipse.org/legal/epl-v10.html
7 | *
8 | * Contributors:
9 | * IBM Zurich Research Lab - initial API, implementation and documentation
10 | *******************************************************************************/
11 |
12 | //! \file
13 | #ifndef _oslmic_h_
14 | #define _oslmic_h_
15 |
16 | // Dependencies required for the LoRa MAC in C to run.
17 | // These settings can be adapted to the underlying system.
18 | // You should not, however, change the lmic.[hc]
19 |
20 | #include "config.h"
21 | #include
22 | #include
23 |
24 | #ifdef __cplusplus
25 | extern "C"{
26 | #endif
27 |
28 | //================================================================================
29 | //================================================================================
30 | // Target platform as C library
31 | typedef uint8_t bit_t;
32 | typedef uint8_t u1_t;
33 | typedef int8_t s1_t;
34 | typedef uint16_t u2_t;
35 | typedef int16_t s2_t;
36 | typedef uint32_t u4_t;
37 | typedef int32_t s4_t;
38 | typedef unsigned int uint;
39 | typedef const char* str_t;
40 |
41 | #include
42 | #include "hal.h"
43 | #define EV(a,b,c) /**/
44 | #define DO_DEVDB(field1,field2) /**/
45 | #if !defined(CFG_noassert)
46 | #define ASSERT(cond) if(!(cond)) hal_failed(__FILE__, __LINE__)
47 | #else
48 | #define ASSERT(cond) /**/
49 | #endif
50 |
51 | #define os_clearMem(a,b) memset(a,0,b)
52 | #define os_copyMem(a,b,c) memcpy(a,b,c)
53 |
54 | typedef struct osjob_t osjob_t;
55 | typedef struct band_t band_t;
56 | typedef struct chnldef_t chnldef_t;
57 | typedef struct rxsched_t rxsched_t;
58 | typedef struct bcninfo_t bcninfo_t;
59 | typedef const u1_t* xref2cu1_t;
60 | typedef u1_t* xref2u1_t;
61 | #define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t
62 | #define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t
63 | #define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t
64 | #define TYPEDEF_xref2band_t typedef band_t* xref2band_t
65 | #define TYPEDEF_xref2osjob_t typedef osjob_t* xref2osjob_t
66 |
67 | #define SIZEOFEXPR(x) sizeof(x)
68 |
69 | #define ON_LMIC_EVENT(ev) onEvent(ev)
70 | #define DECL_ON_LMIC_EVENT void onEvent(ev_t e)
71 |
72 | extern u4_t AESAUX[];
73 | extern u4_t AESKEY[];
74 | #define AESkey ((u1_t*)AESKEY)
75 | #define AESaux ((u1_t*)AESAUX)
76 | #define FUNC_ADDR(func) (&(func))
77 |
78 | u1_t radio_rand1 (void);
79 | #define os_getRndU1() radio_rand1()
80 |
81 | #define DEFINE_LMIC struct lmic_t LMIC
82 | #define DECLARE_LMIC extern struct lmic_t LMIC
83 |
84 | void radio_init (void);
85 | void radio_irq_handler (u1_t dio);
86 | void os_init (void);
87 | void os_runloop (void);
88 | void os_runloop_once (void);
89 |
90 | //================================================================================
91 |
92 |
93 | #ifndef RX_RAMPUP
94 | #define RX_RAMPUP (us2osticks(2000))
95 | #endif
96 | #ifndef TX_RAMPUP
97 | #define TX_RAMPUP (us2osticks(2000))
98 | #endif
99 |
100 | #ifndef OSTICKS_PER_SEC
101 | #define OSTICKS_PER_SEC 32768
102 | #elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516
103 | #error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long.
104 | #endif
105 |
106 | typedef s4_t ostime_t;
107 |
108 | #if !HAS_ostick_conv
109 | #define us2osticks(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC) / 1000000))
110 | #define ms2osticks(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC) / 1000))
111 | #define sec2osticks(sec) ((ostime_t)( (int64_t)(sec) * OSTICKS_PER_SEC))
112 | #define osticks2ms(os) ((s4_t)(((os)*(int64_t)1000 ) / OSTICKS_PER_SEC))
113 | #define osticks2us(os) ((s4_t)(((os)*(int64_t)1000000 ) / OSTICKS_PER_SEC))
114 | // Special versions
115 | #define us2osticksCeil(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 999999) / 1000000))
116 | #define us2osticksRound(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 500000) / 1000000))
117 | #define ms2osticksCeil(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 999) / 1000))
118 | #define ms2osticksRound(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 500) / 1000))
119 | #endif
120 |
121 |
122 | struct osjob_t; // fwd decl.
123 | typedef void (*osjobcb_t) (struct osjob_t*);
124 | struct osjob_t {
125 | struct osjob_t* next;
126 | ostime_t deadline;
127 | osjobcb_t func;
128 | };
129 | TYPEDEF_xref2osjob_t;
130 |
131 |
132 | #ifndef HAS_os_calls
133 |
134 | #ifndef os_getDevKey
135 | void os_getDevKey (xref2u1_t buf);
136 | #endif
137 | #ifndef os_getArtEui
138 | void os_getArtEui (xref2u1_t buf);
139 | #endif
140 | #ifndef os_getDevEui
141 | void os_getDevEui (xref2u1_t buf);
142 | #endif
143 | #ifndef os_setCallback
144 | void os_setCallback (xref2osjob_t job, osjobcb_t cb);
145 | #endif
146 | #ifndef os_setTimedCallback
147 | void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb);
148 | #endif
149 | #ifndef os_clearCallback
150 | void os_clearCallback (xref2osjob_t job);
151 | #endif
152 | #ifndef os_getTime
153 | ostime_t os_getTime (void);
154 | #endif
155 | #ifndef os_getTimeSecs
156 | uint os_getTimeSecs (void);
157 | #endif
158 | #ifndef os_radio
159 | void os_radio (u1_t mode);
160 | #endif
161 | #ifndef os_getBattLevel
162 | u1_t os_getBattLevel (void);
163 | #endif
164 |
165 | #ifndef os_rlsbf4
166 | //! Read 32-bit quantity from given pointer in little endian byte order.
167 | u4_t os_rlsbf4 (xref2cu1_t buf);
168 | #endif
169 | #ifndef os_wlsbf4
170 | //! Write 32-bit quntity into buffer in little endian byte order.
171 | void os_wlsbf4 (xref2u1_t buf, u4_t value);
172 | #endif
173 | #ifndef os_rmsbf4
174 | //! Read 32-bit quantity from given pointer in big endian byte order.
175 | u4_t os_rmsbf4 (xref2cu1_t buf);
176 | #endif
177 | #ifndef os_wmsbf4
178 | //! Write 32-bit quntity into buffer in big endian byte order.
179 | void os_wmsbf4 (xref2u1_t buf, u4_t value);
180 | #endif
181 | #ifndef os_rlsbf2
182 | //! Read 16-bit quantity from given pointer in little endian byte order.
183 | u2_t os_rlsbf2 (xref2cu1_t buf);
184 | #endif
185 | #ifndef os_wlsbf2
186 | //! Write 16-bit quntity into buffer in little endian byte order.
187 | void os_wlsbf2 (xref2u1_t buf, u2_t value);
188 | #endif
189 |
190 | //! Get random number (default impl for u2_t).
191 | #ifndef os_getRndU2
192 | #define os_getRndU2() ((u2_t)((os_getRndU1()<<8)|os_getRndU1()))
193 | #endif
194 | #ifndef os_crc16
195 | u2_t os_crc16 (xref2u1_t d, uint len);
196 | #endif
197 |
198 | #endif // !HAS_os_calls
199 |
200 | // ======================================================================
201 | // Table support
202 | // These macros for defining a table of constants and retrieving values
203 | // from it makes it easier for other platforms (like AVR) to optimize
204 | // table accesses.
205 | // Use CONST_TABLE() whenever declaring or defining a table, and
206 | // TABLE_GET_xx whenever accessing its values. The actual name of the
207 | // declared variable will be modified to prevent accidental direct
208 | // access. The accessor macros forward to an inline function to allow
209 | // proper type checking of the array element type.
210 |
211 | // Helper to add a prefix to the table name
212 | #define RESOLVE_TABLE(table) constant_table_ ## table
213 |
214 | // Accessors for table elements
215 | #define TABLE_GET_U1(table, index) table_get_u1(RESOLVE_TABLE(table), index)
216 | #define TABLE_GET_S1(table, index) table_get_s1(RESOLVE_TABLE(table), index)
217 | #define TABLE_GET_U2(table, index) table_get_u2(RESOLVE_TABLE(table), index)
218 | #define TABLE_GET_S2(table, index) table_get_s2(RESOLVE_TABLE(table), index)
219 | #define TABLE_GET_U4(table, index) table_get_u4(RESOLVE_TABLE(table), index)
220 | #define TABLE_GET_S4(table, index) table_get_s4(RESOLVE_TABLE(table), index)
221 | #define TABLE_GET_OSTIME(table, index) table_get_ostime(RESOLVE_TABLE(table), index)
222 | #define TABLE_GET_U1_TWODIM(table, index1, index2) table_get_u1(RESOLVE_TABLE(table)[index1], index2)
223 |
224 | #if defined(__AVR__)
225 | #include
226 | // Macro to define the getter functions. This loads data from
227 | // progmem using pgm_read_xx, or accesses memory directly when the
228 | // index is a constant so gcc can optimize it away;
229 | #define TABLE_GETTER(postfix, type, pgm_type) \
230 | inline type table_get ## postfix(const type *table, size_t index) { \
231 | if (__builtin_constant_p(table[index])) \
232 | return table[index]; \
233 | return pgm_read_ ## pgm_type(&table[index]); \
234 | }
235 |
236 | TABLE_GETTER(_u1, u1_t, byte);
237 | TABLE_GETTER(_s1, s1_t, byte);
238 | TABLE_GETTER(_u2, u2_t, word);
239 | TABLE_GETTER(_s2, s2_t, word);
240 | TABLE_GETTER(_u4, u4_t, dword);
241 | TABLE_GETTER(_s4, s4_t, dword);
242 |
243 | // This assumes ostime_t is 4 bytes, so error out if it is not
244 | typedef int check_sizeof_ostime_t[(sizeof(ostime_t) == 4) ? 0 : -1];
245 | TABLE_GETTER(_ostime, ostime_t, dword);
246 |
247 | // For AVR, store constants in PROGMEM, saving on RAM usage
248 | #define CONST_TABLE(type, name) const type PROGMEM RESOLVE_TABLE(name)
249 |
250 | #define lmic_printf(fmt, ...) printf_P(PSTR(fmt), ## __VA_ARGS__)
251 | #else
252 | inline u1_t table_get_u1(const u1_t *table, size_t index) { return table[index]; }
253 | inline s1_t table_get_s1(const s1_t *table, size_t index) { return table[index]; }
254 | inline u2_t table_get_u2(const u2_t *table, size_t index) { return table[index]; }
255 | inline s2_t table_get_s2(const s2_t *table, size_t index) { return table[index]; }
256 | inline u4_t table_get_u4(const u4_t *table, size_t index) { return table[index]; }
257 | inline s4_t table_get_s4(const s4_t *table, size_t index) { return table[index]; }
258 | inline ostime_t table_get_ostime(const ostime_t *table, size_t index) { return table[index]; }
259 |
260 | // Declare a table
261 | #define CONST_TABLE(type, name) const type RESOLVE_TABLE(name)
262 | #define lmic_printf printf
263 | #endif
264 |
265 | // ======================================================================
266 | // AES support
267 | // !!Keep in sync with lorabase.hpp!!
268 |
269 | #ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too
270 | #define AES_ENC 0x00
271 | #define AES_DEC 0x01
272 | #define AES_MIC 0x02
273 | #define AES_CTR 0x04
274 | #define AES_MICNOAUX 0x08
275 | #endif
276 | #ifndef AESkey // if AESkey is defined as macro all other values must be too
277 | extern xref2u1_t AESkey;
278 | extern xref2u1_t AESaux;
279 | #endif
280 | #ifndef os_aes
281 | u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len);
282 | #endif
283 |
284 | #ifdef __cplusplus
285 | } // extern "C"
286 | #endif
287 |
288 | #endif // _oslmic_h_
289 |
--------------------------------------------------------------------------------
/src/lmic/radio.c:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2014-2015 IBM Corporation.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Eclipse Public License v1.0
5 | * which accompanies this distribution, and is available at
6 | * http://www.eclipse.org/legal/epl-v10.html
7 | *
8 | * Contributors:
9 | * IBM Zurich Research Lab - initial API, implementation and documentation
10 | *******************************************************************************/
11 |
12 | #include "lmic.h"
13 |
14 | // ----------------------------------------
15 | // Registers Mapping
16 | #define RegFifo 0x00 // common
17 | #define RegOpMode 0x01 // common
18 | #define FSKRegBitrateMsb 0x02
19 | #define FSKRegBitrateLsb 0x03
20 | #define FSKRegFdevMsb 0x04
21 | #define FSKRegFdevLsb 0x05
22 | #define RegFrfMsb 0x06 // common
23 | #define RegFrfMid 0x07 // common
24 | #define RegFrfLsb 0x08 // common
25 | #define RegPaConfig 0x09 // common
26 | #define RegPaRamp 0x0A // common
27 | #define RegOcp 0x0B // common
28 | #define RegLna 0x0C // common
29 | #define FSKRegRxConfig 0x0D
30 | #define LORARegFifoAddrPtr 0x0D
31 | #define FSKRegRssiConfig 0x0E
32 | #define LORARegFifoTxBaseAddr 0x0E
33 | #define FSKRegRssiCollision 0x0F
34 | #define LORARegFifoRxBaseAddr 0x0F
35 | #define FSKRegRssiThresh 0x10
36 | #define LORARegFifoRxCurrentAddr 0x10
37 | #define FSKRegRssiValue 0x11
38 | #define LORARegIrqFlagsMask 0x11
39 | #define FSKRegRxBw 0x12
40 | #define LORARegIrqFlags 0x12
41 | #define FSKRegAfcBw 0x13
42 | #define LORARegRxNbBytes 0x13
43 | #define FSKRegOokPeak 0x14
44 | #define LORARegRxHeaderCntValueMsb 0x14
45 | #define FSKRegOokFix 0x15
46 | #define LORARegRxHeaderCntValueLsb 0x15
47 | #define FSKRegOokAvg 0x16
48 | #define LORARegRxPacketCntValueMsb 0x16
49 | #define LORARegRxpacketCntValueLsb 0x17
50 | #define LORARegModemStat 0x18
51 | #define LORARegPktSnrValue 0x19
52 | #define FSKRegAfcFei 0x1A
53 | #define LORARegPktRssiValue 0x1A
54 | #define FSKRegAfcMsb 0x1B
55 | #define LORARegRssiValue 0x1B
56 | #define FSKRegAfcLsb 0x1C
57 | #define LORARegHopChannel 0x1C
58 | #define FSKRegFeiMsb 0x1D
59 | #define LORARegModemConfig1 0x1D
60 | #define FSKRegFeiLsb 0x1E
61 | #define LORARegModemConfig2 0x1E
62 | #define FSKRegPreambleDetect 0x1F
63 | #define LORARegSymbTimeoutLsb 0x1F
64 | #define FSKRegRxTimeout1 0x20
65 | #define LORARegPreambleMsb 0x20
66 | #define FSKRegRxTimeout2 0x21
67 | #define LORARegPreambleLsb 0x21
68 | #define FSKRegRxTimeout3 0x22
69 | #define LORARegPayloadLength 0x22
70 | #define FSKRegRxDelay 0x23
71 | #define LORARegPayloadMaxLength 0x23
72 | #define FSKRegOsc 0x24
73 | #define LORARegHopPeriod 0x24
74 | #define FSKRegPreambleMsb 0x25
75 | #define LORARegFifoRxByteAddr 0x25
76 | #define LORARegModemConfig3 0x26
77 | #define FSKRegPreambleLsb 0x26
78 | #define FSKRegSyncConfig 0x27
79 | #define LORARegFeiMsb 0x28
80 | #define FSKRegSyncValue1 0x28
81 | #define LORAFeiMib 0x29
82 | #define FSKRegSyncValue2 0x29
83 | #define LORARegFeiLsb 0x2A
84 | #define FSKRegSyncValue3 0x2A
85 | #define FSKRegSyncValue4 0x2B
86 | #define LORARegRssiWideband 0x2C
87 | #define FSKRegSyncValue5 0x2C
88 | #define FSKRegSyncValue6 0x2D
89 | #define FSKRegSyncValue7 0x2E
90 | #define FSKRegSyncValue8 0x2F
91 | #define FSKRegPacketConfig1 0x30
92 | #define FSKRegPacketConfig2 0x31
93 | #define LORARegDetectOptimize 0x31
94 | #define FSKRegPayloadLength 0x32
95 | #define FSKRegNodeAdrs 0x33
96 | #define LORARegInvertIQ 0x33
97 | #define FSKRegBroadcastAdrs 0x34
98 | #define FSKRegFifoThresh 0x35
99 | #define FSKRegSeqConfig1 0x36
100 | #define FSKRegSeqConfig2 0x37
101 | #define LORARegDetectionThreshold 0x37
102 | #define FSKRegTimerResol 0x38
103 | #define FSKRegTimer1Coef 0x39
104 | #define LORARegSyncWord 0x39
105 | #define FSKRegTimer2Coef 0x3A
106 | #define FSKRegImageCal 0x3B
107 | #define FSKRegTemp 0x3C
108 | #define FSKRegLowBat 0x3D
109 | #define FSKRegIrqFlags1 0x3E
110 | #define FSKRegIrqFlags2 0x3F
111 | #define RegDioMapping1 0x40 // common
112 | #define RegDioMapping2 0x41 // common
113 | #define RegVersion 0x42 // common
114 | // #define RegAgcRef 0x43 // common
115 | // #define RegAgcThresh1 0x44 // common
116 | // #define RegAgcThresh2 0x45 // common
117 | // #define RegAgcThresh3 0x46 // common
118 | // #define RegPllHop 0x4B // common
119 | // #define RegTcxo 0x58 // common
120 | #define RegPaDac 0x5A // common
121 | // #define RegPll 0x5C // common
122 | // #define RegPllLowPn 0x5E // common
123 | // #define RegFormerTemp 0x6C // common
124 | // #define RegBitRateFrac 0x70 // common
125 |
126 | // ----------------------------------------
127 | // spread factors and mode for RegModemConfig2
128 | #define SX1272_MC2_FSK 0x00
129 | #define SX1272_MC2_SF7 0x70
130 | #define SX1272_MC2_SF8 0x80
131 | #define SX1272_MC2_SF9 0x90
132 | #define SX1272_MC2_SF10 0xA0
133 | #define SX1272_MC2_SF11 0xB0
134 | #define SX1272_MC2_SF12 0xC0
135 | // bandwidth for RegModemConfig1
136 | #define SX1272_MC1_BW_125 0x00
137 | #define SX1272_MC1_BW_250 0x40
138 | #define SX1272_MC1_BW_500 0x80
139 | // coding rate for RegModemConfig1
140 | #define SX1272_MC1_CR_4_5 0x08
141 | #define SX1272_MC1_CR_4_6 0x10
142 | #define SX1272_MC1_CR_4_7 0x18
143 | #define SX1272_MC1_CR_4_8 0x20
144 | #define SX1272_MC1_IMPLICIT_HEADER_MODE_ON 0x04 // required for receive
145 | #define SX1272_MC1_RX_PAYLOAD_CRCON 0x02
146 | #define SX1272_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12
147 | // transmit power configuration for RegPaConfig
148 | #define SX1272_PAC_PA_SELECT_PA_BOOST 0x80
149 | #define SX1272_PAC_PA_SELECT_RFIO_PIN 0x00
150 |
151 |
152 | // sx1276 RegModemConfig1
153 | #define SX1276_MC1_BW_125 0x70
154 | #define SX1276_MC1_BW_250 0x80
155 | #define SX1276_MC1_BW_500 0x90
156 | #define SX1276_MC1_CR_4_5 0x02
157 | #define SX1276_MC1_CR_4_6 0x04
158 | #define SX1276_MC1_CR_4_7 0x06
159 | #define SX1276_MC1_CR_4_8 0x08
160 |
161 | #define SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01
162 |
163 | // sx1276 RegModemConfig2
164 | #define SX1276_MC2_RX_PAYLOAD_CRCON 0x04
165 |
166 | // sx1276 RegModemConfig3
167 | #define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE 0x08
168 | #define SX1276_MC3_AGCAUTO 0x04
169 |
170 | // preamble for lora networks (nibbles swapped)
171 | #define LORA_MAC_PREAMBLE 0x34
172 |
173 | #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A
174 | #ifdef CFG_sx1276_radio
175 | #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x70
176 | #elif CFG_sx1272_radio
177 | #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x74
178 | #endif
179 |
180 |
181 |
182 | // ----------------------------------------
183 | // Constants for radio registers
184 | #define OPMODE_LORA 0x80
185 | #define OPMODE_MASK 0x07
186 | #define OPMODE_SLEEP 0x00
187 | #define OPMODE_STANDBY 0x01
188 | #define OPMODE_FSTX 0x02
189 | #define OPMODE_TX 0x03
190 | #define OPMODE_FSRX 0x04
191 | #define OPMODE_RX 0x05
192 | #define OPMODE_RX_SINGLE 0x06
193 | #define OPMODE_CAD 0x07
194 |
195 | // ----------------------------------------
196 | // Bits masking the corresponding IRQs from the radio
197 | #define IRQ_LORA_RXTOUT_MASK 0x80
198 | #define IRQ_LORA_RXDONE_MASK 0x40
199 | #define IRQ_LORA_CRCERR_MASK 0x20
200 | #define IRQ_LORA_HEADER_MASK 0x10
201 | #define IRQ_LORA_TXDONE_MASK 0x08
202 | #define IRQ_LORA_CDDONE_MASK 0x04
203 | #define IRQ_LORA_FHSSCH_MASK 0x02
204 | #define IRQ_LORA_CDDETD_MASK 0x01
205 |
206 | #define IRQ_FSK1_MODEREADY_MASK 0x80
207 | #define IRQ_FSK1_RXREADY_MASK 0x40
208 | #define IRQ_FSK1_TXREADY_MASK 0x20
209 | #define IRQ_FSK1_PLLLOCK_MASK 0x10
210 | #define IRQ_FSK1_RSSI_MASK 0x08
211 | #define IRQ_FSK1_TIMEOUT_MASK 0x04
212 | #define IRQ_FSK1_PREAMBLEDETECT_MASK 0x02
213 | #define IRQ_FSK1_SYNCADDRESSMATCH_MASK 0x01
214 | #define IRQ_FSK2_FIFOFULL_MASK 0x80
215 | #define IRQ_FSK2_FIFOEMPTY_MASK 0x40
216 | #define IRQ_FSK2_FIFOLEVEL_MASK 0x20
217 | #define IRQ_FSK2_FIFOOVERRUN_MASK 0x10
218 | #define IRQ_FSK2_PACKETSENT_MASK 0x08
219 | #define IRQ_FSK2_PAYLOADREADY_MASK 0x04
220 | #define IRQ_FSK2_CRCOK_MASK 0x02
221 | #define IRQ_FSK2_LOWBAT_MASK 0x01
222 |
223 | // ----------------------------------------
224 | // DIO function mappings D0D1D2D3
225 | #define MAP_DIO0_LORA_RXDONE 0x00 // 00------
226 | #define MAP_DIO0_LORA_TXDONE 0x40 // 01------
227 | #define MAP_DIO1_LORA_RXTOUT 0x00 // --00----
228 | #define MAP_DIO1_LORA_NOP 0x30 // --11----
229 | #define MAP_DIO2_LORA_NOP 0xC0 // ----11--
230 |
231 | #define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready)
232 | #define MAP_DIO1_FSK_NOP 0x30 // --11----
233 | #define MAP_DIO2_FSK_TXNOP 0x04 // ----01--
234 | #define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10--
235 |
236 |
237 | // FSK IMAGECAL defines
238 | #define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F
239 | #define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80
240 | #define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default
241 |
242 | #define RF_IMAGECAL_IMAGECAL_MASK 0xBF
243 | #define RF_IMAGECAL_IMAGECAL_START 0x40
244 |
245 | #define RF_IMAGECAL_IMAGECAL_RUNNING 0x20
246 | #define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default
247 |
248 |
249 | // RADIO STATE
250 | // (initialized by radio_init(), used by radio_rand1())
251 | static u1_t randbuf[16];
252 |
253 |
254 | #ifdef CFG_sx1276_radio
255 | #define LNA_RX_GAIN (0x20|0x1)
256 | #elif CFG_sx1272_radio
257 | #define LNA_RX_GAIN (0x20|0x03)
258 | #else
259 | #error Missing CFG_sx1272_radio/CFG_sx1276_radio
260 | #endif
261 |
262 |
263 | static void writeReg (u1_t addr, u1_t data ) {
264 | hal_pin_nss(0);
265 | hal_spi(addr | 0x80);
266 | hal_spi(data);
267 | hal_pin_nss(1);
268 | }
269 |
270 | static u1_t readReg (u1_t addr) {
271 | hal_pin_nss(0);
272 | hal_spi(addr & 0x7F);
273 | u1_t val = hal_spi(0x00);
274 | hal_pin_nss(1);
275 | return val;
276 | }
277 |
278 | static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) {
279 | hal_pin_nss(0);
280 | hal_spi(addr | 0x80);
281 | for (u1_t i=0; i>16));
393 | writeReg(RegFrfMid, (u1_t)(frf>> 8));
394 | writeReg(RegFrfLsb, (u1_t)(frf>> 0));
395 | }
396 |
397 |
398 |
399 | static void configPower () {
400 | #ifdef CFG_sx1276_radio
401 | // no boost used for now
402 | s1_t pw = (s1_t)LMIC.txpow;
403 | if(pw >= 17) {
404 | pw = 15;
405 | } else if(pw < 2) {
406 | pw = 2;
407 | }
408 | // check board type for BOOST pin
409 | writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf)));
410 | writeReg(RegPaDac, readReg(RegPaDac)|0x4);
411 |
412 | #elif CFG_sx1272_radio
413 | // set PA config (2-17 dBm using PA_BOOST)
414 | s1_t pw = (s1_t)LMIC.txpow;
415 | if(pw > 17) {
416 | pw = 17;
417 | } else if(pw < 2) {
418 | pw = 2;
419 | }
420 | writeReg(RegPaConfig, (u1_t)(0x80|(pw-2)));
421 | #else
422 | #error Missing CFG_sx1272_radio/CFG_sx1276_radio
423 | #endif /* CFG_sx1272_radio */
424 | }
425 |
426 | static void txfsk () {
427 | // select FSK modem (from sleep mode)
428 | writeReg(RegOpMode, 0x10); // FSK, BT=0.5
429 | ASSERT(readReg(RegOpMode) == 0x10);
430 | // enter standby mode (required for FIFO loading))
431 | opmode(OPMODE_STANDBY);
432 | // set bitrate
433 | writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
434 | writeReg(FSKRegBitrateLsb, 0x80);
435 | // set frequency deviation
436 | writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz
437 | writeReg(FSKRegFdevLsb, 0x99);
438 | // frame and packet handler settings
439 | writeReg(FSKRegPreambleMsb, 0x00);
440 | writeReg(FSKRegPreambleLsb, 0x05);
441 | writeReg(FSKRegSyncConfig, 0x12);
442 | writeReg(FSKRegPacketConfig1, 0xD0);
443 | writeReg(FSKRegPacketConfig2, 0x40);
444 | writeReg(FSKRegSyncValue1, 0xC1);
445 | writeReg(FSKRegSyncValue2, 0x94);
446 | writeReg(FSKRegSyncValue3, 0xC1);
447 | // configure frequency
448 | configChannel();
449 | // configure output power
450 | configPower();
451 |
452 | // set the IRQ mapping DIO0=PacketSent DIO1=NOP DIO2=NOP
453 | writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TXNOP);
454 |
455 | // initialize the payload size and address pointers
456 | writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload))
457 |
458 | // download length byte and buffer to the radio FIFO
459 | writeReg(RegFifo, LMIC.dataLen);
460 | writeBuf(RegFifo, LMIC.frame, LMIC.dataLen);
461 |
462 | // enable antenna switch for TX
463 | hal_pin_rxtx(1);
464 |
465 | // now we actually start the transmission
466 | opmode(OPMODE_TX);
467 | }
468 |
469 | static void txlora () {
470 | // select LoRa modem (from sleep mode)
471 | //writeReg(RegOpMode, OPMODE_LORA);
472 | opmodeLora();
473 | ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0);
474 |
475 | // enter standby mode (required for FIFO loading))
476 | opmode(OPMODE_STANDBY);
477 | // configure LoRa modem (cfg1, cfg2)
478 | configLoraModem();
479 | // configure frequency
480 | configChannel();
481 | // configure output power
482 | writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec
483 | configPower();
484 | // set sync word
485 | writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE);
486 |
487 | // set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP
488 | writeReg(RegDioMapping1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP);
489 | // clear all radio IRQ flags
490 | writeReg(LORARegIrqFlags, 0xFF);
491 | // mask all IRQs but TxDone
492 | writeReg(LORARegIrqFlagsMask, ~IRQ_LORA_TXDONE_MASK);
493 |
494 | // initialize the payload size and address pointers
495 | writeReg(LORARegFifoTxBaseAddr, 0x00);
496 | writeReg(LORARegFifoAddrPtr, 0x00);
497 | writeReg(LORARegPayloadLength, LMIC.dataLen);
498 |
499 | // download buffer to the radio FIFO
500 | writeBuf(RegFifo, LMIC.frame, LMIC.dataLen);
501 |
502 | // enable antenna switch for TX
503 | hal_pin_rxtx(1);
504 |
505 | // now we actually start the transmission
506 | opmode(OPMODE_TX);
507 |
508 | #if LMIC_DEBUG_LEVEL > 0
509 | u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
510 | u1_t bw = getBw(LMIC.rps);
511 | u1_t cr = getCr(LMIC.rps);
512 | lmic_printf("%u: TXMODE, freq=%u, len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
513 | os_getTime(), LMIC.freq, LMIC.dataLen, sf,
514 | bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
515 | cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
516 | getIh(LMIC.rps)
517 | );
518 | #endif
519 | }
520 |
521 | // start transmitter (buf=LMIC.frame, len=LMIC.dataLen)
522 | static void starttx () {
523 | ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
524 | if(getSf(LMIC.rps) == FSK) { // FSK modem
525 | txfsk();
526 | } else { // LoRa modem
527 | txlora();
528 | }
529 | // the radio will go back to STANDBY mode as soon as the TX is finished
530 | // the corresponding IRQ will inform us about completion.
531 | }
532 |
533 | enum { RXMODE_SINGLE, RXMODE_SCAN, RXMODE_RSSI };
534 |
535 | static CONST_TABLE(u1_t, rxlorairqmask)[] = {
536 | [RXMODE_SINGLE] = IRQ_LORA_RXDONE_MASK|IRQ_LORA_RXTOUT_MASK,
537 | [RXMODE_SCAN] = IRQ_LORA_RXDONE_MASK,
538 | [RXMODE_RSSI] = 0x00,
539 | };
540 |
541 | // start LoRa receiver (time=LMIC.rxtime, timeout=LMIC.rxsyms, result=LMIC.frame[LMIC.dataLen])
542 | static void rxlora (u1_t rxmode) {
543 | // select LoRa modem (from sleep mode)
544 | opmodeLora();
545 | ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0);
546 | // enter standby mode (warm up))
547 | opmode(OPMODE_STANDBY);
548 | // don't use MAC settings at startup
549 | if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan
550 | writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1);
551 | writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2);
552 | } else { // single or continuous rx mode
553 | // configure LoRa modem (cfg1, cfg2)
554 | configLoraModem();
555 | // configure frequency
556 | configChannel();
557 | }
558 | // set LNA gain
559 | writeReg(RegLna, LNA_RX_GAIN);
560 | // set max payload size
561 | writeReg(LORARegPayloadMaxLength, 64);
562 | #if !defined(DISABLE_INVERT_IQ_ON_RX)
563 | // use inverted I/Q signal (prevent mote-to-mote communication)
564 | writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ)|(1<<6));
565 | #endif
566 | // set symbol timeout (for single rx)
567 | writeReg(LORARegSymbTimeoutLsb, LMIC.rxsyms);
568 | // set sync word
569 | writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE);
570 |
571 | // configure DIO mapping DIO0=RxDone DIO1=RxTout DIO2=NOP
572 | writeReg(RegDioMapping1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_RXTOUT|MAP_DIO2_LORA_NOP);
573 | // clear all radio IRQ flags
574 | writeReg(LORARegIrqFlags, 0xFF);
575 | // enable required radio IRQs
576 | writeReg(LORARegIrqFlagsMask, ~TABLE_GET_U1(rxlorairqmask, rxmode));
577 |
578 | // enable antenna switch for RX
579 | hal_pin_rxtx(0);
580 |
581 | // now instruct the radio to receive
582 | if (rxmode == RXMODE_SINGLE) { // single rx
583 | hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
584 | opmode(OPMODE_RX_SINGLE);
585 | } else { // continous rx (scan or rssi)
586 | opmode(OPMODE_RX);
587 | }
588 |
589 | #if LMIC_DEBUG_LEVEL > 0
590 | if (rxmode == RXMODE_RSSI) {
591 | lmic_printf("RXMODE_RSSI\n");
592 | } else {
593 | u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
594 | u1_t bw = getBw(LMIC.rps);
595 | u1_t cr = getCr(LMIC.rps);
596 | lmic_printf("%u: %s, freq=%u, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
597 | os_getTime(),
598 | rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"),
599 | LMIC.freq, sf,
600 | bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
601 | cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
602 | getIh(LMIC.rps)
603 | );
604 | }
605 | #endif
606 | }
607 |
608 | static void rxfsk (u1_t rxmode) {
609 | // only single rx (no continuous scanning, no noise sampling)
610 | ASSERT( rxmode == RXMODE_SINGLE );
611 | // select FSK modem (from sleep mode)
612 | //writeReg(RegOpMode, 0x00); // (not LoRa)
613 | opmodeFSK();
614 | ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0);
615 | // enter standby mode (warm up))
616 | opmode(OPMODE_STANDBY);
617 | // configure frequency
618 | configChannel();
619 | // set LNA gain
620 | //writeReg(RegLna, 0x20|0x03); // max gain, boost enable
621 | writeReg(RegLna, LNA_RX_GAIN);
622 | // configure receiver
623 | writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!?
624 | // set receiver bandwidth
625 | writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb
626 | // set AFC bandwidth
627 | writeReg(FSKRegAfcBw, 0x12); // 83.3kHz SSB
628 | // set preamble detection
629 | writeReg(FSKRegPreambleDetect, 0xAA); // enable, 2 bytes, 10 chip errors
630 | // set sync config
631 | writeReg(FSKRegSyncConfig, 0x12); // no auto restart, preamble 0xAA, enable, fill FIFO, 3 bytes sync
632 | // set packet config
633 | writeReg(FSKRegPacketConfig1, 0xD8); // var-length, whitening, crc, no auto-clear, no adr filter
634 | writeReg(FSKRegPacketConfig2, 0x40); // packet mode
635 | // set sync value
636 | writeReg(FSKRegSyncValue1, 0xC1);
637 | writeReg(FSKRegSyncValue2, 0x94);
638 | writeReg(FSKRegSyncValue3, 0xC1);
639 | // set preamble timeout
640 | writeReg(FSKRegRxTimeout2, 0xFF);//(LMIC.rxsyms+1)/2);
641 | // set bitrate
642 | writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
643 | writeReg(FSKRegBitrateLsb, 0x80);
644 | // set frequency deviation
645 | writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz
646 | writeReg(FSKRegFdevLsb, 0x99);
647 |
648 | // configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut
649 | writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT);
650 |
651 | // enable antenna switch for RX
652 | hal_pin_rxtx(0);
653 |
654 | // now instruct the radio to receive
655 | hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
656 | opmode(OPMODE_RX); // no single rx mode available in FSK
657 | }
658 |
659 | static void startrx (u1_t rxmode) {
660 | ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
661 | if(getSf(LMIC.rps) == FSK) { // FSK modem
662 | rxfsk(rxmode);
663 | } else { // LoRa modem
664 | rxlora(rxmode);
665 | }
666 | // the radio will go back to STANDBY mode as soon as the RX is finished
667 | // or timed out, and the corresponding IRQ will inform us about completion.
668 | }
669 |
670 | // get random seed from wideband noise rssi
671 | void radio_init () {
672 | hal_disableIRQs();
673 |
674 | // manually reset radio
675 | #ifdef CFG_sx1276_radio
676 | hal_pin_rst(0); // drive RST pin low
677 | #else
678 | hal_pin_rst(1); // drive RST pin high
679 | #endif
680 | hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us
681 | hal_pin_rst(2); // configure RST pin floating!
682 | hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms
683 |
684 | opmode(OPMODE_SLEEP);
685 |
686 | // some sanity checks, e.g., read version number
687 | u1_t v = readReg(RegVersion);
688 | #ifdef CFG_sx1276_radio
689 | ASSERT(v == 0x12 );
690 | #elif CFG_sx1272_radio
691 | ASSERT(v == 0x22);
692 | #else
693 | #error Missing CFG_sx1272_radio/CFG_sx1276_radio
694 | #endif
695 | // seed 15-byte randomness via noise rssi
696 | rxlora(RXMODE_RSSI);
697 | while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx
698 | for(int i=1; i<16; i++) {
699 | for(int j=0; j<8; j++) {
700 | u1_t b; // wait for two non-identical subsequent least-significant bits
701 | while( (b = readReg(LORARegRssiWideband) & 0x01) == (readReg(LORARegRssiWideband) & 0x01) );
702 | randbuf[i] = (randbuf[i] << 1) | b;
703 | }
704 | }
705 | randbuf[0] = 16; // set initial index
706 |
707 | #ifdef CFG_sx1276mb1_board
708 | // chain calibration
709 | writeReg(RegPaConfig, 0);
710 |
711 | // Launch Rx chain calibration for LF band
712 | writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
713 | while((readReg(FSKRegImageCal)&RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING){ ; }
714 |
715 | // Sets a Frequency in HF band
716 | u4_t frf = 868000000;
717 | writeReg(RegFrfMsb, (u1_t)(frf>>16));
718 | writeReg(RegFrfMid, (u1_t)(frf>> 8));
719 | writeReg(RegFrfLsb, (u1_t)(frf>> 0));
720 |
721 | // Launch Rx chain calibration for HF band
722 | writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
723 | while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; }
724 | #endif /* CFG_sx1276mb1_board */
725 |
726 | opmode(OPMODE_SLEEP);
727 |
728 | hal_enableIRQs();
729 | }
730 |
731 | // return next random byte derived from seed buffer
732 | // (buf[0] holds index of next byte to be returned)
733 | u1_t radio_rand1 () {
734 | u1_t i = randbuf[0];
735 | ASSERT( i != 0 );
736 | if( i==16 ) {
737 | os_aes(AES_ENC, randbuf, 16); // encrypt seed with any key
738 | i = 0;
739 | }
740 | u1_t v = randbuf[i++];
741 | randbuf[0] = i;
742 | return v;
743 | }
744 |
745 | u1_t radio_rssi () {
746 | hal_disableIRQs();
747 | u1_t r = readReg(LORARegRssiValue);
748 | hal_enableIRQs();
749 | return r;
750 | }
751 |
752 | static CONST_TABLE(u2_t, LORA_RXDONE_FIXUP)[] = {
753 | [FSK] = us2osticks(0), // ( 0 ticks)
754 | [SF7] = us2osticks(0), // ( 0 ticks)
755 | [SF8] = us2osticks(1648), // ( 54 ticks)
756 | [SF9] = us2osticks(3265), // ( 107 ticks)
757 | [SF10] = us2osticks(7049), // ( 231 ticks)
758 | [SF11] = us2osticks(13641), // ( 447 ticks)
759 | [SF12] = us2osticks(31189), // (1022 ticks)
760 | };
761 |
762 | // called by hal ext IRQ handler
763 | // (radio goes to stanby mode after tx/rx operations)
764 | void radio_irq_handler (u1_t dio) {
765 | ostime_t now = os_getTime();
766 | if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem
767 | u1_t flags = readReg(LORARegIrqFlags);
768 | #if LMIC_DEBUG_LEVEL > 1
769 | lmic_printf("%u: irq: dio: 0x%x flags: 0x%x\n", now, dio, flags);
770 | #endif
771 | if( flags & IRQ_LORA_TXDONE_MASK ) {
772 | // save exact tx time
773 | LMIC.txend = now - us2osticks(43); // TXDONE FIXUP
774 | } else if( flags & IRQ_LORA_RXDONE_MASK ) {
775 | // save exact rx time
776 | if(getBw(LMIC.rps) == BW125) {
777 | now -= TABLE_GET_U2(LORA_RXDONE_FIXUP, getSf(LMIC.rps));
778 | }
779 | LMIC.rxtime = now;
780 | // read the PDU and inform the MAC that we received something
781 | LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
782 | readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
783 | // set FIFO read address pointer
784 | writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr));
785 | // now read the FIFO
786 | readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
787 | // read rx quality parameters
788 | LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4
789 | LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
790 | } else if( flags & IRQ_LORA_RXTOUT_MASK ) {
791 | // indicate timeout
792 | LMIC.dataLen = 0;
793 | }
794 | // mask all radio IRQs
795 | writeReg(LORARegIrqFlagsMask, 0xFF);
796 | // clear radio IRQ flags
797 | writeReg(LORARegIrqFlags, 0xFF);
798 | } else { // FSK modem
799 | u1_t flags1 = readReg(FSKRegIrqFlags1);
800 | u1_t flags2 = readReg(FSKRegIrqFlags2);
801 | if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) {
802 | // save exact tx time
803 | LMIC.txend = now;
804 | } else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) {
805 | // save exact rx time
806 | LMIC.rxtime = now;
807 | // read the PDU and inform the MAC that we received something
808 | LMIC.dataLen = readReg(FSKRegPayloadLength);
809 | // now read the FIFO
810 | readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
811 | // read rx quality parameters
812 | LMIC.snr = 0; // determine snr
813 | LMIC.rssi = 0; // determine rssi
814 | } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) {
815 | // indicate timeout
816 | LMIC.dataLen = 0;
817 | } else {
818 | ASSERT(0);
819 | }
820 | }
821 | // go from stanby to sleep
822 | opmode(OPMODE_SLEEP);
823 | // run os job (use preset func ptr)
824 | os_setCallback(&LMIC.osjob, LMIC.osjob.func);
825 | }
826 |
827 | void os_radio (u1_t mode) {
828 | hal_disableIRQs();
829 | switch (mode) {
830 | case RADIO_RST:
831 | // put radio to sleep
832 | opmode(OPMODE_SLEEP);
833 | break;
834 |
835 | case RADIO_TX:
836 | // transmit frame now
837 | starttx(); // buf=LMIC.frame, len=LMIC.dataLen
838 | break;
839 |
840 | case RADIO_RX:
841 | // receive frame now (exactly at rxtime)
842 | startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms
843 | break;
844 |
845 | case RADIO_RXON:
846 | // start scanning for beacon now
847 | startrx(RXMODE_SCAN); // buf=LMIC.frame
848 | break;
849 | }
850 | hal_enableIRQs();
851 | }
852 |
--------------------------------------------------------------------------------