text
[RRG-proxmark3.git] / armsrc / usart.c
blobc26a28ea8a4c2d1b031038530855d5bce91d435d
1 //-----------------------------------------------------------------------------
2 // Iceman, July 2018
3 // edits by - Anticat, August 2018
4 //
5 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
6 // at your option, any later version. See the LICENSE.txt file for the text of
7 // the license.
8 //-----------------------------------------------------------------------------
9 // The main USART code, for serial communications over FPC connector
10 //-----------------------------------------------------------------------------
11 #include "usart.h"
12 #include "proxmark3_arm.h"
14 volatile AT91PS_USART pUS1 = AT91C_BASE_US1;
15 volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA;
16 volatile AT91PS_PDC pPDC = AT91C_BASE_PDC_US1;
18 uint32_t g_usart_baudrate = 0;
19 uint8_t g_usart_parity = 0;
21 void usart_close(void) {
22 // Reset the USART mode
23 pUS1->US_MR = 0;
25 // Reset the baud rate divisor register
26 pUS1->US_BRGR = 0;
28 // Reset the Timeguard Register
29 pUS1->US_TTGR = 0;
31 // Disable all interrupts
32 pUS1->US_IDR = 0xFFFFFFFF;
34 // Abort the Peripheral Data Transfers
35 pUS1->US_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
37 // Disable receiver and transmitter and stop any activity immediately
38 pUS1->US_CR = AT91C_US_TXDIS | AT91C_US_RXDIS | AT91C_US_RSTTX | AT91C_US_RSTRX;
42 static uint8_t us_inbuf1[USART_BUFFLEN];
43 static uint8_t us_inbuf2[USART_BUFFLEN];
44 static uint8_t *usart_cur_inbuf = NULL;
45 static uint16_t usart_cur_inbuf_off = 0;
46 static uint8_t us_rxfifo[USART_FIFOLEN];
47 static size_t us_rxfifo_low = 0;
48 static size_t us_rxfifo_high = 0;
51 static void usart_fill_rxfifo(void) {
52 uint16_t rxfifo_free ;
53 if (pUS1->US_RNCR == 0) { // One buffer got filled, backup buffer being used
55 if (us_rxfifo_low > us_rxfifo_high)
56 rxfifo_free = us_rxfifo_low - us_rxfifo_high;
57 else
58 rxfifo_free = sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low;
60 uint16_t available = USART_BUFFLEN - usart_cur_inbuf_off;
62 if (available <= rxfifo_free) {
64 for (uint16_t i = 0; i < available; i++) {
65 us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i];
66 if (us_rxfifo_high == sizeof(us_rxfifo))
67 us_rxfifo_high = 0;
70 // Give next buffer
71 pUS1->US_RNPR = (uint32_t)usart_cur_inbuf;
72 pUS1->US_RNCR = USART_BUFFLEN;
74 // Swap current buff
75 if (usart_cur_inbuf == us_inbuf1)
76 usart_cur_inbuf = us_inbuf2;
77 else
78 usart_cur_inbuf = us_inbuf1;
80 usart_cur_inbuf_off = 0;
81 } else {
82 // Take only what we have room for
83 available = rxfifo_free;
84 for (uint16_t i = 0; i < available; i++) {
85 us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i];
86 if (us_rxfifo_high == sizeof(us_rxfifo))
87 us_rxfifo_high = 0;
89 usart_cur_inbuf_off += available;
90 return;
94 if (pUS1->US_RCR < USART_BUFFLEN - usart_cur_inbuf_off) { // Current buffer partially filled
96 if (us_rxfifo_low > us_rxfifo_high)
97 rxfifo_free = us_rxfifo_low - us_rxfifo_high;
98 else
99 rxfifo_free = sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low;
101 uint16_t available = USART_BUFFLEN - pUS1->US_RCR - usart_cur_inbuf_off;
103 if (available > rxfifo_free)
104 available = rxfifo_free;
105 for (uint16_t i = 0; i < available; i++) {
106 us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i];
107 if (us_rxfifo_high == sizeof(us_rxfifo))
108 us_rxfifo_high = 0;
110 usart_cur_inbuf_off += available;
114 uint16_t usart_rxdata_available(void) {
115 usart_fill_rxfifo();
116 if (us_rxfifo_low <= us_rxfifo_high)
117 return us_rxfifo_high - us_rxfifo_low;
118 else
119 return sizeof(us_rxfifo) - us_rxfifo_low + us_rxfifo_high;
122 uint32_t usart_read_ng(uint8_t *data, size_t len) {
123 if (len == 0) return 0;
124 uint32_t bytes_rcv = 0;
125 uint32_t try = 0;
126 // uint32_t highest_observed_try = 0;
127 // Empirical max try observed: 3000000 / USART_BAUD_RATE
128 // Let's take 10x
130 uint32_t tryconstant = 0;
131 #ifdef USART_SLOW_LINK
132 // Experienced up to 13200 tries on BT link even at 460800
133 tryconstant = 50000;
134 #endif
136 uint32_t maxtry = 10 * (3000000 / USART_BAUD_RATE) + tryconstant;
138 while (len) {
139 uint32_t available = usart_rxdata_available();
141 uint32_t packetSize = MIN(available, len);
142 if (available > 0) {
143 // Dbprintf_usb("Dbg USART ask %d bytes, available %d bytes, packetsize %d bytes", len, available, packetSize);
144 // highest_observed_try = MAX(highest_observed_try, try);
145 try = 0;
147 len -= packetSize;
148 while (packetSize--) {
149 if (us_rxfifo_low == sizeof(us_rxfifo))
150 us_rxfifo_low = 0;
151 data[bytes_rcv++] = us_rxfifo[us_rxfifo_low++];
153 if (try++ == maxtry) {
154 // Dbprintf_usb("Dbg USART TIMEOUT");
155 break;
158 // highest_observed_try = MAX(highest_observed_try, try);
159 // Dbprintf_usb("Dbg USART max observed try %i", highest_observed_try);
160 return bytes_rcv;
163 // transfer from device to client
164 int usart_writebuffer_sync(uint8_t *data, size_t len) {
166 // Wait for current PDC bank to be free
167 // (and check next bank too, in case there will be a usart_writebuffer_async)
168 while (pUS1->US_TNCR || pUS1->US_TCR) {};
169 pUS1->US_TPR = (uint32_t)data;
170 pUS1->US_TCR = len;
171 // Wait until finishing all transfers to make sure "data" buffer can be discarded
172 // (if we don't wait here, bulk send as e.g. "hw status" will fail)
173 while (pUS1->US_TNCR || pUS1->US_TCR) {};
174 return PM3_SUCCESS;
177 void usart_init(uint32_t baudrate, uint8_t parity) {
179 if (baudrate != 0)
180 g_usart_baudrate = baudrate;
181 if ((parity == 'N') || (parity == 'O') || (parity == 'E'))
182 g_usart_parity = parity;
184 // For a nice detailed sample, interrupt driven but still relevant.
185 // See https://www.sparkfun.com/datasheets/DevTools/SAM7/at91sam7%20serial%20communications.pdf
187 // disable & reset receiver / transmitter for configuration
188 pUS1->US_CR = (AT91C_US_RSTRX | AT91C_US_RSTTX | AT91C_US_RXDIS | AT91C_US_TXDIS);
190 //enable the USART1 Peripheral clock
191 AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_US1);
193 // disable PIO control of receive / transmit pins
194 pPIO->PIO_PDR |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1);
196 // enable peripheral mode A on receive / transmit pins
197 pPIO->PIO_ASR |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1);
198 pPIO->PIO_BSR = 0;
200 // enable pull-up on receive / transmit pins (see 31.5.1 I/O Lines)
201 pPIO->PIO_PPUER |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1);
203 // set mode
204 uint32_t mode = AT91C_US_USMODE_NORMAL | // normal mode
205 AT91C_US_CLKS_CLOCK | // MCK (48MHz)
206 AT91C_US_OVER | // oversampling
207 AT91C_US_CHRL_8_BITS | // 8 bits
208 AT91C_US_NBSTOP_1_BIT | // 1 stop bit
209 AT91C_US_CHMODE_NORMAL; // channel mode: normal
211 switch (g_usart_parity) {
212 case 'N':
213 mode |= AT91C_US_PAR_NONE; // parity: none
214 break;
215 case 'O':
216 mode |= AT91C_US_PAR_ODD; // parity: odd
217 break;
218 case 'E':
219 mode |= AT91C_US_PAR_EVEN; // parity: even
220 break;
222 pUS1->US_MR = mode;
224 // all interrupts disabled
225 pUS1->US_IDR = 0xFFFF;
227 // http://ww1.microchip.com/downloads/en/DeviceDoc/doc6175.pdf
228 // note that for very large baudrates, error is not neglectible:
229 // b921600 => 8.6%
230 // b1382400 => 8.6%
231 // FP, Fractional Part (Datasheet p402, Supported in AT91SAM512 / 256) (31.6.1.3)
232 // FP = 0 disabled;
233 // FP = 1-7 Baudrate resolution,
234 // CD, Clock divider,
235 // sync == 0 , (async?)
236 // OVER = 0, -no
237 // baudrate == selected clock/16/CD
238 // OVER = 1, -yes we are oversampling
239 // baudrate == selected clock/8/CD --> this is ours
241 uint32_t brgr = MCK / (g_usart_baudrate << 3);
242 // doing fp = round((mck / (g_usart_baudrate << 3) - brgr) * 8) with integers:
243 uint32_t fp = ((16 * MCK / (g_usart_baudrate << 3) - 16 * brgr) + 1) / 2;
245 pUS1->US_BRGR = (fp << 16) | brgr;
247 // Write the Timeguard Register
248 pUS1->US_TTGR = 0;
249 pUS1->US_RTOR = 0;
250 pUS1->US_FIDI = 0;
251 pUS1->US_IF = 0;
253 // Initialize DMA buffers
254 pUS1->US_TPR = (uint32_t)0;
255 pUS1->US_TCR = 0;
256 pUS1->US_TNPR = (uint32_t)0;
257 pUS1->US_TNCR = 0;
258 pUS1->US_RPR = (uint32_t)us_inbuf1;
259 pUS1->US_RCR = USART_BUFFLEN;
260 usart_cur_inbuf = us_inbuf1;
261 usart_cur_inbuf_off = 0;
262 pUS1->US_RNPR = (uint32_t)us_inbuf2;
263 pUS1->US_RNCR = USART_BUFFLEN;
265 // Initialize our fifo
266 us_rxfifo_low = 0;
267 us_rxfifo_high = 0;
269 // re-enable receiver / transmitter
270 pUS1->US_CR = (AT91C_US_RXEN | AT91C_US_TXEN);
272 // ready to receive and transmit
273 pUS1->US_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;