From 58ec4b16c57f27ec32d7907bcd88421ded1e0d1a Mon Sep 17 00:00:00 2001 From: Conor Patrick Date: Sun, 22 Jul 2018 23:40:27 -0400 Subject: [PATCH] NFC capability started --- efm32/.cproject | 18 +- efm32/EFM32.hwconf | 10 +- efm32/emlib/em_i2c.c | 811 +++++++++++++++++++++++++++++++++++++++++ efm32/inc/nfc.h | 13 + efm32/src/InitDevice.c | 31 +- efm32/src/device.c | 9 +- efm32/src/nfc.c | 178 +++++++++ fido2/main.c | 2 +- fido2/test_power.c | 43 ++- tools/nfcmon.py | 32 ++ 10 files changed, 1112 insertions(+), 35 deletions(-) create mode 100644 efm32/emlib/em_i2c.c create mode 100644 efm32/inc/nfc.h create mode 100644 efm32/src/nfc.c create mode 100644 tools/nfcmon.py diff --git a/efm32/.cproject b/efm32/.cproject index 95d19eb..3c083fe 100644 --- a/efm32/.cproject +++ b/efm32/.cproject @@ -23,7 +23,7 @@ - + @@ -67,9 +67,9 @@ - + @@ -93,9 +93,9 @@ - + @@ -128,7 +128,7 @@ - + @@ -158,7 +158,7 @@ - + @@ -177,9 +177,9 @@ - + - + diff --git a/efm32/EFM32.hwconf b/efm32/EFM32.hwconf index d359390..054e13b 100644 --- a/efm32/EFM32.hwconf +++ b/efm32/EFM32.hwconf @@ -3,7 +3,7 @@ - + @@ -12,7 +12,7 @@ - + @@ -20,6 +20,7 @@ + @@ -60,6 +61,7 @@ + @@ -67,6 +69,10 @@ + + + + diff --git a/efm32/emlib/em_i2c.c b/efm32/emlib/em_i2c.c new file mode 100644 index 0000000..106f0ea --- /dev/null +++ b/efm32/emlib/em_i2c.c @@ -0,0 +1,811 @@ +/***************************************************************************//** + * @file em_i2c.c + * @brief Inter-integrated Circuit (I2C) Peripheral API + * @version 5.2.2 + ******************************************************************************* + * # License + * Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com + ******************************************************************************* + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no + * obligation to support this Software. Silicon Labs is providing the + * Software "AS IS", with no express or implied warranties of any kind, + * including, but not limited to, any implied warranties of merchantability + * or fitness for any particular purpose or warranties against infringement + * of any proprietary rights of a third party. + * + * Silicon Labs will not be liable for any consequential, incidental, or + * special damages, or any other relief, or for any claim by any third party, + * arising from your use of this Software. + * + ******************************************************************************/ + +#include "em_i2c.h" +#if defined(I2C_COUNT) && (I2C_COUNT > 0) + +#include "em_cmu.h" +#include "em_bus.h" +#include "em_assert.h" + + #include + +/***************************************************************************//** + * @addtogroup emlib + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup I2C + * @brief Inter-integrated Circuit (I2C) Peripheral API + * @details + * This module contains functions to control the I2C peripheral of Silicon + * Labs 32-bit MCUs and SoCs. The I2C interface allows communication on I2C + * buses with the lowest energy consumption possible. + * @{ + ******************************************************************************/ + +/******************************************************************************* + ******************************* DEFINES *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Validation of I2C register block pointer reference for assert statements. */ +#if (I2C_COUNT == 1) +#define I2C_REF_VALID(ref) ((ref) == I2C0) +#elif (I2C_COUNT == 2) +#define I2C_REF_VALID(ref) ((ref == I2C0) || (ref == I2C1)) +#elif (I2C_COUNT == 3) +#define I2C_REF_VALID(ref) ((ref == I2C0) || (ref == I2C1) || (ref == I2C2)) +#endif + +/** Error flags indicating I2C transfer has failed somehow. */ +/* Notice that I2C_IF_TXOF (transmit overflow) is not really possible with */ +/* this SW supporting master mode. Likewise for I2C_IF_RXUF (receive underflow) */ +/* RXUF is only likely to occur with this SW if using a debugger peeking into */ +/* RXDATA register. Thus, we ignore those types of fault. */ +#define I2C_IF_ERRORS (I2C_IF_BUSERR | I2C_IF_ARBLOST) + +/* Max I2C transmission rate constant */ +#if defined(_SILICON_LABS_32B_SERIES_0) +#define I2C_CR_MAX 4 +#elif defined(_SILICON_LABS_32B_SERIES_1) +#define I2C_CR_MAX 8 +#else +#warning "Max I2C transmission rate constant is not defined" +#endif + +/** @endcond */ + +/******************************************************************************* + ******************************** ENUMS ************************************ + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Master mode transfer states. */ +typedef enum { + i2cStateStartAddrSend, /**< Send start + (first part of) address. */ + i2cStateAddrWFAckNack, /**< Wait for ACK/NACK on (first part of) address. */ + i2cStateAddrWF2ndAckNack, /**< Wait for ACK/NACK on second part of 10 bit address. */ + i2cStateRStartAddrSend, /**< Send repeated start + (first part of) address. */ + i2cStateRAddrWFAckNack, /**< Wait for ACK/NACK on address sent after repeated start. */ + i2cStateDataSend, /**< Send data. */ + i2cStateDataWFAckNack, /**< Wait for ACK/NACK on data sent. */ + i2cStateWFData, /**< Wait for data. */ + i2cStateWFStopSent, /**< Wait for STOP to have been transmitted. */ + i2cStateDone /**< Transfer completed successfully. */ +} I2C_TransferState_TypeDef; + +/** @endcond */ + +/******************************************************************************* + ******************************* STRUCTS *********************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** Structure used to store state information on an ongoing master mode transfer. */ +typedef struct { + /** Current state. */ + I2C_TransferState_TypeDef state; + + /** Result return code. */ + I2C_TransferReturn_TypeDef result; + + /** Offset in current sequence buffer. */ + uint16_t offset; + + /* Index to current sequence buffer in use. */ + uint8_t bufIndx; + + /** Reference to I2C transfer sequence definition provided by user. */ + I2C_TransferSeq_TypeDef *seq; +} I2C_Transfer_TypeDef; + +/** @endcond */ + +/******************************************************************************* + ***************************** LOCAL DATA *******^************************** + ******************************************************************************/ + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +/** + * Lookup table for Nlow + Nhigh setting defined by CLHR. Set undefined + * index (0x3) to reflect default setting just in case. + */ +static const uint8_t i2cNSum[] = { 4 + 4, 6 + 3, 11 + 6, 4 + 4 }; + +/** Transfer state info for ongoing master mode transfer */ +static I2C_Transfer_TypeDef i2cTransfer[I2C_COUNT]; + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Get current configured I2C bus frequency. + * + * @details + * This frequency is only of relevance when acting as master. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @return + * Current I2C frequency in Hz. + ******************************************************************************/ +uint32_t I2C_BusFreqGet(I2C_TypeDef *i2c) +{ + uint32_t freqHfper; + uint32_t n; + + /* Max frequency is given by freqScl = freqHfper/((Nlow + Nhigh)(DIV + 1) + I2C_CR_MAX) + * More details can be found in the reference manual, + * I2C Clock Generation chapter. */ + freqHfper = CMU_ClockFreqGet(cmuClock_HFPER); + /* n = Nlow + Nhigh */ + n = (uint32_t)(i2cNSum[(i2c->CTRL & _I2C_CTRL_CLHR_MASK) >> _I2C_CTRL_CLHR_SHIFT]); + + return (freqHfper / ((n * (i2c->CLKDIV + 1)) + I2C_CR_MAX)); +} + +/***************************************************************************//** + * @brief + * Set I2C bus frequency. + * + * @details + * The bus frequency is only of relevance when acting as a master. The bus + * frequency should not be set higher than the max frequency accepted by the + * slowest device on the bus. + * + * Notice that due to asymmetric requirements on low and high I2C clock + * cycles by the I2C specification, the actual max frequency allowed in order + * to comply with the specification may be somewhat lower than expected. + * + * Please refer to the reference manual, details on I2C clock generation, + * for max allowed theoretical frequencies for different modes. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] freqRef + * I2C reference clock frequency in Hz that will be used. If set to 0, + * then HFPER clock is used. Setting it to a higher than actual configured + * value only has the consequence of reducing the real I2C frequency. + * + * @param[in] freqScl + * Bus frequency to set (actual bus speed may be lower due to integer + * prescaling). Safe (according to I2C specification) max frequencies for + * standard, fast and fast+ modes are available using I2C_FREQ_ defines. + * (Using I2C_FREQ_ defines requires corresponding setting of @p type.) + * Slowest slave device on bus must always be considered. + * + * @param[in] i2cMode + * Clock low to high ratio type to use. If not using i2cClockHLRStandard, + * make sure all devices on the bus support the specified mode. Using a + * non-standard ratio is useful to achieve higher bus clock in fast and + * fast+ modes. + ******************************************************************************/ +void I2C_BusFreqSet(I2C_TypeDef *i2c, + uint32_t freqRef, + uint32_t freqScl, + I2C_ClockHLR_TypeDef i2cMode) +{ + uint32_t n, minFreq; + int32_t div; + + /* Avoid divide by 0 */ + EFM_ASSERT(freqScl); + if (!freqScl) { + return; + } + + /* Set the CLHR (clock low to high ratio). */ + i2c->CTRL &= ~_I2C_CTRL_CLHR_MASK; + BUS_RegMaskedWrite(&i2c->CTRL, + _I2C_CTRL_CLHR_MASK, + i2cMode << _I2C_CTRL_CLHR_SHIFT); + + if (!freqRef) { + freqRef = CMU_ClockFreqGet(cmuClock_HFPER); + } + + /* Check minumum HF peripheral clock */ + minFreq = UINT_MAX; + if (i2c->CTRL & I2C_CTRL_SLAVE) { + switch (i2cMode) { + case i2cClockHLRStandard: +#if defined(_SILICON_LABS_32B_SERIES_0) + minFreq = 4200000; break; +#elif defined(_SILICON_LABS_32B_SERIES_1) + minFreq = 2000000; break; +#endif + case i2cClockHLRAsymetric: +#if defined(_SILICON_LABS_32B_SERIES_0) + minFreq = 11000000; break; +#elif defined(_SILICON_LABS_32B_SERIES_1) + minFreq = 5000000; break; +#endif + case i2cClockHLRFast: +#if defined(_SILICON_LABS_32B_SERIES_0) + minFreq = 24400000; break; +#elif defined(_SILICON_LABS_32B_SERIES_1) + minFreq = 14000000; break; +#endif + } + } else { + /* For master mode, platform 1 and 2 share the same + min frequencies */ + switch (i2cMode) { + case i2cClockHLRStandard: + minFreq = 2000000; break; + case i2cClockHLRAsymetric: + minFreq = 9000000; break; + case i2cClockHLRFast: + minFreq = 20000000; break; + } + } + + /* Frequency most be larger-than */ + EFM_ASSERT(freqRef > minFreq); + + /* SCL frequency is given by + * freqScl = freqRef/((Nlow + Nhigh) * (DIV + 1) + I2C_CR_MAX) + * + * Thus + * DIV = ((freqRef - (I2C_CR_MAX * freqScl))/((Nlow + Nhigh) * freqScl)) - 1 + * + * More details can be found in the reference manual, + * I2C Clock Generation chapter. */ + + /* n = Nlow + Nhigh */ + n = (uint32_t)(i2cNSum[i2cMode]); + div = ((freqRef - (I2C_CR_MAX * freqScl)) / (n * freqScl)) - 1; + EFM_ASSERT(div >= 0); + EFM_ASSERT((uint32_t)div <= _I2C_CLKDIV_DIV_MASK); + + /* Clock divisor must be at least 1 in slave mode according to reference */ + /* manual (in which case there is normally no need to set bus frequency). */ + if ((i2c->CTRL & I2C_CTRL_SLAVE) && !div) { + div = 1; + } + i2c->CLKDIV = (uint32_t)div; +} + +/***************************************************************************//** + * @brief + * Enable/disable I2C. + * + * @note + * After enabling the I2C (from being disabled), the I2C is in BUSY state. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] enable + * true to enable counting, false to disable. + ******************************************************************************/ +void I2C_Enable(I2C_TypeDef *i2c, bool enable) +{ + EFM_ASSERT(I2C_REF_VALID(i2c)); + + BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, enable); +} + +/***************************************************************************//** + * @brief + * Initialize I2C. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] init + * Pointer to I2C initialization structure. + ******************************************************************************/ +void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init) +{ + EFM_ASSERT(I2C_REF_VALID(i2c)); + + i2c->IEN = 0; + i2c->IFC = _I2C_IFC_MASK; + + /* Set SLAVE select mode */ + BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_SLAVE_SHIFT, init->master ? 0 : 1); + + I2C_BusFreqSet(i2c, init->refFreq, init->freq, init->clhr); + + BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, init->enable); +} + +/***************************************************************************//** + * @brief + * Reset I2C to same state as after a HW reset. + * + * @note + * The ROUTE register is NOT reset by this function, in order to allow for + * centralized setup of this feature. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + ******************************************************************************/ +void I2C_Reset(I2C_TypeDef *i2c) +{ + i2c->CTRL = _I2C_CTRL_RESETVALUE; + i2c->CLKDIV = _I2C_CLKDIV_RESETVALUE; + i2c->SADDR = _I2C_SADDR_RESETVALUE; + i2c->SADDRMASK = _I2C_SADDRMASK_RESETVALUE; + i2c->IEN = _I2C_IEN_RESETVALUE; + i2c->IFC = _I2C_IFC_MASK; + /* Do not reset route register, setting should be done independently */ +} + +/***************************************************************************//** + * @brief + * Continue an initiated I2C transfer (single master mode only). + * + * @details + * This function is used repeatedly after a I2C_TransferInit() in order to + * complete a transfer. It may be used in polled mode as the below example + * shows: + * @verbatim + * I2C_TransferReturn_TypeDef ret; + * + * // Do a polled transfer + * ret = I2C_TransferInit(I2C0, seq); + * while (ret == i2cTransferInProgress) + * { + * ret = I2C_Transfer(I2C0); + * } + * @endverbatim + * It may also be used in interrupt driven mode, where this function is invoked + * from the interrupt handler. Notice that if used in interrupt mode, NVIC + * interrupts must be configured and enabled for the I2C bus used. I2C + * peripheral specific interrupts are managed by this SW. + * + * @note + * Only single master mode is supported. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @return + * Returns status for ongoing transfer. + * @li #i2cTransferInProgress - indicates that transfer not finished. + * @li #i2cTransferDone - transfer completed successfully. + * @li otherwise some sort of error has occurred. + * + ******************************************************************************/ +I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c) +{ + uint32_t tmp; + uint32_t pending; + I2C_Transfer_TypeDef *transfer; + I2C_TransferSeq_TypeDef *seq; + + EFM_ASSERT(I2C_REF_VALID(i2c)); + + /* Support up to 2 I2C buses */ + if (i2c == I2C0) { + transfer = i2cTransfer; + } +#if (I2C_COUNT > 1) + else if (i2c == I2C1) { + transfer = i2cTransfer + 1; + } +#endif +#if (I2C_COUNT > 2) + else if (i2c == I2C2) { + transfer = i2cTransfer + 2; + } +#endif + else { + return i2cTransferUsageFault; + } + + seq = transfer->seq; + for (;; ) { + pending = i2c->IF; + + /* If some sort of fault, abort transfer. */ + if (pending & I2C_IF_ERRORS) { + if (pending & I2C_IF_ARBLOST) { + /* If arbitration fault, it indicates either a slave device */ + /* not responding as expected, or other master which is not */ + /* supported by this SW. */ + transfer->result = i2cTransferArbLost; + } else if (pending & I2C_IF_BUSERR) { + /* A bus error indicates a misplaced start or stop, which should */ + /* not occur in master mode controlled by this SW. */ + transfer->result = i2cTransferBusErr; + } + + /* If error situation occurred, it is difficult to know */ + /* exact cause and how to resolve. It will be up to a wrapper */ + /* to determine how to handle a fault/recovery if possible. */ + transfer->state = i2cStateDone; + goto done; + } + + switch (transfer->state) { + /***************************************************/ + /* Send first start+address (first byte if 10 bit) */ + /***************************************************/ + case i2cStateStartAddrSend: + if (seq->flags & I2C_FLAG_10BIT_ADDR) { + tmp = (((uint32_t)(seq->addr) >> 8) & 0x06) | 0xf0; + + /* In 10 bit address mode, the address following the first */ + /* start always indicate write. */ + } else { + tmp = (uint32_t)(seq->addr) & 0xfe; + + if (seq->flags & I2C_FLAG_READ) { + /* Indicate read request */ + tmp |= 1; + } + } + + transfer->state = i2cStateAddrWFAckNack; + i2c->TXDATA = tmp;/* Data not transmitted until START sent */ + i2c->CMD = I2C_CMD_START; + goto done; + + /*******************************************************/ + /* Wait for ACK/NACK on address (first byte if 10 bit) */ + /*******************************************************/ + case i2cStateAddrWFAckNack: + if (pending & I2C_IF_NACK) { + i2c->IFC = I2C_IFC_NACK; + transfer->result = i2cTransferNack; + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + } else if (pending & I2C_IF_ACK) { + i2c->IFC = I2C_IFC_ACK; + + /* If 10 bit address, send 2nd byte of address. */ + if (seq->flags & I2C_FLAG_10BIT_ADDR) { + transfer->state = i2cStateAddrWF2ndAckNack; + i2c->TXDATA = (uint32_t)(seq->addr) & 0xff; + } else { + /* Determine whether receiving or sending data */ + if (seq->flags & I2C_FLAG_READ) { + transfer->state = i2cStateWFData; + if (seq->buf[transfer->bufIndx].len == 1) { + i2c->CMD = I2C_CMD_NACK; + } + } else { + transfer->state = i2cStateDataSend; + continue; + } + } + } + goto done; + + /******************************************************/ + /* Wait for ACK/NACK on second byte of 10 bit address */ + /******************************************************/ + case i2cStateAddrWF2ndAckNack: + if (pending & I2C_IF_NACK) { + i2c->IFC = I2C_IFC_NACK; + transfer->result = i2cTransferNack; + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + } else if (pending & I2C_IF_ACK) { + i2c->IFC = I2C_IFC_ACK; + + /* If using plain read sequence with 10 bit address, switch to send */ + /* repeated start. */ + if (seq->flags & I2C_FLAG_READ) { + transfer->state = i2cStateRStartAddrSend; + } + /* Otherwise expected to write 0 or more bytes */ + else { + transfer->state = i2cStateDataSend; + } + continue; + } + goto done; + + /*******************************/ + /* Send repeated start+address */ + /*******************************/ + case i2cStateRStartAddrSend: + if (seq->flags & I2C_FLAG_10BIT_ADDR) { + tmp = ((seq->addr >> 8) & 0x06) | 0xf0; + } else { + tmp = seq->addr & 0xfe; + } + + /* If this is a write+read combined sequence, then read is about to start */ + if (seq->flags & I2C_FLAG_WRITE_READ) { + /* Indicate read request */ + tmp |= 1; + } + + transfer->state = i2cStateRAddrWFAckNack; + /* We have to write START cmd first since repeated start, otherwise */ + /* data would be sent first. */ + i2c->CMD = I2C_CMD_START; + i2c->TXDATA = tmp; + goto done; + + /**********************************************************************/ + /* Wait for ACK/NACK on repeated start+address (first byte if 10 bit) */ + /**********************************************************************/ + case i2cStateRAddrWFAckNack: + if (pending & I2C_IF_NACK) { + i2c->IFC = I2C_IFC_NACK; + transfer->result = i2cTransferNack; + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + } else if (pending & I2C_IF_ACK) { + i2c->IFC = I2C_IFC_ACK; + + /* Determine whether receiving or sending data */ + if (seq->flags & I2C_FLAG_WRITE_READ) { + transfer->state = i2cStateWFData; + } else { + transfer->state = i2cStateDataSend; + continue; + } + } + goto done; + + /*****************************/ + /* Send a data byte to slave */ + /*****************************/ + case i2cStateDataSend: + /* Reached end of data buffer? */ + if (transfer->offset >= seq->buf[transfer->bufIndx].len) { + /* Move to next message part */ + transfer->offset = 0; + transfer->bufIndx++; + + /* Send repeated start when switching to read mode on 2nd buffer */ + if (seq->flags & I2C_FLAG_WRITE_READ) { + transfer->state = i2cStateRStartAddrSend; + continue; + } + + /* Only writing from one buffer, or finished both buffers */ + if ((seq->flags & I2C_FLAG_WRITE) || (transfer->bufIndx > 1)) { + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + goto done; + } + + /* Reprocess in case next buffer is empty */ + continue; + } + + /* Send byte */ + i2c->TXDATA = (uint32_t)(seq->buf[transfer->bufIndx].data[transfer->offset++]); + transfer->state = i2cStateDataWFAckNack; + goto done; + + /*********************************************************/ + /* Wait for ACK/NACK from slave after sending data to it */ + /*********************************************************/ + case i2cStateDataWFAckNack: + if (pending & I2C_IF_NACK) { + i2c->IFC = I2C_IFC_NACK; + transfer->result = i2cTransferNack; + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + } else if (pending & I2C_IF_ACK) { + i2c->IFC = I2C_IFC_ACK; + transfer->state = i2cStateDataSend; + continue; + } + goto done; + + /****************************/ + /* Wait for data from slave */ + /****************************/ + case i2cStateWFData: + if (pending & I2C_IF_RXDATAV) { + uint8_t data; + unsigned int rxLen = seq->buf[transfer->bufIndx].len; + + /* Must read out data in order to not block further progress */ + data = (uint8_t)(i2c->RXDATA); + + /* Make sure not storing beyond end of buffer just in case */ + if (transfer->offset < rxLen) { + seq->buf[transfer->bufIndx].data[transfer->offset++] = data; + } + + /* If we have read all requested data, then the sequence should end */ + if (transfer->offset >= rxLen) { + /* If there is only one byte to receive we need to transmit the + NACK now, before the stop. */ + if (1 == rxLen) { + i2c->CMD = I2C_CMD_NACK; + } + + transfer->state = i2cStateWFStopSent; + i2c->CMD = I2C_CMD_STOP; + } else { + /* Send ACK and wait for next byte */ + i2c->CMD = I2C_CMD_ACK; + + if ( (1 < rxLen) && (transfer->offset == (rxLen - 1)) ) { + /* If there is more than one byte to receive and this is the next + to last byte we need to transmit the NACK now, before receiving + the last byte. */ + i2c->CMD = I2C_CMD_NACK; + } + } + } + goto done; + + /***********************************/ + /* Wait for STOP to have been sent */ + /***********************************/ + case i2cStateWFStopSent: + if (pending & I2C_IF_MSTOP) { + i2c->IFC = I2C_IFC_MSTOP; + transfer->state = i2cStateDone; + } + goto done; + + /******************************/ + /* Unexpected state, SW fault */ + /******************************/ + default: + transfer->result = i2cTransferSwFault; + transfer->state = i2cStateDone; + goto done; + } + } + + done: + + if (transfer->state == i2cStateDone) { + /* Disable interrupt sources when done */ + i2c->IEN = 0; + + /* Update result unless some fault already occurred */ + if (transfer->result == i2cTransferInProgress) { + transfer->result = i2cTransferDone; + } + } + /* Until transfer is done keep returning i2cTransferInProgress */ + else { + return i2cTransferInProgress; + } + + return transfer->result; +} + +/***************************************************************************//** + * @brief + * Prepare and start an I2C transfer (single master mode only). + * + * @details + * This function must be invoked in order to start an I2C transfer + * sequence. In order to actually complete the transfer, I2C_Transfer() must + * be used either in polled mode or by adding a small driver wrapper utilizing + * interrupts. + * + * @note + * Only single master mode is supported. + * + * @param[in] i2c + * Pointer to I2C peripheral register block. + * + * @param[in] seq + * Pointer to sequence structure defining the I2C transfer to take place. The + * referenced structure must exist until the transfer has fully completed. + * + * @return + * Returns status for ongoing transfer: + * @li #i2cTransferInProgress - indicates that transfer not finished. + * @li otherwise some sort of error has occurred. + ******************************************************************************/ +I2C_TransferReturn_TypeDef I2C_TransferInit(I2C_TypeDef *i2c, + I2C_TransferSeq_TypeDef *seq) +{ + I2C_Transfer_TypeDef *transfer; + + EFM_ASSERT(I2C_REF_VALID(i2c)); + EFM_ASSERT(seq); + + /* Support up to 2 I2C buses */ + if (i2c == I2C0) { + transfer = i2cTransfer; + } +#if (I2C_COUNT > 1) + else if (i2c == I2C1) { + transfer = i2cTransfer + 1; + } +#endif +#if (I2C_COUNT > 2) + else if (i2c == I2C2) { + transfer = i2cTransfer + 2; + } +#endif + else { + return i2cTransferUsageFault; + } + + /* Check if in busy state. Since this SW assumes single master, we can */ + /* just issue an abort. The BUSY state is normal after a reset. */ + if (i2c->STATE & I2C_STATE_BUSY) { + i2c->CMD = I2C_CMD_ABORT; + } + + /* Make sure user is not trying to read 0 bytes, it is not */ + /* possible according to I2C spec, since slave will always start */ + /* sending first byte ACK on address. The read operation can */ + /* only be stopped by NACKing a received byte, ie minimum 1 byte. */ + if (((seq->flags & I2C_FLAG_READ) && !(seq->buf[0].len)) + || ((seq->flags & I2C_FLAG_WRITE_READ) && !(seq->buf[1].len)) + ) { + return i2cTransferUsageFault; + } + + /* Prepare for a transfer */ + transfer->state = i2cStateStartAddrSend; + transfer->result = i2cTransferInProgress; + transfer->offset = 0; + transfer->bufIndx = 0; + transfer->seq = seq; + + /* Ensure buffers are empty */ + i2c->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX; + if (i2c->IF & I2C_IF_RXDATAV) { + (void)i2c->RXDATA; + } + + /* Clear all pending interrupts prior to starting transfer. */ + i2c->IFC = _I2C_IFC_MASK; + + /* Enable those interrupts we are interested in throughout transfer. */ + /* Notice that the I2C interrupt must also be enabled in the NVIC, but */ + /* that is left for an additional driver wrapper. */ + i2c->IEN |= I2C_IF_NACK | I2C_IF_ACK | I2C_IF_MSTOP + | I2C_IF_RXDATAV | I2C_IF_ERRORS; + + /* Start transfer */ + return I2C_Transfer(i2c); +} + +/** @} (end addtogroup I2C) */ +/** @} (end addtogroup emlib) */ +#endif /* defined(I2C_COUNT) && (I2C_COUNT > 0) */ diff --git a/efm32/inc/nfc.h b/efm32/inc/nfc.h new file mode 100644 index 0000000..6935ebc --- /dev/null +++ b/efm32/inc/nfc.h @@ -0,0 +1,13 @@ +/* + * nfc.h + * + * Created on: Jul 22, 2018 + * Author: conor + */ + +#ifndef INC_NFC_H_ +#define INC_NFC_H_ + +void nfc_test(); + +#endif /* INC_NFC_H_ */ diff --git a/efm32/src/InitDevice.c b/efm32/src/InitDevice.c index d4153fc..5dbfacf 100644 --- a/efm32/src/InitDevice.c +++ b/efm32/src/InitDevice.c @@ -23,7 +23,7 @@ #include "em_cryotimer.h" #include "em_crypto.h" #include "em_gpio.h" -#include "em_ldma.h" +#include "em_i2c.h" #include "em_usart.h" // [Library includes]$ @@ -39,7 +39,7 @@ extern void enter_DefaultMode_from_RESET(void) { ADC0_enter_DefaultMode_from_RESET(); USART0_enter_DefaultMode_from_RESET(); USART1_enter_DefaultMode_from_RESET(); - LDMA_enter_DefaultMode_from_RESET(); + I2C0_enter_DefaultMode_from_RESET(); CRYOTIMER_enter_DefaultMode_from_RESET(); PORTIO_enter_DefaultMode_from_RESET(); // [Config Calls]$ @@ -104,7 +104,7 @@ extern void CMU_enter_DefaultMode_from_RESET(void) { CMU_HFXOInit(&hfxoInit); /* Setting system HFRCO frequency */ - CMU_HFRCOFreqSet (cmuHFRCOFreq_38M0Hz); + CMU_HFRCOFreqSet (cmuHFRCOFreq_13M0Hz); /* Using HFRCO as high frequency clock, HFCLK */ CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFRCO); @@ -135,8 +135,8 @@ extern void CMU_enter_DefaultMode_from_RESET(void) { /* Enable clock for CRYOTIMER */ CMU_ClockEnable(cmuClock_CRYOTIMER, true); - /* Enable clock for LDMA */ - CMU_ClockEnable(cmuClock_LDMA, true); + /* Enable clock for I2C0 */ + CMU_ClockEnable(cmuClock_I2C0, true); /* Enable clock for USART0 */ CMU_ClockEnable(cmuClock_USART0, true); @@ -455,9 +455,24 @@ extern void WDOG0_enter_DefaultMode_from_RESET(void) { extern void I2C0_enter_DefaultMode_from_RESET(void) { // $[I2C0 I/O setup] + /* Set up SCL */ + I2C0->ROUTEPEN = I2C0->ROUTEPEN | I2C_ROUTEPEN_SCLPEN; + I2C0->ROUTELOC0 = (I2C0->ROUTELOC0 & (~_I2C_ROUTELOC0_SCLLOC_MASK)) + | I2C_ROUTELOC0_SCLLOC_LOC3; + /* Set up SDA */ + I2C0->ROUTEPEN = I2C0->ROUTEPEN | I2C_ROUTEPEN_SDAPEN; + I2C0->ROUTELOC0 = (I2C0->ROUTELOC0 & (~_I2C_ROUTELOC0_SDALOC_MASK)) + | I2C_ROUTELOC0_SDALOC_LOC27; // [I2C0 I/O setup]$ // $[I2C0 initialization] + I2C_Init_TypeDef init = I2C_INIT_DEFAULT; + + init.enable = 1; + init.master = 1; + init.freq = I2C_FREQ_STANDARD_MAX; + init.clhr = i2cClockHLRStandard; + I2C_Init(I2C0, &init); // [I2C0 initialization]$ } @@ -610,6 +625,9 @@ extern void PORTIO_enter_DefaultMode_from_RESET(void) { /* Pin PA3 is configured to Push-pull */ GPIO_PinModeSet(gpioPortA, 3, gpioModePushPull, 0); + /* Pin PA4 is configured to Open-drain with pull-up and filter */ + GPIO_PinModeSet(gpioPortA, 4, gpioModeWiredAndPullUpFilter, 0); + /* Pin PA5 is configured to Push-pull */ GPIO_PinModeSet(gpioPortA, 5, gpioModePushPull, 1); // [Port A Configuration]$ @@ -649,6 +667,9 @@ extern void PORTIO_enter_DefaultMode_from_RESET(void) { // $[Port F Configuration] + /* Pin PF3 is configured to Open-drain with pull-up and filter */ + GPIO_PinModeSet(gpioPortF, 3, gpioModeWiredAndPullUpFilter, 0); + /* Pin PF4 is configured to Push-pull */ GPIO_PinModeSet(gpioPortF, 4, gpioModePushPull, 0); diff --git a/efm32/src/device.c b/efm32/src/device.c index 37b7936..ed8b551 100644 --- a/efm32/src/device.c +++ b/efm32/src/device.c @@ -14,6 +14,7 @@ #include "em_adc.h" #include "em_cmu.h" #include "em_msc.h" +#include "em_i2c.h" #include "InitDevice.h" #include "cbor.h" @@ -23,6 +24,7 @@ #include "app.h" #include "uECC.h" #include "crypto.h" +#include "nfc.h" #define MSG_AVAIL_PIN gpioPortC,9 #define RDY_PIN gpioPortC,10 @@ -117,17 +119,19 @@ int ctap_user_presence_test() // Must be implemented by application // data is HID_MESSAGE_SIZE long in bytes +#ifndef TEST_POWER void ctaphid_write_block(uint8_t * data) { printf1(TAG_DUMP,"<< "); dump_hex1(TAG_DUMP, data, HID_MESSAGE_SIZE); usbhid_send(data); } +#endif void heartbeat() { GPIO_PinOutToggle(gpioPortF,4); GPIO_PinOutToggle(gpioPortF,5); - + nfc_test(); // printf("heartbeat %d %d\r\n", beat++,CRYOTIMER->CNT); } @@ -158,6 +162,7 @@ static void wait_for_efm8_busy() ; } +#ifndef TEST_POWER int usbhid_recv(uint8_t * msg) { int i; @@ -188,6 +193,8 @@ int usbhid_recv(uint8_t * msg) return 0; } +#endif + void usbhid_send(uint8_t * msg) { int i; diff --git a/efm32/src/nfc.c b/efm32/src/nfc.c new file mode 100644 index 0000000..9564bb0 --- /dev/null +++ b/efm32/src/nfc.c @@ -0,0 +1,178 @@ +/* + * nfc.c + * + * Created on: Jul 22, 2018 + * Author: conor + */ + +#include +#include +#include + +#include "em_chip.h" +#include "em_gpio.h" +#include "em_i2c.h" + +#include "log.h" +#include "util.h" +#include "nfc.h" +#include "app.h" + + +I2C_TransferReturn_TypeDef I2CSPM_Transfer(I2C_TypeDef *i2c, I2C_TransferSeq_TypeDef *seq) +{ + I2C_TransferReturn_TypeDef ret; + uint32_t timeout = 10000; + /* Do a polled transfer */ + ret = I2C_TransferInit(i2c, seq); + while (ret == i2cTransferInProgress && timeout--) + { + ret = I2C_Transfer(i2c); + } + return ret; +} + +// data must be 16 bytes +void read_block(uint8_t block, uint8_t * data) +{ + uint8_t addr = 0xAA; + I2C_TransferSeq_TypeDef seq; + I2C_TransferReturn_TypeDef ret; + uint8_t i2c_read_data[16]; + uint8_t i2c_write_data[1]; + + seq.addr = addr; + seq.flags = I2C_FLAG_WRITE_READ; + /* Select command to issue */ + i2c_write_data[0] = block; + seq.buf[0].data = i2c_write_data; + seq.buf[0].len = 1; + /* Select location/length of data to be read */ + seq.buf[1].data = i2c_read_data; + seq.buf[1].len = 16; + + ret = I2CSPM_Transfer(I2C0, &seq); + + if (ret != i2cTransferDone) { + printf("I2C fail %04x\r\n",ret); + exit(1); + } + memmove(data, i2c_read_data, 16); +} +void write_reg_flash(uint8_t reg_addr, uint8_t mask,uint8_t data) +{ + uint8_t addr = 0xAA; + I2C_TransferSeq_TypeDef seq; + I2C_TransferReturn_TypeDef ret; + uint8_t i2c_write_data[4]; + + seq.addr = addr; + seq.flags = I2C_FLAG_WRITE; + i2c_write_data[0] = 0x3a; + i2c_write_data[1] = reg_addr; + i2c_write_data[2] = mask; + i2c_write_data[3] = data; + + seq.buf[0].data = i2c_write_data; + seq.buf[0].len = 4; + seq.buf[1].data = NULL; + seq.buf[1].len = 0; + + ret = I2CSPM_Transfer(I2C0, &seq); + + if (ret != i2cTransferDone) { + printf("I2C fail %04x\r\n",ret); + exit(1); + } +} +void write_reg(uint8_t reg_addr, uint8_t mask,uint8_t data) +{ + uint8_t addr = 0xAA; + I2C_TransferSeq_TypeDef seq; + I2C_TransferReturn_TypeDef ret; + uint8_t i2c_write_data[4]; + + seq.addr = addr; + seq.flags = I2C_FLAG_WRITE; + i2c_write_data[0] = 0xFE; + i2c_write_data[1] = reg_addr; + i2c_write_data[2] = mask; + i2c_write_data[3] = data; + + seq.buf[0].data = i2c_write_data; + seq.buf[0].len = 4; + seq.buf[1].data = NULL; + seq.buf[1].len = 0; + + ret = I2CSPM_Transfer(I2C0, &seq); + + if (ret != i2cTransferDone) { + printf("I2C fail %04x\r\n",ret); + exit(1); + } +} + +uint8_t read_reg(uint8_t reg_addr) +{ + I2C_TransferSeq_TypeDef seq; + I2C_TransferReturn_TypeDef ret; + uint8_t write_data[2]; + uint8_t read_data[1]; + + seq.addr = 0xAA; + seq.flags = I2C_FLAG_WRITE_READ; + write_data[0] = 0xFE; + write_data[1] = reg_addr; + + seq.buf[0].data = write_data; + seq.buf[0].len = 2; + seq.buf[1].data = read_data; + seq.buf[1].len = 1; + + ret = I2CSPM_Transfer(I2C0, &seq); + + if (ret != i2cTransferDone) { + printf("I2C fail %04x\r\n",ret); + exit(1); + } + + return read_data[0]; +} + +void read_reg_block(uint8_t * data) +{ + int i; + for (i = 0; i < 7; i++) + { + *data = read_reg(i); +// printf("data %d: %x\n" ,i,(int)(*data)); + data++; + } +} + + +void nfc_test() +{ + uint8_t data[16]; + printf("-NFC test-\n"); + + read_block(0x00, data); + printf("block 00: "); dump_hex(data,16); + + read_reg_block(data); + printf("block 3A [init]:"); dump_hex(data,8); + + write_reg(0, 0xff, 0x43); + write_reg_flash(0, 0xff, 0x43); + write_reg(2, 0xff, 0x01); + + read_reg_block(data); + printf("block 3A [done]:"); dump_hex(data,8); +// +// read_block(0x3A, data); +// printf("block 3A [done]:"); dump_hex(data,16); + + while(1) + ; + +} diff --git a/fido2/main.c b/fido2/main.c index b0fb0a3..bcaf701 100644 --- a/fido2/main.c +++ b/fido2/main.c @@ -9,7 +9,7 @@ #include "util.h" #include "log.h" #include "ctap.h" - +#include "app.h" #if !defined(TEST) diff --git a/fido2/test_power.c b/fido2/test_power.c index e6a88ec..43f8907 100644 --- a/fido2/test_power.c +++ b/fido2/test_power.c @@ -1,3 +1,4 @@ +#include "app.h" #ifdef TEST_POWER /* @@ -13,15 +14,29 @@ #include "cbor.h" #include "device.h" #include "ctaphid.h" -#include "bsp.h" #include "util.h" #include "log.h" #include "ctap.h" +//#define BUTT NRF_GPIO_PIN_MAP(0,11) +//#define TRIG NRF_GPIO_PIN_MAP(0,30) +// +//#define GPIO_CLEAR(PIN) nrf_gpio_pin_clear(PIN) +//#define GPIO_SET(PIN) nrf_gpio_pin_set(PIN) +//#define GPIO_OUTPUT(PIN) nrf_gpio_cfg_output(PIN) +//#define GPIO_INPUT(PIN) nrf_gpio_cfg_input(PIN,NRF_GPIO_PIN_PULLUP) +//#define GPIO_READ(PIN) nrf_gpio_pin_read(PIN) +#include "em_gpio.h" +#define BUTT gpioPortF,6 +#define TRIG gpioPortD,14 + +#define GPIO_CLEAR(PIN) GPIO_PinOutClear(PIN) +#define GPIO_SET(PIN) GPIO_PinOutSet(PIN) +#define GPIO_OUTPUT(PIN) GPIO_PinModeSet(PIN,gpioModePushPull,1) +#define GPIO_INPUT(PIN) GPIO_PinModeSet(PIN,gpioModeInputPull,1) +#define GPIO_READ(PIN) GPIO_PinInGet(PIN) -#define BUTT NRF_GPIO_PIN_MAP(0,11) -#define TRIG NRF_GPIO_PIN_MAP(0,30) int main(int argc, char * argv[]) { @@ -45,16 +60,16 @@ int main(int argc, char * argv[]) /*TAG_DUMP|*/ /*TAG_GREEN|*/ /*TAG_RED|*/ - /*TAG_ERR*/ + |TAG_ERR ); device_init(); ctaphid_init(); ctap_init(); - nrf_gpio_cfg_input(BUTT, NRF_GPIO_PIN_PULLUP); - nrf_gpio_cfg_output(TRIG); - nrf_gpio_pin_clear(TRIG); + GPIO_INPUT(BUTT); + GPIO_OUTPUT(TRIG); + GPIO_CLEAR(TRIG); memset(hidmsg,0,sizeof(hidmsg)); @@ -92,13 +107,7 @@ int main(int argc, char * argv[]) return 0; } -void delay(int ms) -{ - uint64_t t1; - t1 = millis(); - while(millis()-t1 < ms) - ; -} + void ctaphid_write_block(uint8_t * data) { @@ -130,8 +139,8 @@ int usbhid_recv(uint8_t * msg) if (!reading) { delay(1); - nrf_gpio_pin_clear(TRIG); - val = nrf_gpio_pin_read(BUTT); + GPIO_CLEAR(TRIG); + val = GPIO_READ(BUTT); if (val == 0) { if (lastval != 0) @@ -145,7 +154,7 @@ int usbhid_recv(uint8_t * msg) } else { - nrf_gpio_pin_set(TRIG); + GPIO_SET(TRIG); memmove(msg, hidcmds[reading-1], 64); reading++; if (reading-1 == sizeof(hidcmds)/64) diff --git a/tools/nfcmon.py b/tools/nfcmon.py new file mode 100644 index 0000000..c071045 --- /dev/null +++ b/tools/nfcmon.py @@ -0,0 +1,32 @@ + +import datetime,sys +from binascii import hexlify + +import Chameleon + +def verboseLog(text): + formatString = "[{}] {}" + timeString = datetime.datetime.utcnow() + print(formatString.format(timeString, text), ) + +chameleon = Chameleon.Device(verboseLog) + +p = None +for p in Chameleon.Device.listDevices(): + break + +if p: + chameleon.connect(p) +else: + raise RuntimeError('No chameleon mini connected') + +chameleon.execCmd('LOGMODE=LIVE') + +while 1: + b = chameleon.read(1,20) + h = hexlify(b) + h = h.decode() + sys.stdout.write(h) + sys.stdout.flush() + +chameleon.execCmd('LOGMODE=NONE')