//-----------------------------------------------------------------------------
// The main i2c code, for communications with smart card module
//-----------------------------------------------------------------------------
+
#include "i2c.h"
-#include "mifareutil.h" //for mf_dbglevel
+
+#include <stdint.h>
+#include <stdbool.h>
#include "string.h" //for memset memcmp
+#include "proxmark3.h"
+#include "mifareutil.h" // for MF_DBGLEVEL
+#include "BigBuf.h"
+#include "apps.h"
+
+#ifdef WITH_SMARTCARD
+#include "smartcard.h"
+#endif
+
-// ¶¨ÒåÁ¬½ÓÒý½Å
#define GPIO_RST AT91C_PIO_PA1
#define GPIO_SCL AT91C_PIO_PA5
#define GPIO_SDA AT91C_PIO_PA7
#define I2C_ERROR "I2C_WaitAck Error"
-volatile unsigned long c;
+static volatile unsigned long c;
// Ö±½ÓʹÓÃÑ»·À´ÑÓʱ£¬Ò»¸öÑ»· 6 ÌõÖ¸Á48M£¬ Delay=1 ´ó¸ÅΪ 200kbps
// timer.
// I2CSpinDelayClk(4) = 12.31us
// I2CSpinDelayClk(1) = 3.07us
-void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) {
+static void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) {
for (c = delay * 2; c; c--) {};
}
-// ͨѶÑÓ³Ùº¯Êý communication delay function
+// communication delay functions
#define I2C_DELAY_1CLK I2CSpinDelayClk(1)
#define I2C_DELAY_2CLK I2CSpinDelayClk(2)
#define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x))
-
#define ISO7618_MAX_FRAME 255
-void I2C_init(void) {
- // ÅäÖø´Î»Òý½Å£¬¹Ø±ÕÉÏÀ£¬ÍÆÍìÊä³ö£¬Ä¬Èϸß
- // Configure reset pin, close up pull up, push-pull output, default high
- AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST;
- AT91C_BASE_PIOA->PIO_MDDR = GPIO_RST;
+// try i2c bus recovery at 100kHz = 5uS high, 5uS low
+static void I2C_recovery(void) {
+
+ DbpString("Performing i2c bus recovery");
+
+ // reset I2C
+ SDA_H; SCL_H;
+
+ //9nth cycle acts as NACK
+ for (int i = 0; i < 10; i++) {
+ SCL_H; WaitUS(5);
+ SCL_L; WaitUS(5);
+ }
+
+ //a STOP signal (SDA from low to high while CLK is high)
+ SDA_L; WaitUS(5);
+ SCL_H; WaitUS(2);
+ SDA_H; WaitUS(2);
+
+ bool isok = (SCL_read && SDA_read);
+ if (!SDA_read)
+ DbpString("I2C bus recovery error: SDA still LOW");
+ if (!SCL_read)
+ DbpString("I2C bus recovery error: SCL still LOW");
+ if (isok)
+ DbpString("I2C bus recovery complete");
+}
+
+static void I2C_init(void) {
+ // Configure reset pin
+ AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST; // disable pull up resistor
+ AT91C_BASE_PIOA->PIO_MDDR = GPIO_RST; // push-pull output (multidriver disabled)
- // ÅäÖà I2C Òý½Å£¬¿ªÆôÉÏÀ£¬¿ªÂ©Êä³ö
- // Configure I2C pin, open up, open leakage
- AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_SCL | GPIO_SDA); // ´ò¿ªÉÏÀ Open up the pull up
- AT91C_BASE_PIOA->PIO_MDER |= (GPIO_SCL | GPIO_SDA);
+ // Configure SCL and SDA pins
+ AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_SCL | GPIO_SDA); // enable pull up resistor
+ AT91C_BASE_PIOA->PIO_MDER |= (GPIO_SCL | GPIO_SDA); // open drain output (multidriver enabled) - requires external pull up resistor
- // ĬÈÏÈý¸ùÏßÈ«²¿À¸ß
- // default three lines all pull up
+ // set all three outputs to high
AT91C_BASE_PIOA->PIO_SODR |= (GPIO_SCL | GPIO_SDA | GPIO_RST);
- // ÔÊÐíÊä³ö
- // allow output
+ // configure all three pins as output, controlled by PIOA
AT91C_BASE_PIOA->PIO_OER |= (GPIO_SCL | GPIO_SDA | GPIO_RST);
AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST);
-}
+ bool isok = (SCL_read && SDA_read);
+ if ( !isok )
+ I2C_recovery();
+
+}
-// ÉèÖø´Î»×´Ì¬
// set the reset state
-void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) {
+static void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) {
if (LineRST)
HIGH(GPIO_RST);
else
LOW(GPIO_SDA);
}
-// ¸´Î»½øÈëÖ÷³ÌÐò
// Reset the SIM_Adapter, then enter the main program
// Note: the SIM_Adapter will not enter the main program after power up. Please run this function before use SIM_Adapter.
-void I2C_Reset_EnterMainProgram(void) {
- I2C_SetResetStatus(0, 0, 0); // ÀµÍ¸´Î»Ïß
- SpinDelay(30);
- I2C_SetResetStatus(1, 0, 0); // ½â³ý¸´Î»
- SpinDelay(30);
- I2C_SetResetStatus(1, 1, 1); // À¸ßÊý¾ÝÏß
- SpinDelay(10);
-}
-
-// ¸´Î»½øÈëÒýµ¼Ä£Ê½
-// Reset the SIM_Adapter, then enter the bootloader program
-// Reserve£ºFor firmware update.
-void I2C_Reset_EnterBootloader(void) {
- I2C_SetResetStatus(0, 1, 1); // ÀµÍ¸´Î»Ïß
- SpinDelay(100);
- I2C_SetResetStatus(1, 1, 1); // ½â³ý¸´Î»
- SpinDelay(10);
+static void I2C_Reset_EnterMainProgram(void) {
+ StartTicks();
+ I2C_init();
+ I2C_SetResetStatus(0, 0, 0);
+ WaitMS(30);
+ I2C_SetResetStatus(1, 0, 0);
+ WaitMS(30);
+ I2C_SetResetStatus(1, 1, 1);
+ WaitMS(10);
}
-// µÈ´ýʱÖÓ±ä¸ß
// Wait for the clock to go High.
-bool WaitSCL_H_delay(uint32_t delay) {
+static bool WaitSCL_H_delay(uint32_t delay) {
while (delay--) {
if (SCL_read) {
return true;
return false;
}
-// 5000 * 3.07us = 15350us. 15.35ms
-bool WaitSCL_H(void) {
- return WaitSCL_H_delay(5000);
+// 15000 * 3.07us = 46050us. 46.05ms
+static bool WaitSCL_H(void) {
+ return WaitSCL_H_delay(15000);
}
-// Wait max 300ms or until SCL goes LOW.
-// Which ever comes first
-bool WaitSCL_L_300ms(void) {
- volatile uint16_t delay = 300;
- while ( delay-- ) {
- // exit on SCL LOW
- if (!SCL_read)
+bool WaitSCL_L_delay(uint32_t delay) {
+ while (delay--) {
+ if (!SCL_read) {
return true;
-
- SpinDelay(1);
+ }
+ I2C_DELAY_1CLK;
}
- return (delay == 0);
+ return false;
}
-bool I2C_Start(void) {
+bool WaitSCL_L(void) {
+ return WaitSCL_L_delay(15000);
+}
+
+static bool I2C_Start(void) {
I2C_DELAY_XCLK(4);
SDA_H; I2C_DELAY_1CLK;
return true;
}
-bool I2C_WaitForSim() {
- // variable delay here.
- if (!WaitSCL_L_300ms())
- return false;
-
- // 8051 speaks with smart card.
- // 1000*50*3.07 = 153.5ms
- // 1byte transfer == 1ms
- if (!WaitSCL_H_delay(2000*50) )
- return false;
-
- return true;
-}
-
// send i2c STOP
-void I2C_Stop(void) {
+static void I2C_Stop(void) {
SCL_L; I2C_DELAY_2CLK;
SDA_L; I2C_DELAY_2CLK;
SCL_H; I2C_DELAY_2CLK;
I2C_DELAY_XCLK(8);
}
-// Send i2c ACK
-void I2C_Ack(void) {
- SCL_L; I2C_DELAY_2CLK;
- SDA_L; I2C_DELAY_2CLK;
- SCL_H; I2C_DELAY_2CLK;
- SCL_L; I2C_DELAY_2CLK;
-}
-
-// Send i2c NACK
-void I2C_NoAck(void) {
- SCL_L; I2C_DELAY_2CLK;
- SDA_H; I2C_DELAY_2CLK;
- SCL_H; I2C_DELAY_2CLK;
- SCL_L; I2C_DELAY_2CLK;
-}
-
-bool I2C_WaitAck(void) {
+static bool I2C_WaitAck(void) {
SCL_L; I2C_DELAY_1CLK;
SDA_H; I2C_DELAY_1CLK;
SCL_H;
if (!WaitSCL_H())
return false;
+ I2C_DELAY_2CLK;
I2C_DELAY_2CLK;
if (SDA_read) {
SCL_L;
return true;
}
-void I2C_SendByte(uint8_t data) {
- uint8_t i = 8;
+static void I2C_SendByte(uint8_t data) {
+ uint8_t bits = 8;
- while (i--) {
- SCL_L; I2C_DELAY_1CLK;
+ while (bits--) {
+ SCL_L;
+ I2C_DELAY_1CLK;
if (data & 0x80)
SDA_H;
SDA_L;
data <<= 1;
+
I2C_DELAY_1CLK;
SCL_H;
SCL_L;
}
-uint8_t I2C_ReadByte(void) {
- uint8_t i = 8, b = 0;
+bool I2C_is_available(void) {
+ I2C_Reset_EnterMainProgram();
+ if (!I2C_Start()) // some other device is active on the bus
+ return true;
+ I2C_SendByte(I2C_DEVICE_ADDRESS_MAIN & 0xFE);
+ if (!I2C_WaitAck()) { // no response from smartcard reader
+ I2C_Stop();
+ return false;
+ }
+ I2C_Stop();
+ return true;
+}
+
+#ifdef WITH_SMARTCARD
+// Reset the SIM_Adapter, then enter the bootloader program
+// Reserve£ºFor firmware update.
+static void I2C_Reset_EnterBootloader(void) {
+ I2C_SetResetStatus(0, 1, 1);
+ WaitMS(100);
+ I2C_SetResetStatus(1, 1, 1);
+ WaitMS(10);
+}
+
+// Wait max 1800ms or until SCL goes LOW.
+// It timeout reading response from card
+// Which ever comes first
+bool WaitSCL_L_timeout(void){
+ volatile uint16_t delay = 1800;
+ while ( delay-- ) {
+ // exit on SCL LOW
+ if (!SCL_read)
+ return true;
+
+ WaitMS(1);
+ }
+ return (delay == 0);
+}
+
+static bool I2C_WaitForSim() {
+ // wait for data from card
+ if (!WaitSCL_L_timeout())
+ return false;
+
+ // 8051 speaks with smart card.
+ // 1000*50*3.07 = 153.5ms
+ // 1byte transfer == 1ms with max frame being 256bytes
+ if (!WaitSCL_H_delay(10 * 1000 * 50))
+ return false;
+
+ return true;
+}
+
+// Send i2c ACK
+static void I2C_Ack(void) {
+ SCL_L; I2C_DELAY_2CLK;
+ SDA_L; I2C_DELAY_2CLK;
+ SCL_H; I2C_DELAY_2CLK;
+ if (!WaitSCL_H()) return;
+ SCL_L; I2C_DELAY_2CLK;
+}
+
+// Send i2c NACK
+static void I2C_NoAck(void) {
+ SCL_L; I2C_DELAY_2CLK;
+ SDA_H; I2C_DELAY_2CLK;
+ SCL_H; I2C_DELAY_2CLK;
+ if (!WaitSCL_H()) return;
+ SCL_L; I2C_DELAY_2CLK;
+}
+
+static int16_t I2C_ReadByte(void) {
+ uint8_t bits = 8, b = 0;
SDA_H;
- while (i--) {
+ while (bits--) {
b <<= 1;
- SCL_L; I2C_DELAY_2CLK;
+ SCL_L;
+ if (!WaitSCL_L()) return -2;
+
+ I2C_DELAY_1CLK;
+
SCL_H;
- if (!WaitSCL_H())
- return 0;
+ if (!WaitSCL_H()) return -1;
- I2C_DELAY_2CLK;
+ I2C_DELAY_1CLK;
if (SDA_read)
b |= 0x01;
}
}
// Sends one byte ( command to be written, SlaveDevice address)
-bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) {
+static bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) {
bool bBreak = true;
do {
if (!I2C_Start())
return false;
- //[C0]
+
I2C_SendByte(device_address & 0xFE);
if (!I2C_WaitAck())
break;
return true;
}
-// дÈë1×Ö½ÚÊý¾Ý £¨´ýдÈëÊý¾Ý£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£©
// Sends 1 byte data (Data to be written, command to be written , SlaveDevice address ).
-bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) {
+static bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) {
bool bBreak = true;
do {
if (!I2C_Start())
return true;
}
-// дÈë1´®Êý¾Ý£¨´ýдÈëÊý×éµØÖ·£¬´ýдÈ볤¶È£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£©
//Sends a string of data (Array, length, command to be written , SlaveDevice address ).
// len = uint8 (max buffer to write 256bytes)
-bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) {
+static bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) {
bool bBreak = true;
do {
if (!I2C_Start())
return true;
}
-// ¶Á³ö1´®Êý¾Ý£¨´æ·Å¶Á³öÊý¾Ý£¬´ý¶Á³ö³¤¶È£¬´ø¶Á³öµØÖ·£¬Æ÷¼þÀàÐÍ£©
// read 1 strings of data (Data array, Readout length, command to be written , SlaveDevice address ).
// len = uint8 (max buffer to read 256bytes)
-uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) {
+static int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) {
if ( !data || len == 0 )
return 0;
// extra wait 500us (514us measured)
// 200us (xx measured)
- SpinDelayUs(200);
+ WaitUS(600);
bool bBreak = true;
- uint8_t readcount = 0;
+ uint16_t readcount = 0;
do {
if (!I2C_Start())
return 0;
}
- // reading
while (len) {
- *data = I2C_ReadByte();
-
+ int16_t tmp = I2C_ReadByte();
+ if ( tmp < 0 )
+ return tmp;
+
+ *data = (uint8_t)tmp & 0xFF;
+
len--;
// ¶ÁÈ¡µÄµÚÒ»¸ö×Ö½ÚΪºóÐø³¤¶È
}
I2C_Stop();
+
// return bytecount - first byte (which is length byte)
- return (readcount) ? --readcount : 0;
+ return --readcount;
}
-uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) {
+static int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) {
//START, 0xB0, 0x00, 0x00, START, 0xB1, xx, yy, zz, ......, STOP
bool bBreak = true;
uint8_t readcount = 0;
if (!I2C_WaitAck())
break;
- // msb
I2C_SendByte(msb);
if (!I2C_WaitAck())
break;
- // lsb
I2C_SendByte(lsb);
if (!I2C_WaitAck())
break;
// reading
while (len) {
- *data = I2C_ReadByte();
-
+
+ int16_t tmp = I2C_ReadByte();
+ if ( tmp < 0 )
+ return tmp;
+
+ *data = (uint8_t)tmp & 0xFF;
+
data++;
readcount++;
len--;
return readcount;
}
-bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) {
+static bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) {
//START, 0xB0, 0x00, 0x00, xx, yy, zz, ......, STOP
bool bBreak = true;
if (!I2C_WaitAck())
break;
- // msb
I2C_SendByte(msb);
if (!I2C_WaitAck())
break;
- // lsb
I2C_SendByte(lsb);
if (!I2C_WaitAck())
break;
void I2C_print_status(void) {
DbpString("Smart card module (ISO 7816)");
uint8_t resp[] = {0,0,0,0};
- I2C_init();
I2C_Reset_EnterMainProgram();
uint8_t len = I2C_BufferRead(resp, sizeof(resp), I2C_DEVICE_CMD_GETVERSION, I2C_DEVICE_ADDRESS_MAIN);
if ( len > 0 )
DbpString(" version.................FAILED");
}
-bool GetATR(smart_card_atr_t *card_ptr) {
+// Will read response from smart card module, retries 3 times to get the data.
+static bool sc_rx_bytes(uint8_t* dest, uint8_t *destlen) {
+ uint8_t i = 3;
+ int16_t len = 0;
+ while (i--) {
+
+ I2C_WaitForSim();
+
+ len = I2C_BufferRead(dest, *destlen, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN);
+
+ if ( len > 1 ){
+ break;
+ } else if ( len == 1 ) {
+ continue;
+ } else if ( len <= 0 ) {
+ return false;
+ }
+ }
+ // after three
+ if ( len <= 1 )
+ return false;
+
+ *destlen = (uint8_t)len & 0xFF;
+ return true;
+}
+
+static bool GetATR(smart_card_atr_t *card_ptr) {
- // clear
- if ( card_ptr ) {
- card_ptr->atr_len = 0;
- memset(card_ptr->atr, 0, sizeof(card_ptr->atr));
+ if ( !card_ptr ) {
+ return false;
}
+ card_ptr->atr_len = 0;
+ memset(card_ptr->atr, 0, sizeof(card_ptr->atr));
+
// Send ATR
// start [C0 01] stop start C1 len aa bb cc stop]
I2C_WriteCmd(I2C_DEVICE_CMD_GENERATE_ATR, I2C_DEVICE_ADDRESS_MAIN);
- uint8_t cmd[1] = {1};
- LogTrace(cmd, 1, 0, 0, NULL, true);
- //wait for sim card to answer.
+ // wait for sim card to answer.
+ // 1byte = 1ms, max frame 256bytes. Should wait 256ms at least just in case.
if (!I2C_WaitForSim())
return false;
- // read answer
- uint8_t len = I2C_BufferRead(card_ptr->atr, sizeof(card_ptr->atr), I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN);
-
- if ( len == 0 )
+ // read bytes from module
+ uint8_t len = sizeof(card_ptr->atr);
+ if ( !sc_rx_bytes(card_ptr->atr, &len) )
return false;
- // for some reason we only get first byte of atr, if that is so, send dummy command to retrieve the rest of the atr
- if (len == 1) {
-
- uint8_t data[1] = {0};
- I2C_BufferWrite(data, len, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN);
-
- if ( !I2C_WaitForSim() )
- return false;
-
- uint8_t len2 = I2C_BufferRead(card_ptr->atr + len, sizeof(card_ptr->atr) - len, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN);
- len = len + len2;
- }
-
- if ( card_ptr ) {
- card_ptr->atr_len = len;
- LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false);
- }
+ card_ptr->atr_len = len;
+ LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false);
return true;
}
LED_D_ON();
clear_trace();
set_tracing(true);
- I2C_init();
I2C_Reset_EnterMainProgram();
bool isOK = GetATR( &card );
cmd_send(CMD_ACK, isOK, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t));
if ((flags & SC_CONNECT)) {
- I2C_init();
I2C_Reset_EnterMainProgram();
- if ( !(flags & SC_NO_SELECT) ) {
+ if ((flags & SC_SELECT)) {
smart_card_atr_t card;
bool gotATR = GetATR( &card );
//cmd_send(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t));
}
}
- if ((flags & SC_RAW)) {
+ if ((flags & SC_RAW) || (flags & SC_RAW_T0)) {
LogTrace(data, arg1, 0, 0, NULL, true);
// Send raw bytes
// asBytes = A0 A4 00 00 02
// arg1 = len 5
- I2C_BufferWrite(data, arg1, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN);
-
- if ( !I2C_WaitForSim() )
- goto OUT;
-
- len = I2C_BufferRead(resp, ISO7618_MAX_FRAME, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN);
- LogTrace(resp, len, 0, 0, NULL, false);
+ bool res = I2C_BufferWrite(data, arg1, ((flags & SC_RAW_T0) ? I2C_DEVICE_CMD_SEND_T0 : I2C_DEVICE_CMD_SEND), I2C_DEVICE_ADDRESS_MAIN);
+ if ( !res && MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR);
+
+ // read bytes from module
+ len = ISO7618_MAX_FRAME;
+ res = sc_rx_bytes(resp, &len);
+ if ( res ) {
+ LogTrace(resp, len, 0, 0, NULL, false);
+ } else {
+ len = 0;
+ }
}
OUT:
cmd_send(CMD_ACK, len, 0, 0, resp, len);
+ BigBuf_free();
set_tracing(false);
LEDsoff();
}
// write. Sector0, with 11,22,33,44
// erase is 128bytes, and takes 50ms to execute
- I2C_init();
I2C_Reset_EnterBootloader();
bool isOK = true;
- uint8_t res = 0;
+ int16_t res = 0;
uint16_t length = arg0;
uint16_t pos = 0;
uint8_t *fwdata = BigBuf_get_addr();
}
// writing takes time.
- SpinDelay(50);
+ WaitMS(50);
// read
res = I2C_ReadFW(verfiydata, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT);
- if ( res == 0) {
+ if ( res <= 0) {
DbpString("Reading back failed");
isOK = false;
break;
}
cmd_send(CMD_ACK, isOK, pos, 0, 0, 0);
LED_C_OFF();
+ BigBuf_free();
}
// unfinished (or not needed?)
void SmartCardSetClock(uint64_t arg0) {
LED_D_ON();
set_tracing(true);
- I2C_init();
I2C_Reset_EnterMainProgram();
// Send SIM CLC
set_tracing(false);
LEDsoff();
}
+
+#endif
\ No newline at end of file