summaryrefslogtreecommitdiff
path: root/openpicc/application/tc_recv.c
blob: c98e859fa56dd5f75f77ab7021efd317511f6a65 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/***************************************************************
 *
 * OpenPICC - ISO 14443 Layer 2 Type A  T/C based receiver code
 * Implements a receiver using FDT Timer/Counter (TC2) and the
 * FIQ to measure the number of carrier cycles between modulation
 * pauses.
 * 
 * The timing measurements are given to the differential miller
 * decoder on the fly to interleave reception and decoding. This
 * means two things: a) The CPU will be held in an IRQ handler
 * with IRQs disabled for the time of reception and b) The frame
 * will already have been fully decoded to a iso14443_frame
 * structure when reception ends.
 * 
 * Copyright 2008 Henryk Plötz <henryk@ploetzli.ch>
 *
 ***************************************************************

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; version 2.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

*/

#include <FreeRTOS.h>
#include <openpicc.h>
#include <errno.h>
#include <string.h>

#include <task.h>
#include <queue.h>

#include "tc_recv.h"

#include "iso14443a_diffmiller.h"
#include "usb_print.h"
#include "pio_irq.h"
#include "led.h"
#include "cmd.h"

struct tc_recv_handle {
	u_int8_t initialized;
	u_int8_t pauses_count;
	struct diffmiller_state *decoder;
	int current, next;
	tc_recv_callback_t callback;
	iso14443_frame *current_frame;
	xQueueHandle rx_queue;
};

static struct tc_recv_handle _tc;

#define BUFSIZE 1024
typedef struct {
	u_int32_t count;
	u_int32_t data[BUFSIZE];
} fiq_buffer_t;
fiq_buffer_t fiq_buffers[2];

fiq_buffer_t *tc_sniffer_next_buffer_for_fiq = 0;

iso14443_frame rx_frames[TC_RECV_NUMBER_OF_FRAME_BUFFERS];

/* The standard defines EOF as a logical 0 followed by 128 carrier cycles without modulation.
 * That means that the frame end is either 20+128+128 carrier cycles after the end of the
 * last modulation (if there was a 1 bit before the EOF) or 20+64+128 carrier cycles after
 * the last modulation. So the correct REAL_FRAME_END setting would be something like
 * 276. However, we can detect that the last bit period (that without modulation) is not a
 * valid bit much earlier: if the last data bit was 1 there are (ca.) 20 cycles till the start
 * of the EOF. Then there are 128 cycles without modulation. The next bit (were it not part of
 * the EOF) would have to be either sequence X (for a 1 bit) or sequence Z (for a 0 bit):
 * If it were a 0 bit there would be modulation right away, if it were a 1 bit there would be
 * modulation after 64 cycles. So the maximum valid time without modulation that is not signalling
 * and EOF is 20+128+64. Define REAL_FRAME_END as that value (plus 20 cycles error margin).  
 */
#define REAL_FRAME_END (20+128+64+20)

static int tc_recv_buffer_overruns = 0;

static inline iso14443_frame *get_frame_buffer(tc_recv_handle_t th)
{
	if(th->current_frame) return th->current_frame;
	unsigned int i; iso14443_frame *result;
	for(i=0; i<sizeof(rx_frames)/sizeof(rx_frames[0]); i++) {
		if(rx_frames[i].state == FRAME_FREE) {
			result = &rx_frames[i];
			result->state = FRAME_PENDING;
			th->current_frame = result;
			return result;
		}
	}
	tc_recv_buffer_overruns++;
	return NULL;
}

static portBASE_TYPE handle_frame(iso14443_frame *frame, portBASE_TYPE task_woken)
{
	if(_tc.callback) _tc.callback(TC_RECV_CALLBACK_RX_FRAME_ENDED, frame);
	if(frame->state != FRAME_FREE) {
		task_woken = xQueueSendFromISR(_tc.rx_queue, &frame, task_woken);
	}
	_tc.current_frame = NULL;
#ifdef PRINT_PERFORMANCE
	int old=usb_print_set_default_flush(0);
	iso14443a_diffmiller_print_performance(_tc.decoder);
	usb_print_set_default_flush(old);
#endif
	return task_woken;
}

static portBASE_TYPE handle_buffer(u_int32_t data[], unsigned int count, portBASE_TYPE task_woken)
{
	unsigned int offset = 0;
	while(offset < count) {
		iso14443_frame *rx_frame = get_frame_buffer(&_tc);
		if(rx_frame == NULL) return task_woken;
		int ret = iso14443a_decode_diffmiller(_tc.decoder, rx_frame, data, &offset, count);
		if(ret == 0) {
			task_woken = handle_frame(rx_frame, task_woken);
		}
	}
	return task_woken;
}

static inline portBASE_TYPE flush_buffer(fiq_buffer_t *buffer, portBASE_TYPE task_woken)
{
	if(buffer->count > 0) {
		if(buffer->count >= BUFSIZE) {
			usb_print_string_f("Warning: Possible buffer overrun detected\n\r",0);
			//overruns++;
		}
		buffer->count = MIN(buffer->count, BUFSIZE);
		task_woken = handle_buffer(buffer->data, buffer->count, task_woken);
		buffer->count = 0;
	}
	return task_woken;
}

#define NEXT_BUFFER(a) ((a+1)%(sizeof(fiq_buffers)/sizeof(fiq_buffers[0])))

static portBASE_TYPE switch_buffers(portBASE_TYPE task_woken)
{
	_tc.next = NEXT_BUFFER(_tc.current);
	task_woken = flush_buffer( &fiq_buffers[_tc.next] , task_woken);
	
	tc_sniffer_next_buffer_for_fiq = &fiq_buffers[_tc.current=_tc.next];
	return task_woken;
}

static portBASE_TYPE tc_recv_irq(u_int32_t pio, portBASE_TYPE task_woken)
{
	(void)pio;
	/* TODO There should be some emergency exit here to prevent the CPU from
	 * spinning in the IRQ for excessive amounts of time. (Maximum transmission
	 * time for 256 Byte frame is something like 21ms.)
	 */ 
	while(*AT91C_TC2_CV <= REAL_FRAME_END || 
			fiq_buffers[NEXT_BUFFER(_tc.current)].count > 0 || 
			fiq_buffers[_tc.current].count > 0) 
		task_woken = switch_buffers(task_woken);
	
	if(*AT91C_TC2_CV > REAL_FRAME_END) {
		iso14443_frame *rx_frame = get_frame_buffer(&_tc);
		if(rx_frame == NULL) return task_woken;
		int ret = iso14443a_diffmiller_assert_frame_ended(_tc.decoder, rx_frame);
		if(ret == 0) {
			task_woken = handle_frame(rx_frame, task_woken);
		}
	}
	return task_woken;
}


int tc_recv_init(tc_recv_handle_t *_th, int pauses_count, tc_recv_callback_t callback)
{
	if(_tc.initialized) return -EBUSY;
	tc_recv_handle_t th = &_tc;
	
	memset(fiq_buffers, 0, sizeof(fiq_buffers));
	th->current = th->next = 0;
	
	memset(rx_frames, 0, sizeof(rx_frames));
	th->current_frame = NULL;
	
	if(th->rx_queue == NULL) {
		th->rx_queue = xQueueCreate(TC_RECV_NUMBER_OF_FRAME_BUFFERS, sizeof(iso14443_frame*));
		if(th->rx_queue == NULL)
			return -ENOMEM;
	}
	
	th->pauses_count = pauses_count;
	th->decoder = iso14443a_init_diffmiller(th->pauses_count);
	if(!th->decoder) return -EBUSY;

	// The change interrupt is going to be handled by the FIQ and our secondary IRQ handler 
	AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPICC_SSC_DATA);
	if( pio_irq_register(OPENPICC_SSC_DATA, &tc_recv_irq) < 0) 
		return -EBUSY;
	pio_irq_enable(OPENPICC_SSC_DATA);
	
	th->initialized = 1;
	*_th = th;
	
	th->callback = callback;
	if(th->callback) th->callback(TC_RECV_CALLBACK_SETUP, th);
	
	return 0;
}

int tc_recv_receive(tc_recv_handle_t th, iso14443_frame* *frame, unsigned int timeout)
{
	if(th == NULL) return -EINVAL;
	if(!th->initialized) return -EINVAL;
	
	if(xQueueReceive(th->rx_queue, frame, timeout)){
		if(*frame != NULL) return 0;
		else return -EINTR;
	}
	
	return -ETIMEDOUT;
}
personal git repositories of Harald Welte. Your mileage may vary