mirror of
				https://github.com/RfidResearchGroup/proxmark3.git
				synced 2025-11-01 00:46:39 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			230 lines
		
	
	
	
		
			8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			230 lines
		
	
	
	
		
			8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| //-----------------------------------------------------------------------------
 | |
| // Iceman, July 2018
 | |
| // edits by - Anticat, August 2018
 | |
| //
 | |
| // This code is licensed to you under the terms of the GNU GPL, version 2 or,
 | |
| // at your option, any later version. See the LICENSE.txt file for the text of
 | |
| // the license.
 | |
| //-----------------------------------------------------------------------------
 | |
| // The main USART code, for serial communications over FPC connector
 | |
| //-----------------------------------------------------------------------------
 | |
| #include "usart.h"
 | |
| #include "string.h"
 | |
| #include "../armsrc/ticks.h"  // startcountus
 | |
| 
 | |
| volatile AT91PS_USART pUS1 = AT91C_BASE_US1;
 | |
| volatile AT91PS_PIO pPIO   = AT91C_BASE_PIOA;
 | |
| volatile AT91PS_PDC pPDC   = AT91C_BASE_PDC_US1;
 | |
| 
 | |
| /*
 | |
| void usart_close(void) {
 | |
|     // Reset the USART mode
 | |
|     pUS1->US_MR = 0;
 | |
| 
 | |
|     // Reset the baud rate divisor register
 | |
|     pUS1->US_BRGR = 0;
 | |
| 
 | |
|     // Reset the Timeguard Register
 | |
|     pUS1->US_TTGR = 0;
 | |
| 
 | |
|     // Disable all interrupts
 | |
|     pUS1->US_IDR = 0xFFFFFFFF;
 | |
| 
 | |
|     // Abort the Peripheral Data Transfers
 | |
|     pUS1->US_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
 | |
| 
 | |
|     // Disable receiver and transmitter and stop any activity immediately
 | |
|     pUS1->US_CR = AT91C_US_TXDIS | AT91C_US_RXDIS | AT91C_US_RSTTX | AT91C_US_RSTRX;
 | |
| }
 | |
| */
 | |
| 
 | |
| static uint8_t us_inbuf1[USART_BUFFLEN];
 | |
| static uint8_t us_inbuf2[USART_BUFFLEN];
 | |
| uint8_t *usart_cur_inbuf = NULL;
 | |
| uint16_t usart_cur_inbuf_off = 0;
 | |
| static uint8_t us_rxfifo[USART_FIFOLEN];
 | |
| static size_t us_rxfifo_low = 0;
 | |
| static size_t us_rxfifo_high = 0;
 | |
| 
 | |
| 
 | |
| static void usart_fill_rxfifo(void) {
 | |
|     uint16_t rxfifo_free = 0;
 | |
|     if (pUS1->US_RNCR == 0) { // One buffer got filled, backup buffer being used
 | |
|         if (us_rxfifo_low > us_rxfifo_high)
 | |
|             rxfifo_free = us_rxfifo_low - us_rxfifo_high;
 | |
|         else
 | |
|             rxfifo_free = sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low;
 | |
|         uint16_t available = USART_BUFFLEN - usart_cur_inbuf_off;
 | |
|         if (available <= rxfifo_free) {
 | |
|             for (uint16_t i = 0; i < available; i++) {
 | |
|                 us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i];
 | |
|                 if (us_rxfifo_high == sizeof(us_rxfifo))
 | |
|                     us_rxfifo_high = 0;
 | |
|             }
 | |
|             // Give next buffer
 | |
|             pUS1->US_RNPR = (uint32_t)usart_cur_inbuf;
 | |
|             pUS1->US_RNCR = USART_BUFFLEN;
 | |
|             // Swap current buff
 | |
|             if (usart_cur_inbuf == us_inbuf1)
 | |
|                 usart_cur_inbuf = us_inbuf2;
 | |
|             else
 | |
|                 usart_cur_inbuf = us_inbuf1;
 | |
|             usart_cur_inbuf_off = 0;
 | |
|         } else {
 | |
|             // Take only what we have room for
 | |
|             available = rxfifo_free;
 | |
|             for (uint16_t i = 0; i < available; i++) {
 | |
|                 us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i];
 | |
|                 if (us_rxfifo_high == sizeof(us_rxfifo))
 | |
|                     us_rxfifo_high = 0;
 | |
|             }
 | |
|             usart_cur_inbuf_off += available;
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     if (pUS1->US_RCR < USART_BUFFLEN - usart_cur_inbuf_off) { // Current buffer partially filled
 | |
|         if (us_rxfifo_low > us_rxfifo_high)
 | |
|             rxfifo_free = us_rxfifo_low - us_rxfifo_high;
 | |
|         else
 | |
|             rxfifo_free = sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low;
 | |
|         uint16_t available = USART_BUFFLEN - pUS1->US_RCR - usart_cur_inbuf_off;
 | |
|         if (available > rxfifo_free)
 | |
|             available = rxfifo_free;
 | |
|         for (uint16_t i = 0; i < available; i++) {
 | |
|             us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i];
 | |
|             if (us_rxfifo_high == sizeof(us_rxfifo))
 | |
|                 us_rxfifo_high = 0;
 | |
|         }
 | |
|         usart_cur_inbuf_off += available;
 | |
|     }
 | |
| }
 | |
| 
 | |
| uint16_t usart_rxdata_available(void) {
 | |
|     usart_fill_rxfifo();
 | |
|     if (us_rxfifo_low <= us_rxfifo_high)
 | |
|         return us_rxfifo_high - us_rxfifo_low;
 | |
|     else
 | |
|         return sizeof(us_rxfifo) - us_rxfifo_low + us_rxfifo_high;
 | |
| }
 | |
| 
 | |
| extern bool reply_via_fpc;
 | |
| extern void Dbprintf(const char *fmt, ...);
 | |
| #define Dbprintf_usb(...) {\
 | |
|         bool tmp = reply_via_fpc;\
 | |
|         reply_via_fpc = false;\
 | |
|         Dbprintf(__VA_ARGS__);\
 | |
|         reply_via_fpc = tmp;}
 | |
| 
 | |
| uint32_t usart_read_ng(uint8_t *data, size_t len) {
 | |
|     if (len == 0) return 0;
 | |
|     uint32_t packetSize, nbBytesRcv = 0;
 | |
|     uint32_t try = 0;
 | |
| //    uint32_t highest_observed_try = 0;
 | |
|     // Empirical max try observed: 3000000 / USART_BAUD_RATE
 | |
|     // Let's take 10x
 | |
| 
 | |
|     uint32_t tryconstant = 0;
 | |
| #ifdef USART_SLOW_LINK
 | |
|     // Experienced up to 13200 tries on BT link even at 460800
 | |
|     tryconstant = 50000;
 | |
| #endif
 | |
| 
 | |
|     uint32_t maxtry = 10 * (3000000 / USART_BAUD_RATE) + tryconstant;
 | |
| 
 | |
|     while (len) {
 | |
|         uint32_t available = usart_rxdata_available();
 | |
| 
 | |
|         packetSize = MIN(available, len);
 | |
|         if (available > 0) {
 | |
| //            Dbprintf_usb("Dbg USART ask %d bytes, available %d bytes, packetsize %d bytes", len, available, packetSize);
 | |
| //            highest_observed_try = MAX(highest_observed_try, try);
 | |
|             try = 0;
 | |
|         }
 | |
|         len -= packetSize;
 | |
|         while (packetSize--) {
 | |
|             data[nbBytesRcv++] = us_rxfifo[us_rxfifo_low++];
 | |
|             if (us_rxfifo_low == sizeof(us_rxfifo))
 | |
|                 us_rxfifo_low = 0;
 | |
|         }
 | |
|         if (try++ == maxtry) {
 | |
| //            Dbprintf_usb("Dbg USART TIMEOUT");
 | |
|                 break;
 | |
|             }
 | |
|     }
 | |
| //    highest_observed_try = MAX(highest_observed_try, try);
 | |
| //    Dbprintf_usb("Dbg USART max observed try %i", highest_observed_try);
 | |
|     return nbBytesRcv;
 | |
| }
 | |
| 
 | |
| // transfer from device to client
 | |
| inline int usart_writebuffer_sync(uint8_t *data, size_t len) {
 | |
| 
 | |
|     // Wait for current PDC bank to be free
 | |
|     // (and check next bank too, in case there will be a usart_writebuffer_async)
 | |
|     while (pUS1->US_TNCR || pUS1->US_TCR) {};
 | |
|     pUS1->US_TPR = (uint32_t)data;
 | |
|     pUS1->US_TCR = len;
 | |
|     // Wait until finishing all transfers to make sure "data" buffer can be discarded
 | |
|     // (if we don't wait here, bulk send as e.g. "hw status" will fail)
 | |
|     while (pUS1->US_TNCR || pUS1->US_TCR) {};
 | |
|     return PM3_SUCCESS;
 | |
| }
 | |
| 
 | |
| void usart_init(void) {
 | |
| 
 | |
|     // For a nice detailed sample, interrupt driven but still relevant.
 | |
|     // See https://www.sparkfun.com/datasheets/DevTools/SAM7/at91sam7%20serial%20communications.pdf
 | |
| 
 | |
|     // disable & reset receiver / transmitter for configuration
 | |
|     pUS1->US_CR = (AT91C_US_RSTRX | AT91C_US_RSTTX | AT91C_US_RXDIS | AT91C_US_TXDIS);
 | |
| 
 | |
|     //enable the USART1 Peripheral clock
 | |
|     AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_US1);
 | |
| 
 | |
|     // disable PIO control of receive / transmit pins
 | |
|     pPIO->PIO_PDR |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1);
 | |
| 
 | |
|     // enable peripheral mode A on receive / transmit pins
 | |
|     pPIO->PIO_ASR |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1);
 | |
|     pPIO->PIO_BSR = 0;
 | |
| 
 | |
|     // enable pull-up on receive / transmit pins (see 31.5.1 I/O Lines)
 | |
|     pPIO->PIO_PPUER |= (AT91C_PA21_RXD1 | AT91C_PA22_TXD1);
 | |
| 
 | |
|     // set mode
 | |
|     pUS1->US_MR = AT91C_US_USMODE_NORMAL |      // normal mode
 | |
|                   AT91C_US_CLKS_CLOCK |            // MCK (48MHz)
 | |
|                   AT91C_US_OVER |                  // oversampling
 | |
|                   AT91C_US_CHRL_8_BITS |           // 8 bits
 | |
|                   AT91C_US_PAR_NONE |              // parity: none
 | |
|                   AT91C_US_NBSTOP_1_BIT |          // 1 stop bit
 | |
|                   AT91C_US_CHMODE_NORMAL;          // channel mode: normal
 | |
| 
 | |
|     // all interrupts disabled
 | |
|     pUS1->US_IDR = 0xFFFF;
 | |
| 
 | |
|     pUS1->US_BRGR =  48054841 / (USART_BAUD_RATE << 3);
 | |
| 
 | |
|     // Write the Timeguard Register
 | |
|     pUS1->US_TTGR = 0;
 | |
|     pUS1->US_RTOR = 0;
 | |
|     pUS1->US_FIDI = 0;
 | |
|     pUS1->US_IF = 0;
 | |
| 
 | |
|     // Initialize DMA buffers
 | |
|     pUS1->US_TPR = (uint32_t)0;
 | |
|     pUS1->US_TCR = 0;
 | |
|     pUS1->US_TNPR = (uint32_t)0;
 | |
|     pUS1->US_TNCR = 0;
 | |
|     pUS1->US_RPR = (uint32_t)us_inbuf1;
 | |
|     pUS1->US_RCR = USART_BUFFLEN;
 | |
|     usart_cur_inbuf = us_inbuf1;
 | |
|     pUS1->US_RNPR = (uint32_t)us_inbuf2;
 | |
|     pUS1->US_RNCR = USART_BUFFLEN;
 | |
| 
 | |
|     // re-enable receiver / transmitter
 | |
|     pUS1->US_CR = (AT91C_US_RXEN | AT91C_US_TXEN);
 | |
| 
 | |
|     // ready to receive and transmit
 | |
|     pUS1->US_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
 | |
| }
 |