Fix onscreen PIN
Add onscreen PIN modification Fix default PIN mode management
This commit is contained in:
parent
8430858dfb
commit
fc0e37d5f0
@ -10,7 +10,10 @@ The application supports:
|
|||||||
- EDDSA with Ed25519 curve
|
- EDDSA with Ed25519 curve
|
||||||
- ECDH with secp256k1, secp256r1, brainpool 256r1, brainpool 256t1 and curve25519 curves
|
- ECDH with secp256k1, secp256r1, brainpool 256r1, brainpool 256t1 and curve25519 curves
|
||||||
|
|
||||||
To compile it, use at least the Nano S SDK 1.3.1.4 on firmware 1.3.1
|
|
||||||
|
To compile it
|
||||||
|
- use at least the Nano S SDK 1.3.1.4 on firmware 1.3.1
|
||||||
|
- replace lib_stusb/STM32_USB_Device_Library/Class/CCID/src/usbd_ccid_if.c by the one provided in sdk/ directory
|
||||||
|
|
||||||
This release is in beta stage with known missing parts (see also Add-on) :
|
This release is in beta stage with known missing parts (see also Add-on) :
|
||||||
|
|
||||||
|
587
sdk/usbd_ccid_if.c
Normal file
587
sdk/usbd_ccid_if.c
Normal file
@ -0,0 +1,587 @@
|
|||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* @file usbd_ccid_if.c
|
||||||
|
* @author MCD Application Team
|
||||||
|
* @version V1.0.1
|
||||||
|
* @date 31-January-2014
|
||||||
|
* @brief This file provides all the functions for USB Interface for CCID
|
||||||
|
******************************************************************************
|
||||||
|
* @attention
|
||||||
|
*
|
||||||
|
* <h2><center>© COPYRIGHT 2014 STMicroelectronics</center></h2>
|
||||||
|
*
|
||||||
|
* Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.st.com/software_license_agreement_liberty_v2
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Includes ------------------------------------------------------------------*/
|
||||||
|
#include "usbd_ccid_if.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_USB_CLASS_CCID
|
||||||
|
|
||||||
|
/* Private typedef -----------------------------------------------------------*/
|
||||||
|
/* Private define ------------------------------------------------------------*/
|
||||||
|
/* Private macro -------------------------------------------------------------*/
|
||||||
|
/* Private variables ---------------------------------------------------------*/
|
||||||
|
uint8_t Ccid_BulkState;
|
||||||
|
uint8_t UsbIntMessageBuffer[INTR_MAX_PACKET_SIZE]; /* data buffer*/
|
||||||
|
__IO uint8_t PrevXferComplete_IntrIn;
|
||||||
|
usb_ccid_param_t usb_ccid_param;
|
||||||
|
|
||||||
|
uint8_t* pUsbMessageBuffer;
|
||||||
|
static uint32_t UsbMessageLength;
|
||||||
|
Ccid_SlotStatus_t Ccid_SlotStatus;
|
||||||
|
Protocol0_DataStructure_t Protocol0_DataStructure;
|
||||||
|
|
||||||
|
Ccid_bulk_data_t Ccid_bulk_data;
|
||||||
|
|
||||||
|
/* Private function prototypes -----------------------------------------------*/
|
||||||
|
static void CCID_Response_SendData (USBD_HandleTypeDef *pdev,
|
||||||
|
uint8_t* pbuf,
|
||||||
|
uint16_t len);
|
||||||
|
/* Private function ----------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* @brief CCID_Init
|
||||||
|
* Initialize the CCID USB Layer
|
||||||
|
* @param pdev: device instance
|
||||||
|
* @retval None
|
||||||
|
*/
|
||||||
|
void CCID_Init (USBD_HandleTypeDef *pdev)
|
||||||
|
{
|
||||||
|
memset(&Ccid_BulkState, 0, sizeof(Ccid_BulkState));
|
||||||
|
memset(&UsbIntMessageBuffer, 0, sizeof(UsbIntMessageBuffer));
|
||||||
|
memset(&PrevXferComplete_IntrIn, 0, sizeof(PrevXferComplete_IntrIn));
|
||||||
|
memset(&usb_ccid_param, 0, sizeof(usb_ccid_param));
|
||||||
|
memset(&pUsbMessageBuffer, 0, sizeof(pUsbMessageBuffer));
|
||||||
|
memset(&UsbMessageLength, 0, sizeof(UsbMessageLength));
|
||||||
|
memset(&Ccid_SlotStatus, 0, sizeof(Ccid_SlotStatus));
|
||||||
|
memset(&Protocol0_DataStructure, 0, sizeof(Protocol0_DataStructure));
|
||||||
|
memset(&Ccid_bulk_data, 0, sizeof(Ccid_bulk_data));
|
||||||
|
|
||||||
|
/* CCID Related Initialization */
|
||||||
|
CCID_SetIntrTransferStatus(1); /* Transfer Complete Status */
|
||||||
|
CCID_UpdSlotChange(1);
|
||||||
|
SC_InitParams();
|
||||||
|
|
||||||
|
/* Prepare Out endpoint to receive 1st packet */
|
||||||
|
Ccid_BulkState = CCID_STATE_IDLE;
|
||||||
|
USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CCID_DeInit
|
||||||
|
* Uninitialize the CCID Machine
|
||||||
|
* @param pdev: device instance
|
||||||
|
* @retval None
|
||||||
|
*/
|
||||||
|
void CCID_DeInit (USBD_HandleTypeDef *pdev)
|
||||||
|
{
|
||||||
|
UNUSED(pdev);
|
||||||
|
Ccid_BulkState = CCID_STATE_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CCID_Message_In
|
||||||
|
* Handle Bulk IN & Intr IN data stage
|
||||||
|
* @param pdev: device instance
|
||||||
|
* @param uint8_t epnum: endpoint index
|
||||||
|
* @retval None
|
||||||
|
*/
|
||||||
|
void CCID_BulkMessage_In (USBD_HandleTypeDef *pdev,
|
||||||
|
uint8_t epnum)
|
||||||
|
{
|
||||||
|
if (epnum == (CCID_BULK_IN_EP & 0x7F))
|
||||||
|
{/* Filter the epnum by masking with 0x7f (mask of IN Direction) */
|
||||||
|
|
||||||
|
/*************** Handle Bulk Transfer IN data completion *****************/
|
||||||
|
|
||||||
|
switch (Ccid_BulkState)
|
||||||
|
{
|
||||||
|
case CCID_STATE_SEND_RESP: {
|
||||||
|
unsigned int remLen = UsbMessageLength;
|
||||||
|
|
||||||
|
// advance with acknowledged sent chunk
|
||||||
|
pUsbMessageBuffer += MIN(CCID_BULK_EPIN_SIZE, UsbMessageLength);
|
||||||
|
UsbMessageLength -= MIN(CCID_BULK_EPIN_SIZE, UsbMessageLength);
|
||||||
|
|
||||||
|
// if remaining length is > EPIN_SIZE: send a filled bulk packet
|
||||||
|
if (UsbMessageLength >= CCID_BULK_EPIN_SIZE) {
|
||||||
|
CCID_Response_SendData(pdev, pUsbMessageBuffer,
|
||||||
|
// use the header declared size packet must be well formed
|
||||||
|
CCID_BULK_EPIN_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if remaining length is 0; send an empty packet and prepare to receive a new command
|
||||||
|
else if (UsbMessageLength == 0 && remLen == CCID_BULK_EPIN_SIZE) {
|
||||||
|
CCID_Response_SendData(pdev, pUsbMessageBuffer,
|
||||||
|
// use the header declared size packet must be well formed
|
||||||
|
0);
|
||||||
|
goto last_xfer; // won't wait ack to avoid missing a command
|
||||||
|
}
|
||||||
|
// else if no more data, then last packet sent, go back to idle (done on transfer ack)
|
||||||
|
else if (UsbMessageLength == 0) { // robustness only
|
||||||
|
last_xfer:
|
||||||
|
Ccid_BulkState = CCID_STATE_IDLE;
|
||||||
|
|
||||||
|
/* Prepare EP to Receive First Cmd */
|
||||||
|
USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if remaining length is < EPIN_SIZE: send packet and prepare to receive a new command
|
||||||
|
else if (UsbMessageLength < CCID_BULK_EPIN_SIZE) {
|
||||||
|
CCID_Response_SendData(pdev, pUsbMessageBuffer,
|
||||||
|
// use the header declared size packet must be well formed
|
||||||
|
UsbMessageLength);
|
||||||
|
goto last_xfer; // won't wait ack to avoid missing a command
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (epnum == (CCID_INTR_IN_EP & 0x7F))
|
||||||
|
{
|
||||||
|
/* Filter the epnum by masking with 0x7f (mask of IN Direction) */
|
||||||
|
CCID_SetIntrTransferStatus(1); /* Transfer Complete Status */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CCID_BulkMessage_Out
|
||||||
|
* Proccess CCID OUT data
|
||||||
|
* @param pdev: device instance
|
||||||
|
* @param uint8_t epnum: endpoint index
|
||||||
|
* @retval None
|
||||||
|
*/
|
||||||
|
void CCID_BulkMessage_Out (USBD_HandleTypeDef *pdev,
|
||||||
|
uint8_t epnum, uint8_t* buffer, uint16_t dataLen)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (Ccid_BulkState)
|
||||||
|
{
|
||||||
|
case CCID_STATE_IDLE:
|
||||||
|
if (dataLen == 0x00)
|
||||||
|
{ /* Zero Length Packet Received */
|
||||||
|
Ccid_BulkState = CCID_STATE_IDLE;
|
||||||
|
}
|
||||||
|
else if (dataLen >= CCID_MESSAGE_HEADER_SIZE)
|
||||||
|
{
|
||||||
|
UsbMessageLength = dataLen; /* Store for future use */
|
||||||
|
|
||||||
|
/* Expected Data Length Packet Received */
|
||||||
|
pUsbMessageBuffer = (uint8_t*) &Ccid_bulk_data;
|
||||||
|
|
||||||
|
/* Fill CCID_BulkOut Data Buffer from USB Buffer */
|
||||||
|
memmove(pUsbMessageBuffer, buffer, dataLen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Refer : 6 CCID Messages
|
||||||
|
The response messages always contain the exact same slot number,
|
||||||
|
and sequence number fields from the header that was contained in
|
||||||
|
the Bulk-OUT command message.
|
||||||
|
*/
|
||||||
|
Ccid_bulk_data.header.bulkin.bSlot = Ccid_bulk_data.header.bulkout.bSlot;
|
||||||
|
Ccid_bulk_data.header.bulkin.bSeq = Ccid_bulk_data.header.bulkout.bSeq;
|
||||||
|
|
||||||
|
if (dataLen < CCID_BULK_EPOUT_SIZE)
|
||||||
|
{/* Short message, less than the EP Out Size, execute the command,
|
||||||
|
if parameter like dwLength is too big, the appropriate command will
|
||||||
|
give an error */
|
||||||
|
CCID_CmdDecode(pdev);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* Long message, receive additional data with command */
|
||||||
|
/* (u8dataLen == CCID_BULK_EPOUT_SIZE) */
|
||||||
|
|
||||||
|
if (Ccid_bulk_data.header.bulkout.dwLength > ABDATA_SIZE)
|
||||||
|
{ /* Check if length of data to be sent by host is > buffer size */
|
||||||
|
|
||||||
|
/* Too long data received.... Error ! */
|
||||||
|
Ccid_BulkState = CCID_STATE_UNCORRECT_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{ /* Expect more data on OUT EP */
|
||||||
|
Ccid_BulkState = CCID_STATE_RECEIVE_DATA;
|
||||||
|
pUsbMessageBuffer += dataLen; /* Point to new offset */
|
||||||
|
|
||||||
|
/* Prepare EP to Receive next Cmd */
|
||||||
|
USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE);
|
||||||
|
|
||||||
|
} /* if (dataLen == CCID_BULK_EPOUT_SIZE) ends */
|
||||||
|
} /* if (dataLen >= CCID_BULK_EPOUT_SIZE) ends */
|
||||||
|
} /* if (dataLen >= CCID_MESSAGE_HEADER_SIZE) ends */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CCID_STATE_RECEIVE_DATA:
|
||||||
|
|
||||||
|
UsbMessageLength += dataLen;
|
||||||
|
|
||||||
|
if (dataLen < CCID_BULK_EPOUT_SIZE)
|
||||||
|
{/* Short message, less than the EP Out Size, execute the command,
|
||||||
|
if parameter like dwLength is too big, the appropriate command will
|
||||||
|
give an error */
|
||||||
|
|
||||||
|
/* Full command is received, process the Command */
|
||||||
|
memmove(pUsbMessageBuffer, buffer, dataLen);
|
||||||
|
CCID_CmdDecode(pdev);
|
||||||
|
}
|
||||||
|
else if (dataLen == CCID_BULK_EPOUT_SIZE)
|
||||||
|
{
|
||||||
|
if (UsbMessageLength < (Ccid_bulk_data.header.bulkout.dwLength + CCID_CMD_HEADER_SIZE))
|
||||||
|
{
|
||||||
|
memmove(pUsbMessageBuffer, buffer, dataLen);
|
||||||
|
pUsbMessageBuffer += dataLen;
|
||||||
|
/* Increment the pointer to receive more data */
|
||||||
|
|
||||||
|
/* Prepare EP to Receive next Cmd */
|
||||||
|
USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE);
|
||||||
|
}
|
||||||
|
else if (UsbMessageLength == (Ccid_bulk_data.header.bulkout.dwLength + CCID_CMD_HEADER_SIZE))
|
||||||
|
{
|
||||||
|
/* Full command is received, process the Command */
|
||||||
|
memmove(pUsbMessageBuffer, buffer, dataLen);
|
||||||
|
CCID_CmdDecode(pdev);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Too long data received.... Error ! */
|
||||||
|
Ccid_BulkState = CCID_STATE_UNCORRECT_LENGTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CCID_STATE_UNCORRECT_LENGTH:
|
||||||
|
Ccid_BulkState = CCID_STATE_IDLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCID_Send_Reply(USBD_HandleTypeDef *pdev) {
|
||||||
|
/********** Decide for all commands ***************/
|
||||||
|
if (Ccid_BulkState == CCID_STATE_SEND_RESP)
|
||||||
|
{
|
||||||
|
UsbMessageLength = Ccid_bulk_data.header.bulkin.dwLength+CCID_MESSAGE_HEADER_SIZE; /* Store for future use */
|
||||||
|
|
||||||
|
/* Expected Data Length Packet Received */
|
||||||
|
pUsbMessageBuffer = (uint8_t*) &Ccid_bulk_data;
|
||||||
|
|
||||||
|
CCID_Response_SendData(pdev, pUsbMessageBuffer,
|
||||||
|
// use the header declared size packet must be well formed
|
||||||
|
MIN(CCID_BULK_EPIN_SIZE, UsbMessageLength));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CCID_CmdDecode
|
||||||
|
* Parse the commands and Proccess command
|
||||||
|
* @param pdev: device instance
|
||||||
|
* @retval None
|
||||||
|
*/
|
||||||
|
void CCID_CmdDecode(USBD_HandleTypeDef *pdev)
|
||||||
|
{
|
||||||
|
uint8_t errorCode;
|
||||||
|
|
||||||
|
switch (Ccid_bulk_data.header.bulkout.bMessageType)
|
||||||
|
{
|
||||||
|
case PC_TO_RDR_ICCPOWERON:
|
||||||
|
errorCode = PC_to_RDR_IccPowerOn();
|
||||||
|
RDR_to_PC_DataBlock(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_ICCPOWEROFF:
|
||||||
|
errorCode = PC_to_RDR_IccPowerOff();
|
||||||
|
RDR_to_PC_SlotStatus(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_GETSLOTSTATUS:
|
||||||
|
errorCode = PC_to_RDR_GetSlotStatus();
|
||||||
|
RDR_to_PC_SlotStatus(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_XFRBLOCK:
|
||||||
|
errorCode = PC_to_RDR_XfrBlock();
|
||||||
|
// asynchronous // RDR_to_PC_DataBlock(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_GETPARAMETERS:
|
||||||
|
errorCode = PC_to_RDR_GetParameters();
|
||||||
|
RDR_to_PC_Parameters(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_RESETPARAMETERS:
|
||||||
|
errorCode = PC_to_RDR_ResetParameters();
|
||||||
|
RDR_to_PC_Parameters(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_SETPARAMETERS:
|
||||||
|
errorCode = PC_to_RDR_SetParameters();
|
||||||
|
RDR_to_PC_Parameters(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_ESCAPE:
|
||||||
|
errorCode = PC_to_RDR_Escape();
|
||||||
|
RDR_to_PC_Escape(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_ICCCLOCK:
|
||||||
|
errorCode = PC_to_RDR_IccClock();
|
||||||
|
RDR_to_PC_SlotStatus(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_ABORT:
|
||||||
|
errorCode = PC_to_RDR_Abort();
|
||||||
|
RDR_to_PC_SlotStatus(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_T0APDU:
|
||||||
|
errorCode = PC_TO_RDR_T0Apdu();
|
||||||
|
RDR_to_PC_SlotStatus(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_MECHANICAL:
|
||||||
|
errorCode = PC_TO_RDR_Mechanical();
|
||||||
|
RDR_to_PC_SlotStatus(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY:
|
||||||
|
errorCode = PC_TO_RDR_SetDataRateAndClockFrequency();
|
||||||
|
RDR_to_PC_DataRateAndClockFrequency(errorCode);
|
||||||
|
break;
|
||||||
|
case PC_TO_RDR_SECURE:
|
||||||
|
errorCode = PC_TO_RDR_Secure();
|
||||||
|
// asynchronous // RDR_to_PC_DataBlock(errorCode);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
RDR_to_PC_SlotStatus(SLOTERROR_CMD_NOT_SUPPORTED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CCID_Send_Reply(pdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transfer_Data_Request
|
||||||
|
* Prepare the request response to be sent to the host
|
||||||
|
* @param uint8_t* dataPointer: Pointer to the data buffer to send
|
||||||
|
* @param uint16_t dataLen : number of bytes to send
|
||||||
|
* @retval None
|
||||||
|
*/
|
||||||
|
void Transfer_Data_Request(void)
|
||||||
|
{
|
||||||
|
/********** Update Global Variables ***************/
|
||||||
|
Ccid_BulkState = CCID_STATE_SEND_RESP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CCID_Response_SendData
|
||||||
|
* Send the data on bulk-in EP
|
||||||
|
* @param pdev: device instance
|
||||||
|
* @param uint8_t* buf: pointer to data buffer
|
||||||
|
* @param uint16_t len: Data Length
|
||||||
|
* @retval None
|
||||||
|
*/
|
||||||
|
static void CCID_Response_SendData(USBD_HandleTypeDef *pdev,
|
||||||
|
uint8_t* buf,
|
||||||
|
uint16_t len)
|
||||||
|
{
|
||||||
|
// don't ask the MCU to perform bulk split, we could quickly get into a buffer overflow
|
||||||
|
if (len > CCID_BULK_EPIN_SIZE) {
|
||||||
|
THROW(EXCEPTION_IO_OVERFLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_io_seproxyhal_spi_buffer[0] = SEPROXYHAL_TAG_USB_EP_PREPARE;
|
||||||
|
G_io_seproxyhal_spi_buffer[1] = (3+len)>>8;
|
||||||
|
G_io_seproxyhal_spi_buffer[2] = (3+len);
|
||||||
|
G_io_seproxyhal_spi_buffer[3] = CCID_BULK_IN_EP;
|
||||||
|
G_io_seproxyhal_spi_buffer[4] = SEPROXYHAL_TAG_USB_EP_PREPARE_DIR_IN;
|
||||||
|
G_io_seproxyhal_spi_buffer[5] = len;
|
||||||
|
io_seproxyhal_spi_send(G_io_seproxyhal_spi_buffer, 6);
|
||||||
|
io_seproxyhal_spi_send(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CCID_IntMessage
|
||||||
|
* Send the Interrupt-IN data to the host
|
||||||
|
* @param pdev: device instance
|
||||||
|
* @retval None
|
||||||
|
*/
|
||||||
|
void CCID_IntMessage(USBD_HandleTypeDef *pdev)
|
||||||
|
{
|
||||||
|
/* Check if there us change in Smartcard Slot status */
|
||||||
|
if ( CCID_IsSlotStatusChange() && CCID_IsIntrTransferComplete() )
|
||||||
|
{
|
||||||
|
/* Check Slot Status is changed. Card is Removed/ Fitted */
|
||||||
|
RDR_to_PC_NotifySlotChange();
|
||||||
|
|
||||||
|
CCID_SetIntrTransferStatus(0); /* Reset the Status */
|
||||||
|
CCID_UpdSlotChange(0); /* Reset the Status of Slot Change */
|
||||||
|
|
||||||
|
G_io_seproxyhal_spi_buffer[0] = SEPROXYHAL_TAG_USB_EP_PREPARE;
|
||||||
|
G_io_seproxyhal_spi_buffer[1] = (3+2)>>8;
|
||||||
|
G_io_seproxyhal_spi_buffer[2] = (3+2);
|
||||||
|
G_io_seproxyhal_spi_buffer[3] = CCID_INTR_IN_EP;
|
||||||
|
G_io_seproxyhal_spi_buffer[4] = SEPROXYHAL_TAG_USB_EP_PREPARE_DIR_IN;
|
||||||
|
G_io_seproxyhal_spi_buffer[5] = 2;
|
||||||
|
io_seproxyhal_spi_send(G_io_seproxyhal_spi_buffer, 6);
|
||||||
|
io_seproxyhal_spi_send(UsbIntMessageBuffer, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CCID_IsIntrTransferComplete
|
||||||
|
* Provides the status of previous Interrupt transfer status
|
||||||
|
* @param None
|
||||||
|
* @retval uint8_t PrevXferComplete_IntrIn: Value of the previous transfer status
|
||||||
|
*/
|
||||||
|
uint8_t CCID_IsIntrTransferComplete (void)
|
||||||
|
{
|
||||||
|
return PrevXferComplete_IntrIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CCID_IsIntrTransferComplete
|
||||||
|
* Set the value of the Interrupt transfer status
|
||||||
|
* @param uint8_t xfer_Status: Value of the Interrupt transfer status to set
|
||||||
|
* @retval None
|
||||||
|
*/
|
||||||
|
void CCID_SetIntrTransferStatus (uint8_t xfer_Status)
|
||||||
|
{
|
||||||
|
PrevXferComplete_IntrIn = xfer_Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t SC_Detect(void) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SC_Poweroff(void) {
|
||||||
|
// nothing to do
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SC_InitParams (void) {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SC_SetParams (Protocol0_DataStructure_t* pt0) {
|
||||||
|
return SLOT_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SC_ExecuteEscape (uint8_t* escapePtr, uint32_t escapeLen,
|
||||||
|
uint8_t* responseBuff,
|
||||||
|
uint16_t* responseLen) {
|
||||||
|
io_seproxyhal_se_reset();
|
||||||
|
}
|
||||||
|
uint8_t SC_SetClock (uint8_t bClockCommand) {
|
||||||
|
return SLOT_NO_ERROR;
|
||||||
|
}
|
||||||
|
uint8_t SC_Request_GetClockFrequencies(uint8_t* pbuf, uint16_t* len);
|
||||||
|
uint8_t SC_Request_GetDataRates(uint8_t* pbuf, uint16_t* len);
|
||||||
|
uint8_t SC_T0Apdu(uint8_t bmChanges, uint8_t bClassGetResponse,
|
||||||
|
uint8_t bClassEnvelope) {
|
||||||
|
return SLOTERROR_CMD_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
uint8_t SC_Mechanical(uint8_t bFunction) {
|
||||||
|
return SLOTERROR_CMD_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
uint8_t SC_SetDataRateAndClockFrequency(uint32_t dwClockFrequency,
|
||||||
|
uint32_t dwDataRate) {
|
||||||
|
return SLOT_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SC_Secure(uint32_t dwLength, uint8_t bBWI, uint16_t wLevelParameter,
|
||||||
|
uint8_t* pbuf, uint32_t* returnLen ) {
|
||||||
|
// return SLOTERROR_CMD_NOT_SUPPORTED;
|
||||||
|
uint16_t ret_len,off;
|
||||||
|
switch(pbuf[0]) {
|
||||||
|
case 0: // verify pin
|
||||||
|
//ret_len = dwLength - 15;
|
||||||
|
ret_len = 5;
|
||||||
|
os_memmove(G_io_apdu_buffer, pbuf+15, 5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // modify pin
|
||||||
|
switch(pbuf[11]) {
|
||||||
|
case 3:
|
||||||
|
off = 20;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 1:
|
||||||
|
off = 19;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
off = 18;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//ret_len = dwLength - off;
|
||||||
|
ret_len = 5;
|
||||||
|
os_memmove(G_io_apdu_buffer, pbuf+off, 5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // unsupported
|
||||||
|
Ccid_bulk_data.header.bulkin.dwLength = 0;
|
||||||
|
RDR_to_PC_DataBlock(SLOTERROR_CMD_NOT_SUPPORTED);
|
||||||
|
CCID_Send_Reply(&USBD_Device);
|
||||||
|
return SLOTERROR_CMD_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
return SC_XferBlock(G_io_apdu_buffer, ret_len, &ret_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare the apdu to be processed by the application
|
||||||
|
uint8_t SC_XferBlock (uint8_t* ptrBlock, uint32_t blockLen, uint16_t* expectedLen) {
|
||||||
|
// check for overflow
|
||||||
|
if (blockLen > IO_APDU_BUFFER_SIZE) {
|
||||||
|
return SLOTERROR_BAD_LENTGH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy received apdu
|
||||||
|
memmove(G_io_apdu_buffer, ptrBlock, blockLen);
|
||||||
|
G_io_apdu_length = blockLen;
|
||||||
|
G_io_apdu_media = IO_APDU_MEDIA_USB_CCID; // for application code
|
||||||
|
G_io_apdu_state = APDU_USB_CCID; // for next call to io_exchange
|
||||||
|
|
||||||
|
return SLOT_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void io_usb_ccid_reply(unsigned char* buffer, unsigned short length) {
|
||||||
|
// avoid memory overflow
|
||||||
|
if (length > sizeof(Ccid_bulk_data.abData)) {
|
||||||
|
THROW(EXCEPTION_IO_OVERFLOW);
|
||||||
|
}
|
||||||
|
// copy the responde apdu
|
||||||
|
memmove(Ccid_bulk_data.abData, buffer, length);
|
||||||
|
Ccid_bulk_data.header.bulkin.dwLength = length;
|
||||||
|
// forge reply
|
||||||
|
RDR_to_PC_DataBlock(SLOT_NO_ERROR);
|
||||||
|
|
||||||
|
// start sending rpely
|
||||||
|
CCID_Send_Reply(&USBD_Device);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask for power on
|
||||||
|
void io_usb_ccid_poweron(void) {
|
||||||
|
CCID_UpdSlotChange(1);
|
||||||
|
CCID_IntMessage(&USBD_Device);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // HAVE_USB_CLASS_CCID
|
||||||
|
|
||||||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
@ -210,6 +210,15 @@ int gpg_apdu_change_ref_data(int id) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
//avoid any-overflow whitout giving info
|
//avoid any-overflow whitout giving info
|
||||||
|
if (G_gpg_vstate.io_length == 0) {
|
||||||
|
if (G_gpg_vstate.pinmode != PIN_MODE_HOST) {
|
||||||
|
//Delegate pin change to ui
|
||||||
|
gpg_io_discard(1);
|
||||||
|
ui_menu_pinentry_display(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pin->length > G_gpg_vstate.io_length) {
|
if (pin->length > G_gpg_vstate.io_length) {
|
||||||
len = G_gpg_vstate.io_length;
|
len = G_gpg_vstate.io_length;
|
||||||
} else {
|
} else {
|
||||||
|
@ -237,7 +237,24 @@ void ui_menu_pinentry_display(unsigned int value) {
|
|||||||
|
|
||||||
unsigned int ui_pinentry_prepro(const bagl_element_t* element) {
|
unsigned int ui_pinentry_prepro(const bagl_element_t* element) {
|
||||||
if (element->component.userid == 1) {
|
if (element->component.userid == 1) {
|
||||||
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "%s PIN", (G_gpg_vstate.io_p2 == 0x83)?"Admin":"User");
|
if (G_gpg_vstate.io_ins == 0x24) {
|
||||||
|
switch (G_gpg_vstate.io_p1) {
|
||||||
|
case 0:
|
||||||
|
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Current %s PIN", (G_gpg_vstate.io_p2 == 0x83)?"Admin":"User");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "New %s PIN", (G_gpg_vstate.io_p2 == 0x83)?"Admin":"User");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "Confirm %s PIN", (G_gpg_vstate.io_p2 == 0x83)?"Admin":"User");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "WAT %s PIN", (G_gpg_vstate.io_p2 == 0x83)?"Admin":"User");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "%s PIN", (G_gpg_vstate.io_p2 == 0x83)?"Admin":"User");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (element->component.userid == 2) {
|
else if (element->component.userid == 2) {
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@ -256,9 +273,11 @@ unsigned int ui_pinentry_prepro(const bagl_element_t* element) {
|
|||||||
|
|
||||||
unsigned int ui_pinentry_nanos_button(unsigned int button_mask, unsigned int button_mask_counter) {
|
unsigned int ui_pinentry_nanos_button(unsigned int button_mask, unsigned int button_mask_counter) {
|
||||||
unsigned int offset = G_gpg_vstate.ux_pinentry[0];
|
unsigned int offset = G_gpg_vstate.ux_pinentry[0];
|
||||||
|
unsigned m_pinentry;
|
||||||
char digit ;
|
char digit ;
|
||||||
|
|
||||||
|
m_pinentry = 1;
|
||||||
|
|
||||||
switch(button_mask) {
|
switch(button_mask) {
|
||||||
case BUTTON_EVT_RELEASED|BUTTON_LEFT: // Down
|
case BUTTON_EVT_RELEASED|BUTTON_LEFT: // Down
|
||||||
if (G_gpg_vstate.ux_pinentry[offset]) {
|
if (G_gpg_vstate.ux_pinentry[offset]) {
|
||||||
@ -266,6 +285,7 @@ unsigned int ui_pinentry_nanos_button(unsigned int button_mask, unsigned int but
|
|||||||
} else {
|
} else {
|
||||||
G_gpg_vstate.ux_pinentry[offset] = sizeof(C_pin_digit)-1;
|
G_gpg_vstate.ux_pinentry[offset] = sizeof(C_pin_digit)-1;
|
||||||
}
|
}
|
||||||
|
ui_menu_pinentry_display(1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BUTTON_EVT_RELEASED|BUTTON_RIGHT: //up
|
case BUTTON_EVT_RELEASED|BUTTON_RIGHT: //up
|
||||||
@ -273,6 +293,7 @@ unsigned int ui_pinentry_nanos_button(unsigned int button_mask, unsigned int but
|
|||||||
if (G_gpg_vstate.ux_pinentry[offset] == sizeof(C_pin_digit)) {
|
if (G_gpg_vstate.ux_pinentry[offset] == sizeof(C_pin_digit)) {
|
||||||
G_gpg_vstate.ux_pinentry[offset] = 0;
|
G_gpg_vstate.ux_pinentry[offset] = 0;
|
||||||
}
|
}
|
||||||
|
ui_menu_pinentry_display(1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BUTTON_EVT_RELEASED|BUTTON_LEFT|BUTTON_RIGHT:
|
case BUTTON_EVT_RELEASED|BUTTON_LEFT|BUTTON_RIGHT:
|
||||||
@ -282,10 +303,11 @@ unsigned int ui_pinentry_nanos_button(unsigned int button_mask, unsigned int but
|
|||||||
offset ++;
|
offset ++;
|
||||||
G_gpg_vstate.ux_pinentry[0] = offset;
|
G_gpg_vstate.ux_pinentry[0] = offset;
|
||||||
if (offset == GPG_MAX_PW_LENGTH+1) {
|
if (offset == GPG_MAX_PW_LENGTH+1) {
|
||||||
return validate_pin() ;
|
validate_pin();
|
||||||
|
} else {
|
||||||
|
G_gpg_vstate.ux_pinentry[offset] = 5;
|
||||||
|
ui_menu_pinentry_display(1);
|
||||||
}
|
}
|
||||||
G_gpg_vstate.ux_pinentry[offset] = 5;
|
|
||||||
}
|
}
|
||||||
//cancel digit
|
//cancel digit
|
||||||
else if (digit == 'C') {
|
else if (digit == 'C') {
|
||||||
@ -293,11 +315,12 @@ unsigned int ui_pinentry_nanos_button(unsigned int button_mask, unsigned int but
|
|||||||
offset--;
|
offset--;
|
||||||
G_gpg_vstate.ux_pinentry[0] = offset;
|
G_gpg_vstate.ux_pinentry[0] = offset;
|
||||||
}
|
}
|
||||||
|
ui_menu_pinentry_display(1);
|
||||||
}
|
}
|
||||||
//validate pin
|
//validate pin
|
||||||
else if (digit == 'V') {
|
else if (digit == 'V') {
|
||||||
G_gpg_vstate.ux_pinentry[0] = offset-1;
|
G_gpg_vstate.ux_pinentry[0] = offset-1;
|
||||||
return validate_pin() ;
|
validate_pin();
|
||||||
}
|
}
|
||||||
//cancel input without check
|
//cancel input without check
|
||||||
else { //(digit == 'A')
|
else { //(digit == 'A')
|
||||||
@ -305,13 +328,11 @@ unsigned int ui_pinentry_nanos_button(unsigned int button_mask, unsigned int but
|
|||||||
gpg_io_insert_u16(SW_CONDITIONS_NOT_SATISFIED);
|
gpg_io_insert_u16(SW_CONDITIONS_NOT_SATISFIED);
|
||||||
gpg_io_do(IO_RETURN_AFTER_TX);
|
gpg_io_do(IO_RETURN_AFTER_TX);
|
||||||
ui_menu_main_display(0);
|
ui_menu_main_display(0);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui_menu_pinentry_display(1);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// >= 0
|
||||||
static unsigned int validate_pin() {
|
static unsigned int validate_pin() {
|
||||||
unsigned int offset, len, sw;
|
unsigned int offset, len, sw;
|
||||||
|
|
||||||
@ -337,12 +358,12 @@ static unsigned int validate_pin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (G_gpg_vstate.io_ins == 0x24) {
|
if (G_gpg_vstate.io_ins == 0x24) {
|
||||||
if (G_gpg_vstate.work.io_buffer[0] <= 2) {
|
if (G_gpg_vstate.io_p1 <= 2) {
|
||||||
gpg_io_insert_u8(G_gpg_vstate.ux_pinentry[0]);
|
gpg_io_insert_u8(G_gpg_vstate.ux_pinentry[0]);
|
||||||
gpg_io_insert((unsigned char*)(G_gpg_vstate.menu+1), G_gpg_vstate.ux_pinentry[0]);
|
gpg_io_insert((unsigned char*)(G_gpg_vstate.menu+1), G_gpg_vstate.ux_pinentry[0]);
|
||||||
G_gpg_vstate.work.io_buffer[0]++;
|
G_gpg_vstate.io_p1++;
|
||||||
}
|
}
|
||||||
if (G_gpg_vstate.work.io_buffer[0] == 3) {
|
if (G_gpg_vstate.io_p1 == 3) {
|
||||||
if (!gpg_check_pin(G_gpg_vstate.io_p2&0x0F, G_gpg_vstate.work.io_buffer+1, G_gpg_vstate.work.io_buffer[0])) {
|
if (!gpg_check_pin(G_gpg_vstate.io_p2&0x0F, G_gpg_vstate.work.io_buffer+1, G_gpg_vstate.work.io_buffer[0])) {
|
||||||
gpg_io_discard(1);
|
gpg_io_discard(1);
|
||||||
gpg_io_insert_u16(SW_CONDITIONS_NOT_SATISFIED);
|
gpg_io_insert_u16(SW_CONDITIONS_NOT_SATISFIED);
|
||||||
@ -358,13 +379,16 @@ static unsigned int validate_pin() {
|
|||||||
gpg_io_insert_u16(SW_CONDITIONS_NOT_SATISFIED);
|
gpg_io_insert_u16(SW_CONDITIONS_NOT_SATISFIED);
|
||||||
gpg_io_do(IO_RETURN_AFTER_TX);
|
gpg_io_do(IO_RETURN_AFTER_TX);
|
||||||
ui_info(PIN_DIFFERS, NULL, ui_menu_main_display, 0);
|
ui_info(PIN_DIFFERS, NULL, ui_menu_main_display, 0);
|
||||||
|
} else {
|
||||||
|
gpg_change_pin(G_gpg_vstate.io_p2&0x0F, G_gpg_vstate.work.io_buffer+offset+ 1, len);
|
||||||
|
gpg_io_discard(1);
|
||||||
|
gpg_io_insert_u16(SW_OK);
|
||||||
|
gpg_io_do(IO_RETURN_AFTER_TX);
|
||||||
|
ui_info(PIN_CHANGED, NULL, ui_menu_main_display, 0);
|
||||||
}
|
}
|
||||||
gpg_change_pin(G_gpg_vstate.io_p2&0x0F, G_gpg_vstate.work.io_buffer+offset+ 1, len);
|
|
||||||
gpg_io_discard(1);
|
|
||||||
gpg_io_insert_u16(SW_OK);
|
|
||||||
gpg_io_do(IO_RETURN_AFTER_TX);
|
|
||||||
ui_info(PIN_CHANGED, NULL, ui_menu_main_display, 0);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
} else {
|
||||||
|
ui_menu_pinentry_display(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -797,7 +821,7 @@ const ux_menu_entry_t ui_menu_main[] = {
|
|||||||
{NULL, os_sched_exit, 0, &C_icon_dashboard, "Quit app" , NULL, 50, 29},
|
{NULL, os_sched_exit, 0, &C_icon_dashboard, "Quit app" , NULL, 50, 29},
|
||||||
UX_MENU_END
|
UX_MENU_END
|
||||||
};
|
};
|
||||||
|
extern const uint8_t N_USBD_CfgDesc[];
|
||||||
const bagl_element_t* ui_menu_main_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
|
const bagl_element_t* ui_menu_main_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) {
|
||||||
if (entry == &ui_menu_main[0]) {
|
if (entry == &ui_menu_main[0]) {
|
||||||
if(element->component.userid==0x20) {
|
if(element->component.userid==0x20) {
|
||||||
@ -814,6 +838,7 @@ const bagl_element_t* ui_menu_main_preprocessor(const ux_menu_entry_t* entry, ba
|
|||||||
os_memset(G_gpg_vstate.menu, 0, sizeof(G_gpg_vstate.menu));
|
os_memset(G_gpg_vstate.menu, 0, sizeof(G_gpg_vstate.menu));
|
||||||
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "< User: %s / SLOT: %d / Serial: %x >",
|
snprintf(G_gpg_vstate.menu, sizeof(G_gpg_vstate.menu), "< User: %s / SLOT: %d / Serial: %x >",
|
||||||
name, G_gpg_vstate.slot+1, serial);
|
name, G_gpg_vstate.slot+1, serial);
|
||||||
|
|
||||||
element->component.stroke = 10; // 1 second stop in each way
|
element->component.stroke = 10; // 1 second stop in each way
|
||||||
element->component.icon_id = 26; // roundtrip speed in pixel/s
|
element->component.icon_id = 26; // roundtrip speed in pixel/s
|
||||||
element->text = G_gpg_vstate.menu;
|
element->text = G_gpg_vstate.menu;
|
||||||
|
@ -161,7 +161,7 @@ static const uint8_t const USBD_DeviceDesc[]= {
|
|||||||
/* USB Mass storage device Configuration Descriptor */
|
/* USB Mass storage device Configuration Descriptor */
|
||||||
/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
|
/* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
|
||||||
#define USBD_OFFSET_CfgDesc_bPINSupport 70
|
#define USBD_OFFSET_CfgDesc_bPINSupport 70
|
||||||
static const uint8_t N_USBD_CfgDesc[] =
|
const uint8_t N_USBD_CfgDesc[] =
|
||||||
{
|
{
|
||||||
|
|
||||||
0x09, /* bLength: Configuration Descriptor size */
|
0x09, /* bLength: Configuration Descriptor size */
|
||||||
@ -438,7 +438,7 @@ static const USBD_ClassTypeDef USBD_CCID =
|
|||||||
void USBD_CCID_activate_pinpad(int enabled) {
|
void USBD_CCID_activate_pinpad(int enabled) {
|
||||||
unsigned char e;
|
unsigned char e;
|
||||||
e = enabled?3:0;
|
e = enabled?3:0;
|
||||||
nvm_write(USBD_GetCfgDesc_impl+USBD_OFFSET_CfgDesc_bPINSupport, &e,1);
|
nvm_write(((char*)PIC(N_USBD_CfgDesc))+USBD_OFFSET_CfgDesc_bPINSupport, &e,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void USB_CCID_power(unsigned char enabled) {
|
void USB_CCID_power(unsigned char enabled) {
|
||||||
|
Loading…
Reference in New Issue
Block a user