+#include "legic_prng.h"
+#include "legic.h"
+#include "crc.h"
+
+static legic_card_select_t card;/* metadata of currently selected card */
+static crc_t legic_crc;
+
+//-----------------------------------------------------------------------------
+// Frame timing and pseudorandom number generator
+//
+// The Prng is forwarded every 100us (TAG_BIT_PERIOD), except when the reader is
+// transmitting. In that case the prng has to be forwarded every bit transmitted:
+// - 60us for a 0 (RWD_TIME_0)
+// - 100us for a 1 (RWD_TIME_1)
+//
+// The data dependent timing makes writing comprehensible code significantly
+// harder. The current aproach forwards the prng data based if there is data on
+// air and time based, using GET_TICKS, during computational and wait periodes.
+//
+// To not have the necessity to calculate/guess exection time dependend timeouts
+// tx_frame and rx_frame use a shared timestamp to coordinate tx and rx timeslots.
+//-----------------------------------------------------------------------------
+
+static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */
+
+#define RWD_TIME_PAUSE 30 /* 20us */
+#define RWD_TIME_1 150 /* READER_TIME_PAUSE 20us off + 80us on = 100us */
+#define RWD_TIME_0 90 /* READER_TIME_PAUSE 20us off + 40us on = 60us */
+#define RWD_FRAME_WAIT 330 /* 220us from TAG frame end to READER frame start */
+#define TAG_FRAME_WAIT 495 /* 330us from READER frame end to TAG frame start */
+#define TAG_BIT_PERIOD 150 /* 100us */
+#define TAG_WRITE_TIMEOUT 60 /* 40 * 100us (write should take at most 3.6ms) */
+
+#define LEGIC_READ 0x01 /* Read Command */
+#define LEGIC_WRITE 0x00 /* Write Command */
+
+#define SESSION_IV 0x55 /* An arbitrary chose session IV, all shoud work */
+#define OFFSET_LOG 1024 /* The largest Legic Prime card is 1k */
+#define WRITE_LOWERLIMIT 4 /* UID and MCC are not writable */
+
+#define INPUT_THRESHOLD 8 /* heuristically determined, lower values */
+ /* lead to detecting false ack during write */
+
+//-----------------------------------------------------------------------------
+// I/O interface abstraction (FPGA -> ARM)
+//-----------------------------------------------------------------------------
+
+static inline uint8_t rx_byte_from_fpga() {
+ for(;;) {
+ WDT_HIT();
+
+ // wait for byte be become available in rx holding register
+ if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
+ return AT91C_BASE_SSC->SSC_RHR;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Demodulation (Reader)
+//-----------------------------------------------------------------------------
+
+// Returns a demedulated bit
+//
+// The FPGA running xcorrelation samples the subcarrier at ~13.56 MHz. The mode
+// was initialy designed to receive BSPK/2-PSK. Hance, it reports an I/Q pair
+// every 4.7us (8 bits i and 8 bits q).
+//
+// The subcarrier amplitude can be calculated using Pythagoras sqrt(i^2 + q^2).
+// To reduce CPU time the amplitude is approximated by using linear functions:
+// am = MAX(ABS(i),ABS(q)) + 1/2*MIN(ABS(i),ABSq))
+//
+// Note: The SSC receiver is never synchronized the calculation my be performed
+// on a I/Q pair from two subsequent correlations, but does not matter.
+//
+// The bit time is 99.1us (21 I/Q pairs). The receiver skips the first 5 samples
+// and averages the next (most stable) 8 samples. The final 8 samples are dropped
+// also.
+//
+// The demedulated should be alligned to the bit periode by the caller. This is
+// done in rx_bit and rx_ack.
+static inline bool rx_bit() {
+ int32_t cq = 0;
+ int32_t ci = 0;
+
+ // skip first 5 I/Q pairs
+ for(size_t i = 0; i<5; ++i) {
+ (int8_t)rx_byte_from_fpga();
+ (int8_t)rx_byte_from_fpga();
+ }
+
+ // sample next 8 I/Q pairs
+ for(size_t i = 0; i<8; ++i) {
+ cq += (int8_t)rx_byte_from_fpga();
+ ci += (int8_t)rx_byte_from_fpga();
+ }
+
+ // calculate power
+ int32_t power = (MAX(ABS(ci), ABS(cq)) + (MIN(ABS(ci), ABS(cq)) >> 1));
+
+ // compare average (power / 8) to threshold
+ return ((power >> 3) > INPUT_THRESHOLD);
+}
+
+//-----------------------------------------------------------------------------
+// Modulation (Reader)
+//
+// I've tried to modulate the Legic specific pause-puls using ssc and the default
+// ssc clock of 105.4 kHz (bit periode of 9.4us) - previous commit. However,
+// the timing was not precise enough. By increasing the ssc clock this could
+// be circumvented, but the adventage over bitbang would be little.
+//-----------------------------------------------------------------------------
+
+static inline void tx_bit(bool bit) {
+ // insert pause
+ LOW(GPIO_SSC_DOUT);
+ last_frame_end += RWD_TIME_PAUSE;
+ while(GET_TICKS < last_frame_end) { };
+ HIGH(GPIO_SSC_DOUT);
+
+ // return to high, wait for bit periode to end
+ last_frame_end += (bit ? RWD_TIME_1 : RWD_TIME_0) - RWD_TIME_PAUSE;
+ while(GET_TICKS < last_frame_end) { };
+}
+
+//-----------------------------------------------------------------------------
+// Frame Handling (Reader)
+//
+// The LEGIC RF protocol from card to reader does not include explicit frame
+// start/stop information or length information. The reader must know beforehand
+// how many bits it wants to receive.
+// Notably: a card sending a stream of 0-bits is indistinguishable from no card
+// present.
+//-----------------------------------------------------------------------------
+
+static void tx_frame(uint32_t frame, uint8_t len) {
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);
+
+ // wait for next tx timeslot
+ last_frame_end += RWD_FRAME_WAIT;
+ while(GET_TICKS < last_frame_end) { };
+
+ // transmit frame, MSB first
+ for(uint8_t i = 0; i < len; ++i) {
+ bool bit = (frame >> i) & 0x01;
+ tx_bit(bit ^ legic_prng_get_bit());
+ legic_prng_forward(1);
+ };
+
+ // add pause to mark end of the frame
+ LOW(GPIO_SSC_DOUT);
+ last_frame_end += RWD_TIME_PAUSE;
+ while(GET_TICKS < last_frame_end) { };
+ HIGH(GPIO_SSC_DOUT);
+}
+
+static uint32_t rx_frame(uint8_t len) {
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR
+ | FPGA_HF_READER_RX_XCORR_848_KHZ
+ | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ);
+
+ // hold sampling until card is expected to respond
+ last_frame_end += TAG_FRAME_WAIT;
+ while(GET_TICKS < last_frame_end) { };
+
+ uint32_t frame = 0;
+ for(uint8_t i = 0; i < len; ++i) {
+ frame |= (rx_bit() ^ legic_prng_get_bit()) << i;
+ legic_prng_forward(1);
+
+ // rx_bit runs only 95us, resync to TAG_BIT_PERIOD
+ last_frame_end += TAG_BIT_PERIOD;
+ while(GET_TICKS < last_frame_end) { };
+ }
+
+ return frame;
+}
+
+static bool rx_ack() {
+ // change fpga into rx mode
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR
+ | FPGA_HF_READER_RX_XCORR_848_KHZ
+ | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ);
+
+ // hold sampling until card is expected to respond
+ last_frame_end += TAG_FRAME_WAIT;
+ while(GET_TICKS < last_frame_end) { };
+
+ uint32_t ack = 0;
+ for(uint8_t i = 0; i < TAG_WRITE_TIMEOUT; ++i) {
+ // sample bit
+ ack = rx_bit();
+ legic_prng_forward(1);
+
+ // rx_bit runs only 95us, resync to TAG_BIT_PERIOD
+ last_frame_end += TAG_BIT_PERIOD;
+ while(GET_TICKS < last_frame_end) { };
+
+ // check if it was an ACK
+ if(ack) {
+ break;
+ }
+ }