Skip to content

Commit

Permalink
improve global buffer management
Browse files Browse the repository at this point in the history
  • Loading branch information
z4yx committed Mar 3, 2024
1 parent 8cb70fd commit 97a7ea5
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 25 deletions.
96 changes: 75 additions & 21 deletions interfaces/USB/class/ccid/ccid.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <usbd_ccid.h>

#define CCID_UpdateCommandStatus(cmd_status, icc_status) bulkin_data.bStatus = (cmd_status | icc_status)
#define CCID_IsShortCommand() (bulkout_data.dwLength <= SHORT_ABDATA_SIZE)

static uint8_t CCID_CheckCommandParams(uint32_t param_type);

Expand Down Expand Up @@ -36,47 +37,74 @@ uint8_t CCID_Init(void) {
send_data_spinlock = 0;
bulkout_state = CCID_STATE_IDLE;
has_cmd = 0;
bulkout_data.abData = bulkin_data.abData;
apdu_cmd.data = bulkin_data.abData;
apdu_resp.data = bulkin_data.abData;
card_status = BM_ICC_PRESENT_ACTIVE;
card_status = BM_ICC_PRESENT_INACTIVE;
return 0;
}

uint8_t CCID_OutEvent(uint8_t *data, uint8_t len) {
uint8_t *abData = NULL;
switch (bulkout_state) {
case CCID_STATE_IDLE:
if (len == 0)
bulkout_state = CCID_STATE_IDLE;
else if (len >= CCID_CMD_HEADER_SIZE) {
ab_data_length = len - CCID_CMD_HEADER_SIZE;
memcpy(&bulkout_data, data, CCID_CMD_HEADER_SIZE);
memcpy(bulkout_data.abData, data + CCID_CMD_HEADER_SIZE, ab_data_length);
bulkout_data.dwLength = letoh32(bulkout_data.dwLength);
bulkin_data.bSlot = bulkout_data.bSlot;
bulkin_data.bSeq = bulkout_data.bSeq;
if (ab_data_length == bulkout_data.dwLength)
ab_data_length = len - CCID_CMD_HEADER_SIZE;
if (ab_data_length > bulkout_data.dwLength)
ab_data_length = bulkout_data.dwLength; // abnormal packet received, truncate data
if (CCID_IsShortCommand()) {
// abDataShort is large enough for most commands
abData = bulkout_data.abDataShort;
} else {
if (bulkout_data.dwLength > ABDATA_SIZE ||
acquire_apdu_buffer(BUFFER_OWNER_CCID) != 0) {
// global_buffer is not available, discarding abData
// only PC_to_RDR_XfrBlock and PC_to_RDR_Secure should get here
bulkout_data.bMessageType = PC_TO_RDR_DISCARDED;
} else {
// global_buffer is acquired before use
abData = global_buffer;
}
}
if (abData) memcpy(abData, data + CCID_CMD_HEADER_SIZE, ab_data_length);
if (ab_data_length >= bulkout_data.dwLength)
has_cmd = 1;
else if (ab_data_length < bulkout_data.dwLength) {
if (bulkout_data.dwLength > ABDATA_SIZE)
bulkout_state = CCID_STATE_IDLE;
else { // ab_data_length < bulkout_data.dwLength
if (!abData)
bulkout_state = CCID_STATE_DISCARD_DATA;
else
bulkout_state = CCID_STATE_RECEIVE_DATA;
} else
bulkout_state = CCID_STATE_IDLE;
}
}
break;

case CCID_STATE_RECEIVE_DATA:
abData = CCID_IsShortCommand() ? bulkout_data.abDataShort : global_buffer;
if (ab_data_length + len < bulkout_data.dwLength) {
memcpy(bulkout_data.abData + ab_data_length, data, len);
memcpy(abData + ab_data_length, data, len);
ab_data_length += len;
} else if (ab_data_length + len == bulkout_data.dwLength) {
memcpy(bulkout_data.abData + ab_data_length, data, len);
} else {
if (ab_data_length + len > bulkout_data.dwLength)
len = bulkout_data.dwLength - ab_data_length; // abnormal packet received, truncate data
memcpy(abData + ab_data_length, data, len);
bulkout_state = CCID_STATE_IDLE;
has_cmd = 1;
} else
}
break;

case CCID_STATE_DISCARD_DATA:
if (ab_data_length + len < bulkout_data.dwLength) {
ab_data_length += len;
} else {
bulkout_state = CCID_STATE_IDLE;
has_cmd = 1;
}
break;
}
return 0;
}
Expand All @@ -98,6 +126,11 @@ static uint8_t PC_to_RDR_IccPowerOn(void) {
return SLOTERROR_BAD_POWERSELECT;
}

// acquire the global buffer before using bulkin_data.abData
if (acquire_apdu_buffer(BUFFER_OWNER_CCID) != 0) {
CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_NO_ICC_PRESENT);
return SLOTERROR_ICC_MUTE;
}
applets_poweroff();
memcpy(bulkin_data.abData, atr_ccid, sizeof(atr_ccid));
bulkin_data.dwLength = sizeof(atr_ccid);
Expand Down Expand Up @@ -141,6 +174,7 @@ static uint8_t PC_to_RDR_GetSlotStatus(void) {
* @retval uint8_t status of the command execution
*/
uint8_t PC_to_RDR_XfrBlock(void) {
uint8_t *abData = CCID_IsShortCommand() ? bulkout_data.abDataShort : global_buffer;
uint8_t error = CCID_CheckCommandParams(CHK_PARAM_SLOT);
if (error != 0) return error;

Expand All @@ -149,18 +183,18 @@ uint8_t PC_to_RDR_XfrBlock(void) {
return SLOTERROR_ICC_MUTE;
}

// acquire the global buffer before using bulkin_data.abData
if (acquire_apdu_buffer(BUFFER_OWNER_CCID) != 0) {
CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE);
return SLOTERROR_BAD_GUARDTIME;
CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_NO_ICC_PRESENT);
return SLOTERROR_ICC_MUTE;
}

DBG_MSG("O: ");
PRINT_HEX(bulkout_data.abData, bulkout_data.dwLength);
PRINT_HEX(abData, bulkout_data.dwLength);

CAPDU *capdu = &apdu_cmd;
RAPDU *rapdu = &apdu_resp;

if (build_capdu(&apdu_cmd, bulkout_data.abData, bulkout_data.dwLength) < 0) {
if (build_capdu(&apdu_cmd, abData, bulkout_data.dwLength) < 0) {
// abandon malformed apdu
LL = 0;
SW = SW_WRONG_LENGTH;
Expand All @@ -176,7 +210,6 @@ uint8_t PC_to_RDR_XfrBlock(void) {
DBG_MSG("I: ");
PRINT_HEX(bulkin_data.abData, bulkin_data.dwLength);

release_apdu_buffer(BUFFER_OWNER_CCID);
CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, BM_ICC_PRESENT_ACTIVE);

return SLOT_NO_ERROR;
Expand Down Expand Up @@ -240,6 +273,12 @@ static void RDR_to_PC_Parameters(uint8_t errorCode) {
else
bulkin_data.dwLength = 0;

// acquire the global buffer before using bulkin_data.abData
if (acquire_apdu_buffer(BUFFER_OWNER_CCID) != 0) {
CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_NO_ICC_PRESENT);
bulkin_data.bError = SLOTERROR_ICC_MUTE;
return;
}
bulkin_data.abData[0] = 0x11; // Fi=372, Di=1
bulkin_data.abData[1] = 0x10; // Checksum: LRC, Convention: direct, ignored by CCID
bulkin_data.abData[2] = 0x00; // No extra guard time
Expand Down Expand Up @@ -314,6 +353,7 @@ void CCID_Loop(void) {
has_cmd = 0;

uint8_t errorCode;
CCID_UpdateCommandStatus(BM_COMMAND_STATUS_NO_ERROR, card_status);
switch (bulkout_data.bMessageType) {
case PC_TO_RDR_ICCPOWERON:
DBG_MSG("Slot power on\n");
Expand Down Expand Up @@ -350,6 +390,11 @@ void CCID_Loop(void) {
bulkin_data.dwLength = 0;
RDR_to_PC_DataBlock(SLOTERROR_CMD_NOT_SUPPORTED);
break;
case PC_TO_RDR_DISCARDED:
bulkin_data.dwLength = 0;
CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_NO_ICC_PRESENT);
RDR_to_PC_DataBlock(SLOTERROR_ICC_MUTE);
break;
case PC_TO_RDR_ICCCLOCK:
case PC_TO_RDR_T0APDU:
case PC_TO_RDR_MECHANICAL:
Expand All @@ -366,6 +411,15 @@ void CCID_Loop(void) {
device_spinlock_unlock(&send_data_spinlock);
}

void CCID_InFinished(uint8_t is_time_extension_request)
{
if (is_time_extension_request) return;

// Release the buffer after bulkin_data is transmitted
// If the buffer has not been acquired by CCID, ownership is unchanged
release_apdu_buffer(BUFFER_OWNER_CCID);
}

void CCID_TimeExtensionLoop(void) {
if (device_spinlock_lock(&send_data_spinlock, false) == 0) { // try lock
DBG_MSG("send t-ext\r\n");
Expand All @@ -388,5 +442,5 @@ void CCID_eject(void) {
}

void CCID_insert(void) {
card_status = BM_ICC_PRESENT_ACTIVE;
card_status = BM_ICC_PRESENT_INACTIVE;
}
6 changes: 5 additions & 1 deletion interfaces/USB/class/ccid/ccid.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <common.h>

#define ABDATA_SIZE (APDU_BUFFER_SIZE + 2)
#define SHORT_ABDATA_SIZE 8 /* Enough for most CCID messages except XfrBlock/Secure */
#define CCID_CMD_HEADER_SIZE 10
#define CCID_NUMBER_OF_SLOTS 1
#define TIME_EXTENSION_PERIOD 1500
Expand All @@ -18,7 +19,7 @@ typedef struct {
uint8_t bSpecific_0; /* Offset = 7*/
uint8_t bSpecific_1; /* Offset = 8*/
uint8_t bSpecific_2; /* Offset = 9*/
uint8_t *abData; /* Offset = 10*/
uint8_t abDataShort[SHORT_ABDATA_SIZE]; /* Offset = 10*/
} __packed ccid_bulkout_data_t;

typedef struct {
Expand Down Expand Up @@ -121,6 +122,8 @@ typedef enum {
#define PC_TO_RDR_ABORT 0x72
#define PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73

#define PC_TO_RDR_DISCARDED 0xDD /* For internal use only */

#define RDR_TO_PC_DATABLOCK 0x80
#define RDR_TO_PC_SLOTSTATUS 0x81
#define RDR_TO_PC_PARAMETERS 0x82
Expand All @@ -129,6 +132,7 @@ typedef enum {

uint8_t CCID_Init(void);
uint8_t CCID_OutEvent(uint8_t *data, uint8_t len);
void CCID_InFinished(uint8_t is_time_extension_request);
void CCID_Loop(void);
void CCID_TimeExtensionLoop(void);
uint8_t PC_to_RDR_XfrBlock(void); // Exported for test purposes
Expand Down
6 changes: 5 additions & 1 deletion interfaces/USB/class/ccid/usbd_ccid.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ uint8_t USBD_CCID_DataIn(USBD_HandleTypeDef *pdev) {
uint8_t addr = EP_OUT(ccid);
USBD_LL_Transmit(pdev, addr, NULL, 0);
} else {
CCID_InFinished(bulk_in_state == CCID_STATE_DATA_IN_TIME_EXTENSION);
bulk_in_state = CCID_STATE_IDLE;
}
return USBD_OK;
Expand Down Expand Up @@ -55,7 +56,10 @@ uint8_t CCID_Response_SendData(USBD_HandleTypeDef *pdev, const uint8_t *buf, uin
}
uint8_t addr = EP_OUT(ccid);
uint8_t ep_size = EP_SIZE(ccid);
bulk_in_state = len % ep_size == 0 ? CCID_STATE_DATA_IN_WITH_ZLP : CCID_STATE_DATA_IN;
if (is_time_extension_request)
bulk_in_state = CCID_STATE_DATA_IN_TIME_EXTENSION;
else
bulk_in_state = len % ep_size == 0 ? CCID_STATE_DATA_IN_WITH_ZLP : CCID_STATE_DATA_IN;
ret = USBD_LL_Transmit(pdev, addr, buf, len);
}
return ret;
Expand Down
2 changes: 2 additions & 0 deletions interfaces/USB/class/ccid/usbd_ccid.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#define CCID_STATE_DATA_IN 2
#define CCID_STATE_DATA_IN_WITH_ZLP 3
#define CCID_STATE_PROCESS_DATA 4
#define CCID_STATE_DISCARD_DATA 5
#define CCID_STATE_DATA_IN_TIME_EXTENSION 6

uint8_t USBD_CCID_Init(USBD_HandleTypeDef *pdev);
uint8_t USBD_CCID_DataIn(USBD_HandleTypeDef *pdev);
Expand Down
6 changes: 4 additions & 2 deletions interfaces/USB/class/webusb/webusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,15 @@ void WebUSB_Loop(void) {
DBG_MSG("R: ");
PRINT_HEX(global_buffer, apdu_buffer_size);
state = STATE_SENDING_RESP;
release_apdu_buffer(BUFFER_OWNER_WEBUSB);
}

uint8_t USBD_WEBUSB_TxSent(USBD_HandleTypeDef *pdev) {
UNUSED(pdev);

if (state == STATE_SENT_RESP) state = STATE_IDLE;
if (state == STATE_SENT_RESP) {
release_apdu_buffer(BUFFER_OWNER_WEBUSB);
state = STATE_IDLE;
}

return USBD_OK;
}
Expand Down

0 comments on commit 97a7ea5

Please sign in to comment.