1
|
/* AT91SAM7 SSC controller routines for OpenPICC
|
2
|
* (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
|
3
|
* (C) 2007-2008 Henryk Plötz <henryk@ploetzli.ch>
|
4
|
*
|
5
|
* This program is free software; you can redistribute it and/or modify
|
6
|
* it under the terms of the GNU General Public License as published by
|
7
|
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
14
|
*
|
15
|
* You should have received a copy of the GNU General Public License
|
16
|
* along with this program; if not, write to the Free Software
|
17
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
18
|
*
|
19
|
* We use SSC for both TX and RX side.
|
20
|
*
|
21
|
* RX side is interconnected with demodulated carrier
|
22
|
*
|
23
|
* TX side is interconnected with load modulation circuitry
|
24
|
*/
|
25
|
|
26
|
#include <FreeRTOS.h>
|
27
|
#include <queue.h>
|
28
|
#include <task.h>
|
29
|
#include <openpicc.h>
|
30
|
|
31
|
#include <string.h>
|
32
|
#include <errno.h>
|
33
|
|
34
|
#include "ssc.h"
|
35
|
#include "iso14443.h"
|
36
|
|
37
|
#include "clock_switch.h"
|
38
|
#include "tc_cdiv_sync.h"
|
39
|
#include "tc_fdt.h"
|
40
|
#include "led.h"
|
41
|
|
42
|
#include "usb_print.h"
|
43
|
#include "cmd.h"
|
44
|
|
45
|
#define PRINT_DEBUG 0
|
46
|
#define DEBUG_DATA_GATING 0
|
47
|
|
48
|
struct _ssc_handle {
|
49
|
enum ssc_mode mode;
|
50
|
const struct openpicc_hardware *openpicc;
|
51
|
ssc_dma_rx_buffer_t* rx_buffer[2];
|
52
|
ssc_dma_tx_buffer_t* tx_buffer;
|
53
|
ssc_callback_t callback;
|
54
|
xQueueHandle rx_queue;
|
55
|
AT91PS_PDC pdc;
|
56
|
AT91PS_SSC ssc;
|
57
|
u_int8_t rx_enabled, tx_enabled;
|
58
|
u_int8_t rx_running, tx_running, tx_need_switching;
|
59
|
};
|
60
|
|
61
|
static ssc_handle_t _ssc;
|
62
|
static const ssc_mode_def ssc_modes[] = {
|
63
|
/* Undefined, no size set */
|
64
|
[SSC_MODE_NONE] = {SSC_MODE_NONE, 0, 0, 0},
|
65
|
/* 14443A Frame */
|
66
|
[SSC_MODE_14443A] = {SSC_MODE_14443A,
|
67
|
ISO14443_BITS_PER_SSC_TRANSFER * ISO14443A_SAMPLE_LEN, // transfersize_ssc
|
68
|
ISO14443_BITS_PER_SSC_TRANSFER * ISO14443A_SAMPLE_LEN <= 8 ? 8 : 16, // transfersize_pdc
|
69
|
DIV_ROUND_UP(ISO14443A_MAX_RX_FRAME_SIZE_IN_BITS, ISO14443_BITS_PER_SSC_TRANSFER) },
|
70
|
};
|
71
|
static struct {
|
72
|
ssc_metric metric;
|
73
|
char *name;
|
74
|
int value;
|
75
|
} ssc_metrics[] = {
|
76
|
[METRIC_RX_OVERFLOWS] = {METRIC_RX_OVERFLOWS, "Rx overflows", 0},
|
77
|
[METRIC_FREE_RX_BUFFERS] = {METRIC_FREE_RX_BUFFERS, "Free Rx buffers", 0},
|
78
|
[METRIC_MANAGEMENT_ERRORS] = {METRIC_MANAGEMENT_ERRORS, "Internal buffer management error", 0},
|
79
|
[METRIC_MANAGEMENT_ERRORS_1] = {METRIC_MANAGEMENT_ERRORS_1, "Internal buffer management error type 1", 0},
|
80
|
[METRIC_MANAGEMENT_ERRORS_2] = {METRIC_MANAGEMENT_ERRORS_2, "Internal buffer management error type 2", 0},
|
81
|
[METRIC_MANAGEMENT_ERRORS_3] = {METRIC_MANAGEMENT_ERRORS_3, "Internal buffer management error type 3", 0},
|
82
|
[METRIC_LATE_TX_FRAMES] = {METRIC_LATE_TX_FRAMES, "Late Tx frames", 0},
|
83
|
[METRIC_RX_FRAMES] = {METRIC_RX_FRAMES, "Rx frames", 0},
|
84
|
[METRIC_TX_FRAMES] = {METRIC_TX_FRAMES, "Tx frames", 0},
|
85
|
[METRIC_TX_ABORTED_FRAMES] = {METRIC_TX_ABORTED_FRAMES, "Aborted Tx frames", 0},
|
86
|
};
|
87
|
|
88
|
static ssc_dma_rx_buffer_t _rx_buffers[SSC_DMA_BUFFER_COUNT];
|
89
|
ssc_dma_tx_buffer_t _tx_buffer;
|
90
|
|
91
|
/******* PRIVATE "meat" code *************************************************/
|
92
|
|
93
|
#define SSC_RX_IRQ_MASK (AT91C_SSC_RXRDY | \
|
94
|
AT91C_SSC_OVRUN | \
|
95
|
AT91C_SSC_ENDRX | \
|
96
|
AT91C_SSC_RXBUFF | \
|
97
|
AT91C_SSC_RXSYN | \
|
98
|
AT91C_SSC_CP0 | \
|
99
|
AT91C_SSC_CP1)
|
100
|
|
101
|
#define SSC_TX_IRQ_MASK (AT91C_SSC_TXRDY | \
|
102
|
AT91C_SSC_TXEMPTY | \
|
103
|
AT91C_SSC_ENDTX | \
|
104
|
AT91C_SSC_TXBUFE | \
|
105
|
AT91C_SSC_TXSYN)
|
106
|
|
107
|
static __ramfunc ssc_dma_rx_buffer_t* _unload_rx(ssc_handle_t *sh);
|
108
|
static __ramfunc int _reload_rx(ssc_handle_t *sh);
|
109
|
|
110
|
|
111
|
static int __ramfunc _ssc_rx_irq(u_int32_t orig_sr, int start_asserted, portBASE_TYPE task_woken)
|
112
|
{
|
113
|
int end_asserted = 0;
|
114
|
ssc_handle_t *sh = &_ssc;
|
115
|
u_int32_t orig_rcmr = sh->ssc->SSC_RCMR;
|
116
|
|
117
|
#if PRINT_DEBUG
|
118
|
int old = usb_print_set_default_flush(0); // DEBUG OUTPUT
|
119
|
DumpStringToUSB("orig:"); // DEBUG OUTPUT
|
120
|
DumpUIntToUSB(orig_sr); // DEBUG OUTPUT
|
121
|
DumpStringToUSB("\n\r"); // DEBUG OUTPUT
|
122
|
#endif
|
123
|
u_int32_t sr = orig_sr | _ssc.ssc->SSC_SR;
|
124
|
|
125
|
if( (sr & AT91C_SSC_RXSYN) || start_asserted) {
|
126
|
sh->ssc->SSC_RCMR = (orig_rcmr & (~AT91C_SSC_START)) | (AT91C_SSC_START_CONTINOUS);
|
127
|
/* Receiving has started */
|
128
|
if(sh->callback != NULL) {
|
129
|
sh->callback(SSC_CALLBACK_RX_FRAME_BEGIN, &end_asserted);
|
130
|
if(end_asserted)
|
131
|
sr = orig_sr | _ssc.ssc->SSC_SR;
|
132
|
}
|
133
|
}
|
134
|
|
135
|
#if PRINT_DEBUG
|
136
|
DumpStringToUSB("sr:"); // DEBUG OUTPUT
|
137
|
DumpUIntToUSB(sr); // DEBUG OUTPUT
|
138
|
DumpStringToUSB("\n\r"); // DEBUG OUTPUT
|
139
|
usb_print_set_default_flush(old); // DEBUG OUTPUT
|
140
|
#endif
|
141
|
|
142
|
if(((sr & (AT91C_SSC_CP1 | AT91C_SSC_ENDRX)) || end_asserted) && (sh->rx_buffer[0] != NULL)) {
|
143
|
/* Receiving has ended */
|
144
|
AT91F_PDC_DisableRx(sh->pdc);
|
145
|
AT91F_SSC_DisableRx(sh->ssc);
|
146
|
sh->ssc->SSC_RCMR = ((sh->ssc->SSC_RCMR) & (~AT91C_SSC_START)) | (AT91C_SSC_START_0);
|
147
|
|
148
|
ssc_dma_rx_buffer_t *buffer = _unload_rx(sh);
|
149
|
if(buffer != NULL) {
|
150
|
if(sh->callback != NULL)
|
151
|
sh->callback(SSC_CALLBACK_RX_FRAME_ENDED, buffer);
|
152
|
|
153
|
if(buffer->state != SSC_FREE) {
|
154
|
task_woken = xQueueSendFromISR(sh->rx_queue, &buffer, task_woken);
|
155
|
}
|
156
|
}
|
157
|
|
158
|
if(_reload_rx(sh)) {
|
159
|
/* Clear the receive holding register. Is this necessary? */
|
160
|
int dummy = sh->ssc->SSC_RHR; (void)dummy;
|
161
|
AT91F_PDC_EnableRx(sh->pdc);
|
162
|
if(!sh->tx_running) {
|
163
|
/* Only start the receiver if no Tx has been started. Warning: Need
|
164
|
* to make sure that the receiver is restarted again when the Tx is over.
|
165
|
* Note that this is only necessary when not using Compare 0 to start
|
166
|
* reception. When using Compare 0 the data gating mechanism will keep
|
167
|
* CP0 from happening and thus keep the receiver stopped.
|
168
|
*
|
169
|
* Note also that we're not resetting rx_running to 0, because conceptionally
|
170
|
* the Rx is still running,. So rx_running=1 and tx_running=1 means SSC_RX
|
171
|
* stopped and SSC_RX should be started as soon as SSC_TX stops.
|
172
|
*/
|
173
|
AT91F_SSC_EnableRx(sh->ssc);
|
174
|
}
|
175
|
} else {
|
176
|
sh->ssc->SSC_IDR = SSC_RX_IRQ_MASK;
|
177
|
sh->rx_running = 0;
|
178
|
sh->callback(SSC_CALLBACK_RX_STOPPED, sh);
|
179
|
}
|
180
|
|
181
|
}
|
182
|
|
183
|
sh->ssc->SSC_RCMR = orig_rcmr;
|
184
|
return task_woken;
|
185
|
}
|
186
|
|
187
|
/* Exported callback for the case when the frame start has been detected externally */
|
188
|
void ssc_frame_started(void)
|
189
|
{
|
190
|
_ssc_rx_irq(_ssc.ssc->SSC_SR, 1, pdFALSE);
|
191
|
}
|
192
|
|
193
|
static void __ramfunc _ssc_tx_end(ssc_handle_t *sh, int is_an_abort)
|
194
|
{
|
195
|
AT91F_PDC_DisableTx(sh->pdc);
|
196
|
AT91F_SSC_DisableTx(sh->ssc);
|
197
|
sh->ssc->SSC_IDR = SSC_TX_IRQ_MASK;
|
198
|
|
199
|
AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, AT91C_PA15_TF, 0);
|
200
|
usb_print_string_f(">",0);
|
201
|
|
202
|
AT91F_PDC_SetTx(sh->pdc, 0, 0);
|
203
|
AT91F_PDC_SetNextTx(sh->pdc, 0, 0);
|
204
|
|
205
|
if(sh->tx_buffer) {
|
206
|
sh->tx_buffer->state = SSC_FREE;
|
207
|
sh->tx_running = 0;
|
208
|
}
|
209
|
|
210
|
if(sh->rx_running) {
|
211
|
/* Receiver has been suspended by the pending transmission. Restart it. */
|
212
|
AT91F_SSC_EnableRx(sh->ssc);
|
213
|
}
|
214
|
|
215
|
if(sh->callback) {
|
216
|
if(is_an_abort)
|
217
|
sh->callback(SSC_CALLBACK_TX_FRAME_ABORTED, sh->tx_buffer);
|
218
|
else
|
219
|
sh->callback(SSC_CALLBACK_TX_FRAME_ENDED, sh->tx_buffer);
|
220
|
}
|
221
|
|
222
|
sh->tx_buffer = NULL;
|
223
|
}
|
224
|
|
225
|
static int __ramfunc _ssc_tx_irq(u_int32_t sr, portBASE_TYPE task_woken)
|
226
|
{
|
227
|
ssc_handle_t *sh = &_ssc;
|
228
|
|
229
|
if( sr & AT91C_SSC_TXSYN ) {
|
230
|
/* Tx starting, hardwire TF pin to high */
|
231
|
AT91F_PIO_SetOutput(AT91C_BASE_PIOA, AT91C_PA15_TF);
|
232
|
AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, AT91C_PA15_TF);
|
233
|
usb_print_string_f("<",0);
|
234
|
|
235
|
/* Also set SSC mode to continous
|
236
|
* FIXME BUG: This will somehow add two samples on the SSC
|
237
|
* The abomination below is designed to find a pause in the outgoing modulation data
|
238
|
* (WARNING: Will only work with manchester, not with PSK) so that the SSC start type
|
239
|
* switch will happen during a time of no subcarrier modulation (in order to not
|
240
|
* upset the subcarrier). Still need to find a way to 'absorb' the extra two samples. */
|
241
|
if(sh->tx_need_switching) {
|
242
|
vLedSetGreen(0);
|
243
|
int j = 0;
|
244
|
off_pre:
|
245
|
if(j++ > 100) goto out;
|
246
|
if(!AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_MOD_PWM)) {
|
247
|
goto off_pre;
|
248
|
}
|
249
|
on:
|
250
|
if(j++ > 100) goto out;
|
251
|
if(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_MOD_PWM)) goto on;
|
252
|
int i=0;
|
253
|
off:
|
254
|
if(j++ > 100) goto out;
|
255
|
if(!AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_MOD_PWM)) {
|
256
|
if(i++ > 2) goto out; else goto off;
|
257
|
} else goto on;
|
258
|
out:
|
259
|
sh->ssc->SSC_TCMR = (sh->ssc->SSC_TCMR & ~AT91C_SSC_START) | AT91C_SSC_START_CONTINOUS;
|
260
|
vLedSetGreen(1);
|
261
|
}
|
262
|
|
263
|
if(sh->callback)
|
264
|
sh->callback(SSC_CALLBACK_TX_FRAME_BEGIN, NULL);
|
265
|
}
|
266
|
|
267
|
if( sr & AT91C_SSC_TXEMPTY ) {
|
268
|
/* Tx has ended */
|
269
|
ssc_metrics[METRIC_TX_FRAMES].value++;
|
270
|
_ssc_tx_end(sh, 0);
|
271
|
}
|
272
|
|
273
|
return task_woken;
|
274
|
}
|
275
|
|
276
|
static void __ramfunc ssc_irq(void) __attribute__ ((naked));
|
277
|
static void __ramfunc ssc_irq(void)
|
278
|
{
|
279
|
portENTER_SWITCHING_ISR();
|
280
|
vLedSetRed(1);
|
281
|
portBASE_TYPE task_woken = pdFALSE;
|
282
|
|
283
|
u_int32_t sr = _ssc.ssc->SSC_SR;
|
284
|
if(sr & AT91C_SSC_RXSYN) {
|
285
|
task_woken = _ssc_rx_irq(sr, 1, task_woken);
|
286
|
} else if(sr & SSC_RX_IRQ_MASK) {
|
287
|
task_woken = _ssc_rx_irq(sr, 0, task_woken);
|
288
|
}
|
289
|
|
290
|
if(sr & SSC_TX_IRQ_MASK) {
|
291
|
task_woken = _ssc_tx_irq(sr, task_woken);
|
292
|
}
|
293
|
|
294
|
AT91F_AIC_ClearIt(AT91C_ID_SSC);
|
295
|
AT91F_AIC_AcknowledgeIt();
|
296
|
|
297
|
vLedSetRed(0);
|
298
|
portEXIT_SWITCHING_ISR(task_woken);
|
299
|
}
|
300
|
|
301
|
static __ramfunc ssc_dma_rx_buffer_t *_get_buffer(ssc_dma_buffer_state_t old, ssc_dma_buffer_state_t new)
|
302
|
{
|
303
|
ssc_dma_rx_buffer_t *buffer = NULL;
|
304
|
int i;
|
305
|
for(i=0; i < SSC_DMA_BUFFER_COUNT; i++) {
|
306
|
if(_rx_buffers[i].state == old) {
|
307
|
buffer = &_rx_buffers[i];
|
308
|
buffer->state = new;
|
309
|
break;
|
310
|
}
|
311
|
}
|
312
|
return buffer;
|
313
|
}
|
314
|
|
315
|
/* Doesn't check sh, must be called with interrupts disabled */
|
316
|
static __ramfunc int _reload_rx(ssc_handle_t *sh)
|
317
|
{
|
318
|
int result = 0;
|
319
|
if(sh->rx_buffer[0] != NULL) {
|
320
|
ssc_metrics[METRIC_MANAGEMENT_ERRORS_1].value++;
|
321
|
result = 1;
|
322
|
goto out;
|
323
|
}
|
324
|
|
325
|
ssc_dma_rx_buffer_t *buffer = _get_buffer(SSC_FREE, SSC_PENDING);
|
326
|
|
327
|
if(buffer == NULL) {
|
328
|
ssc_metrics[METRIC_RX_OVERFLOWS].value++;
|
329
|
goto out;
|
330
|
}
|
331
|
|
332
|
buffer->reception_mode = &ssc_modes[sh->mode];
|
333
|
buffer->len_transfers = ssc_modes[sh->mode].transfers;
|
334
|
|
335
|
AT91F_PDC_SetRx(sh->pdc, buffer->data, buffer->len_transfers);
|
336
|
sh->rx_buffer[0] = buffer;
|
337
|
|
338
|
result = 1;
|
339
|
out:
|
340
|
return result;
|
341
|
}
|
342
|
|
343
|
// Doesn't check sh, call with interrupts disabled, SSC/PDC stopped
|
344
|
static __ramfunc ssc_dma_rx_buffer_t* _unload_rx(ssc_handle_t *sh)
|
345
|
{
|
346
|
if(sh->rx_buffer[0] == NULL) {
|
347
|
ssc_metrics[METRIC_MANAGEMENT_ERRORS_2].value++;
|
348
|
return NULL;
|
349
|
}
|
350
|
|
351
|
ssc_dma_rx_buffer_t *buffer = sh->rx_buffer[0];
|
352
|
u_int32_t rpr = sh->pdc->PDC_RPR,
|
353
|
rcr = sh->pdc->PDC_RCR;
|
354
|
AT91F_PDC_SetRx(sh->pdc, 0, 0);
|
355
|
sh->rx_buffer[0] = NULL;
|
356
|
buffer->state = SSC_FULL;
|
357
|
|
358
|
if(rcr == 0) {
|
359
|
buffer->flags.overflow = 1;
|
360
|
} else {
|
361
|
buffer->flags.overflow = 0;
|
362
|
}
|
363
|
|
364
|
if(rcr > 0) {
|
365
|
/* Append a zero to make sure the buffer decoder finds the stop bit */
|
366
|
switch(buffer->reception_mode->transfersize_pdc) {
|
367
|
case 8:
|
368
|
//*((u_int8_t*)rpr++) = sh->ssc->SSC_RSHR;
|
369
|
*((u_int8_t*)rpr++) = 0;
|
370
|
--rcr;
|
371
|
break;
|
372
|
case 16:
|
373
|
*((u_int16_t*)rpr++) = 0;
|
374
|
--rcr;
|
375
|
break;
|
376
|
case 32:
|
377
|
*((u_int32_t*)rpr++) = 0;
|
378
|
--rcr;
|
379
|
break;
|
380
|
}
|
381
|
}
|
382
|
|
383
|
if((buffer->len_transfers - rcr) != (rpr - (unsigned int)buffer->data)*(buffer->reception_mode->transfersize_pdc/8)) {
|
384
|
ssc_metrics[METRIC_MANAGEMENT_ERRORS_3].value++;
|
385
|
buffer->state = SSC_FREE;
|
386
|
return NULL;
|
387
|
}
|
388
|
|
389
|
buffer->len_transfers = buffer->len_transfers - rcr;
|
390
|
|
391
|
return buffer;
|
392
|
}
|
393
|
|
394
|
void ssc_set_gate(int data_enabled) {
|
395
|
if(OPENPICC->features.data_gating) {
|
396
|
if(data_enabled) {
|
397
|
AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE);
|
398
|
if(DEBUG_DATA_GATING) usb_print_string_f("(", 0);
|
399
|
} else {
|
400
|
AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE);
|
401
|
if(DEBUG_DATA_GATING) usb_print_string_f(")", 0);
|
402
|
}
|
403
|
}
|
404
|
}
|
405
|
|
406
|
static void _ssc_start_rx(ssc_handle_t *sh)
|
407
|
{
|
408
|
taskENTER_CRITICAL();
|
409
|
if(sh != &_ssc) goto out;
|
410
|
if(sh->rx_running) goto out;
|
411
|
if(!sh->rx_enabled) goto out;
|
412
|
|
413
|
// Load buffer
|
414
|
if(!_reload_rx(sh))
|
415
|
goto out;
|
416
|
|
417
|
sh->ssc->SSC_IER = AT91C_SSC_RXSYN | \
|
418
|
AT91C_SSC_CP1 | AT91C_SSC_ENDRX;
|
419
|
sh->rx_running = 1;
|
420
|
if(sh->callback != NULL)
|
421
|
sh->callback(SSC_CALLBACK_RX_STARTING, sh);
|
422
|
|
423
|
// Actually enable reception
|
424
|
int dummy = sh->ssc->SSC_RHR; (void)dummy;
|
425
|
AT91F_PDC_EnableRx(sh->pdc);
|
426
|
AT91F_SSC_EnableRx(sh->ssc);
|
427
|
|
428
|
out:
|
429
|
taskEXIT_CRITICAL();
|
430
|
usb_print_string_f(sh->rx_running ? "SSC now running\n\r":"SSC not running\n\r", 0);
|
431
|
}
|
432
|
|
433
|
static void _ssc_stop_rx(ssc_handle_t *sh)
|
434
|
{
|
435
|
taskENTER_CRITICAL();
|
436
|
sh->ssc->SSC_IDR = SSC_RX_IRQ_MASK;
|
437
|
sh->rx_running = 0;
|
438
|
if(sh->callback != NULL)
|
439
|
sh->callback(SSC_CALLBACK_RX_STOPPED, sh);
|
440
|
taskEXIT_CRITICAL();
|
441
|
}
|
442
|
|
443
|
/******* PRIVATE Initialization Code *****************************************/
|
444
|
static void _ssc_rx_mode_set(ssc_handle_t *sh, enum ssc_mode ssc_mode)
|
445
|
{
|
446
|
taskENTER_CRITICAL();
|
447
|
int was_running = sh->rx_running;
|
448
|
if(was_running) _ssc_stop_rx(sh);
|
449
|
|
450
|
u_int8_t data_len=0, num_data=0, sync_len=0;
|
451
|
u_int32_t start_cond=0;
|
452
|
u_int32_t clock_gating=0;
|
453
|
u_int8_t stop = 0, invert=0;
|
454
|
|
455
|
switch(ssc_mode) {
|
456
|
case SSC_MODE_14443A:
|
457
|
/* Start on Compare 0. The funky calculations down there are designed to allow a different
|
458
|
* (longer) compare length for Compare 1 than for Compare 0. Both lengths are set by the
|
459
|
* same register. */
|
460
|
start_cond = AT91C_SSC_START_0;
|
461
|
sync_len = ISO14443A_EOF_LEN;
|
462
|
sh->ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE << (ISO14443A_EOF_LEN-ISO14443A_SOF_LEN);
|
463
|
sh->ssc->SSC_RC1R = ISO14443A_EOF_SAMPLE;
|
464
|
|
465
|
data_len = ssc_modes[SSC_MODE_14443A].transfersize_ssc;
|
466
|
|
467
|
/* We are using stop on Compare 1. The docs are ambiguous but my interpretation is that
|
468
|
* this means that num_data is basically ignored and reception is continuous until stop
|
469
|
* event. Set num_data to the maximum anyways. */
|
470
|
num_data = 16;
|
471
|
stop = 1;
|
472
|
|
473
|
stop = 0;
|
474
|
start_cond = AT91C_SSC_START_CONTINOUS;
|
475
|
sync_len = 0;
|
476
|
|
477
|
/* We can't use receive clock gating with RF because RF will only go up with the first
|
478
|
* edge, this doesn't cooperate with the longer sync len above.
|
479
|
* FIXME: What's the interaction between clock BURST on v0.4p1, RF and Compare 0?
|
480
|
* In theory this shouldn't work even without SSC clock_gating because BURST gates the
|
481
|
* clock before the SSC and so it shouldn't get sync_len samples before Compare 0.
|
482
|
* I believe there's a bug waiting to happen somewhere here. */
|
483
|
clock_gating = (0x0 << 6);
|
484
|
//invert = AT91C_SSC_CKI;
|
485
|
break;
|
486
|
case SSC_MODE_NONE:
|
487
|
goto out;
|
488
|
break;
|
489
|
}
|
490
|
|
491
|
/* Receive frame mode register */
|
492
|
sh->ssc->SSC_RFMR = ((data_len-1) & 0x1f) |
|
493
|
(((num_data-1) & 0x0f) << 8) |
|
494
|
(((sync_len-1) & 0x0f) << 16);
|
495
|
|
496
|
/* Receive clock mode register, Clock selection: RK, Clock output: None */
|
497
|
sh->ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE |
|
498
|
clock_gating | invert | start_cond | (stop << 12);
|
499
|
|
500
|
out:
|
501
|
sh->mode = ssc_mode;
|
502
|
if(was_running) _ssc_start_rx(sh);
|
503
|
taskEXIT_CRITICAL();
|
504
|
}
|
505
|
|
506
|
#if SSC_DMA_BUFFER_COUNT > 0
|
507
|
static inline int _init_ssc_rx(ssc_handle_t *sh)
|
508
|
{
|
509
|
tc_cdiv_sync_init();
|
510
|
tc_cdiv_sync_enable();
|
511
|
|
512
|
if(sh->rx_queue == NULL) {
|
513
|
sh->rx_queue = xQueueCreate(10, sizeof(sh->rx_buffer[0]));
|
514
|
if(sh->rx_queue == NULL)
|
515
|
goto out_fail_queue;
|
516
|
}
|
517
|
|
518
|
AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA,
|
519
|
OPENPICC_SSC_DATA | OPENPICC_SSC_CLOCK |
|
520
|
OPENPICC_PIO_FRAME,
|
521
|
0);
|
522
|
|
523
|
/* FIXME: This is handled by tc_cdiv_sync and shouldn't be necessary */
|
524
|
AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SSC_DATA_CONTROL);
|
525
|
AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SSC_DATA_CONTROL);
|
526
|
|
527
|
if(OPENPICC->features.clock_switching) {
|
528
|
AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC->CLOCK_SWITCH);
|
529
|
clock_switch(CLOCK_SELECT_PLL);
|
530
|
}
|
531
|
|
532
|
/* Disable all interrupts */
|
533
|
sh->ssc->SSC_IDR = SSC_RX_IRQ_MASK;
|
534
|
|
535
|
/* don't divide clock inside SSC, we do that in tc_cdiv */
|
536
|
sh->ssc->SSC_CMR = 0;
|
537
|
|
538
|
unsigned int i;
|
539
|
for(i=0; i<sizeof(_rx_buffers)/sizeof(_rx_buffers[0]); i++)
|
540
|
memset(&_rx_buffers[i], 0, sizeof(_rx_buffers[i]));
|
541
|
|
542
|
sh->rx_buffer[0] = sh->rx_buffer[1] = NULL;
|
543
|
|
544
|
/* Will be set to a real value some time later
|
545
|
* FIXME Layering? */
|
546
|
tc_fdt_set(0xff00);
|
547
|
|
548
|
return 1;
|
549
|
|
550
|
out_fail_queue:
|
551
|
return 0;
|
552
|
}
|
553
|
#endif
|
554
|
|
555
|
static inline int _init_ssc_tx(ssc_handle_t *sh)
|
556
|
{
|
557
|
/* IMPORTANT: Disable PA23 (PWM0) output, since it is connected to
|
558
|
* PA17 !! */
|
559
|
AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPICC_MOD_PWM);
|
560
|
AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, OPENPICC_MOD_SSC |
|
561
|
OPENPICC_SSC_CLOCK | OPENPICC_SSC_TF, 0);
|
562
|
|
563
|
if(OPENPICC->features.clock_switching) {
|
564
|
clock_switch_init();
|
565
|
/* Users: remember to clock_switch(CLOCK_SELECT_CARRIER) as
|
566
|
* early as possible, e.g. right after receive end */
|
567
|
}
|
568
|
|
569
|
/* Disable all interrupts */
|
570
|
sh->ssc->SSC_IDR = SSC_TX_IRQ_MASK;
|
571
|
|
572
|
/* don't divide clock inside SSC, we do that in tc_cdiv */
|
573
|
sh->ssc->SSC_CMR = 0;
|
574
|
|
575
|
return 1;
|
576
|
}
|
577
|
|
578
|
static int _ssc_register_callback(ssc_handle_t *sh, ssc_callback_t _callback)
|
579
|
{
|
580
|
if(!sh) return -EINVAL;
|
581
|
if(sh->callback != NULL) return -EBUSY;
|
582
|
sh->callback = _callback;
|
583
|
if(sh->callback != NULL)
|
584
|
sh->callback(SSC_CALLBACK_SETUP, sh);
|
585
|
return 0;
|
586
|
}
|
587
|
|
588
|
static int _ssc_unregister_callback(ssc_handle_t *sh, ssc_callback_t _callback)
|
589
|
{
|
590
|
if(!sh) return -EINVAL;
|
591
|
if(_callback == NULL || sh->callback == _callback) {
|
592
|
if(sh->callback != NULL)
|
593
|
sh->callback(SSC_CALLBACK_TEARDOWN, sh);
|
594
|
sh->callback = NULL;
|
595
|
}
|
596
|
return 0;
|
597
|
}
|
598
|
|
599
|
/******* PUBLIC API **********************************************************/
|
600
|
ssc_handle_t* ssc_open(u_int8_t init_rx, u_int8_t init_tx, enum ssc_mode mode, ssc_callback_t callback)
|
601
|
{
|
602
|
ssc_handle_t *sh = &_ssc;
|
603
|
|
604
|
if(sh->rx_enabled || sh->tx_enabled || sh->rx_running) {
|
605
|
if( ssc_close(sh) != 0) {
|
606
|
return NULL;
|
607
|
}
|
608
|
}
|
609
|
|
610
|
if(init_rx || init_tx) {
|
611
|
sh->ssc = AT91C_BASE_SSC;
|
612
|
sh->pdc = (AT91PS_PDC) &(sh->ssc->SSC_RPR);
|
613
|
|
614
|
AT91F_SSC_CfgPMC();
|
615
|
|
616
|
if(OPENPICC->features.data_gating) {
|
617
|
AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE);
|
618
|
ssc_set_gate(1);
|
619
|
}
|
620
|
}
|
621
|
|
622
|
|
623
|
if(init_tx) {
|
624
|
sh->tx_enabled = _init_ssc_tx(sh);
|
625
|
if(!sh->tx_enabled) {
|
626
|
ssc_close(sh);
|
627
|
return NULL;
|
628
|
}
|
629
|
}
|
630
|
|
631
|
if(init_rx) {
|
632
|
#if SSC_DMA_BUFFER_COUNT > 0
|
633
|
sh->rx_enabled = _init_ssc_rx(sh);
|
634
|
if(!sh->rx_enabled) {
|
635
|
ssc_close(sh);
|
636
|
return NULL;
|
637
|
}
|
638
|
#else
|
639
|
ssc_close(sh);
|
640
|
return NULL;
|
641
|
#endif
|
642
|
}
|
643
|
|
644
|
if(sh->rx_enabled || sh->tx_enabled) {
|
645
|
_ssc_rx_mode_set(sh, mode);
|
646
|
AT91F_AIC_ConfigureIt(AT91C_ID_SSC,
|
647
|
OPENPICC_IRQ_PRIO_SSC,
|
648
|
AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, (THandler)&ssc_irq);
|
649
|
//AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE, (THandler)&ssc_irq);
|
650
|
AT91F_AIC_EnableIt(AT91C_ID_SSC);
|
651
|
}
|
652
|
|
653
|
if(callback != NULL)
|
654
|
_ssc_register_callback(sh, callback);
|
655
|
|
656
|
if(init_rx)
|
657
|
_ssc_start_rx(sh);
|
658
|
|
659
|
return sh;
|
660
|
}
|
661
|
|
662
|
int ssc_recv(ssc_handle_t* sh, ssc_dma_rx_buffer_t* *buffer,unsigned int timeout)
|
663
|
{
|
664
|
if(sh == NULL) return -EINVAL;
|
665
|
if(!sh->rx_enabled) return -EINVAL;
|
666
|
|
667
|
taskENTER_CRITICAL();
|
668
|
if(sh->rx_running) {
|
669
|
if(PRINT_DEBUG) usb_print_string_f("Continuing SSC Rx\n\r",0); // DEBUG OUTPUT
|
670
|
} else {
|
671
|
if(PRINT_DEBUG) usb_print_string_f("ERR: SSC halted\n\r",0); // DEBUG OUTPUT
|
672
|
/* Try starting the Reception */
|
673
|
_ssc_start_rx(sh);
|
674
|
}
|
675
|
taskEXIT_CRITICAL();
|
676
|
|
677
|
if(xQueueReceive(sh->rx_queue, buffer, timeout)){
|
678
|
if(*buffer != NULL) return 0;
|
679
|
else return -EINTR;
|
680
|
}
|
681
|
|
682
|
return -ETIMEDOUT;
|
683
|
}
|
684
|
extern u_int32_t fdt_offset;
|
685
|
/* Must be called with IRQs disabled. E.g. from IRQ context or within a critical section. */
|
686
|
int ssc_send(ssc_handle_t* sh, ssc_dma_tx_buffer_t *buffer)
|
687
|
{
|
688
|
if(sh == NULL) return -EINVAL;
|
689
|
if(!sh->tx_enabled) return -EINVAL;
|
690
|
if(sh->tx_running) return -EBUSY;
|
691
|
|
692
|
sh->tx_buffer = buffer;
|
693
|
sh->tx_running = 1;
|
694
|
|
695
|
/* disable Tx */
|
696
|
sh->ssc->SSC_IDR = SSC_TX_IRQ_MASK;
|
697
|
AT91F_PDC_DisableTx(sh->pdc);
|
698
|
AT91F_SSC_DisableTx(sh->ssc);
|
699
|
|
700
|
int start_cond = AT91C_SSC_START_HIGH_RF;
|
701
|
|
702
|
int sync_len = 1;
|
703
|
int data_len = 32;
|
704
|
int num_data = buffer->len/(data_len/8); /* FIXME This is broken for more than 64 bytes, or is it? */
|
705
|
int num_data_ssc = num_data > 16 ? 16 : num_data;
|
706
|
sh->tx_need_switching = (num_data > 16);
|
707
|
|
708
|
sh->ssc->SSC_TFMR = ((data_len-1) & 0x1f) |
|
709
|
(((num_data_ssc-1) & 0x0f) << 8) |
|
710
|
(((sync_len-1) & 0x0f) << 16);
|
711
|
sh->ssc->SSC_TCMR = 0x01 | AT91C_SSC_CKO_NONE | (AT91C_SSC_CKI&0) | start_cond;
|
712
|
|
713
|
AT91F_PDC_SetTx(sh->pdc, buffer->data, num_data);
|
714
|
AT91F_PDC_SetNextTx(sh->pdc, 0, 0);
|
715
|
buffer->state = SSC_PENDING;
|
716
|
|
717
|
sh->ssc->SSC_IER = AT91C_SSC_TXEMPTY | AT91C_SSC_TXSYN;
|
718
|
/* Enable DMA */
|
719
|
sh->ssc->SSC_THR = 0;
|
720
|
AT91F_PDC_EnableTx(sh->pdc);
|
721
|
|
722
|
/* Disable Receiver, see comments in _ssc_rx_irq */
|
723
|
AT91F_SSC_DisableRx(sh->ssc);
|
724
|
/* Start Transmission */
|
725
|
AT91F_SSC_EnableTx(sh->ssc);
|
726
|
vLedSetGreen(1);
|
727
|
|
728
|
if(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_SSC_TF)) {
|
729
|
ssc_metrics[METRIC_LATE_TX_FRAMES].value++;
|
730
|
}
|
731
|
|
732
|
return 0;
|
733
|
}
|
734
|
|
735
|
int ssc_send_abort(ssc_handle_t* sh)
|
736
|
{
|
737
|
if(!sh) return -EINVAL;
|
738
|
if(!sh->tx_enabled) return -EINVAL;
|
739
|
if(!sh->tx_running) return -EINVAL;
|
740
|
|
741
|
ssc_metrics[METRIC_TX_ABORTED_FRAMES].value++;
|
742
|
_ssc_tx_end(sh, 1);
|
743
|
|
744
|
return 0;
|
745
|
}
|
746
|
|
747
|
int ssc_close(ssc_handle_t* sh)
|
748
|
{
|
749
|
if(sh->rx_running)
|
750
|
_ssc_stop_rx(sh);
|
751
|
|
752
|
if(sh->rx_enabled) {
|
753
|
// FIXME Implement
|
754
|
sh->rx_enabled = 0;
|
755
|
}
|
756
|
if(sh->tx_enabled) {
|
757
|
// FIXME Implement
|
758
|
sh->tx_enabled = 0;
|
759
|
}
|
760
|
|
761
|
_ssc_unregister_callback(sh, NULL);
|
762
|
return 0;
|
763
|
}
|
764
|
|
765
|
/* Hard reset the SSC to flush all buffers and whatnot. Call with IRQs disabled */
|
766
|
void ssc_hard_reset(ssc_handle_t *sh)
|
767
|
{
|
768
|
if(sh == NULL) return;
|
769
|
if(!sh->rx_enabled && !sh->tx_enabled) return;
|
770
|
|
771
|
u_int32_t
|
772
|
cmr = sh->ssc->SSC_CMR,
|
773
|
rcmr = sh->ssc->SSC_RCMR,
|
774
|
rfmr = sh->ssc->SSC_RFMR,
|
775
|
tcmr = sh->ssc->SSC_TCMR,
|
776
|
tfmr = sh->ssc->SSC_TFMR,
|
777
|
rc0r = sh->ssc->SSC_RC0R,
|
778
|
rc1r = sh->ssc->SSC_RC1R,
|
779
|
sr = sh->ssc->SSC_SR,
|
780
|
imr = sh->ssc->SSC_IMR;
|
781
|
|
782
|
sh->ssc->SSC_CR = AT91C_SSC_SWRST;
|
783
|
|
784
|
sh->ssc->SSC_CMR = cmr;
|
785
|
sh->ssc->SSC_RCMR = rcmr;
|
786
|
sh->ssc->SSC_RFMR = rfmr;
|
787
|
sh->ssc->SSC_TCMR = tcmr;
|
788
|
sh->ssc->SSC_TFMR = tfmr;
|
789
|
sh->ssc->SSC_RC0R = rc0r;
|
790
|
sh->ssc->SSC_RC1R = rc1r;
|
791
|
|
792
|
sh->ssc->SSC_IDR = ~imr;
|
793
|
sh->ssc->SSC_IER = imr;
|
794
|
|
795
|
if(sr & AT91C_SSC_RXEN)
|
796
|
AT91F_SSC_EnableRx(sh->ssc);
|
797
|
else
|
798
|
AT91F_SSC_DisableRx(sh->ssc);
|
799
|
|
800
|
if(sr & AT91C_SSC_TXEN)
|
801
|
AT91F_SSC_EnableTx(sh->ssc);
|
802
|
else
|
803
|
AT91F_SSC_DisableTx(sh->ssc);
|
804
|
}
|
805
|
|
806
|
|
807
|
int ssc_get_metric(ssc_metric metric, char **description, int *value)
|
808
|
{
|
809
|
char *_name="Undefined";
|
810
|
int _value=0;
|
811
|
int valid=0;
|
812
|
|
813
|
if(metric < sizeof(ssc_metrics)/sizeof(ssc_metrics[0])) {
|
814
|
_name = ssc_metrics[metric].name;
|
815
|
_value = ssc_metrics[metric].value;
|
816
|
valid = 1;
|
817
|
}
|
818
|
|
819
|
switch(metric) {
|
820
|
case METRIC_FREE_RX_BUFFERS:
|
821
|
_value = 0;
|
822
|
int i;
|
823
|
for(i=0; i < SSC_DMA_BUFFER_COUNT; i++)
|
824
|
if(_rx_buffers[i].state == SSC_FREE) _value++;
|
825
|
break;
|
826
|
case METRIC_MANAGEMENT_ERRORS:
|
827
|
_value = ssc_metrics[METRIC_MANAGEMENT_ERRORS_1].value +
|
828
|
ssc_metrics[METRIC_MANAGEMENT_ERRORS_2].value +
|
829
|
ssc_metrics[METRIC_MANAGEMENT_ERRORS_3].value;
|
830
|
break;
|
831
|
default:
|
832
|
break;
|
833
|
}
|
834
|
|
835
|
if(!valid) return 0;
|
836
|
|
837
|
if(description != NULL) *description = _name;
|
838
|
if(value != NULL) *value = _value;
|
839
|
return 1;
|
840
|
}
|
841
|
|