From 5789b21ca01f62de10ce6a9f94a770432b011b5f Mon Sep 17 00:00:00 2001 From: CinderSocket Date: Fri, 6 Mar 2026 22:08:56 -0800 Subject: [PATCH 1/2] refactor(ccid,t1): introduce typed protocol state and named constants --- ccid.c | 163 +++++++++++++----------- ccid.h | 97 +++++++------- scenes/seader_scene_read_card_success.c | 2 +- seader_bridge.h | 33 ++++- t_1.c | 159 ++++++++++++----------- t_1.h | 11 +- uart.c | 4 +- 7 files changed, 272 insertions(+), 197 deletions(-) diff --git a/ccid.c b/ccid.c index 695f8e3..5d52055 100644 --- a/ccid.c +++ b/ccid.c @@ -1,40 +1,52 @@ #include "seader_i.h" #define TAG "SeaderCCID" - -bool hasSAM = false; const uint8_t SAM_ATR[] = {0x3b, 0x95, 0x96, 0x80, 0xb1, 0xfe, 0x55, 0x1f, 0xc7, 0x47, 0x72, 0x61, 0x63, 0x65, 0x13}; const uint8_t SAM_ATR2[] = {0x3b, 0x90, 0x96, 0x91, 0x81, 0xb1, 0xfe, 0x55, 0x1f, 0xc7, 0xd4}; //3b95968011fc47726163653c const uint8_t SAM_ATR3[] = {0x3b, 0x95, 0x96, 0x80, 0x11, 0xfc, 0x47, 0x72, 0x61, 0x63, 0x65, 0x3c}; -bool powered[2] = {false, false}; -uint8_t sam_slot = 0; -uint8_t sequence[2] = {0, 0}; -uint8_t retries = 3; +static SeaderCcidState* seader_ccid_state(SeaderUartBridge* seader_uart) { + return &seader_uart->ccid; +} -uint8_t getSequence(uint8_t slot) { - if(sequence[slot] > 254) { - sequence[slot] = 0; +static SeaderCcidSlotState* seader_ccid_slot_state(SeaderUartBridge* seader_uart, uint8_t slot) { + furi_check(slot < SEADER_CCID_SLOT_COUNT); + return &seader_ccid_state(seader_uart)->slots[slot]; +} + +static uint8_t seader_ccid_current_slot(SeaderUartBridge* seader_uart) { + return seader_ccid_state(seader_uart)->sam_slot; +} + +static void seader_ccid_reset_slot_sequence(SeaderUartBridge* seader_uart, uint8_t slot) { + seader_ccid_slot_state(seader_uart, slot)->sequence = 0; +} + +static uint8_t seader_ccid_next_sequence(SeaderUartBridge* seader_uart, uint8_t slot) { + SeaderCcidSlotState* slot_state = seader_ccid_slot_state(seader_uart, slot); + if(slot_state->sequence > 254) { + slot_state->sequence = 0; } - return sequence[slot]++; + return slot_state->sequence++; } void seader_ccid_IccPowerOn(SeaderUartBridge* seader_uart, uint8_t slot) { - if(powered[slot]) { + SeaderCcidSlotState* slot_state = seader_ccid_slot_state(seader_uart, slot); + if(slot_state->powered) { return; } - powered[slot] = true; + slot_state->powered = true; FURI_LOG_D(TAG, "Sending Power On (%d)", slot); memset(seader_uart->tx_buf, 0, SEADER_UART_RX_BUF_SIZE); seader_uart->tx_buf[0] = SYNC; seader_uart->tx_buf[1] = CTRL; - seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn; + seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_ON; seader_uart->tx_buf[2 + 5] = slot; - seader_uart->tx_buf[2 + 6] = getSequence(slot); + seader_uart->tx_buf[2 + 6] = seader_ccid_next_sequence(seader_uart, slot); seader_uart->tx_buf[2 + 7] = 1; //power seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, 2 + 10); @@ -42,26 +54,29 @@ void seader_ccid_IccPowerOn(SeaderUartBridge* seader_uart, uint8_t slot) { } void seader_ccid_IccPowerOff(SeaderUartBridge* seader_uart, uint8_t slot) { - powered[slot] = false; + seader_ccid_slot_state(seader_uart, slot)->powered = false; FURI_LOG_D(TAG, "Sending Power Off (%d)", slot); memset(seader_uart->tx_buf, 0, SEADER_UART_RX_BUF_SIZE); seader_uart->tx_buf[0] = SYNC; seader_uart->tx_buf[1] = CTRL; - seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff; + seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_OFF; seader_uart->tx_buf[2 + 5] = slot; - seader_uart->tx_buf[2 + 6] = getSequence(slot); + seader_uart->tx_buf[2 + 6] = seader_ccid_next_sequence(seader_uart, slot); seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, 2 + 10); furi_thread_flags_set(furi_thread_get_id(seader_uart->tx_thread), WorkerEvtSamRx); } void seader_ccid_check_for_sam(SeaderUartBridge* seader_uart) { - hasSAM = false; // If someone is calling this, reset sam state - powered[0] = false; - powered[1] = false; - retries = 3; + SeaderCcidState* ccid_state = seader_ccid_state(seader_uart); + ccid_state->has_sam = false; // If someone is calling this, reset sam state + ccid_state->sam_slot = 0; + ccid_state->retries = 3; + for(size_t slot = 0; slot < SEADER_CCID_SLOT_COUNT; slot++) { + ccid_state->slots[slot].powered = false; + } seader_ccid_GetSlotStatus(seader_uart, 0); } @@ -70,9 +85,9 @@ void seader_ccid_GetSlotStatus(SeaderUartBridge* seader_uart, uint8_t slot) { memset(seader_uart->tx_buf, 0, SEADER_UART_RX_BUF_SIZE); seader_uart->tx_buf[0] = SYNC; seader_uart->tx_buf[1] = CTRL; - seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus; + seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_GET_SLOT_STATUS; seader_uart->tx_buf[2 + 5] = slot; - seader_uart->tx_buf[2 + 6] = getSequence(slot); + seader_uart->tx_buf[2 + 6] = seader_ccid_next_sequence(seader_uart, slot); seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, 2 + 10); furi_thread_flags_set(furi_thread_get_id(seader_uart->tx_thread), WorkerEvtSamRx); @@ -92,16 +107,16 @@ void seader_ccid_SetParameters(Seader* seader, uint8_t slot) { memset(seader_uart->tx_buf, 0, SEADER_UART_RX_BUF_SIZE); seader_uart->tx_buf[0] = SYNC; seader_uart->tx_buf[1] = CTRL; - seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters; + seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_SET_PARAMETERS; seader_uart->tx_buf[2 + 1] = payloadLen; seader_uart->tx_buf[2 + 5] = slot; - seader_uart->tx_buf[2 + 6] = getSequence(slot); + seader_uart->tx_buf[2 + 6] = seader_ccid_next_sequence(seader_uart, slot); seader_uart->tx_buf[2 + 7] = seader_uart->T; seader_uart->tx_buf[2 + 8] = 0; seader_uart->tx_buf[2 + 9] = 0; uint8_t* atr = seader->ATR; - seader_uart->IFSC = atr[5]; + seader_uart->t1.ifsc = atr[5]; if(seader_uart->T == 0) { // I'm leaving this here for completeness, but it was actually causing ICC_MUTE on the first apdu. @@ -116,7 +131,7 @@ void seader_ccid_SetParameters(Seader* seader, uint8_t slot) { seader_uart->tx_buf[2 + 12] = 0xfe; //bGuardTimeT1 seader_uart->tx_buf[2 + 13] = atr[6]; //bWaitingIntegerT1 seader_uart->tx_buf[2 + 14] = atr[8]; //bClockStop - seader_uart->tx_buf[2 + 15] = seader_uart->IFSC; //bIFSC + seader_uart->tx_buf[2 + 15] = seader_uart->t1.ifsc; //bIFSC seader_uart->tx_buf[2 + 16] = 0x00; //bNadValue } else if(seader_uart->T == 1) { seader_uart->tx_buf[2 + 10] = 0x11; //atr[2]; //bmFindexDindex @@ -124,7 +139,7 @@ void seader_ccid_SetParameters(Seader* seader, uint8_t slot) { seader_uart->tx_buf[2 + 12] = 0x00; //bGuardTimeT1 seader_uart->tx_buf[2 + 13] = 0x4d; //atr[6]; //bWaitingIntegerT1 seader_uart->tx_buf[2 + 14] = 0x00; //atr[8]; //bClockStop - seader_uart->tx_buf[2 + 15] = seader_uart->IFSC; //bIFSC + seader_uart->tx_buf[2 + 15] = seader_uart->t1.ifsc; //bIFSC seader_uart->tx_buf[2 + 16] = 0x00; //bNadValue } @@ -136,10 +151,11 @@ void seader_ccid_GetParameters(SeaderUartBridge* seader_uart) { memset(seader_uart->tx_buf, 0, SEADER_UART_RX_BUF_SIZE); seader_uart->tx_buf[0] = SYNC; seader_uart->tx_buf[1] = CTRL; - seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters; + seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_GET_PARAMETERS; seader_uart->tx_buf[2 + 1] = 0; - seader_uart->tx_buf[2 + 5] = sam_slot; - seader_uart->tx_buf[2 + 6] = getSequence(sam_slot); + seader_uart->tx_buf[2 + 5] = seader_ccid_current_slot(seader_uart); + seader_uart->tx_buf[2 + 6] = + seader_ccid_next_sequence(seader_uart, seader_ccid_current_slot(seader_uart)); seader_uart->tx_buf[2 + 7] = 0; seader_uart->tx_buf[2 + 8] = 0; seader_uart->tx_buf[2 + 9] = 0; @@ -150,7 +166,7 @@ void seader_ccid_GetParameters(SeaderUartBridge* seader_uart) { } void seader_ccid_XfrBlock(SeaderUartBridge* seader_uart, uint8_t* data, size_t len) { - seader_ccid_XfrBlockToSlot(seader_uart, sam_slot, data, len); + seader_ccid_XfrBlockToSlot(seader_uart, seader_ccid_current_slot(seader_uart), data, len); } void seader_ccid_XfrBlockToSlot( @@ -191,13 +207,13 @@ void seader_ccid_XfrBlockToSlot( frame[0] = SYNC; frame[1] = CTRL; - frame[2 + 0] = CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock; + frame[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_XFR_BLOCK; frame[2 + 1] = (len >> 0) & 0xff; frame[2 + 2] = (len >> 8) & 0xff; frame[2 + 3] = (len >> 16) & 0xff; frame[2 + 4] = (len >> 24) & 0xff; frame[2 + 5] = slot; - frame[2 + 6] = getSequence(slot); + frame[2 + 6] = seader_ccid_next_sequence(seader_uart, slot); frame[2 + 7] = 5; frame[2 + 8] = 0; frame[2 + 9] = 0; @@ -221,6 +237,7 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { SeaderUartBridge* seader_uart = seader_worker->uart; CCID_Message message; message.consumed = 0; + SeaderCcidState* ccid_state = seader_ccid_state(seader_uart); char* display = malloc(cmd_len * 2 + 1); for(uint8_t i = 0; i < cmd_len; i++) { @@ -230,26 +247,26 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { free(display); if(cmd_len == 2) { - if(cmd[0] == CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange) { - switch(cmd[1] & SLOT_0_MASK) { + if(cmd[0] == CCID_MESSAGE_TYPE_RDR_TO_PC_NOTIFY_SLOT_CHANGE) { + switch(cmd[1] & CCID_SLOT_0_MASK) { case 0: case 1: // No change, no-op break; - case CARD_IN_1: + case CCID_SLOT_0_CARD_IN: FURI_LOG_D(TAG, "Card Inserted (0)"); - if(hasSAM && sam_slot == 0) { + if(ccid_state->has_sam && ccid_state->sam_slot == 0) { break; } - sequence[0] = 0; + seader_ccid_reset_slot_sequence(seader_uart, 0); seader_ccid_IccPowerOn(seader_uart, 0); break; - case CARD_OUT_1: + case CCID_SLOT_0_CARD_OUT: FURI_LOG_D(TAG, "Card Removed (0)"); - if(hasSAM && sam_slot == 0) { - powered[0] = false; - hasSAM = false; - retries = 3; + if(ccid_state->has_sam && ccid_state->sam_slot == 0) { + ccid_state->slots[0].powered = false; + ccid_state->has_sam = false; + ccid_state->retries = 3; if(seader_worker->callback) { seader_worker->callback( SeaderWorkerEventSamMissing, seader_worker->context); @@ -258,25 +275,25 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { break; }; - switch(cmd[1] & SLOT_1_MASK) { + switch(cmd[1] & CCID_SLOT_1_MASK) { case 0: case 1: // No change, no-op break; - case CARD_IN_2: + case CCID_SLOT_1_CARD_IN: FURI_LOG_D(TAG, "Card Inserted (1)"); - if(hasSAM && sam_slot == 1) { + if(ccid_state->has_sam && ccid_state->sam_slot == 1) { break; } - sequence[1] = 0; + seader_ccid_reset_slot_sequence(seader_uart, 1); seader_ccid_IccPowerOn(seader_uart, 1); break; - case CARD_OUT_2: + case CCID_SLOT_1_CARD_OUT: FURI_LOG_D(TAG, "Card Removed (1)"); - if(hasSAM && sam_slot == 1) { - powered[1] = false; - hasSAM = false; - retries = 3; + if(ccid_state->has_sam && ccid_state->sam_slot == 1) { + ccid_state->slots[1].powered = false; + ccid_state->has_sam = false; + ccid_state->retries = 3; if(seader_worker->callback) { seader_worker->callback( SeaderWorkerEventSamMissing, seader_worker->context); @@ -332,16 +349,16 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { //0306 81 00000000 0000 0200 01 87 //0306 81 00000000 0000 0100 01 84 - if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus) { + if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_TO_PC_SLOT_STATUS) { uint8_t status = (message.bStatus & BMICCSTATUS_MASK); if(status == 0 || status == 1) { seader_ccid_IccPowerOn(seader_uart, message.bSlot); return message.consumed; - } else if(status == 2) { - FURI_LOG_W(TAG, "No ICC is present [retries %d]", retries); - if(retries-- > 1 && hasSAM == false) { + } else if(status == CCID_ICC_STATUS_NOT_PRESENT) { + FURI_LOG_W(TAG, "No ICC is present [retries %d]", ccid_state->retries); + if(ccid_state->retries-- > 1 && ccid_state->has_sam == false) { furi_delay_ms(100); - seader_ccid_GetSlotStatus(seader_uart, retries % 2); + seader_ccid_GetSlotStatus(seader_uart, ccid_state->retries % 2); } else { if(seader_worker->callback) { seader_worker->callback( @@ -355,7 +372,7 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { //0306 80 00000000 0001 42fe 00 38 if(message.bStatus == 0x41 && message.bError == 0xfe) { FURI_LOG_W(TAG, "card probably upside down"); - hasSAM = false; + ccid_state->has_sam = false; if(seader_worker->callback) { seader_worker->callback(SeaderWorkerEventSamMissing, seader_worker->context); } @@ -370,10 +387,10 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { } if(message.bError != 0) { switch(message.bError) { - case 0xfe: // CCID_ERROR_ICC_MUTE: + case CCID_ERROR_ICC_MUTE: FURI_LOG_W(TAG, "CCID error ICC_MUTE"); break; - case 0xfb: // HW_ERROR + case CCID_ERROR_HW_ERROR: FURI_LOG_W(TAG, "CCID error HW_ERROR"); break; default: @@ -387,7 +404,7 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { return message.consumed; } - if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_to_PC_Parameters) { + if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_TO_PC_PARAMETERS) { FURI_LOG_D(TAG, "Got Parameters"); if(seader_uart->T == 1) { seader_t_1_set_IFSD(seader); @@ -397,9 +414,9 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { seader_worker->callback(SeaderWorkerEventSamPresent, seader_worker->context); } } - } else if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock) { - if(hasSAM) { - if(message.bSlot == sam_slot) { + } else if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK) { + if(ccid_state->has_sam) { + if(message.bSlot == ccid_state->sam_slot) { if(seader_uart->T == 0) { seader_worker_process_sam_message( seader, message.payload, message.dwLength); @@ -412,33 +429,33 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { } else { if(memcmp(SAM_ATR, message.payload, sizeof(SAM_ATR)) == 0) { FURI_LOG_I(TAG, "SAM ATR!"); - hasSAM = true; - sam_slot = message.bSlot; + ccid_state->has_sam = true; + ccid_state->sam_slot = message.bSlot; seader->ATR_len = sizeof(SAM_ATR); memcpy(seader->ATR, message.payload, seader->ATR_len); if(seader_uart->T == 0) { seader_ccid_GetParameters(seader_uart); } else if(seader_uart->T == 1) { - seader_ccid_SetParameters(seader, sam_slot); + seader_ccid_SetParameters(seader, ccid_state->sam_slot); } } else if(memcmp(SAM_ATR2, message.payload, sizeof(SAM_ATR2)) == 0) { FURI_LOG_I(TAG, "SAM ATR2!"); - hasSAM = true; - sam_slot = message.bSlot; + ccid_state->has_sam = true; + ccid_state->sam_slot = message.bSlot; seader->ATR_len = sizeof(SAM_ATR); memcpy(seader->ATR, message.payload, seader->ATR_len); // I don't have an ATR2 to test with seader_ccid_GetParameters(seader_uart); } else if(memcmp(SAM_ATR3, message.payload, sizeof(SAM_ATR3)) == 0) { FURI_LOG_I(TAG, "SAM ATR3!"); - hasSAM = true; - sam_slot = message.bSlot; + ccid_state->has_sam = true; + ccid_state->sam_slot = message.bSlot; seader->ATR_len = sizeof(SAM_ATR); memcpy(seader->ATR, message.payload, seader->ATR_len); if(seader_uart->T == 0) { seader_ccid_GetParameters(seader_uart); } else if(seader_uart->T == 1) { - seader_ccid_SetParameters(seader, sam_slot); + seader_ccid_SetParameters(seader, ccid_state->sam_slot); } } else { FURI_LOG_W(TAG, "Unknown ATR"); diff --git a/ccid.h b/ccid.h index 33a2489..a03b38d 100644 --- a/ccid.h +++ b/ccid.h @@ -22,67 +22,74 @@ */ // TODO: rename/renumber -#define SLOT_0_MASK 0x03 -#define CARD_OUT_1 0x02 -#define CARD_IN_1 0x03 -#define SLOT_1_MASK 0x0C -#define CARD_IN_2 0x04 -#define CARD_OUT_2 0x0C +#define CCID_SLOT_0_MASK 0x03 +#define CCID_SLOT_0_CARD_OUT 0x02 +#define CCID_SLOT_0_CARD_IN 0x03 +#define CCID_SLOT_1_MASK 0x0C +#define CCID_SLOT_1_CARD_IN 0x04 +#define CCID_SLOT_1_CARD_OUT 0x0C /* * * BULK_OUT messages from PC to Reader * * Defined in CCID Rev 1.1 6.1 (page 26) * */ -#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn 0x62 -#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff 0x63 -#define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus 0x65 -#define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock 0x6f -#define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters 0x6c -#define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters 0x6d -#define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters 0x61 -#define CCID_MESSAGE_TYPE_PC_to_RDR_Escape 0x6b -#define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock 0x6e -#define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU 0x6a -#define CCID_MESSAGE_TYPE_PC_to_RDR_Secure 0x69 -#define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical 0x71 -#define CCID_MESSAGE_TYPE_PC_to_RDR_Abort 0x72 -#define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency 0x73 +typedef enum { + CCID_MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_ON = 0x62, + CCID_MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_OFF = 0x63, + CCID_MESSAGE_TYPE_PC_TO_RDR_GET_SLOT_STATUS = 0x65, + CCID_MESSAGE_TYPE_PC_TO_RDR_XFR_BLOCK = 0x6f, + CCID_MESSAGE_TYPE_PC_TO_RDR_GET_PARAMETERS = 0x6c, + CCID_MESSAGE_TYPE_PC_TO_RDR_RESET_PARAMETERS = 0x6d, + CCID_MESSAGE_TYPE_PC_TO_RDR_SET_PARAMETERS = 0x61, + CCID_MESSAGE_TYPE_PC_TO_RDR_ESCAPE = 0x6b, + CCID_MESSAGE_TYPE_PC_TO_RDR_ICC_CLOCK = 0x6e, + CCID_MESSAGE_TYPE_PC_TO_RDR_T0_APDU = 0x6a, + CCID_MESSAGE_TYPE_PC_TO_RDR_SECURE = 0x69, + CCID_MESSAGE_TYPE_PC_TO_RDR_MECHANICAL = 0x71, + CCID_MESSAGE_TYPE_PC_TO_RDR_ABORT = 0x72, + CCID_MESSAGE_TYPE_PC_TO_RDR_SET_DATA_RATE_AND_CLOCK_FREQUENCY = 0x73, +} SeaderCcidPcToRdrMessageType; /* * * BULK_IN messages from Reader to PC * * Defined in CCID Rev 1.1 6.2 (page 48) * */ -#define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock 0x80 -#define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus 0x81 -#define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters 0x82 -#define CCID_MESSAGE_TYPE_RDR_to_PC_Escape 0x83 -#define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency 0x84 +typedef enum { + CCID_MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK = 0x80, + CCID_MESSAGE_TYPE_RDR_TO_PC_SLOT_STATUS = 0x81, + CCID_MESSAGE_TYPE_RDR_TO_PC_PARAMETERS = 0x82, + CCID_MESSAGE_TYPE_RDR_TO_PC_ESCAPE = 0x83, + CCID_MESSAGE_TYPE_RDR_TO_PC_DATA_RATE_AND_CLOCK_FREQUENCY = 0x84, +} SeaderCcidRdrToPcMessageType; /* * * INTERRUPT_IN messages from Reader to PC * * Defined in CCID Rev 1.1 6.3 (page 56) * */ -#define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange 0x50 -#define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError 0x51 +typedef enum { + CCID_MESSAGE_TYPE_RDR_TO_PC_NOTIFY_SLOT_CHANGE = 0x50, + CCID_MESSAGE_TYPE_RDR_TO_PC_HARDWARE_ERROR = 0x51, +} SeaderCcidInterruptMessageType; /* Status codes that go in bStatus (see 6.2.6) */ -enum { - ICC_STATUS_PRESENT_ACTIVE = 0, - ICC_STATUS_PRESENT_INACTIVE, - ICC_STATUS_NOT_PRESENT -}; -enum { - COMMAND_STATUS_NO_ERROR = 0, - COMMAND_STATUS_FAILED, - COMMAND_STATUS_TIME_EXTENSION_REQUIRED -}; +typedef enum { + CCID_ICC_STATUS_PRESENT_ACTIVE = 0, + CCID_ICC_STATUS_PRESENT_INACTIVE = 1, + CCID_ICC_STATUS_NOT_PRESENT = 2, +} SeaderCcidIccStatus; + +typedef enum { + CCID_COMMAND_STATUS_NO_ERROR = 0, + CCID_COMMAND_STATUS_FAILED = 1, + CCID_COMMAND_STATUS_TIME_EXTENSION_REQUIRED = 2, +} SeaderCcidCommandStatus; /* Error codes that go in bError (see 6.2.6) */ -enum { - ERROR_CMD_NOT_SUPPORTED = 0, - ERROR_CMD_ABORTED = -1, - ERROR_ICC_MUTE = -2, - ERROR_XFR_PARITY_ERROR = -3, - ERROR_XFR_OVERRUN = -4, - ERROR_HW_ERROR = -5, -}; +typedef enum { + CCID_ERROR_CMD_NOT_SUPPORTED = 0x00, + CCID_ERROR_CMD_ABORTED = 0xff, + CCID_ERROR_ICC_MUTE = 0xfe, + CCID_ERROR_XFR_PARITY_ERROR = 0xfd, + CCID_ERROR_XFR_OVERRUN = 0xfc, + CCID_ERROR_HW_ERROR = 0xfb, +} SeaderCcidError; struct CCID_Message { uint8_t bMessageType; diff --git a/scenes/seader_scene_read_card_success.c b/scenes/seader_scene_read_card_success.c index a1607d1..75b04ba 100644 --- a/scenes/seader_scene_read_card_success.c +++ b/scenes/seader_scene_read_card_success.c @@ -57,7 +57,7 @@ void seader_scene_read_card_success_on_enter(void* context) { SeaderWorker* seader_worker = seader->worker; SeaderUartBridge* seader_uart = seader_worker->uart; - seader_t_1_reset(); + seader_t_1_reset(seader->uart); seader_ccid_check_for_sam(seader_uart); } diff --git a/seader_bridge.h b/seader_bridge.h index 3b624aa..2a5a4a2 100644 --- a/seader_bridge.h +++ b/seader_bridge.h @@ -11,6 +11,9 @@ // https://ww1.microchip.com/downloads/en/DeviceDoc/00001561C.pdf #define SEADER_UART_RX_BUF_SIZE (300) +#define SEADER_CCID_SLOT_COUNT (2U) + +typedef struct BitBuffer BitBuffer; typedef struct { uint8_t uart_ch; @@ -23,6 +26,33 @@ typedef struct { uint8_t protocol; } SeaderUartState; +typedef struct { + bool powered; + /* CCID sequence counter for this slot. */ + uint8_t sequence; +} SeaderCcidSlotState; + +typedef struct { + bool has_sam; + uint8_t sam_slot; + uint8_t retries; + SeaderCcidSlotState slots[SEADER_CCID_SLOT_COUNT]; +} SeaderCcidState; + +typedef struct { + /* ICC information field size for T=1. */ + uint8_t ifsc; + /* Host NAD used for T=1 block exchange. */ + uint8_t nad; + /* Last transmit I-block sequence bit. */ + uint8_t send_pcb; + /* Last receive I-block sequence bit. */ + uint8_t recv_pcb; + BitBuffer* tx_buffer; + size_t tx_buffer_offset; + BitBuffer* rx_buffer; +} SeaderT1State; + struct SeaderUartBridge { SeaderUartConfig cfg; SeaderUartConfig cfg_new; @@ -43,7 +73,8 @@ struct SeaderUartBridge { // T=0 or T=1 uint8_t T; - uint8_t IFSC; + SeaderCcidState ccid; + SeaderT1State t1; }; typedef struct SeaderUartBridge SeaderUartBridge; diff --git a/t_1.c b/t_1.c index b372654..43621e8 100644 --- a/t_1.c +++ b/t_1.c @@ -6,47 +6,53 @@ /* I know my T=1 is terrible, but I'm also only targetting one specific 'card' */ -#define MORE_BIT 0x20 -#define R_BLOCK 0x80 -#define S_BLOCK 0xC0 -#define R_SEQUENCE_NUMBER_MASK 0x10 -#define T1_S_IFS 0x01 +static SeaderT1State* seader_t1_state(SeaderUartBridge* seader_uart) { + return &seader_uart->t1; +} -// TODO: T1 struct -uint8_t NAD = 0x00; -uint8_t dPCB = 0x40; // Init to 0x40 so first call to next_pcb will return 0x00 -uint8_t cPCB = 0x00; // Init to 0x40 so first call to next_pcb will return 0x00 - -uint8_t seader_next_dpcb() { - uint8_t next_pcb = dPCB ^ 0x40; +static uint8_t seader_next_dpcb(SeaderUartBridge* seader_uart) { + SeaderT1State* t1 = seader_t1_state(seader_uart); + uint8_t next_pcb = t1->send_pcb ^ SEADER_T1_PCB_SEQUENCE_BIT; //FURI_LOG_D(TAG, "dPCB was: %02X, current dPCB: %02X", dPCB, next_pcb); - dPCB = next_pcb; - return dPCB; + t1->send_pcb = next_pcb; + return t1->send_pcb; } -uint8_t seader_next_cpcb() { - uint8_t next_pcb = cPCB ^ 0x40; +static uint8_t seader_next_cpcb(SeaderUartBridge* seader_uart) { + SeaderT1State* t1 = seader_t1_state(seader_uart); + uint8_t next_pcb = t1->recv_pcb ^ SEADER_T1_PCB_SEQUENCE_BIT; //FURI_LOG_D(TAG, "cPCB was: %02X, current cPCB: %02X", cPCB, next_pcb); - cPCB = next_pcb; - return cPCB; + t1->recv_pcb = next_pcb; + return t1->recv_pcb; } -void seader_t_1_reset() { - NAD = 0x00; - dPCB = 0x40; - cPCB = 0x00; +void seader_t_1_reset(SeaderUartBridge* seader_uart) { + SeaderT1State* t1 = seader_t1_state(seader_uart); + t1->nad = 0x00; + t1->send_pcb = SEADER_T1_PCB_SEQUENCE_BIT; + t1->recv_pcb = 0x00; + if(t1->tx_buffer != NULL) { + bit_buffer_free(t1->tx_buffer); + } + t1->tx_buffer = NULL; + t1->tx_buffer_offset = 0; + if(t1->rx_buffer != NULL) { + bit_buffer_free(t1->rx_buffer); + } + t1->rx_buffer = NULL; } void seader_t_1_set_IFSD(Seader* seader) { SeaderWorker* seader_worker = seader->worker; SeaderUartBridge* seader_uart = seader_worker->uart; + SeaderT1State* t1 = seader_t1_state(seader_uart); uint8_t frame[5]; uint8_t frame_len = 0; - frame[0] = NAD; - frame[1] = S_BLOCK | T1_S_IFS; // S(IFS request) + frame[0] = t1->nad; + frame[1] = SEADER_T1_PCB_S_BLOCK | SEADER_T1_S_BLOCK_IFS; // S(IFS request) frame[2] = 0x01; - frame[3] = seader_uart->IFSC; + frame[3] = t1->ifsc; frame_len = 4; frame_len = seader_add_lrc(frame, frame_len); @@ -57,13 +63,14 @@ void seader_t_1_set_IFSD(Seader* seader) { void seader_t_1_IFSD_response(Seader* seader) { SeaderWorker* seader_worker = seader->worker; SeaderUartBridge* seader_uart = seader_worker->uart; + SeaderT1State* t1 = seader_t1_state(seader_uart); uint8_t frame[5]; uint8_t frame_len = 0; - frame[0] = NAD; + frame[0] = t1->nad; frame[1] = 0xE0 | 0x01; // S(IFS response) frame[2] = 0x01; - frame[3] = seader_uart->IFSC; + frame[3] = t1->ifsc; frame_len = 4; frame_len = seader_add_lrc(frame, frame_len); @@ -74,11 +81,12 @@ void seader_t_1_IFSD_response(Seader* seader) { void seader_t_1_send_ack(Seader* seader) { SeaderWorker* seader_worker = seader->worker; SeaderUartBridge* seader_uart = seader_worker->uart; + SeaderT1State* t1 = seader_t1_state(seader_uart); uint8_t frame[4]; uint8_t frame_len = 0; - frame[0] = NAD; - frame[1] = R_BLOCK | (seader_next_cpcb() >> 2); + frame[0] = t1->nad; + frame[1] = SEADER_T1_PCB_R_BLOCK | (seader_next_cpcb(seader_uart) >> 2); frame[2] = 0x00; frame_len = 3; @@ -89,14 +97,12 @@ void seader_t_1_send_ack(Seader* seader) { seader_ccid_XfrBlock(seader_uart, frame, frame_len); } -BitBuffer* seader_t_1_tx_buffer; -size_t seader_t_1_tx_buffer_offset = 0; - void seader_send_t1_chunk(SeaderUartBridge* seader_uart, uint8_t PCB, uint8_t* chunk, size_t len) { + SeaderT1State* t1 = seader_t1_state(seader_uart); uint8_t* frame = malloc(3 + len + 1); uint8_t frame_len = 0; - frame[0] = NAD; + frame[0] = t1->nad; frame[1] = PCB; frame[2] = len; frame_len = 3; @@ -117,8 +123,9 @@ void seader_send_t1_scratchpad( uint8_t PCB, uint8_t* apdu, size_t len) { + SeaderT1State* t1 = seader_t1_state(seader_uart); uint8_t* frame = apdu - 3; - frame[0] = NAD; + frame[0] = t1->nad; frame[1] = PCB; frame[2] = (uint8_t)len; @@ -127,50 +134,51 @@ void seader_send_t1_scratchpad( } void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len) { - uint8_t ifsc = seader_uart->IFSC; + SeaderT1State* t1 = seader_t1_state(seader_uart); + uint8_t ifsc = t1->ifsc; bool in_scratchpad = (apdu >= seader_uart->tx_buf + 3 && apdu < seader_uart->tx_buf + SEADER_UART_RX_BUF_SIZE); if(len > ifsc) { - if(seader_t_1_tx_buffer == NULL) { - seader_t_1_tx_buffer = bit_buffer_alloc(768); - bit_buffer_copy_bytes(seader_t_1_tx_buffer, apdu, len); + if(t1->tx_buffer == NULL) { + t1->tx_buffer = bit_buffer_alloc(768); + bit_buffer_copy_bytes(t1->tx_buffer, apdu, len); } size_t remaining = - (bit_buffer_get_size_bytes(seader_t_1_tx_buffer) - seader_t_1_tx_buffer_offset); + (bit_buffer_get_size_bytes(t1->tx_buffer) - t1->tx_buffer_offset); size_t copy_length = remaining > ifsc ? ifsc : remaining; - uint8_t* chunk = - (uint8_t*)bit_buffer_get_data(seader_t_1_tx_buffer) + seader_t_1_tx_buffer_offset; + uint8_t* chunk = (uint8_t*)bit_buffer_get_data(t1->tx_buffer) + t1->tx_buffer_offset; if(remaining > ifsc) { - uint8_t PCB = seader_next_dpcb() | MORE_BIT; + uint8_t PCB = seader_next_dpcb(seader_uart) | SEADER_T1_PCB_I_BLOCK_MORE; seader_send_t1_chunk(seader_uart, PCB, chunk, copy_length); } else { - uint8_t PCB = seader_next_dpcb(); + uint8_t PCB = seader_next_dpcb(seader_uart); seader_send_t1_chunk(seader_uart, PCB, chunk, copy_length); } - seader_t_1_tx_buffer_offset += copy_length; - if(seader_t_1_tx_buffer_offset >= bit_buffer_get_size_bytes(seader_t_1_tx_buffer)) { - bit_buffer_free(seader_t_1_tx_buffer); - seader_t_1_tx_buffer = NULL; - seader_t_1_tx_buffer_offset = 0; + t1->tx_buffer_offset += copy_length; + if(t1->tx_buffer_offset >= bit_buffer_get_size_bytes(t1->tx_buffer)) { + bit_buffer_free(t1->tx_buffer); + t1->tx_buffer = NULL; + t1->tx_buffer_offset = 0; } return; } if(in_scratchpad) { - seader_send_t1_scratchpad(seader_uart, seader_next_dpcb(), apdu, len); + seader_send_t1_scratchpad(seader_uart, seader_next_dpcb(seader_uart), apdu, len); } else { - seader_send_t1_chunk(seader_uart, seader_next_dpcb(), apdu, len); + seader_send_t1_chunk(seader_uart, seader_next_dpcb(seader_uart), apdu, len); } } -BitBuffer* seader_t_1_rx_buffer; - bool seader_recv_t1(Seader* seader, CCID_Message* message) { + SeaderWorker* seader_worker = seader->worker; + SeaderUartBridge* seader_uart = seader_worker->uart; + SeaderT1State* t1 = seader_t1_state(seader_uart); // remove/validate NAD, PCB, LEN, LRC if(message->dwLength < 4) { FURI_LOG_W(TAG, "Invalid T=1 frame: too short"); @@ -193,20 +201,20 @@ bool seader_recv_t1(Seader* seader, CCID_Message* message) { return false; } - if(rPCB == cPCB) { - seader_next_cpcb(); - if(seader_t_1_rx_buffer != NULL) { - bit_buffer_append_bytes(seader_t_1_rx_buffer, message->payload + 3, LEN); + if(rPCB == t1->recv_pcb) { + seader_next_cpcb(seader_uart); + if(t1->rx_buffer != NULL) { + bit_buffer_append_bytes(t1->rx_buffer, message->payload + 3, LEN); // TODO: validate LRC seader_worker_process_sam_message( seader, - (uint8_t*)bit_buffer_get_data(seader_t_1_rx_buffer), - bit_buffer_get_size_bytes(seader_t_1_rx_buffer)); + (uint8_t*)bit_buffer_get_data(t1->rx_buffer), + bit_buffer_get_size_bytes(t1->rx_buffer)); - bit_buffer_free(seader_t_1_rx_buffer); - seader_t_1_rx_buffer = NULL; + bit_buffer_free(t1->rx_buffer); + t1->rx_buffer = NULL; return true; } @@ -223,24 +231,24 @@ bool seader_recv_t1(Seader* seader, CCID_Message* message) { return true; } return seader_worker_process_sam_message(seader, message->payload, message->dwLength); - } else if(rPCB == (cPCB | MORE_BIT)) { + } else if(rPCB == (t1->recv_pcb | SEADER_T1_PCB_I_BLOCK_MORE)) { //FURI_LOG_D(TAG, "Received T=1 frame with more bit set"); - if(seader_t_1_rx_buffer == NULL) { - seader_t_1_rx_buffer = bit_buffer_alloc(512); + if(t1->rx_buffer == NULL) { + t1->rx_buffer = bit_buffer_alloc(512); } - bit_buffer_append_bytes(seader_t_1_rx_buffer, message->payload + 3, LEN); + bit_buffer_append_bytes(t1->rx_buffer, message->payload + 3, LEN); seader_t_1_send_ack(seader); return false; - } else if((rPCB & S_BLOCK) == S_BLOCK) { + } else if((rPCB & SEADER_T1_PCB_S_BLOCK) == SEADER_T1_PCB_S_BLOCK) { //FURI_LOG_D(TAG, "Received S-Block"); - if((rPCB & 0x0F) == T1_S_IFS) { + if((rPCB & 0x0F) == SEADER_T1_S_BLOCK_IFS) { seader_t_1_IFSD_response(seader); return false; } - } else if((rPCB & R_BLOCK) == R_BLOCK) { - uint8_t R_SEQ = (rPCB & R_SEQUENCE_NUMBER_MASK) >> 4; - uint8_t I_SEQ = (dPCB ^ 0x40) >> 6; + } else if((rPCB & SEADER_T1_PCB_R_BLOCK) == SEADER_T1_PCB_R_BLOCK) { + uint8_t R_SEQ = (rPCB & SEADER_T1_R_BLOCK_SEQUENCE_MASK) >> 4; + uint8_t I_SEQ = (t1->send_pcb ^ SEADER_T1_PCB_SEQUENCE_BIT) >> 6; if(R_SEQ != I_SEQ) { /* FURI_LOG_D( @@ -254,19 +262,20 @@ bool seader_recv_t1(Seader* seader, CCID_Message* message) { return false; } - if(seader_t_1_tx_buffer != NULL) { + if(t1->tx_buffer != NULL) { // Send more data, re-using the buffer to trigger the code path that sends the next block - SeaderWorker* seader_worker = seader->worker; - SeaderUartBridge* seader_uart = seader_worker->uart; seader_send_t1( seader_uart, - (uint8_t*)bit_buffer_get_data(seader_t_1_tx_buffer), - bit_buffer_get_size_bytes(seader_t_1_tx_buffer)); + (uint8_t*)bit_buffer_get_data(t1->tx_buffer), + bit_buffer_get_size_bytes(t1->tx_buffer)); return false; } } else { FURI_LOG_W( - TAG, "Invalid T=1 frame: PCB mismatch. Expected: %02X, Received: %02X", cPCB, rPCB); + TAG, + "Invalid T=1 frame: PCB mismatch. Expected: %02X, Received: %02X", + t1->recv_pcb, + rPCB); } return false; diff --git a/t_1.h b/t_1.h index 98129fa..cedf532 100644 --- a/t_1.h +++ b/t_1.h @@ -4,7 +4,16 @@ typedef struct CCID_Message CCID_Message; +typedef enum { + SEADER_T1_PCB_I_BLOCK_MORE = 0x20, + SEADER_T1_PCB_SEQUENCE_BIT = 0x40, + SEADER_T1_PCB_R_BLOCK = 0x80, + SEADER_T1_PCB_S_BLOCK = 0xC0, + SEADER_T1_R_BLOCK_SEQUENCE_MASK = 0x10, + SEADER_T1_S_BLOCK_IFS = 0x01, +} SeaderT1Constant; + void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len); bool seader_recv_t1(Seader* seader, CCID_Message* message); void seader_t_1_set_IFSD(Seader* seader); -void seader_t_1_reset(); +void seader_t_1_reset(SeaderUartBridge* seader_uart); diff --git a/uart.c b/uart.c index fcfe54b..443761a 100644 --- a/uart.c +++ b/uart.c @@ -148,9 +148,11 @@ int32_t seader_uart_worker(void* context) { } SeaderUartBridge* seader_uart_enable(SeaderUartConfig* cfg, Seader* seader) { - SeaderUartBridge* seader_uart = malloc(sizeof(SeaderUartBridge)); + SeaderUartBridge* seader_uart = calloc(1, sizeof(SeaderUartBridge)); seader_uart->T = 1; + seader_t_1_reset(seader_uart); + seader_uart->ccid.retries = 3; memcpy(&(seader_uart->cfg_new), cfg, sizeof(SeaderUartConfig)); From 810f143242add5a8d9bdf46aba3dd05d23b14bc5 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 7 Mar 2026 12:47:04 -0800 Subject: [PATCH 2/2] ufbt format --- ccid.h | 12 ++++++------ t_1.c | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ccid.h b/ccid.h index a03b38d..890d058 100644 --- a/ccid.h +++ b/ccid.h @@ -22,12 +22,12 @@ */ // TODO: rename/renumber -#define CCID_SLOT_0_MASK 0x03 -#define CCID_SLOT_0_CARD_OUT 0x02 -#define CCID_SLOT_0_CARD_IN 0x03 -#define CCID_SLOT_1_MASK 0x0C -#define CCID_SLOT_1_CARD_IN 0x04 -#define CCID_SLOT_1_CARD_OUT 0x0C +#define CCID_SLOT_0_MASK 0x03 +#define CCID_SLOT_0_CARD_OUT 0x02 +#define CCID_SLOT_0_CARD_IN 0x03 +#define CCID_SLOT_1_MASK 0x0C +#define CCID_SLOT_1_CARD_IN 0x04 +#define CCID_SLOT_1_CARD_OUT 0x0C /* * * BULK_OUT messages from PC to Reader diff --git a/t_1.c b/t_1.c index 43621e8..0eda220 100644 --- a/t_1.c +++ b/t_1.c @@ -145,8 +145,7 @@ void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len) { t1->tx_buffer = bit_buffer_alloc(768); bit_buffer_copy_bytes(t1->tx_buffer, apdu, len); } - size_t remaining = - (bit_buffer_get_size_bytes(t1->tx_buffer) - t1->tx_buffer_offset); + size_t remaining = (bit_buffer_get_size_bytes(t1->tx_buffer) - t1->tx_buffer_offset); size_t copy_length = remaining > ifsc ? ifsc : remaining; uint8_t* chunk = (uint8_t*)bit_buffer_get_data(t1->tx_buffer) + t1->tx_buffer_offset;