Merge pull request #2593 from Akury83/master
[RRG-proxmark3.git] / armsrc / usart.c
blob1dcaf751790c9a833a2d4c7d433dbfc386fe4fcd
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
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.
8 //
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 //-----------------------------------------------------------------------------
18 #include "usart.h"
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
58 pUS1->US_MR = 0;
60 // Reset the baud rate divisor register
61 pUS1->US_BRGR = 0;
63 // Reset the Timeguard Register
64 pUS1->US_TTGR = 0;
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) {
88 uint16_t rxfifo_free;
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;
94 } else {
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)) {
105 us_rxfifo_high = 0;
109 // Give next buffer
110 pUS1->US_RNPR = (uint32_t)usart_cur_inbuf;
111 pUS1->US_RNCR = USART_BUFFLEN;
113 // Swap current buff
114 if (usart_cur_inbuf == us_in_a) {
115 usart_cur_inbuf = us_in_b;
116 } else {
117 usart_cur_inbuf = us_in_a;
120 usart_cur_inbuf_off = 0;
121 } else {
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)) {
129 us_rxfifo_high = 0;
132 usart_cur_inbuf_off += available;
133 return;
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);
141 } else {
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)) {
154 us_rxfifo_high = 0;
157 usart_cur_inbuf_off += available;
161 uint16_t usart_rxdata_available(void) {
162 usart_fill_rxfifo();
163 if (us_rxfifo_low <= us_rxfifo_high) {
164 return (us_rxfifo_high - us_rxfifo_low);
165 } else {
166 return (sizeof(us_rxfifo) - us_rxfifo_low + us_rxfifo_high);
170 uint32_t usart_read_ng(uint8_t *data, size_t len) {
172 if (len == 0) {
173 return 0;
176 uint32_t bytes_rcv = 0;
177 uint32_t try = 0;
178 // uint32_t highest_observed_try = 0;
179 // Empirical max try observed: 3000000 / USART_BAUD_RATE
180 // Let's take 10x
182 uint32_t tryconstant = 0;
183 #ifdef USART_SLOW_LINK
184 // Experienced up to 13200 tries on BT link even at 460800
185 tryconstant = 50000;
186 #endif
188 uint32_t maxtry = 10 * (3000000 / USART_BAUD_RATE) + tryconstant;
190 while (len) {
192 uint32_t available = usart_rxdata_available();
193 uint32_t packetSize = MIN(available, len);
195 if (available > 0) {
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);
198 try = 0;
201 len -= packetSize;
203 while (packetSize--) {
204 if (us_rxfifo_low == sizeof(us_rxfifo)) {
205 us_rxfifo_low = 0;
207 data[bytes_rcv++] = us_rxfifo[us_rxfifo_low++];
210 if (try++ == maxtry) {
211 // Dbprintf_usb("Dbg USART TIMEOUT");
212 break;
215 // highest_observed_try = MAX(highest_observed_try, try);
216 // Dbprintf_usb("Dbg USART max observed try %i", highest_observed_try);
217 return bytes_rcv;
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;
227 pUS1->US_TCR = len;
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) {};
231 return PM3_SUCCESS;
234 void usart_init(uint32_t baudrate, uint8_t parity) {
236 if (baudrate != 0) {
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);
258 pPIO->PIO_BSR = 0;
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);
263 // set mode
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) {
272 case 'N':
273 mode |= AT91C_US_PAR_NONE; // parity: none
274 break;
275 case 'O':
276 mode |= AT91C_US_PAR_ODD; // parity: odd
277 break;
278 case 'E':
279 mode |= AT91C_US_PAR_EVEN; // parity: even
280 break;
282 pUS1->US_MR = mode;
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:
289 // b921600 => 8.6%
290 // b1382400 => 8.6%
291 // FP, Fractional Part (Datasheet p402, Supported in AT91SAM512 / 256) (31.6.1.3)
292 // FP = 0 disabled;
293 // FP = 1-7 Baudrate resolution,
294 // CD, Clock divider,
295 // sync == 0 , (async?)
296 // OVER = 0, -no
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
308 pUS1->US_TTGR = 0;
309 pUS1->US_RTOR = 0;
310 pUS1->US_FIDI = 0;
311 pUS1->US_IF = 0;
313 // Initialize DMA buffers
314 pUS1->US_TPR = (uint32_t)0;
315 pUS1->US_TCR = 0;
316 pUS1->US_TNPR = (uint32_t)0;
317 pUS1->US_TNCR = 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
326 us_rxfifo_low = 0;
327 us_rxfifo_high = 0;
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;