/*************************************************************************** * Copyright (c) 2009-2010, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: $ * $brcm_Revision: $ * $brcm_Date: $ * * Module Description: * * Revision History: * * $brcm_Log: $ * ***************************************************************************/ #include "bsettop_smartcard.h" #include "bstd.h" #include "bscd.h" #include "gist.h" BDBG_MODULE(smartcard); bresult bscd_settop_err(const char* func,int line,char *err_str,bresult err); #define BSETTOP_ERROR(x) bscd_settop_err(__FUNCTION__,__LINE__,#x,x) #define B_ID_IS_INDEX(x) (x == 0) /* * error debugging function */ bresult bscd_settop_err(const char* func,int line,char *err_str,bresult err) { BDBG_ERR(("%s:%d - %s\n",func,line,err_str)); return err; } static b_task_t bsmartcard_task_h; typedef struct bsmartcard_event_t { b_queue_t queue; b_event_t events[4]; }bsmartcard_event_t; static bsmartcard_event_t s_bsmartcard_event; struct bsmartcard { BSCD_ChannelHandle channelHandle; /* scd channel */ BSCD_ChannelSettings channelSettings; bsmartcard_settings_t settings; bsmartcard_state state; unsigned int *p_stack; } ; struct scd_module { BSCD_Handle moduleHandle; BSCD_Settings moduleSettings; struct bsmartcard cliChannel[BSCD_MAX_SUPPOTED_CHANNELS]; bool card_inserted[BSCD_MAX_SUPPOTED_CHANNELS]; bool card_removed[BSCD_MAX_SUPPOTED_CHANNELS]; bool done; } g_smartcard; static bresult bsmartcard_p_set_settings( BSCD_ChannelSettings *inoutp_sSettings, bsmartcard_standard standard, bsmartcard_protocol protocol ); static void bsmartcard_p_card_inserted( BSCD_ChannelHandle in_channelHandle, void * inp_data ) { int i; /* Sadly, there does not currently appear to be a means of setting inp_data to the specific bsmartcard. */ BSTD_UNUSED(inp_data); BDBG_MSG(("Card insertion detected.")); for (i=0; i < BSCD_MAX_SUPPOTED_CHANNELS; i++) { if (g_smartcard.cliChannel[i].channelHandle == in_channelHandle) { g_smartcard.card_inserted[i] = 1; } } bos_post_event(s_bsmartcard_event.queue,(b_event_t*)(s_bsmartcard_event.events)); } static void bsmartcard_p_card_removed( BSCD_ChannelHandle in_channelHandle, void * inp_data ) { int i; /* Sadly, there does not currently appear to be a means of setting inp_data to the specific bsmartcard. */ BSTD_UNUSED(inp_data); BDBG_MSG(("Card removal detected")); for (i=0; i < BSCD_MAX_SUPPOTED_CHANNELS; i++) { if (g_smartcard.cliChannel[i].channelHandle == in_channelHandle) { g_smartcard.card_removed[i] = 1; } } bos_post_event(s_bsmartcard_event.queue,(b_event_t*)(s_bsmartcard_event.events)); } bresult bsmartcard_init(void) { BERR_Code rc; rc = BSCD_GetDefaultSettings(&g_smartcard.moduleSettings, GetCHP()); if (rc!=BERR_SUCCESS) { return BSETTOP_ERROR(berr_external_error); } g_smartcard.done = false; g_smartcard.moduleSettings.moduleClkFreq.FreqSrc = BSCD_ClockFreqSrc_eInternalClock; g_smartcard.moduleSettings.moduleClkFreq.ulClkFreq = 27000000; g_smartcard.moduleSettings.ucMaxChannels = BSCD_MAX_SUPPOTED_CHANNELS; g_smartcard.card_inserted[0] = 0; g_smartcard.card_inserted[1] = 0; g_smartcard.card_removed[0] = 0; g_smartcard.card_removed[1] = 0; rc = BSCD_Open(&g_smartcard.moduleHandle, GetREG(), GetCHP(), GetINT(), &g_smartcard.moduleSettings); if (rc!=BERR_SUCCESS) { return BSETTOP_ERROR(berr_external_error); } bos_create_queue(&s_bsmartcard_event.queue, s_bsmartcard_event.events, 4); return b_ok; } void bsmartcard_shutdown(void) { BSCD_Close(g_smartcard.moduleHandle); return; } void bsmartcard_settings_init(bsmartcard_settings_t *settings) { BDBG_ASSERT(settings); BKNI_Memset(settings,0,sizeof(*settings)); /* these match the old hard-coded values */ settings->protocol = bsmartcard_protocol_t0; settings->standard = bsmartcard_standard_nds; settings->callback_context = NULL; settings->card_insertion = NULL; settings->card_removal = NULL; } static void bsmardcard_task(void *data) { int i; while (!g_smartcard.done) { bos_pend_event(s_bsmartcard_event.queue, 0xFF); for (i=0; i < BSCD_MAX_SUPPOTED_CHANNELS; i++) { if (g_smartcard.card_inserted[i]) { if (g_smartcard.cliChannel[i].settings.card_insertion) { g_smartcard.cliChannel[i].settings.card_insertion(g_smartcard.cliChannel[i].settings.callback_context); } g_smartcard.card_inserted[i] = 0; } if (g_smartcard.card_removed[i]) { if (g_smartcard.cliChannel[i].settings.card_removal) { g_smartcard.cliChannel[i].settings.card_removal(g_smartcard.cliChannel[i].settings.callback_context); } g_smartcard.card_removed[i] = 0; } } } } bsmartcard_t bsmartcard_open(bobject_t smartcard_id, bsmartcard_t * smartcard, const bsmartcard_settings_t *settings) { BERR_Code rc; unsigned int index; b_task_params params; b_task_t task_h; BDBG_ASSERT(settings); if (B_ID_IS_INDEX(smartcard_id)) { index = B_ID_GET_INDEX(smartcard_id); } else { if (smartcard_id==0) { BSETTOP_ERROR(berr_invalid_parameter); goto error; } BSETTOP_ERROR(berr_not_available); goto error; } if (index >= BSCD_MAX_SUPPOTED_CHANNELS) { BSETTOP_ERROR(berr_invalid_parameter); goto error; } g_smartcard.cliChannel[index].p_stack = BKNI_Malloc(256*4); if (!(g_smartcard.cliChannel[index].p_stack)) { BSETTOP_ERROR(berr_out_of_memory); goto error; } g_smartcard.cliChannel[index].state = bsmartcard_state_unknown; g_smartcard.cliChannel[index].settings = *settings; rc = BSCD_GetChannelDefaultSettings(g_smartcard.moduleHandle, index, &g_smartcard.cliChannel[index].channelSettings); if (rc!=BERR_SUCCESS) { BSETTOP_ERROR(berr_external_error); goto error; } if (settings->standard == bsmartcard_standard_nds || settings->standard == bsmartcard_standard_iso) { if (bsmartcard_p_set_settings(&g_smartcard.cliChannel[index].channelSettings, settings->standard, settings->protocol) != b_ok) { BSETTOP_ERROR(berr_external_error); goto error; } } else { BDBG_ERR(("The requested standard is not supported at this time")); BSETTOP_ERROR(berr_not_available); } rc = BSCD_Channel_Open(g_smartcard.moduleHandle, &g_smartcard.cliChannel[index].channelHandle, index, &g_smartcard.cliChannel[index].channelSettings); if (rc!=BERR_SUCCESS) { BSETTOP_ERROR(berr_external_error); goto error; } BSCD_Channel_EnableIntrCallback_isr(g_smartcard.cliChannel[index].channelHandle,BSCD_IntType_eCardInsertInt,bsmartcard_p_card_inserted); BSCD_Channel_EnableIntrCallback_isr(g_smartcard.cliChannel[index].channelHandle,BSCD_IntType_eCardRemoveInt,bsmartcard_p_card_removed); *smartcard = &g_smartcard.cliChannel[index]; params.name="smartcard_task"; params.priority = 8; params.stack_size = 256; params.stack = g_smartcard.cliChannel[index].p_stack; bos_start_task(&task_h,¶ms, bsmardcard_task, &task_h); if (!task_h) { BDBG_ERR(("task_h create failed\n")); } bsmartcard_task_h = task_h; return *smartcard; error: *smartcard = NULL; return *smartcard; } void bsmartcard_close(bsmartcard_t smartcard) { BDBG_ASSERT(smartcard); smartcard->state = bsmartcard_state_unknown; if (BSCD_Channel_Close(smartcard->channelHandle) != b_ok) BSETTOP_ERROR(berr_external_error); bos_stop_task(bsmartcard_task_h); bos_delete_queue(&s_bsmartcard_event.queue); if (smartcard->p_stack) { BKNI_Free(smartcard->p_stack); smartcard->p_stack = NULL; } } bresult bsmartcard_reset(bsmartcard_t smartcard, bool warm_reset) { BDBG_ASSERT(smartcard); smartcard->state = warm_reset ? bsmartcard_state_warm_resetting : bsmartcard_state_cold_resetting; if (BSCD_Channel_ResetIFD(smartcard->channelHandle, warm_reset ? BSCD_ResetType_eWarm : BSCD_ResetType_eCold)) { smartcard->state = bsmartcard_state_reset_done; return BSETTOP_ERROR(berr_external_error); } smartcard->state = bsmartcard_state_reset_done; return b_ok; } bresult bsmartcard_read(bsmartcard_t smartcard, void *data, size_t length, size_t *length_read) { BDBG_ASSERT(smartcard); smartcard->state = bsmartcard_state_receiving; if (BSCD_Channel_Receive(smartcard->channelHandle, data, (unsigned long *) length_read, length) != b_ok) { smartcard->state = bsmartcard_state_ready; return BSETTOP_ERROR(berr_external_error); } smartcard->state = bsmartcard_state_ready; if (*length_read <= 0) { return BSETTOP_ERROR(berr_external_error);; } return b_ok; } bresult bsmartcard_write(bsmartcard_t smartcard, const void *data, size_t length, size_t *length_written) { BDBG_ASSERT(smartcard); smartcard->state = bsmartcard_state_transmitting; if (BSCD_Channel_Transmit( smartcard->channelHandle, (uint8_t *) data, length) != b_ok) { *length_written = 0; smartcard->state = bsmartcard_state_transmitted; return BSETTOP_ERROR(berr_external_error);; } else { *length_written = length; smartcard->state = bsmartcard_state_transmitted; return b_ok; } } bresult bsmartcard_get_status(bsmartcard_t smartcard, bsmartcard_status *status) { BSCD_Status internal_status; BDBG_ASSERT(smartcard); if (BSCD_Channel_GetStatus(smartcard->channelHandle, &internal_status) != b_ok) { return BSETTOP_ERROR(berr_external_error); } status->err = bsmartcard_no_error; status->card_present = internal_status.bCardPresent; status->state = smartcard->state; return b_ok; } bresult bsmartcard_detect_card(bsmartcard_t smartcard) { BDBG_ASSERT(smartcard); if (BSCD_Channel_DetectCard(smartcard->channelHandle, BSCD_CardPresent_eInserted) != b_ok) { return BSETTOP_ERROR(berr_external_error); } return b_ok; } bresult bsmartcard_reset_card(bsmartcard_t smartcard, void *data, size_t length, size_t *length_read) { BDBG_ASSERT(smartcard); smartcard->state = bsmartcard_state_unknown; if (BSCD_Channel_ResetCard(smartcard->channelHandle, BSCD_ResetCardAction_eNoAction) != b_ok) { return BSETTOP_ERROR(berr_external_error); } if (data) { if (BSCD_Channel_Receive(smartcard->channelHandle, data, (unsigned long *) length_read, length) != b_ok) return BSETTOP_ERROR(berr_external_error); if (*length_read > length) { return BSETTOP_ERROR(berr_invalid_parameter); } if (*length_read <= 0) { return BSETTOP_ERROR(berr_external_error); } else { smartcard->state = bsmartcard_state_reset_done; /* interpret ATR data and set the settings */ smartcard->channelSettings.extraGuardTime.ulValue = ((unsigned long *) data )[4]; return b_ok; } } return BSETTOP_ERROR(berr_external_error); } bresult bsmartcard_set_params_from_atr ( bsmartcard_t smartcard ) { BDBG_ASSERT(smartcard); smartcard->state = bsmartcard_state_receive_decode_atr; if (BSCD_Channel_SetParameters(smartcard->channelHandle, &(smartcard->channelSettings)) != b_ok) { smartcard->state = bsmartcard_state_unknown; return BSETTOP_ERROR(berr_external_error); } if (BSCD_Channel_EnableInterrupts(smartcard->channelHandle) != b_ok) { smartcard->state = bsmartcard_state_unknown; return BSETTOP_ERROR(berr_external_error); } smartcard->state = bsmartcard_state_ready; return b_ok; } static bresult bsmartcard_p_set_settings( BSCD_ChannelSettings *inoutp_sSettings, bsmartcard_standard standard, bsmartcard_protocol protocol ) { /* Smart Card Standard */ switch (standard) { case bsmartcard_standard_nds: inoutp_sSettings->scStandard = BSCD_Standard_eNDS; break; case bsmartcard_standard_iso: inoutp_sSettings->scStandard = BSCD_Standard_eISO; break; default: inoutp_sSettings->scStandard = BSCD_Standard_eUnknown; break; } /* Asynchronous Protocol Types. */ switch (protocol) { case bsmartcard_protocol_t1: inoutp_sSettings->eProtocolType = BSCD_AsyncProtocolType_e1; break; case bsmartcard_protocol_t14: inoutp_sSettings->eProtocolType = BSCD_AsyncProtocolType_e14_IRDETO; break; case bsmartcard_protocol_t0: default: inoutp_sSettings->eProtocolType = BSCD_AsyncProtocolType_e0; break; } /* Set F, Clock Rate Conversion Factor */ inoutp_sSettings->ucFFactor = 1; /* Set D, Baud Rate Adjustor */ inoutp_sSettings->ucDFactor = 1; /* Set ETU Clock Divider */ inoutp_sSettings->ucEtuClkDiv = 6; /* Set SC Clock Divider */ inoutp_sSettings->ucScClkDiv = 1; /* Set external Clock Divisor. For TDA only 1, 2,4,8 are valid value. */ inoutp_sSettings->ucExternalClockDivisor = 1; /* Set Prescale */ inoutp_sSettings->unPrescale = 0x0B; /* Set baud divisor */ inoutp_sSettings->ucBaudDiv = 0x1F; /* Set ICC CLK Freq */ /* If the final ISO baudrate is not equal to the final BRCM baudrate, there is a potential mismatch */ /* Set maximum IFSD */ /* Set current IFSD */ inoutp_sSettings->unCurrentIFSD = BSCD_MAX_TX_SIZE; /* Set Number of transmit parity retries */ inoutp_sSettings->ucTxRetries = 4; /* Set Number of receive parity retries */ inoutp_sSettings->ucRxRetries = 4; /* Set work waiting time */ inoutp_sSettings->workWaitTime.ulValue = BSCD_DEFAULT_WORK_WAITING_TIME ; inoutp_sSettings->workWaitTime.unit = BSCD_TimerUnit_eETU; /* Set block Wait time */ inoutp_sSettings->blockWaitTime.ulValue = BSCD_DEFAULT_BLOCK_WAITING_TIME ; inoutp_sSettings->blockWaitTime.unit = BSCD_TimerUnit_eETU; /* Set Extra Guard Time */ inoutp_sSettings->extraGuardTime.ulValue = BSCD_DEFAULT_EXTRA_GUARD_TIME ; inoutp_sSettings->extraGuardTime.unit = BSCD_TimerUnit_eETU; /* Set block Guard time */ inoutp_sSettings->blockGuardTime.ulValue = BSCD_DEFAULT_BLOCK_GUARD_TIME ; inoutp_sSettings->blockGuardTime.unit = BSCD_TimerUnit_eETU; /* Set character Wait time Integer */ inoutp_sSettings->ulCharacterWaitTimeInteger = BSCD_DEFAULT_CHARACTER_WAIT_TIME_INTEGER ; /* Set EDC encoding */ inoutp_sSettings->edcSetting.bIsEnabled = false; /* Set transaction time out */ inoutp_sSettings->timeOut.ulValue = BSCD_DEFAULT_TIME_OUT ; inoutp_sSettings->timeOut.unit = BSCD_TimerUnit_eMilliSec; /* auto deactivation sequence */ inoutp_sSettings->bAutoDeactiveReq = false; /* nullFilter */ inoutp_sSettings->bNullFilter = false; /* debounce info */ inoutp_sSettings->scPresDbInfo.bIsEnabled = false; /* Use TDA chip */ inoutp_sSettings->scPresDbInfo.ucDbWidth = BSCD_DEFAULT_DB_WIDTH; inoutp_sSettings->scPresDbInfo.scPresMode = BSCD_ScPresMode_eMask; /* Specify if we want the driver to read, decode and program registers */ inoutp_sSettings->resetCardAction = BSCD_ResetCardAction_eNoAction; return b_ok ; } #ifdef CONFIG_SMARTCARD_TEST #include "ministd.h" void bsettop_smartcard_test(void) { int cnt = 0; bsmartcard_settings_t settings; bsmartcard_t smartcard; bsmartcard_init(); bsmartcard_settings_init(&settings); smartcard = bsmartcard_open(0,&smartcard,&settings); bsmartcard_reset(smartcard,false); while (cnt++ < 120) { printf("Check for smartcard...\n"); if (bsmartcard_detect_card(smartcard) == b_ok) { #define MAX_ATR_LEN 256 size_t atr_len = MAX_ATR_LEN; static char *atr_buffer[MAX_ATR_LEN+1]; printf("Smartcard detected\n"); /* Must reset interface again and detect card again */ printf("Reset smartcard interface... \n"); bsmartcard_reset(smartcard,false); if (bsmartcard_detect_card(smartcard) != b_ok) { printf("Reset smartcard interface failed... \n"); continue; } printf("Smartcard reset card\n"); if (bsmartcard_reset_card(smartcard,atr_buffer,atr_len,&atr_len) != b_ok) { printf("Smartcard reset card failed\n"); continue; } if (atr_len > 0) { atr_buffer[atr_len] = 0; printf("Smartcard reset card ATR %d bytes = %s\n",atr_len,atr_buffer); if (bsmartcard_set_params_from_atr(smartcard) != b_ok) { printf("Smartcard bsmartcard_set_params_from_atr failed\n"); continue; } break; } else { printf("Smartcard reset card failed. ATR length = 0\n"); } } else printf("No smartcard detected\n"); bos_sleep(1000); } bsmartcard_close(smartcard); bsmartcard_shutdown(); } #endif /* CONFIG_SMARTCARD_TEST */