1. Trammell Hudson
  2. rfid

Source

rfid / avrfid2.c

Trammell Hudson 34a1133 



Trammell Hudson 80b4ec3 
Trammell Hudson 34a1133 




Trammell Hudson 80b4ec3 
Trammell Hudson 34a1133 
Trammell Hudson 80b4ec3 




Trammell Hudson 34a1133 
Trammell Hudson 80b4ec3 


Trammell Hudson 34a1133 
Trammell Hudson 318bd2a 
Trammell Hudson 34a1133 
Trammell Hudson 80b4ec3 
Trammell Hudson 34a1133 




























Trammell Hudson 318bd2a 
Trammell Hudson 80b4ec3 



Trammell Hudson 34a1133 


Trammell Hudson 80b4ec3 



Trammell Hudson 34a1133 


Trammell Hudson 80b4ec3 

Trammell Hudson 34a1133 
Trammell Hudson 80b4ec3 
Trammell Hudson 34a1133 












Trammell Hudson 80b4ec3 


Trammell Hudson 34a1133 













Trammell Hudson 80b4ec3 



Trammell Hudson 34a1133 




Trammell Hudson 80b4ec3 














Trammell Hudson 34a1133 

Trammell Hudson 318bd2a 
Trammell Hudson 3dd0f57 



Trammell Hudson 80b4ec3 



Trammell Hudson 3dd0f57 
Trammell Hudson 318bd2a 
Trammell Hudson 80b4ec3 
Trammell Hudson 318bd2a 



Trammell Hudson 3dd0f57 
Trammell Hudson 318bd2a 
Trammell Hudson 3dd0f57 







Trammell Hudson 318bd2a 

Trammell Hudson 97a7d6d 


Trammell Hudson 318bd2a 


Trammell Hudson 80b4ec3 






Trammell Hudson 3dd0f57 

Trammell Hudson 34a1133 



Trammell Hudson 80b4ec3 

Trammell Hudson 34a1133 


Trammell Hudson 80b4ec3 




Trammell Hudson 34a1133 

Trammell Hudson 318bd2a 



Trammell Hudson 34a1133 
Trammell Hudson 14238f8 


Trammell Hudson 318bd2a 

Trammell Hudson 34a1133 





Trammell Hudson 80b4ec3 

Trammell Hudson 34a1133 





















Trammell Hudson 318bd2a 
Trammell Hudson 80b4ec3 






Trammell Hudson 318bd2a 
Trammell Hudson 3dd0f57 
Trammell Hudson 34a1133 
Trammell Hudson 318bd2a 
Trammell Hudson 34a1133 









Trammell Hudson 318bd2a 


Trammell Hudson 34a1133 





Trammell Hudson 318bd2a 
Trammell Hudson 3dd0f57 
Trammell Hudson 34a1133 
Trammell Hudson 318bd2a 
Trammell Hudson 34a1133 






















Trammell Hudson 80b4ec3 





Trammell Hudson 34a1133 




Trammell Hudson 318bd2a 


Trammell Hudson 34a1133 

Trammell Hudson 80b4ec3 







Trammell Hudson 34a1133 


Trammell Hudson 318bd2a 
Trammell Hudson 34a1133 



Trammell Hudson 80b4ec3 
Trammell Hudson 34a1133 


Trammell Hudson 318bd2a 

Trammell Hudson 34a1133 

Trammell Hudson 80b4ec3 
Trammell Hudson 318bd2a 


Trammell Hudson 80b4ec3 








Trammell Hudson 34a1133 





Trammell Hudson 80b4ec3 
Trammell Hudson 34a1133 

Trammell Hudson 80b4ec3 









Trammell Hudson 34a1133 





Trammell Hudson 80b4ec3 
Trammell Hudson 34a1133 
Trammell Hudson 3dd0f57 
Trammell Hudson 80b4ec3 






Trammell Hudson 318bd2a 
Trammell Hudson 34a1133 
Trammell Hudson 318bd2a 


Trammell Hudson 97a7d6d 
Trammell Hudson 318bd2a 
Trammell Hudson 34a1133 
Trammell Hudson 80b4ec3 

Trammell Hudson 318bd2a 
/** \file
 * AVR RFID card.
 *
 * Based on avrfrid.S by Beth at Scanlime.
 * http://scanlime.org/2008/09/using-an-avr-as-an-rfid-tag/
 *
 * 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 constaints if we tried to make those calls.
 *
 * However, the IJMP only takes 2 clock cycles, so we can build a state
 * machine and use it to make "function calls".  LPM also takes three
 * clocks, so we can't load a full address and jump to it within the
 * timing constraint, but we can split these operations across the
 * ten 5-cycle transitions during sending a baseband 1. 
 *
 * Each of these transitions takes 2 cycles for the XOR and OUT to
 * set the state, which leaves three cycles for our work.
 *
 */
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/sfr_defs.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.
 *
 * These are hard coded in toggle_raw().
 */
volatile register uint8_t r16 __asm__("r16"); 
volatile register uint8_t r17 __asm__("r17"); 

/** r15 tracks which bit are we currently sending.
 *
 * This is hard coded in hid_header().
 */
volatile register uint8_t bit_num __asm__("r15"); 


/** Load from a flash address pointed to by z_hi:z_lo and increment Z.
 *
 * Z = r31:r30.
 * 3 clocks.
 */
static inline uint8_t
lpm_z_inc(void)
{
	uint8_t r;
	__asm__ __volatile__(
		"lpm %0, Z+"
		: "=r"(r)
	);
	return r;
}


/** Copy a value + offset into z_lo.
 * 1 clock if offset is a constant 0, 2 clocks otherwise.
 */
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));
}


/** Copy a value into z_hi.
 * 1 clock.
 */
static inline void
z_hi(
	uint8_t x
)
{
	__asm__ __volatile__("mov r31, %0" : :  "r"(x));
}


/** Jump to what ever has been stored into Z with z_lo() and z_hi()
 *
 * PC <- Z
 * 2 clocks
 */
static inline void
__attribute__((__noreturn__))
ijmp(void)
{
	__asm__ __volatile__("ijmp");
	while(1); // make gcc happy
}


/**
 * Delay a specific number of clock cycles.
 *
 * rjmp is 2 clocks, nop is 1.
 *
 * So do one nop if the delay is an odd value and then rjmp's for n/2
 * to maximize code density.  Doesn't matter for the state machine version,
 * but otherwise the straight-code version would overflow the 8 KB space.
 */
static inline void
__attribute__((__always_inline__))
delay(
	const uint8_t n
)
{
	switch (n/2)
	{
	case 8: asm("rjmp .+0");
	case 7: asm("rjmp .+0");
	case 6: asm("rjmp .+0");
	case 5: asm("rjmp .+0");
	case 4: asm("rjmp .+0");
	case 3: asm("rjmp .+0");
	case 2: asm("rjmp .+0");
	case 1: asm("rjmp .+0");
	case 0: break;
	}

	if (n % 2 == 1)
		asm("nop");
}


/** Toggle the output pins to change the coil state.
 *
 * The DDRB pins are used to short the coil, which causes
 * an increase in current draw at the reader.
 *
 * 2 clocks.
 */
static void
__attribute__((__always_inline__))
toggle_raw(void)
{
	__asm__ __volatile__(
		"eor r16, r17\n"
		"out %0, r16\n"
		: : "I"(_SFR_IO_ADDR(DDRB))
	);
}


/** Toggle the state of the output pins and delay for some clocks.
 *
 * The toggle_raw() takes 2 clocks, so we delay for the remainder.
 */
static void
__attribute__((__always_inline__))
toggle(	
	const uint8_t 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 delays after the last FSK slot will not be
 * done, instead allowing the caller to make use of three 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
}



/** Send a 1 at the baseband layer.
 *
 * This is only used by the header during setup since it must send
 * several 1 bits in a row.  Only the last one computes the next state.
 * There are no delay slots following this function.
 */
static void
__attribute__((__always_inline__))
baseband_1(void)
{
	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_load(void)
{
	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);
				// word indexed, 2 clocks
				__asm__ __volatile__(
					"inc %0\n"
					"inc %0\n"
					: "=r"(bit_num)
				);

	toggle(ONE_FREQ); // 35
	toggle(ONE_FREQ); // 40
	toggle(ONE_FREQ); // 45
	toggle(0); // 50
}


/** Send the HID header start bits.
 *
 * The HID header is an illegal state in the Manchester encoding
 * used to indicate the start of the packet.
 *
 * The last baseband 1 will load the first state machine function
 * pointer and jump into the statemachine.
 *
 * The last state in the state machine will
 * bring us back here for a continuous loop.
 */
static void
hid_header(void)
{

	baseband_0(1);
	baseband_0(1);
	baseband_0(0);
				// force this operation here
				__asm__ __volatile__ ("eor r15, r15");
				delay(1);

	baseband_1();
	baseband_1();
	baseband_1_load();
				delay(1);
				ijmp();
}


/** Output a manchester 0.
 *
 * Output a baseband 0, followed by a baseband 1.
 * During the baseband 1 the Z register will be updated
 * to contain the pointer to the next function in the state machine.
 *
 * After the 1, with one delay slot since ijmp() takes two clocks,
 * we jump to the next state.
 */
static void
_0(void)
{
	baseband_0(1);
	baseband_1_load();
				delay(1);
				ijmp();
}


/** Output a manchester 1.
 *
 * Output a baseband 1, followed by a baseband 0.
 * During the baseband 1 the Z register will be updated
 * to contain the pointer to the next function in the state machine.
 *
 * After the 0, with no delay slots since ijmp() takes two clocks,
 * we jump to the next state.
 */
static void
_1(void)
{
	baseband_1_load();
				delay(3); // 3 delays slots remain
	baseband_0(0);
				ijmp();
}


/** Entry point at 0x0
 *
 * Since we linking with -nostdlib, main needs to be at 0x0.
 * The easiest way to force that with the default linker script
 * is to put it in the .vectors text section.
 */
int
__attribute__((section(".vectors")))
main(void)
{
	r16 = 0;
	r17 = _BV(PINB3) | _BV(PINB4);

	hid_header();

	/* Never returns */
}