Trammell Hudson avatar Trammell Hudson committed 34a1133 Draft

rewrote with state machine -- only 800 bytes of code now!

Comments (0)

Files changed (1)

+/** \file
+ * AVR RFID card.
+ *
+ * Based on avrfrid.S by Beth at Scanlime.
+ *
+ * Normal C code doesn't really work here since we are limited to
+ * very small number of cycles per bit.  The HID Prox cards are
+ * FSK modulated with only four or five RF cycles per baseband cycle.
+ * Since the AVR RCALL and RET instructions take four clocks each
+ * we would miss all of our timing values if we tried to make those calls.
+ *
+ * Instead a state machine is generated and the IJMP instruction is
+ * used.  This only takes two clocks and allows us to avoid RCALL+RET
+ * on each bit.
+ * In between each FSK toggle we have two, possibly three clocks.
+ * Since we can't count on having three, there is always a NOP
+ * and then the two user slots.  Since we have up to ten cycles
+ * and a guaranteed one/zero or zero/one transition per bit, we
+ * can do up to forty instructions during the FSK bits.  We don't
+ * need that many.
+ *
+ * LPM == 3 clocks
+ * IJMP == 2 clocks
+ */
 #include <avr/io.h>
+#include <avr/pgmspace.h>
+
+static void _0(void);
+static void _1(void);
+static void hid_header(void);
+
+#define HID_MFG_CODE        0x01002  // Do not modify
+#define HID_SITE_CODE       42
+#define HID_UNIQUE_ID       23946     // May be written on the back of the card
+
+static const void * hid_bits[] PROGMEM = {
+// HID manufacturer code (20 bits) == 0x01002
+_0, _0, _0, _0,
+_0, _0, _0, _1, _0, _0, _0, _0,
+_0, _0, _0, _0, _0, _0, _1, _0,
+
+// Facility code (8 bits) == 42
+_0, _0, _1, _0, _1, _0, _1, _0,
+
+// ID (16 bits) == 23946
+_0, _1, _0, _1, _1, _1, _0, _1,
+_1, _0, _0, _0, _1, _0, _1, _0,
+
+// Parity
+_0,
+
+// And return to the header when we're done
+hid_header
+};
+
 
 /* Use r16 and r17 to track the state of the pins */
-volatile  register  uint8_t r16 __asm__("r16"); 
-volatile  register  uint8_t r17 __asm__("r17"); 
+volatile register uint8_t r16 __asm__("r16"); 
+volatile register uint8_t r17 __asm__("r17"); 
+
+/* Which bit are we currently sending? */
+volatile register uint8_t bit_num __asm__("r15"); 
+
+
+/** Load from a flash address pointed to by z_hi:z_lo
+ * Z = r31:r30.
+ * Duration: 3 clocks.
+ */
+static inline uint8_t
+lpm_z_inc(void)
+{
+	uint8_t r;
+	__asm__ __volatile__(
+		"lpm %0, Z+"
+		: "=r"(r)
+	);
+	return r;
+}
+
+
+static inline void
+z_lo(
+	uint8_t x,
+	uint8_t offset
+)
+{
+	__asm__ __volatile__("mov r30, %0" : : "r"(x));
+
+	// Only add the offset if it is not known at compile time
+	// or if it is known at compile time and non-zero.
+	if (!__builtin_constant_p(offset) || offset != 0)
+		__asm__ __volatile__("add r30, %0" : : "r"(offset));
+}
+
+static inline void
+z_hi(
+	uint8_t x
+)
+{
+	__asm__ __volatile__(
+		"mov r31, %0" : :  "r"(x)
+	);
+}
+
 
 /**
  * Delay a specific number of clock cycles.
 
 static void
 __attribute__((__always_inline__))
+toggle_raw(void)
+{
+	__asm__ __volatile__(
+		"eor r16, r17\n"
+		"out 0x17, r16\n"
+	);
+}
+
+static void
+__attribute__((__always_inline__))
 toggle(	
 	const uint8_t n
 )
 {
-	asm(
-		"eor r16, r17\n"
-		"out 0x17, r16\n"
-	);
+	toggle_raw();
 
 	if (n > 2)
 		delay(n-2);
 }
 
+#define ZERO_FREQ	4
+#define ONE_FREQ	5
+
+
+/** Send a 0 at the baseband layer.
+ *
+ * If delay_slot is set, the last FSK slot will not be filled,
+ * instead allowing the caller to make use of two extra clock
+ * cycles for their own usage.
+ */
+static void
+__attribute__((__always_inline__))
+baseband_0(
+	uint8_t delay_slot
+)
+{
+	toggle(ZERO_FREQ); // 4
+	toggle(ZERO_FREQ); // 8
+	toggle(ZERO_FREQ); // 12
+	toggle(ZERO_FREQ); // 16
+	toggle(ZERO_FREQ); // 20
+	toggle(ZERO_FREQ); // 24
+	toggle(ZERO_FREQ); // 28
+	toggle(ZERO_FREQ); // 32
+	toggle(ZERO_FREQ); // 36
+	toggle(ZERO_FREQ); // 40
+	toggle(ZERO_FREQ); // 44
+	toggle(delay_slot ? ZERO_FREQ : 0); // 48
+}
+
 
 static void
 __attribute__((__always_inline__))
-baseband_0(void
-	//uint8_t startloop
-)
+baseband_1(void)
 {
-	toggle(4); // 4
-	toggle(4); // 8
-	toggle(4); // 12
-	toggle(4); // 16
-	toggle(4); // 20
-	toggle(4); // 24
-	toggle(4); // 28
-	toggle(4); // 32
-	toggle(4); // 36
-	toggle(4); // 40
-	toggle(4); // 44
-	toggle(4); // 48
-	toggle(4); // 52
+	toggle(ONE_FREQ); //  5
+	toggle(ONE_FREQ); // 10
+	toggle(ONE_FREQ); // 15
+	toggle(ONE_FREQ); // 20
+	toggle(ONE_FREQ); // 25
+	toggle(ONE_FREQ); // 30
+	toggle(ONE_FREQ); // 35
+	toggle(ONE_FREQ); // 40
+	toggle(ONE_FREQ); // 45
+	toggle(ONE_FREQ); // 50
 }
 
 
+/** Send a 1 at the baseband layer.
+ *
+ * Interleaved with the FSK are the operations to load the next
+ * function pointer.  Once the function "returns", the Z register
+ * will contain the address of the next function in the state machine.
+ */
 static void
 __attribute__((__always_inline__))
-baseband_1(void
-	//uint8_t startloop
-)
+baseband_1_load(void)
 {
-	toggle(5); //  5
-	toggle(5); // 10
-	toggle(5); // 15
-	toggle(5); // 20
-	toggle(5); // 25
-	toggle(5); // 30
-	toggle(5); // 35
-	toggle(5); // 40
-	toggle(5); // 45
-	toggle(5); // 50
+	toggle(0); //  5
+				uint16_t x = (uint16_t) hid_bits;
+				uint8_t ptr_lo = x >> 0;
+				uint8_t ptr_hi = x >> 8;
+				delay(1);
+
+	toggle(0); // 10
+				z_lo(ptr_lo, bit_num);
+				z_hi(ptr_hi);
+
+	toggle(0); // 15
+				uint8_t next_lo = lpm_z_inc();
+
+	toggle(0); // 20
+				uint8_t next_hi = lpm_z_inc();
+
+	toggle(0); // 25
+				delay(1);
+				z_lo(next_lo, 0);
+				z_hi(next_hi);
+
+	toggle(0); // 30
+				delay(1);
+				bit_num += 2; // word indexed, 2 clocks
+
+	toggle(ONE_FREQ); // 35
+	toggle(ONE_FREQ); // 40
+	toggle(ONE_FREQ); // 45
+	toggle(0); // 50
 }
 
 
-static inline void
-header(void)
+/** Send the HID header start bits.
+ *
+ * The last baseband 1 will load the first bit and off we go.
+ */
+static void
+hid_header(void)
 {
-	baseband_0();
-	baseband_0();
-	baseband_0();
+
+	baseband_0(1);
+	baseband_0(1);
+	baseband_0(0);
+				__asm__ __volatile__ ("eor r15, r15");
+				delay(1);
+
 	baseband_1();
 	baseband_1();
-	baseband_1();
+	baseband_1_load();
+				delay(1);
+				asm("ijmp");
 }
 
 
-static void
-__attribute__((__always_inline__))
-manchester_bit(
-	const uint8_t x
-)
-{
-	if (x & 1)
-	{
-		baseband_1();
-		baseband_0();
-	} else {
-		baseband_0();
-		baseband_1();
-	}
-}
 
-static inline void
-__attribute__((__always_inline__))
-manchester(
-	const uint32_t x,
-	const uint8_t bits
-)
-{
-	switch(bits - 1)
-	{
-	case 20: manchester_bit(x >> 20);
-	case 19: manchester_bit(x >> 19);
-	case 18: manchester_bit(x >> 18);
-	case 17: manchester_bit(x >> 17);
-	case 16: manchester_bit(x >> 16);
-	case 15: manchester_bit(x >> 15);
-	case 14: manchester_bit(x >> 14);
-	case 13: manchester_bit(x >> 13);
-	case 12: manchester_bit(x >> 12);
-	case 11: manchester_bit(x >> 11);
-	case 10: manchester_bit(x >> 10);
-	case  9: manchester_bit(x >>  9);
-	case  8: manchester_bit(x >>  8);
-	case  7: manchester_bit(x >>  7);
-	case  6: manchester_bit(x >>  6);
-	case  5: manchester_bit(x >>  5);
-	case  4: manchester_bit(x >>  4);
-	case  3: manchester_bit(x >>  3);
-	case  2: manchester_bit(x >>  2);
-	case  1: manchester_bit(x >>  1);
-	case  0: manchester_bit(x >>  0);
-	}
-}
-
-
-#define HID_MFG_CODE        0x01002  // Do not modify
-#define HID_SITE_CODE       42
-#define HID_UNIQUE_ID       23946     // May be written on the back of the card
-
+#if 0
 static void
 __attribute__((__noinline__))
 //__attribute__((section(".fini8")))
 hid_output(void)
 {
 	header();
-	asm("/* hid_mfg_code */\n");
 	manchester(HID_MFG_CODE, 20);
-	asm("/* hid_site_code */\n");
 	manchester(HID_SITE_CODE, 8);
-	asm("/* hid_unique_id */\n");
 	manchester(HID_UNIQUE_ID, 16);
-	asm("/* hid_parity */\n");
 	manchester(0, 1);
 }
+#endif
+
+static void
+_0(void)
+{
+	baseband_0(1);
+	baseband_1_load();
+				delay(1);
+				asm("ijmp"); // PC <- Z, 2 clocks
+}
+
+static void
+_1(void)
+{
+	baseband_1_load();
+				delay(3); // 3 delays slots remain
+	baseband_0(0);
+				asm("ijmp"); // PC <- Z, 2 clocks
+}
 
 int
-__attribute__((section(".init9")))
+__attribute__((section(".vectors")))
 main(void)
 {
 	r16 = 0;
 	r17 = _BV(PINB3) | _BV(PINB4);
 
-	while (1)
-		hid_output();
+	hid_header();
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.