//***************************************************************************** // // am_hal_scard.c //! @file //! //! @brief Functions for interfacing with the SCARD. //! //! @addtogroup scard3 //! @ingroup apollo3hal //! @{ // //***************************************************************************** //***************************************************************************** // // Copyright (c) 2020, Ambiq Micro // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // Third party software included in this distribution is subject to the // additional license terms as defined in the /docs/licenses directory. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // This is part of revision 2.4.2 of the AmbiqSuite Development Package. // //***************************************************************************** #include #include #include "am_mcu_apollo.h" //***************************************************************************** // // SCARD magic number for handle verification. // //***************************************************************************** #define AM_HAL_MAGIC_SCARD 0xEA9E06 #define AM_HAL_SCARD_CHK_HANDLE(h) \ ((h) && \ ((am_hal_handle_prefix_t *)(h))->s.bInit && \ (((am_hal_handle_prefix_t *)(h))->s.magic == AM_HAL_MAGIC_SCARD)) //***************************************************************************** // // Convenience macro for passing errors. // //***************************************************************************** #define RETURN_ON_ERROR(x) \ if ((x) != AM_HAL_STATUS_SUCCESS) \ { \ return (x); \ }; //***************************************************************************** // // Baudrate to byte-time in microseconds with a little extra margin. // //***************************************************************************** #define ONE_BIT_US(baudrate) (AM_HAL_SCARD_CLK_FREQ/(baudrate)) #define ONE_BIT_DELAY(handle) \ am_hal_flash_delay(FLASH_CYCLES_US(ONE_BIT_US((handle)->ui32BaudRate))) #define SCARD_MAX_SYNC_TIME_MS 10 #define delay_ms(ms) am_hal_flash_delay(FLASH_CYCLES_US(1000 * (ms))) #define delay_us(us) am_hal_flash_delay(FLASH_CYCLES_US(us)) #define SCARD_WHILE_TIMEOUT_MS(expr, timeout, error) \ { \ uint32_t ui32Timeout = 0; \ while ( expr ) \ { \ if ( ui32Timeout == (timeout * 1000) ) \ { \ return error; \ } \ \ delay_us(1); \ ui32Timeout++; \ } \ } #define SCARD_SYNC_OPER(module, operation) do{\ SCARDn(module)->SR1_b.SYNCEND = 1;\ operation;\ SCARD_WHILE_TIMEOUT_MS(!SCARDn(module)->SR1_b.SYNCEND, SCARD_MAX_SYNC_TIME_MS, AM_HAL_SCARD_STATUS_BUS_ERROR) ;\ } while ( 0 ) //***************************************************************************** // // Transmission parameters F and D look-up tables // Per the ETU 7816-3 protocol ETU is computed from 2 parameters, FI and DI. // ETU: Elementary Time Unit // FI: Clock rate conversion factor // DI: Bit rate adjustment factor // //***************************************************************************** static uint16_t g_F_Integer[16][2] = { // FI { F, f(max)} /*0000*/{ 372, 4}, /*0001*/{ 372, 5}, /*0010*/{ 558, 6}, /*0011*/{ 744, 8}, /*0100*/{1116, 12}, /*0101*/{1488, 16}, /*0110*/{1860, 20}, /*0111*/{ 0, 0}, /*1000*/{ 0, 0}, /*1001*/{ 512, 5}, /*1010*/{ 768, 7}, //7.5 /*1011*/{1024, 10}, /*1100*/{1536, 15}, /*1101*/{2048, 20}, /*1110*/{ 0, 0}, /*1111*/{ 0, 0} }; static uint8_t g_D_Integer[16] = { //DI 0000 0001 0010 0011 0100 0101 0110 0111 /*D*/ 0, 1, 2, 4, 8, 16, 32, 64, //DI 1000 1001 1010 1011 1100 1101 1110 1111 /*D*/ 12, 20, 0, 0, 0, 0, 0, 0 }; static uint16_t g_WaitTime = AM_HAL_SCARD_WAIT_MAX_TIME; //Set to max //***************************************************************************** // // Structure for handling SCARD register state information for power up/down // //***************************************************************************** typedef struct { bool bValid; uint32_t regIER; uint32_t regTCR; uint32_t regUCR; uint32_t regBPRL; uint32_t regBPRH; uint32_t regUCR1; uint32_t regIER1; uint32_t regGTR; uint32_t regRETXCNT; uint32_t regCLKCTRL; } am_hal_scard_register_state_t; //***************************************************************************** // // Structure for handling SCARD instance state information. // //***************************************************************************** typedef struct { am_hal_handle_prefix_t prefix; am_hal_scard_register_state_t sRegState; uint32_t ui32Module; bool bEnableTxQueue; am_hal_queue_t sTxQueue; bool bEnableRxQueue; am_hal_queue_t sRxQueue; uint32_t ui32BaudRate; } am_hal_scard_state_t; //***************************************************************************** // // State structure for each module. // //***************************************************************************** am_hal_scard_state_t g_am_hal_scard_states[AM_REG_SCARD_NUM_MODULES]; //***************************************************************************** // // Allows the SCARD HAL to use extra space to store TX and RX data. // //***************************************************************************** static uint32_t buffer_configure(void *pHandle, uint8_t *pui8TxBuffer, uint32_t ui32TxBufferSize, uint8_t *pui8RxBuffer, uint32_t ui32RxBufferSize) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32ErrorStatus; // // Check to make sure this is a valid handle. // if ( !AM_HAL_SCARD_CHK_HANDLE(pHandle) ) { return AM_HAL_STATUS_INVALID_HANDLE; } // // Check to see if we have a TX buffer. // if ( pui8TxBuffer && ui32TxBufferSize ) { // // If so, initialzie the transmit queue, and enable the TX FIFO // interrupt. // pState->bEnableTxQueue = true; am_hal_queue_init(&pState->sTxQueue, pui8TxBuffer, 1, ui32TxBufferSize); ui32ErrorStatus = am_hal_scard_interrupt_enable(pHandle, 0, AM_HAL_SCARD_INT_TBERBFEN); RETURN_ON_ERROR(ui32ErrorStatus); } else { // // If not, make sure the TX FIFO interrupt is disabled. // pState->bEnableTxQueue = false; ui32ErrorStatus = am_hal_scard_interrupt_disable(pHandle, 0, AM_HAL_SCARD_INT_TBERBFEN); RETURN_ON_ERROR(ui32ErrorStatus); } // // Check to see if we have an RX buffer. // if ( pui8RxBuffer && ui32RxBufferSize ) { // // If so, initialize the receive queue and the associated interupts. // pState->bEnableRxQueue = true; am_hal_queue_init(&pState->sRxQueue, pui8RxBuffer, 1, ui32RxBufferSize); ui32ErrorStatus = am_hal_scard_interrupt_enable(pHandle, 0, (AM_HAL_SCARD_INT_FHFEN | AM_HAL_SCARD_INT_FNEEN)); RETURN_ON_ERROR(ui32ErrorStatus); } else { pState->bEnableRxQueue = false; ui32ErrorStatus = am_hal_scard_interrupt_disable(pHandle, 0, (AM_HAL_SCARD_INT_FHFEN | AM_HAL_SCARD_INT_FNEEN)); RETURN_ON_ERROR(ui32ErrorStatus); } return AM_HAL_STATUS_SUCCESS; } // buffer_configure() //***************************************************************************** // // Initialization function. // //***************************************************************************** uint32_t am_hal_scard_initialize(uint32_t ui32Module, void **ppHandle) { // // Check that the request module is in range. // #ifndef AM_HAL_DISABLE_API_VALIDATION if ( ui32Module >= AM_REG_SCARD_NUM_MODULES ) { return AM_HAL_STATUS_OUT_OF_RANGE; } #endif // AM_HAL_DISABLE_API_VALIDATION // // Check for valid arguements. // if ( !ppHandle ) { return AM_HAL_STATUS_INVALID_ARG; } // // Check if the handle is unallocated. // if ( g_am_hal_scard_states[ui32Module].prefix.s.bInit ) { return AM_HAL_STATUS_INVALID_OPERATION; } // // Initialize the handle. // g_am_hal_scard_states[ui32Module].prefix.s.bInit = true; g_am_hal_scard_states[ui32Module].prefix.s.magic = AM_HAL_MAGIC_SCARD; g_am_hal_scard_states[ui32Module].ui32Module = ui32Module; g_am_hal_scard_states[ui32Module].sRegState.bValid = false; g_am_hal_scard_states[ui32Module].ui32BaudRate = 0; // // Return the handle. // *ppHandle = (void *)&g_am_hal_scard_states[ui32Module]; // // Return the status. // return AM_HAL_STATUS_SUCCESS; } // am_hal_scard_initialize() //***************************************************************************** // // De-Initialization function. // //***************************************************************************** uint32_t am_hal_scard_deinitialize(void *pHandle) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *)pHandle; // // Check the handle. // #ifndef AM_HAL_DISABLE_API_VALIDATION if ( !AM_HAL_SCARD_CHK_HANDLE(pHandle) ) { return AM_HAL_STATUS_INVALID_HANDLE; } #endif // AM_HAL_DISABLE_API_VALIDATION // // Reset the handle. // pState->prefix.s.bInit = false; pState->ui32Module = 0; pState->sRegState.bValid = false; // // Return the status. // return AM_HAL_STATUS_SUCCESS; } // am_hal_scard_deinitialize() //***************************************************************************** // // Power control functions. // //***************************************************************************** uint32_t am_hal_scard_power_control(void *pHandle, am_hal_sysctrl_power_state_e ePowerState, bool bRetainState) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; #ifndef AM_HAL_DISABLE_API_VALIDATION if ( ui32Module >= AM_REG_SCARD_NUM_MODULES ) { return AM_HAL_STATUS_OUT_OF_RANGE; } #endif // AM_HAL_DISABLE_API_VALIDATION am_hal_pwrctrl_periph_e eSCCPowerModule = ((am_hal_pwrctrl_periph_e) (AM_HAL_PWRCTRL_PERIPH_SCARD + ui32Module)); // // Check to make sure this is a valid handle. // if ( !AM_HAL_SCARD_CHK_HANDLE(pHandle) ) { return AM_HAL_STATUS_INVALID_HANDLE; } // // Decode the requested power state and update SCARD operation accordingly. // switch (ePowerState) { // // Turn on the SCC. // case AM_HAL_SYSCTRL_WAKE: // // Make sure we don't try to restore an invalid state. // if ( bRetainState && !pState->sRegState.bValid ) { return AM_HAL_STATUS_INVALID_OPERATION; } // // Enable power control. // am_hal_pwrctrl_periph_enable(eSCCPowerModule); if ( bRetainState ) { // // Restore SCC registers // AM_CRITICAL_BEGIN SCARDn(ui32Module)->IER = pState->sRegState.regIER; SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR = pState->sRegState.regTCR); SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->UCR = pState->sRegState.regUCR); SCARDn(ui32Module)->BPRL = pState->sRegState.regBPRL; SCARDn(ui32Module)->BPRH = pState->sRegState.regBPRH; SCARDn(ui32Module)->UCR1 = pState->sRegState.regUCR1; SCARDn(ui32Module)->IER1 = pState->sRegState.regIER1; SCARDn(ui32Module)->GTR = pState->sRegState.regGTR; SCARDn(ui32Module)->RETXCNT = pState->sRegState.regRETXCNT; SCARDn(ui32Module)->CLKCTRL = pState->sRegState.regCLKCTRL; pState->sRegState.bValid = false; AM_CRITICAL_END } break; // // Turn off the SCARD. // case AM_HAL_SYSCTRL_NORMALSLEEP: case AM_HAL_SYSCTRL_DEEPSLEEP: if ( bRetainState ) { AM_CRITICAL_BEGIN pState->sRegState.regIER = SCARDn(ui32Module)->IER; pState->sRegState.regTCR = SCARDn(ui32Module)->TCR; pState->sRegState.regUCR = SCARDn(ui32Module)->UCR; pState->sRegState.regBPRL = SCARDn(ui32Module)->BPRL; pState->sRegState.regBPRH = SCARDn(ui32Module)->BPRH; pState->sRegState.regUCR1 = SCARDn(ui32Module)->UCR1; pState->sRegState.regIER1 = SCARDn(ui32Module)->IER1; pState->sRegState.regGTR = SCARDn(ui32Module)->GTR; pState->sRegState.regRETXCNT = SCARDn(ui32Module)->RETXCNT; pState->sRegState.regCLKCTRL = SCARDn(ui32Module)->CLKCTRL; pState->sRegState.bValid = true; AM_CRITICAL_END } // // Clear all interrupts before sleeping as having a pending SCARD // interrupt burns power. // am_hal_scard_interrupt_clear(pState, 0, AM_HAL_SCARD_INT_ALL); am_hal_scard_interrupt_clear(pState, 1, AM_HAL_SCARD_INT_ALL); // // Disable power control. // am_hal_pwrctrl_periph_disable(eSCCPowerModule); break; default: return AM_HAL_STATUS_INVALID_ARG; } // // Return the status. // return AM_HAL_STATUS_SUCCESS; } // am_hal_scard_power_control() //***************************************************************************** // // SCARD configuration. // //***************************************************************************** uint32_t am_hal_scard_configure(void *pHandle, am_hal_scard_config_t *psConfig) { uint32_t status = AM_HAL_STATUS_SUCCESS; am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; // // Check to make sure this is a valid handle. // #ifndef AM_HAL_DISABLE_API_VALIDATION if ( !AM_HAL_SCARD_CHK_HANDLE(pHandle) ) { return AM_HAL_STATUS_INVALID_HANDLE; } #endif // AM_HAL_DISABLE_API_VALIDATION // // Start by enabling the clocks, which needs to happen in a critical // section. // AM_CRITICAL_BEGIN SCARDn(ui32Module)->CLKCTRL_b.APBCLKEN = 1; SCARDn(ui32Module)->CLKCTRL_b.CLKEN = 1; AM_CRITICAL_END // // Set the baud rate. // status = am_hal_scard_control(pHandle, AM_HAL_SCARD_REQ_BAUDRATE, &psConfig->ui32Fidi); //RETURN_ON_ERROR(ui32ErrorStatus); // // Copy the configuration options into the appropriate registers. // status = am_hal_scard_control(pHandle, AM_HAL_SCARD_REQ_PROTOCOL, &psConfig->ui32Protocol); status = am_hal_scard_control(pHandle, AM_HAL_SCARD_REQ_CARD_FORMAT, &psConfig->ui32Direction); status = am_hal_scard_control(pHandle, AM_HAL_SCARD_REQ_PARITY, &psConfig->ui32Parity); status = am_hal_scard_control(pHandle, AM_HAL_SCARD_REQ_GUARDTIME, &psConfig->ui32GuardTime); SCARDn(ui32Module)->UCR1_b.CLKIOV = psConfig->ui32ClkLevel; status = am_hal_scard_control(pHandle, AM_HAL_SCARD_REQ_CLK_STOP, NULL); SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->UCR_b.RIU = 1); if ( AM_HAL_STATUS_SUCCESS != status ) { return AM_HAL_STATUS_FAIL; } // // Set up any buffers that might exist. // buffer_configure(pHandle, psConfig->pui8TxBuffer, psConfig->ui32TxBufferSize, psConfig->pui8RxBuffer, psConfig->ui32RxBufferSize); return AM_HAL_STATUS_SUCCESS; } // am_hal_scard_configure() //***************************************************************************** // // Set Baud Rate Register based on the parameters F and D. // //***************************************************************************** static void config_baudrate(void *pHandle, uint32_t ui32Fidi) { uint16_t bpr; uint32_t ui32ActualBaud; am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; // F is the clock rate conversion integer // D is the baud rate adjustment integer // 1 ETU = (F/D)*(1/f) s // The default values of these parameters are: // F = 372 ; D = 1; f (max.) = 5 MHz // // BPRL and BPRH are used for counting ETU // bpr = ((g_F_Integer[AM_HAL_SCARD_FI(ui32Fidi)][0] != 0) && (g_D_Integer[AM_HAL_SCARD_DI(ui32Fidi)] != 0)) ? \ g_F_Integer[AM_HAL_SCARD_FI(ui32Fidi)][0] / g_D_Integer[AM_HAL_SCARD_DI(ui32Fidi)] : \ g_F_Integer[AM_HAL_SCARD_FI(AM_HAL_SCARD_FI_DI_DEFAULT)][0] / g_D_Integer[AM_HAL_SCARD_DI(AM_HAL_SCARD_FI_DI_DEFAULT)]; SCARDn(ui32Module)->BPRL = bpr & 0xFF; SCARDn(ui32Module)->BPRH = (SCARDn(ui32Module)->BPRH & (~SCARD_BPRH_BPRH_Msk)) | ((bpr >> 8) & SCARD_BPRH_BPRH_Msk) ; ui32ActualBaud = (uint32_t)(AM_HAL_SCARD_CLK_FREQ / bpr); pState->ui32BaudRate = ui32ActualBaud; } // config_baudrate() //***************************************************************************** // // Set card format, direct convention or inverse convention // //***************************************************************************** static uint32_t config_cardformat(uint32_t ui32Module, uint32_t ui32Format) { switch(ui32Format) { // // Inverse convention // case AM_HAL_SCARD_CONV_MSB_0X3F: SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.AUTOCONV = 1); SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.CONV = 1); break; // // Direct convention // case AM_HAL_SCARD_CONV_LSB_0X3B: SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.AUTOCONV = 1); SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.CONV = 0); break; // // Not set by software, configured by the first received byte // case AM_HAL_SCARD_CONV_AUTO: default: SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.AUTOCONV = 0); SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.SS = 1); break; } return AM_HAL_STATUS_SUCCESS; } //***************************************************************************** // // Enable/disbale parity and set it to odd/even // //***************************************************************************** static uint32_t config_parity(uint32_t ui32Module, uint32_t ui32Parity) { // // T1 protocol // if ( SCARDn(ui32Module)->TCR_b.PROT ) { // // Enable parity // if ( ui32Parity & 0xF0 ) { SCARDn(ui32Module)->UCR1_b.T1PAREN = 1; SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.FIP = ui32Parity & 0xF); } // // Disbale parity // else { SCARDn(ui32Module)->UCR1_b.T1PAREN = 0; } } // // T0 protocol, always enable parity // else { SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.FIP = ui32Parity & 0xF); } return AM_HAL_STATUS_SUCCESS; } //***************************************************************************** // // Set protocol, T0 or T1 // //***************************************************************************** static uint32_t config_protocol(uint32_t ui32Module, uint32_t ui32Protocol) { if ( 1 == ui32Protocol ) { SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.PROT = AM_HAL_SCARD_PROTOCOL_T1); } else if ( 0 == ui32Protocol ) { SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.PROT = AM_HAL_SCARD_PROTOCOL_T0); } else { return AM_HAL_SCARD_STATUS_PROTOCAL_NOT_SUPPORT; } return AM_HAL_STATUS_SUCCESS; } //***************************************************************************** // // Set and start ETU counter // //***************************************************************************** static uint32_t config_etucounter(uint32_t ui32Module, uint16_t ui16Etu) { // // Set low-8bit first, then set high-8bit, after software writes ECNTH, ETU counter starts counting // SCARD_WHILE_TIMEOUT_MS(!SCARDn(ui32Module)->SR1_b.IDLE, 100, AM_HAL_SCARD_STATUS_BUS_ERROR); SCARDn(ui32Module)->SR1_b.SYNCEND = 1; SCARDn(ui32Module)->ECNTL = (ui16Etu) & 0xFF; SCARD_WHILE_TIMEOUT_MS(!SCARDn(ui32Module)->SR1_b.SYNCEND, 100, AM_HAL_SCARD_STATUS_BUS_ERROR); SCARDn(ui32Module)->ECNTH = ((ui16Etu) >> 8); return AM_HAL_STATUS_SUCCESS; } //***************************************************************************** // // Read as much data from the SCARD FIFO as possible, up to ui32NumBytes // //***************************************************************************** uint32_t scard_fifo_read(void *pHandle, uint8_t *pui8Data, uint32_t ui32NumBytes, uint32_t *pui32NumBytesRead) { uint32_t ui32ErrorStatus = AM_HAL_STATUS_SUCCESS; uint32_t i = 0; am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; uint8_t ui8Index = 0; SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.TR = 0); while ( ui32NumBytes ) { config_etucounter(ui32Module, g_WaitTime); while ( (!SCARDn(ui32Module)->SR_b.FNE) && (!SCARDn(ui32Module)->SR1_b.ECNTOVER) && (!SCARDn(ui32Module)->SR_b.PE) && (!SCARDn(ui32Module)->SR_b.FER) ); // // Read times out // if ( SCARDn(ui32Module)->SR1_b.ECNTOVER ) { break; } // // Parity error or Frame error // else if ( (SCARDn(ui32Module)->SR_b.PE) || (SCARDn(ui32Module)->SR_b.FER) ) { SCARDn(ui32Module)->SR_b.PE = 0; SCARDn(ui32Module)->SR_b.FER = 0; ui32ErrorStatus = AM_HAL_STATUS_FAIL; break; } // // RX FIFO is full, read 8 bytes out // else if ( SCARDn(ui32Module)->SR_b.TBERBF ) { for ( ui8Index = 0; ui8Index < AM_HAL_SCARD_FIFO_MAX; ui8Index++ ) { pui8Data[i++] = SCARDn(ui32Module)->DR_b.DR; } ui32NumBytes -= AM_HAL_SCARD_FIFO_MAX; } // // RX FIFO is half full, read 4 bytes out // else if ( SCARDn(ui32Module)->SR_b.FHF ) { for ( ui8Index = 0; ui8Index < AM_HAL_SCARD_FIFO_MAX / 2; ui8Index++ ) { pui8Data[i++] = SCARDn(ui32Module)->DR_b.DR; } ui32NumBytes -= AM_HAL_SCARD_FIFO_MAX / 2; } // // RX FIFO is not empty, read as much as we can // else if ( SCARDn(ui32Module)->SR_b.FNE ) { pui8Data[i++] = SCARDn(ui32Module)->DR_b.DR; ui32NumBytes--; } } if ( pui32NumBytesRead ) { *pui32NumBytesRead = i; } return ui32ErrorStatus; } //***************************************************************************** // // Read as much data from the SCARD FIFO as possible, up to ui32NumBytes // //***************************************************************************** uint32_t scard_fifo_write(void *pHandle, uint8_t *pui8Data, uint32_t ui32NumBytes, uint32_t *pui32NumBytesWritten) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; uint32_t i = 0; if ( ui32NumBytes ) { SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.TR = 1); while ( 1 != ui32NumBytes-- ) { // // Write 1 byte into DR // SCARDn(ui32Module)->DR_b.DR = pui8Data[i++]; SCARD_WHILE_TIMEOUT_MS((!SCARDn(ui32Module)->SR_b.TBERBF) && (!SCARDn(ui32Module)->SR_b.PE) && (!SCARDn(ui32Module)->SR_b.FER), 100, AM_HAL_SCARD_STATUS_BUS_ERROR); // // Parity error or Frame error // if ( (SCARDn(ui32Module)->SR_b.PE) || (SCARDn(ui32Module)->SR_b.FER) ) { SCARDn(ui32Module)->SR_b.PE = 0; SCARDn(ui32Module)->SR_b.FER = 0; return AM_HAL_STATUS_FAIL; } } // // Enable fast TX to RX function before the last byte // SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.LCT = 1); SCARDn(ui32Module)->DR_b.DR = pui8Data[i++]; // // SCC should switch back to RX after all data sent out // SCARD_WHILE_TIMEOUT_MS((!SCARDn(ui32Module)->SR_b.FT2REND) && (!SCARDn(ui32Module)->SR_b.PE) && (!SCARDn(ui32Module)->SR_b.FER), 100, AM_HAL_SCARD_STATUS_BUS_ERROR); // // Parity error or Frame error // if ( (SCARDn(ui32Module)->SR_b.PE) || (SCARDn(ui32Module)->SR_b.FER) ) { SCARDn(ui32Module)->SR_b.PE = 0; SCARDn(ui32Module)->SR_b.FER = 0; return AM_HAL_STATUS_FAIL; } SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->TCR_b.TR = 0); } if ( pui32NumBytesWritten ) { *pui32NumBytesWritten = i; } return AM_HAL_STATUS_SUCCESS; } //***************************************************************************** // // Empty the SCARD RX FIFO, and place the data into the RX queue. // //***************************************************************************** static uint32_t rx_queue_update(void *pHandle) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint8_t pui8Data[AM_HAL_SCARD_FIFO_MAX]; uint32_t ui32BytesTransferred; uint32_t ui32ErrorStatus; AM_CRITICAL_BEGIN // // Read as much of the FIFO as we can. // ui32ErrorStatus = scard_fifo_read(pHandle, pui8Data, AM_HAL_SCARD_FIFO_MAX, &ui32BytesTransferred); // // If we were successful, go ahead and transfer the data along to the // buffer. // if ( ui32ErrorStatus == AM_HAL_STATUS_SUCCESS ) { if ( !am_hal_queue_item_add(&pState->sRxQueue, pui8Data, ui32BytesTransferred) ) { ui32ErrorStatus = AM_HAL_SCARD_STATUS_RX_QUEUE_FULL; } } AM_CRITICAL_END return ui32ErrorStatus; } // rx_queue_update() //***************************************************************************** // // Transfer as much data as possible from the TX queue to the TX FIFO. // //***************************************************************************** static uint32_t tx_queue_update(void *pHandle) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint8_t pui8Data; uint32_t ui32BytesTransferred; uint32_t ui32ErrorStatus = AM_HAL_STATUS_SUCCESS; AM_CRITICAL_BEGIN // // Attempt to grab an item from the queue, and add it to the fifo. // while ( 1 ) { if ( am_hal_queue_item_get(&pState->sTxQueue, &pui8Data, 1) ) { ui32ErrorStatus = scard_fifo_write(pHandle, &pui8Data, 1, &ui32BytesTransferred); } else { // // If we didn't get anything from the queue, we can just return. // break; } } AM_CRITICAL_END return ui32ErrorStatus; } // tx_queue_update() //***************************************************************************** // // Attempt to read N bytes from the FIFO, but give up if they aren't there. // //***************************************************************************** static uint32_t read_nonblocking(void *pHandle, uint8_t *pui8Data, uint32_t ui32NumBytes, uint32_t *pui32NumBytesRead) { uint32_t ui32BufferData; uint32_t ui32BytesTransferred; uint32_t ui32ErrorStatus = AM_HAL_STATUS_SUCCESS; am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; // // Check to make sure this is a valid handle. // #ifndef AM_HAL_DISABLE_API_VALIDATION if ( (!AM_HAL_SCARD_CHK_HANDLE(pHandle)) || (NULL == pui8Data) || (NULL == pui32NumBytesRead) ) { return AM_HAL_STATUS_INVALID_HANDLE; } #endif // AM_HAL_DISABLE_API_VALIDATION // // Start by setting the number of bytes read to 0. // *pui32NumBytesRead = 0; if ( ui32NumBytes == 0 ) { return AM_HAL_STATUS_SUCCESS; } // // Check to see if the circular receive buffer has been enabled. // if ( pState->bEnableRxQueue ) { // // If it is, update it, and then try to read the requested number of // bytes, giving up if fewer were actually found. // ui32ErrorStatus = rx_queue_update(pHandle); RETURN_ON_ERROR(ui32ErrorStatus); ui32BufferData = am_hal_queue_data_left(&pState->sRxQueue); ui32BytesTransferred = (ui32NumBytes < ui32BufferData ? ui32NumBytes : ui32BufferData); am_hal_queue_item_get(&pState->sRxQueue, pui8Data, ui32BytesTransferred); } else { // // If the buffer isn't enabled, just read straight from the FIFO. // ui32ErrorStatus = scard_fifo_read(pHandle, pui8Data, ui32NumBytes, &ui32BytesTransferred); } // // Let the caller know how much we transferred if they provided us with a // pointer. // *pui32NumBytesRead = ui32BytesTransferred; return ui32ErrorStatus; } // read_nonblocking() //***************************************************************************** // // Attempt to write N bytes to the FIFO, but give up if there's no space. // //***************************************************************************** static uint32_t write_nonblocking(void *pHandle, uint8_t *pui8Data, uint32_t ui32NumBytes, uint32_t *pui32NumBytesWritten) { uint32_t ui32ErrorStatus; uint32_t ui32BufferSpace; uint32_t ui32BytesTransferred; am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; // // Check to make sure this is a valid handle. // #ifndef AM_HAL_DISABLE_API_VALIDATION if ( (!AM_HAL_SCARD_CHK_HANDLE(pHandle)) || (NULL == pui8Data) || (NULL == pui32NumBytesWritten) ) { return AM_HAL_STATUS_INVALID_HANDLE; } #endif // AM_HAL_DISABLE_API_VALIDATION // // Let the caller know how much we transferred if they provided us with a // pointer. // *pui32NumBytesWritten = 0; if ( ui32NumBytes == 0 ) { return AM_HAL_STATUS_SUCCESS; } // // Check to see if the circular transmit buffer has been enabled. // if ( pState->bEnableTxQueue ) { // // If it has, been enabled, write as much data to it as we can, and let // the caller know how much that was. // ui32BufferSpace = am_hal_queue_space_left(&pState->sTxQueue); ui32BytesTransferred = (ui32NumBytes < ui32BufferSpace ? ui32NumBytes : ui32BufferSpace); am_hal_queue_item_add(&pState->sTxQueue, pui8Data, ui32BytesTransferred); // // Transfer as much data as possible from the queue to the fifo. // ui32ErrorStatus = tx_queue_update(pHandle); RETURN_ON_ERROR(ui32ErrorStatus); } else { // // If the buffer isn't enabled, just write straight to the FIFO. // scard_fifo_write(pHandle, pui8Data, ui32NumBytes, &ui32BytesTransferred); } // // Let the caller know how much we transferred if they provided us with a // pointer. // *pui32NumBytesWritten = ui32BytesTransferred; return AM_HAL_STATUS_SUCCESS; } // write_nonblocking() //***************************************************************************** // // This function will keep reading bytes until it either gets N bytes or runs // into an error. // //***************************************************************************** static uint32_t read_timeout(void *pHandle, uint8_t *pui8Data, uint32_t ui32NumBytes, uint32_t *pui32NumBytesRead, uint32_t ui32TimeoutMs) { uint32_t ui32Status, ui32BytesRead, ui32RemainingBytes, ui32TimeSpent, i; // // If we don't have a timeout, just pass this directly to the nonblocking // call. // if ( ui32TimeoutMs == 0 ) { return read_nonblocking(pHandle, pui8Data, ui32NumBytes, pui32NumBytesRead); } i = 0; ui32RemainingBytes = ui32NumBytes; ui32TimeSpent = 0; // // Loop until we're done reading. This will either be because we hit a // timeout, or we got the right number of bytes. If the caller specified // "wait forever", then don't check the timeout. // while ( ui32RemainingBytes && (ui32TimeSpent < ui32TimeoutMs) ) { // // Read as much as we can. // ui32BytesRead = 0; ui32Status = read_nonblocking(pHandle, &pui8Data[i], ui32RemainingBytes, &ui32BytesRead); // // Update the tracking variables. // i += ui32BytesRead; ui32RemainingBytes -= ui32BytesRead; if ( ui32Status != AM_HAL_STATUS_SUCCESS ) { if ( pui32NumBytesRead ) { *pui32NumBytesRead = i; } return ui32Status; } // // Update the timeout. // if ( ui32RemainingBytes ) { delay_us(1); if ( ui32TimeoutMs != AM_HAL_SCARD_WAIT_FOREVER ) { ui32TimeSpent++; } } } if ( pui32NumBytesRead ) { *pui32NumBytesRead = i; } return AM_HAL_STATUS_SUCCESS; } // read_timeout() //***************************************************************************** // // This function will keep writing bytes until it either sends N bytes or runs // into an error. // //***************************************************************************** static uint32_t write_timeout(void *pHandle, uint8_t *pui8Data, uint32_t ui32NumBytes, uint32_t *pui32NumBytesWritten, uint32_t ui32TimeoutMs) { uint32_t ui32Status, ui32BytesWritten, ui32RemainingBytes, ui32TimeSpent, i; i = 0; ui32RemainingBytes = ui32NumBytes; ui32TimeSpent = 0; // // If we don't have a timeout, just pass this directly to the nonblocking // call. // if ( ui32TimeoutMs == 0 ) { return write_nonblocking(pHandle, pui8Data, ui32NumBytes, pui32NumBytesWritten); } // // Loop until we're done write. This will either be because we hit a // timeout, or we sent the right number of bytes. If the caller specified // "wait forever", then don't check the timeout. // while ( ui32RemainingBytes && (ui32TimeSpent < ui32TimeoutMs) ) { // // Write as much as we can. // ui32BytesWritten = 0; ui32Status = write_nonblocking(pHandle, &pui8Data[i], ui32RemainingBytes, &ui32BytesWritten); // // Update the tracking variables. // i += ui32BytesWritten; ui32RemainingBytes -= ui32BytesWritten; if ( ui32Status != AM_HAL_STATUS_SUCCESS ) { if ( pui32NumBytesWritten ) { *pui32NumBytesWritten = i; } return ui32Status; } // // Update the timeout. // if ( ui32RemainingBytes ) { delay_us(1); if ( ui32TimeoutMs != AM_HAL_SCARD_WAIT_FOREVER ) { ui32TimeSpent++; } } } if ( pui32NumBytesWritten ) { *pui32NumBytesWritten = i; } return AM_HAL_STATUS_SUCCESS; } // write_timeout() //***************************************************************************** // // Send or receive bytes. // //***************************************************************************** uint32_t am_hal_scard_transfer(void *pHandle, const am_hal_scard_transfer_t *pTransfer) { // // Pick the right function to use based on the transfer structure. // if ( pTransfer->ui32Direction == AM_HAL_SCARD_WRITE ) { return write_timeout(pHandle, pTransfer->pui8Data, pTransfer->ui32NumBytes, pTransfer->pui32BytesTransferred, pTransfer->ui32TimeoutMs); } else if ( pTransfer->ui32Direction == AM_HAL_SCARD_READ ) { return read_timeout(pHandle, pTransfer->pui8Data, pTransfer->ui32NumBytes, pTransfer->pui32BytesTransferred, pTransfer->ui32TimeoutMs); } return AM_HAL_STATUS_INVALID_OPERATION; } // am_hal_scard_transfer() //***************************************************************************** // // Wait for all of the traffic in the TX pipeline to be sent. // //***************************************************************************** uint32_t am_hal_scard_tx_flush(void *pHandle) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; // // If we have a TX queue, we should wait for it to empty. // if ( pState->bEnableTxQueue ) { while ( am_hal_queue_data_left(&(pState->sTxQueue)) ) { ONE_BIT_DELAY(pState); } } // // Wait for the IDLE bit to go high. // while ( SCARDn(ui32Module)->SR1_b.IDLE != 1 ) { ONE_BIT_DELAY(pState); } return AM_HAL_STATUS_SUCCESS; } // am_hal_scard_tx_flush() //***************************************************************************** // // Interrupt service // //***************************************************************************** uint32_t am_hal_scard_interrupt_service(void *pHandle, uint32_t ui32Status, uint32_t *pui32ScardTxIdle) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; uint32_t ui32ErrorStatus; #ifndef AM_HAL_DISABLE_API_VALIDATION if ( !AM_HAL_SCARD_CHK_HANDLE(pHandle) ) { return AM_HAL_STATUS_INVALID_HANDLE; } #endif // AM_HAL_DISABLE_API_VALIDATION // // Check to see if we have filled the Rx FIFO past the configured limit, or // if we have an 'old' character or two sitting in the FIFO. // if ( (ui32Status & (SCARD_SR_TBERBF_Msk | SCARD_SR_FHF_Msk | SCARD_SR_FNE_Msk) ) && pState->bEnableRxQueue) { ui32ErrorStatus = rx_queue_update(pHandle); RETURN_ON_ERROR(ui32ErrorStatus); } // // Check to see if our TX buffer has been recently emptied. If so, we // should refill it from the TX ring buffer. // if ( (ui32Status & SCARD_SR_TBERBF_Msk) && pState->bEnableTxQueue ) { ui32ErrorStatus = tx_queue_update(pHandle); RETURN_ON_ERROR(ui32ErrorStatus); } // // If this pointer is null, we can just return success now. There is no // need to figure out if the SCC is idle. // if ( pui32ScardTxIdle == 0 ) { return AM_HAL_STATUS_SUCCESS; } if ( SCARDn(ui32Module)->SR1_b.IDLE == 1 ) { *pui32ScardTxIdle = true; } else { *pui32ScardTxIdle = false; } return AM_HAL_STATUS_SUCCESS; } //***************************************************************************** // // Interrupt enable. // //***************************************************************************** uint32_t am_hal_scard_interrupt_enable(void *pHandle, uint32_t ui32Index, uint32_t ui32IntMask) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; #ifndef AM_HAL_DISABLE_API_VALIDATION if ( (!AM_HAL_SCARD_CHK_HANDLE(pHandle)) || (ui32Index > 1) ) { return AM_HAL_STATUS_INVALID_HANDLE; } #endif // AM_HAL_DISABLE_API_VALIDATION if ( 0 == ui32Index ) { SCARDn(ui32Module)->IER |= ui32IntMask; } else { SCARDn(ui32Module)->IER1 |= ui32IntMask; } return AM_HAL_STATUS_SUCCESS; } // am_hal_scard_interrupt_enable() //***************************************************************************** // // Interrupt disable. // //***************************************************************************** uint32_t am_hal_scard_interrupt_disable(void *pHandle, uint32_t ui32Index, uint32_t ui32IntMask) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; #ifndef AM_HAL_DISABLE_API_VALIDATION if ( (!AM_HAL_SCARD_CHK_HANDLE(pHandle)) || (ui32Index > 1) ) { return AM_HAL_STATUS_INVALID_HANDLE; } #endif // AM_HAL_DISABLE_API_VALIDATION if ( 0 == ui32Index ) { SCARDn(ui32Module)->IER &= ~ui32IntMask; } else { SCARDn(ui32Module)->IER1 &= ~ui32IntMask; } return AM_HAL_STATUS_SUCCESS; } // am_hal_scard_interrupt_disable() //***************************************************************************** // // Interrupt clear. // //***************************************************************************** uint32_t am_hal_scard_interrupt_clear(void *pHandle, uint32_t ui32Index, uint32_t ui32IntMask) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; #ifndef AM_HAL_DISABLE_API_VALIDATION if ( (!AM_HAL_SCARD_CHK_HANDLE(pHandle)) || (ui32Index > 1) ) { return AM_HAL_STATUS_INVALID_HANDLE; } #endif // AM_HAL_DISABLE_API_VALIDATION if ( 0 == ui32Index ) { SCARDn(ui32Module)->SR = ui32IntMask; } else { SCARDn(ui32Module)->SR1 = ui32IntMask; } return AM_HAL_STATUS_SUCCESS; } // am_hal_scard_interrupt_clear() //***************************************************************************** // // Returns the interrupt status. // //***************************************************************************** uint32_t am_hal_scard_interrupt_status_get(void *pHandle, uint32_t ui32Index, uint32_t *pui32Status) { am_hal_scard_state_t *pState = (am_hal_scard_state_t *) pHandle; uint32_t ui32Module = pState->ui32Module; #ifndef AM_HAL_DISABLE_API_VALIDATION if ( !AM_HAL_SCARD_CHK_HANDLE(pHandle) ) { return AM_HAL_STATUS_INVALID_HANDLE; } #endif // AM_HAL_DISABLE_API_VALIDATION // // If requested, only return the interrupts that are enabled. // *pui32Status = ui32Index ? SCARDn(ui32Module)->SR1 : SCARDn(ui32Module)->SR; return AM_HAL_STATUS_SUCCESS; } // am_hal_scard_interrupt_status_get() //***************************************************************************** // //! @brief SCARD control function //! //! @param handle - handle for the SCARD. //! @param eReq - device specific special request code. //! @param pArgs - pointer to the request specific arguments. //! //! This function allows advanced settings //! //! @return status - generic or interface specific status. // //***************************************************************************** uint32_t am_hal_scard_control(void *pHandle, am_hal_scard_request_e eReq, void *pArgs) { am_hal_scard_state_t *pSCCState = (am_hal_scard_state_t*)pHandle; uint32_t status = AM_HAL_STATUS_SUCCESS; #ifndef AM_HAL_DISABLE_API_VALIDATION if ( !AM_HAL_SCARD_CHK_HANDLE(pHandle) ) { return AM_HAL_STATUS_INVALID_HANDLE; } // // Validate the parameters // if ( eReq >= AM_HAL_SCARD_REQ_MAX ) { return AM_HAL_STATUS_INVALID_ARG; } #endif // AM_HAL_DISABLE_API_VALIDATION uint32_t ui32Module = pSCCState->ui32Module; switch (eReq) { case AM_HAL_SCARD_REQ_ACTIVATE: { uint16_t etu; etu = ((SCARDn(ui32Module)->BPRH & SCARD_BPRH_BPRH_Msk) << 8); etu = etu | SCARDn(ui32Module)->BPRL; etu = (SCARD_RST_LOW_TIME / etu) + 1; config_etucounter(ui32Module, etu); SCARD_WHILE_TIMEOUT_MS(!SCARDn(ui32Module)->SR1_b.ECNTOVER, 1000, AM_HAL_SCARD_STATUS_BUS_ERROR); SCARDn(ui32Module)->SR1_b.ECNTOVER = 1; SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->UCR_b.RSTIN = 1); } break; case AM_HAL_SCARD_REQ_DEACTIVATE: SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->UCR_b.RSTIN = 0); break; case AM_HAL_SCARD_REQ_BAUDRATE: if ( pArgs ) { config_baudrate(pHandle, *(uint32_t*)pArgs); } else { status = AM_HAL_STATUS_INVALID_ARG; } break; case AM_HAL_SCARD_REQ_CARD_FORMAT: if ( pArgs ) { config_cardformat(ui32Module, *(uint32_t*)pArgs); } else { status = AM_HAL_STATUS_INVALID_ARG; } break; case AM_HAL_SCARD_REQ_PARITY: if ( pArgs ) { config_parity(ui32Module, *(uint32_t*)pArgs); } else { status = AM_HAL_STATUS_INVALID_ARG; } break; case AM_HAL_SCARD_REQ_PROTOCOL: if ( pArgs ) { if ( AM_HAL_STATUS_SUCCESS != config_protocol(ui32Module, *(uint32_t*)pArgs) ) { status = AM_HAL_STATUS_INVALID_ARG; } } else { status = AM_HAL_STATUS_INVALID_ARG; } break; case AM_HAL_SCARD_REQ_GUARDTIME: if ( pArgs ) { SCARDn(ui32Module)->GTR = *(uint32_t*)pArgs; } else { status = AM_HAL_STATUS_INVALID_ARG; } break; case AM_HAL_SCARD_REQ_CLK_START: SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->UCR_b.CST = 0); break; case AM_HAL_SCARD_REQ_CLK_STOP: SCARD_SYNC_OPER(ui32Module, SCARDn(ui32Module)->UCR_b.CST = 1); break; default: status = AM_HAL_STATUS_INVALID_ARG; } return status; }