1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // The main USART code, for serial communications over FPC connector
17 //-----------------------------------------------------------------------------
19 #include "proxmark3_arm.h"
21 #define Dbprintf_usb(...) {\
22 bool tmpfpc = g_reply_via_fpc;\
23 bool tmpusb = g_reply_via_usb;\
24 g_reply_via_fpc = false;\
25 g_reply_via_usb = true;\
26 Dbprintf(__VA_ARGS__);\
27 g_reply_via_fpc = tmpfpc;\
28 g_reply_via_usb = tmpusb;}
30 #define Dbprintf_fpc(...) {\
31 bool tmpfpc = g_reply_via_fpc;\
32 bool tmpusb = g_reply_via_usb;\
33 g_reply_via_fpc = true;\
34 g_reply_via_usb = false;\
35 Dbprintf(__VA_ARGS__);\
36 g_reply_via_fpc = tmpfpc;\
37 g_reply_via_usb = tmpusb;}
39 #define Dbprintf_all(...) {\
40 bool tmpfpc = g_reply_via_fpc;\
41 bool tmpusb = g_reply_via_usb;\
42 g_reply_via_fpc = true;\
43 g_reply_via_usb = true;\
44 Dbprintf(__VA_ARGS__);\
45 g_reply_via_fpc = tmpfpc;\
46 g_reply_via_usb = tmpusb;}
49 static volatile AT91PS_USART pUS1
= AT91C_BASE_US1
;
50 static volatile AT91PS_PIO pPIO
= AT91C_BASE_PIOA
;
51 static volatile AT91PS_PDC pPDC
= AT91C_BASE_PDC_US1
;
53 uint32_t g_usart_baudrate
= 0;
54 uint8_t g_usart_parity
= 0;
56 void usart_close(void) {
57 // Reset the USART mode
60 // Reset the baud rate divisor register
63 // Reset the Timeguard Register
66 // Disable all interrupts
67 pUS1->US_IDR = 0xFFFFFFFF;
69 // Abort the Peripheral Data Transfers
70 pUS1->US_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
72 // Disable receiver and transmitter and stop any activity immediately
73 pUS1->US_CR = AT91C_US_TXDIS | AT91C_US_RXDIS | AT91C_US_RSTTX | AT91C_US_RSTRX;
77 static uint8_t us_in_a
[USART_BUFFLEN
];
78 static uint8_t us_in_b
[USART_BUFFLEN
];
79 static uint8_t *usart_cur_inbuf
= NULL
;
80 static uint16_t usart_cur_inbuf_off
= 0;
81 static uint8_t us_rxfifo
[USART_FIFOLEN
];
82 static size_t us_rxfifo_low
= 0;
83 static size_t us_rxfifo_high
= 0;
86 static void usart_fill_rxfifo(void) {
90 if (pUS1
->US_RNCR
== 0) { // One buffer got filled, backup buffer being used
92 if (us_rxfifo_low
> us_rxfifo_high
) {
93 rxfifo_free
= us_rxfifo_low
- us_rxfifo_high
;
95 rxfifo_free
= sizeof(us_rxfifo
) - us_rxfifo_high
+ us_rxfifo_low
;
98 uint16_t available
= USART_BUFFLEN
- usart_cur_inbuf_off
;
100 if (available
<= rxfifo_free
) {
102 for (uint16_t i
= 0; i
< available
; i
++) {
103 us_rxfifo
[us_rxfifo_high
++] = usart_cur_inbuf
[usart_cur_inbuf_off
+ i
];
104 if (us_rxfifo_high
== sizeof(us_rxfifo
)) {
110 pUS1
->US_RNPR
= (uint32_t)usart_cur_inbuf
;
111 pUS1
->US_RNCR
= USART_BUFFLEN
;
114 if (usart_cur_inbuf
== us_in_a
) {
115 usart_cur_inbuf
= us_in_b
;
117 usart_cur_inbuf
= us_in_a
;
120 usart_cur_inbuf_off
= 0;
122 // Take only what we have room for
123 available
= rxfifo_free
;
124 for (uint16_t i
= 0; i
< available
; i
++) {
126 us_rxfifo
[us_rxfifo_high
++] = usart_cur_inbuf
[usart_cur_inbuf_off
+ i
];
128 if (us_rxfifo_high
== sizeof(us_rxfifo
)) {
132 usart_cur_inbuf_off
+= available
;
137 if (pUS1
->US_RCR
< USART_BUFFLEN
- usart_cur_inbuf_off
) { // Current buffer partially filled
139 if (us_rxfifo_low
> us_rxfifo_high
) {
140 rxfifo_free
= (us_rxfifo_low
- us_rxfifo_high
);
142 rxfifo_free
= (sizeof(us_rxfifo
) - us_rxfifo_high
+ us_rxfifo_low
);
145 uint16_t available
= (USART_BUFFLEN
- pUS1
->US_RCR
- usart_cur_inbuf_off
);
147 if (available
> rxfifo_free
) {
148 available
= rxfifo_free
;
151 for (uint16_t i
= 0; i
< available
; i
++) {
152 us_rxfifo
[us_rxfifo_high
++] = usart_cur_inbuf
[usart_cur_inbuf_off
+ i
];
153 if (us_rxfifo_high
== sizeof(us_rxfifo
)) {
157 usart_cur_inbuf_off
+= available
;
161 uint16_t usart_rxdata_available(void) {
163 if (us_rxfifo_low
<= us_rxfifo_high
) {
164 return (us_rxfifo_high
- us_rxfifo_low
);
166 return (sizeof(us_rxfifo
) - us_rxfifo_low
+ us_rxfifo_high
);
170 uint32_t usart_read_ng(uint8_t *data
, size_t len
) {
176 uint32_t bytes_rcv
= 0;
178 // uint32_t highest_observed_try = 0;
179 // Empirical max try observed: 3000000 / USART_BAUD_RATE
182 uint32_t tryconstant
= 0;
183 #ifdef USART_SLOW_LINK
184 // Experienced up to 13200 tries on BT link even at 460800
188 uint32_t maxtry
= 10 * (3000000 / USART_BAUD_RATE
) + tryconstant
;
192 uint32_t available
= usart_rxdata_available();
193 uint32_t packetSize
= MIN(available
, len
);
196 // Dbprintf_usb("Dbg USART ask %d bytes, available %d bytes, packetsize %d bytes", len, available, packetSize);
197 // highest_observed_try = MAX(highest_observed_try, try);
203 while (packetSize
--) {
204 if (us_rxfifo_low
== sizeof(us_rxfifo
)) {
207 data
[bytes_rcv
++] = us_rxfifo
[us_rxfifo_low
++];
210 if (try++ == maxtry
) {
211 // Dbprintf_usb("Dbg USART TIMEOUT");
215 // highest_observed_try = MAX(highest_observed_try, try);
216 // Dbprintf_usb("Dbg USART max observed try %i", highest_observed_try);
220 // transfer from device to client
221 int usart_writebuffer_sync(uint8_t *data
, size_t len
) {
223 // Wait for current PDC bank to be free
224 // (and check next bank too, in case there will be a usart_writebuffer_async)
225 while (pUS1
->US_TNCR
|| pUS1
->US_TCR
) {};
226 pUS1
->US_TPR
= (uint32_t)data
;
228 // Wait until finishing all transfers to make sure "data" buffer can be discarded
229 // (if we don't wait here, bulk send as e.g. "hw status" will fail)
230 while (pUS1
->US_TNCR
|| pUS1
->US_TCR
) {};
234 void usart_init(uint32_t baudrate
, uint8_t parity
) {
237 g_usart_baudrate
= baudrate
;
240 if ((parity
== 'N') || (parity
== 'O') || (parity
== 'E')) {
241 g_usart_parity
= parity
;
244 // For a nice detailed sample, interrupt driven but still relevant.
245 // See https://www.sparkfun.com/datasheets/DevTools/SAM7/at91sam7%20serial%20communications.pdf
247 // disable & reset receiver / transmitter for configuration
248 pUS1
->US_CR
= (AT91C_US_RSTRX
| AT91C_US_RSTTX
| AT91C_US_RXDIS
| AT91C_US_TXDIS
);
250 //enable the USART1 Peripheral clock
251 AT91C_BASE_PMC
->PMC_PCER
= (1 << AT91C_ID_US1
);
253 // disable PIO control of receive / transmit pins
254 pPIO
->PIO_PDR
|= (AT91C_PA21_RXD1
| AT91C_PA22_TXD1
);
256 // enable peripheral mode A on receive / transmit pins
257 pPIO
->PIO_ASR
|= (AT91C_PA21_RXD1
| AT91C_PA22_TXD1
);
260 // enable pull-up on receive / transmit pins (see 31.5.1 I/O Lines)
261 pPIO
->PIO_PPUER
|= (AT91C_PA21_RXD1
| AT91C_PA22_TXD1
);
264 uint32_t mode
= AT91C_US_USMODE_NORMAL
| // normal mode
265 AT91C_US_CLKS_CLOCK
| // MCK (48MHz)
266 AT91C_US_OVER
| // oversampling
267 AT91C_US_CHRL_8_BITS
| // 8 bits
268 AT91C_US_NBSTOP_1_BIT
| // 1 stop bit
269 AT91C_US_CHMODE_NORMAL
; // channel mode: normal
271 switch (g_usart_parity
) {
273 mode
|= AT91C_US_PAR_NONE
; // parity: none
276 mode
|= AT91C_US_PAR_ODD
; // parity: odd
279 mode
|= AT91C_US_PAR_EVEN
; // parity: even
284 // all interrupts disabled
285 pUS1
->US_IDR
= 0xFFFF;
287 // http://ww1.microchip.com/downloads/en/DeviceDoc/doc6175.pdf
288 // note that for very large baudrates, error is not neglectible:
291 // FP, Fractional Part (Datasheet p402, Supported in AT91SAM512 / 256) (31.6.1.3)
293 // FP = 1-7 Baudrate resolution,
294 // CD, Clock divider,
295 // sync == 0 , (async?)
297 // baudrate == selected clock/16/CD
298 // OVER = 1, -yes we are oversampling
299 // baudrate == selected clock/8/CD --> this is ours
301 uint32_t brgr
= MCK
/ (g_usart_baudrate
<< 3);
302 // doing fp = round((mck / (g_usart_baudrate << 3) - brgr) * 8) with integers:
303 uint32_t fp
= ((16 * MCK
/ (g_usart_baudrate
<< 3) - 16 * brgr
) + 1) / 2;
305 pUS1
->US_BRGR
= (fp
<< 16) | brgr
;
307 // Write the Timeguard Register
313 // Initialize DMA buffers
314 pUS1
->US_TPR
= (uint32_t)0;
316 pUS1
->US_TNPR
= (uint32_t)0;
318 pUS1
->US_RPR
= (uint32_t)us_in_a
;
319 pUS1
->US_RCR
= USART_BUFFLEN
;
320 usart_cur_inbuf
= us_in_a
;
321 usart_cur_inbuf_off
= 0;
322 pUS1
->US_RNPR
= (uint32_t)us_in_b
;
323 pUS1
->US_RNCR
= USART_BUFFLEN
;
325 // Initialize our fifo
329 // re-enable receiver / transmitter
330 pUS1
->US_CR
= (AT91C_US_RXEN
| AT91C_US_TXEN
);
332 // ready to receive and transmit
333 pUS1
->US_PTCR
= AT91C_PDC_RXTEN
| AT91C_PDC_TXTEN
;