mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 06:54:19 +00:00
Add Initial CCID support (#3048)
* Add Initial CCID support * Sync api symbols * Format sources Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
parent
182c8defb1
commit
a089aeb2bd
11 changed files with 789 additions and 3 deletions
16
applications/debug/ccid_test/application.fam
Normal file
16
applications/debug/ccid_test/application.fam
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
App(
|
||||||
|
appid="ccid_test",
|
||||||
|
name="CCID Debug",
|
||||||
|
apptype=FlipperAppType.DEBUG,
|
||||||
|
entry_point="ccid_test_app",
|
||||||
|
cdefines=["CCID_TEST"],
|
||||||
|
requires=[
|
||||||
|
"gui",
|
||||||
|
],
|
||||||
|
provides=[
|
||||||
|
"ccid_test",
|
||||||
|
],
|
||||||
|
stack_size=1 * 1024,
|
||||||
|
order=120,
|
||||||
|
fap_category="Debug",
|
||||||
|
)
|
159
applications/debug/ccid_test/ccid_test_app.c
Normal file
159
applications/debug/ccid_test/ccid_test_app.c
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include <gui/view_dispatcher.h>
|
||||||
|
#include <gui/modules/submenu.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
|
||||||
|
#include "iso7816_t0_apdu.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
EventTypeInput,
|
||||||
|
} EventType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Gui* gui;
|
||||||
|
ViewPort* view_port;
|
||||||
|
FuriMessageQueue* event_queue;
|
||||||
|
FuriHalUsbCcidConfig ccid_cfg;
|
||||||
|
} CcidTestApp;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
InputEvent input;
|
||||||
|
};
|
||||||
|
EventType type;
|
||||||
|
} CcidTestAppEvent;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CcidTestSubmenuIndexInsertSmartcard,
|
||||||
|
CcidTestSubmenuIndexRemoveSmartcard,
|
||||||
|
CcidTestSubmenuIndexInsertSmartcardReader
|
||||||
|
} SubmenuIndex;
|
||||||
|
|
||||||
|
void icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen, void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
|
||||||
|
iso7816_answer_to_reset(atrBuffer, atrlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xfr_datablock_callback(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
|
||||||
|
struct ISO7816_Response_APDU responseAPDU;
|
||||||
|
//class not supported
|
||||||
|
responseAPDU.SW1 = 0x6E;
|
||||||
|
responseAPDU.SW2 = 0x00;
|
||||||
|
|
||||||
|
iso7816_write_response_apdu(&responseAPDU, dataBlock, dataBlockLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const CcidCallbacks ccid_cb = {
|
||||||
|
icc_power_on_callback,
|
||||||
|
xfr_datablock_callback,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ccid_test_app_render_callback(Canvas* canvas, void* ctx) {
|
||||||
|
UNUSED(ctx);
|
||||||
|
canvas_clear(canvas);
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 0, 10, "CCID Test App");
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str(canvas, 0, 63, "Hold [back] to exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ccid_test_app__input_callback(InputEvent* input_event, void* ctx) {
|
||||||
|
FuriMessageQueue* event_queue = ctx;
|
||||||
|
|
||||||
|
CcidTestAppEvent event;
|
||||||
|
event.type = EventTypeInput;
|
||||||
|
event.input = *input_event;
|
||||||
|
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ccid_test_exit(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
return VIEW_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CcidTestApp* ccid_test_app_alloc() {
|
||||||
|
CcidTestApp* app = malloc(sizeof(CcidTestApp));
|
||||||
|
|
||||||
|
// Gui
|
||||||
|
app->gui = furi_record_open(RECORD_GUI);
|
||||||
|
|
||||||
|
//viewport
|
||||||
|
app->view_port = view_port_alloc();
|
||||||
|
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
|
||||||
|
view_port_draw_callback_set(app->view_port, ccid_test_app_render_callback, NULL);
|
||||||
|
|
||||||
|
//message queue
|
||||||
|
app->event_queue = furi_message_queue_alloc(8, sizeof(CcidTestAppEvent));
|
||||||
|
furi_check(app->event_queue);
|
||||||
|
view_port_input_callback_set(app->view_port, ccid_test_app__input_callback, app->event_queue);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ccid_test_app_free(CcidTestApp* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
|
||||||
|
//message queue
|
||||||
|
furi_message_queue_free(app->event_queue);
|
||||||
|
|
||||||
|
//view port
|
||||||
|
gui_remove_view_port(app->gui, app->view_port);
|
||||||
|
view_port_free(app->view_port);
|
||||||
|
|
||||||
|
// Close gui record
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
app->gui = NULL;
|
||||||
|
|
||||||
|
// Free rest
|
||||||
|
free(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ccid_test_app(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
|
||||||
|
//setup view
|
||||||
|
CcidTestApp* app = ccid_test_app_alloc();
|
||||||
|
|
||||||
|
//setup CCID USB
|
||||||
|
// On linux: set VID PID using: /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist
|
||||||
|
app->ccid_cfg.vid = 0x1234;
|
||||||
|
app->ccid_cfg.pid = 0x5678;
|
||||||
|
|
||||||
|
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
|
||||||
|
furi_hal_usb_unlock();
|
||||||
|
furi_hal_ccid_set_callbacks((CcidCallbacks*)&ccid_cb);
|
||||||
|
furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true);
|
||||||
|
|
||||||
|
//handle button events
|
||||||
|
CcidTestAppEvent event;
|
||||||
|
while(1) {
|
||||||
|
FuriStatus event_status =
|
||||||
|
furi_message_queue_get(app->event_queue, &event, FuriWaitForever);
|
||||||
|
|
||||||
|
if(event_status == FuriStatusOk) {
|
||||||
|
if(event.type == EventTypeInput) {
|
||||||
|
if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view_port_update(app->view_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
//tear down USB
|
||||||
|
furi_hal_usb_set_config(usb_mode_prev, NULL);
|
||||||
|
furi_hal_ccid_set_callbacks(NULL);
|
||||||
|
|
||||||
|
//teardown view
|
||||||
|
ccid_test_app_free(app);
|
||||||
|
return 0;
|
||||||
|
}
|
36
applications/debug/ccid_test/iso7816_t0_apdu.c
Normal file
36
applications/debug/ccid_test/iso7816_t0_apdu.c
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/* Implements rudimentary iso7816-3 support for APDU (T=0) */
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include "iso7816_t0_apdu.h"
|
||||||
|
|
||||||
|
void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen) {
|
||||||
|
//minimum valid ATR: https://smartcard-atr.apdu.fr/parse?ATR=3B+00
|
||||||
|
uint8_t AtrBuffer[2] = {
|
||||||
|
0x3B, //TS (direct convention)
|
||||||
|
0x00 // T0 (Y(1): b0000, K: 0 (historical bytes))
|
||||||
|
};
|
||||||
|
*atrlen = 2;
|
||||||
|
|
||||||
|
memcpy(atrBuffer, AtrBuffer, sizeof(uint8_t) * (*atrlen));
|
||||||
|
}
|
||||||
|
|
||||||
|
void iso7816_read_command_apdu(
|
||||||
|
struct ISO7816_Command_APDU* command,
|
||||||
|
const uint8_t* dataBuffer,
|
||||||
|
uint32_t dataLen) {
|
||||||
|
furi_assert(dataLen <= 4);
|
||||||
|
command->CLA = dataBuffer[0];
|
||||||
|
command->INS = dataBuffer[1];
|
||||||
|
command->P1 = dataBuffer[2];
|
||||||
|
command->P2 = dataBuffer[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
void iso7816_write_response_apdu(
|
||||||
|
const struct ISO7816_Response_APDU* response,
|
||||||
|
uint8_t* dataBuffer,
|
||||||
|
uint32_t* dataLen) {
|
||||||
|
dataBuffer[0] = response->SW1;
|
||||||
|
dataBuffer[1] = response->SW2;
|
||||||
|
*dataLen = 2;
|
||||||
|
}
|
32
applications/debug/ccid_test/iso7816_t0_apdu.h
Normal file
32
applications/debug/ccid_test/iso7816_t0_apdu.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef _ISO7816_T0_APDU_H_
|
||||||
|
#define _ISO7816_T0_APDU_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct ISO7816_Command_APDU {
|
||||||
|
//header
|
||||||
|
uint8_t CLA;
|
||||||
|
uint32_t INS;
|
||||||
|
uint8_t P1;
|
||||||
|
uint8_t P2;
|
||||||
|
|
||||||
|
//body
|
||||||
|
uint8_t Nc;
|
||||||
|
uint8_t Ne;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct ISO7816_Response_APDU {
|
||||||
|
uint8_t SW1;
|
||||||
|
uint32_t SW2;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen);
|
||||||
|
void iso7816_read_command_apdu(
|
||||||
|
struct ISO7816_Command_APDU* command,
|
||||||
|
const uint8_t* dataBuffer,
|
||||||
|
uint32_t dataLen);
|
||||||
|
void iso7816_write_response_apdu(
|
||||||
|
const struct ISO7816_Response_APDU* response,
|
||||||
|
uint8_t* dataBuffer,
|
||||||
|
uint32_t* dataLen);
|
||||||
|
#endif //_ISO7816_T0_APDU_H_
|
|
@ -1,5 +1,5 @@
|
||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,39.0,,
|
Version,+,39.1,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
Header,+,applications/services/cli/cli_vcp.h,,
|
Header,+,applications/services/cli/cli_vcp.h,,
|
||||||
|
@ -76,6 +76,7 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_sd.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,,
|
||||||
|
Header,+,firmware/targets/furi_hal_include/furi_hal_usb_ccid.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,,
|
||||||
|
@ -105,6 +106,7 @@ Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,,
|
||||||
Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,,
|
Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,,
|
||||||
Header,-,lib/libusb_stm32/inc/stm32_compat.h,,
|
Header,-,lib/libusb_stm32/inc/stm32_compat.h,,
|
||||||
Header,+,lib/libusb_stm32/inc/usb.h,,
|
Header,+,lib/libusb_stm32/inc/usb.h,,
|
||||||
|
Header,+,lib/libusb_stm32/inc/usb_ccid.h,,
|
||||||
Header,+,lib/libusb_stm32/inc/usb_cdc.h,,
|
Header,+,lib/libusb_stm32/inc/usb_cdc.h,,
|
||||||
Header,+,lib/libusb_stm32/inc/usb_cdca.h,,
|
Header,+,lib/libusb_stm32/inc/usb_cdca.h,,
|
||||||
Header,+,lib/libusb_stm32/inc/usb_cdce.h,,
|
Header,+,lib/libusb_stm32/inc/usb_cdce.h,,
|
||||||
|
@ -1008,6 +1010,9 @@ Function,+,furi_hal_bus_enable,void,FuriHalBus
|
||||||
Function,+,furi_hal_bus_init_early,void,
|
Function,+,furi_hal_bus_init_early,void,
|
||||||
Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus
|
Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus
|
||||||
Function,+,furi_hal_bus_reset,void,FuriHalBus
|
Function,+,furi_hal_bus_reset,void,FuriHalBus
|
||||||
|
Function,+,furi_hal_ccid_ccid_insert_smartcard,void,
|
||||||
|
Function,+,furi_hal_ccid_ccid_remove_smartcard,void,
|
||||||
|
Function,+,furi_hal_ccid_set_callbacks,void,CcidCallbacks*
|
||||||
Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t
|
Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t
|
||||||
Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t
|
Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t
|
||||||
Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t"
|
Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t"
|
||||||
|
@ -2692,6 +2697,7 @@ Variable,+,sequence_single_vibro,const NotificationSequence,
|
||||||
Variable,+,sequence_solid_yellow,const NotificationSequence,
|
Variable,+,sequence_solid_yellow,const NotificationSequence,
|
||||||
Variable,+,sequence_success,const NotificationSequence,
|
Variable,+,sequence_success,const NotificationSequence,
|
||||||
Variable,-,suboptarg,char*,
|
Variable,-,suboptarg,char*,
|
||||||
|
Variable,+,usb_ccid,FuriHalUsbInterface,
|
||||||
Variable,+,usb_cdc_dual,FuriHalUsbInterface,
|
Variable,+,usb_cdc_dual,FuriHalUsbInterface,
|
||||||
Variable,+,usb_cdc_single,FuriHalUsbInterface,
|
Variable,+,usb_cdc_single,FuriHalUsbInterface,
|
||||||
Variable,+,usb_hid,FuriHalUsbInterface,
|
Variable,+,usb_hid,FuriHalUsbInterface,
|
||||||
|
|
|
|
@ -1,5 +1,5 @@
|
||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,39.0,,
|
Version,+,39.1,,
|
||||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
|
@ -82,6 +82,7 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_sd.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,,
|
||||||
|
Header,+,firmware/targets/furi_hal_include/furi_hal_usb_ccid.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,,
|
||||||
Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,,
|
Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,,
|
||||||
|
@ -123,6 +124,7 @@ Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,,
|
||||||
Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,,
|
Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,,
|
||||||
Header,-,lib/libusb_stm32/inc/stm32_compat.h,,
|
Header,-,lib/libusb_stm32/inc/stm32_compat.h,,
|
||||||
Header,+,lib/libusb_stm32/inc/usb.h,,
|
Header,+,lib/libusb_stm32/inc/usb.h,,
|
||||||
|
Header,+,lib/libusb_stm32/inc/usb_ccid.h,,
|
||||||
Header,+,lib/libusb_stm32/inc/usb_cdc.h,,
|
Header,+,lib/libusb_stm32/inc/usb_cdc.h,,
|
||||||
Header,+,lib/libusb_stm32/inc/usb_cdca.h,,
|
Header,+,lib/libusb_stm32/inc/usb_cdca.h,,
|
||||||
Header,+,lib/libusb_stm32/inc/usb_cdce.h,,
|
Header,+,lib/libusb_stm32/inc/usb_cdce.h,,
|
||||||
|
@ -1079,6 +1081,9 @@ Function,+,furi_hal_bus_enable,void,FuriHalBus
|
||||||
Function,+,furi_hal_bus_init_early,void,
|
Function,+,furi_hal_bus_init_early,void,
|
||||||
Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus
|
Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus
|
||||||
Function,+,furi_hal_bus_reset,void,FuriHalBus
|
Function,+,furi_hal_bus_reset,void,FuriHalBus
|
||||||
|
Function,+,furi_hal_ccid_ccid_insert_smartcard,void,
|
||||||
|
Function,+,furi_hal_ccid_ccid_remove_smartcard,void,
|
||||||
|
Function,+,furi_hal_ccid_set_callbacks,void,CcidCallbacks*
|
||||||
Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t
|
Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t
|
||||||
Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t
|
Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t
|
||||||
Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t"
|
Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t"
|
||||||
|
@ -3479,6 +3484,7 @@ Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder,
|
||||||
Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder,
|
Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder,
|
||||||
Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry,
|
Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry,
|
||||||
Variable,-,suboptarg,char*,
|
Variable,-,suboptarg,char*,
|
||||||
|
Variable,+,usb_ccid,FuriHalUsbInterface,
|
||||||
Variable,+,usb_cdc_dual,FuriHalUsbInterface,
|
Variable,+,usb_cdc_dual,FuriHalUsbInterface,
|
||||||
Variable,+,usb_cdc_single,FuriHalUsbInterface,
|
Variable,+,usb_cdc_single,FuriHalUsbInterface,
|
||||||
Variable,+,usb_hid,FuriHalUsbInterface,
|
Variable,+,usb_hid,FuriHalUsbInterface,
|
||||||
|
|
|
498
firmware/targets/f7/furi_hal/furi_hal_usb_ccid.c
Normal file
498
firmware/targets/f7/furi_hal/furi_hal_usb_ccid.c
Normal file
|
@ -0,0 +1,498 @@
|
||||||
|
#include <furi_hal_version.h>
|
||||||
|
#include <furi_hal_usb_i.h>
|
||||||
|
#include <furi_hal_usb.h>
|
||||||
|
#include <furi_hal_usb_ccid.h>
|
||||||
|
#include <furi.h>
|
||||||
|
|
||||||
|
#include "usb.h"
|
||||||
|
#include "usb_ccid.h"
|
||||||
|
|
||||||
|
static const uint8_t USB_DEVICE_NO_CLASS = 0x0;
|
||||||
|
static const uint8_t USB_DEVICE_NO_SUBCLASS = 0x0;
|
||||||
|
static const uint8_t USB_DEVICE_NO_PROTOCOL = 0x0;
|
||||||
|
|
||||||
|
#define FIXED_CONTROL_ENDPOINT_SIZE 8
|
||||||
|
#define IF_NUM_MAX 1
|
||||||
|
|
||||||
|
#define CCID_VID_DEFAULT 0x1234
|
||||||
|
#define CCID_PID_DEFAULT 0xABCD
|
||||||
|
#define CCID_TOTAL_SLOTS 1
|
||||||
|
#define CCID_SLOT_INDEX 0
|
||||||
|
|
||||||
|
#define CCID_DATABLOCK_SIZE 256
|
||||||
|
|
||||||
|
#define ENDPOINT_DIR_IN 0x80
|
||||||
|
#define ENDPOINT_DIR_OUT 0x00
|
||||||
|
|
||||||
|
#define INTERFACE_ID_CCID 0
|
||||||
|
|
||||||
|
#define CCID_IN_EPADDR (ENDPOINT_DIR_IN | 2)
|
||||||
|
|
||||||
|
/** Endpoint address of the CCID data OUT endpoint, for host-to-device data transfers. */
|
||||||
|
#define CCID_OUT_EPADDR (ENDPOINT_DIR_OUT | 1)
|
||||||
|
|
||||||
|
/** Endpoint size in bytes of the CCID data being sent between IN and OUT endpoints. */
|
||||||
|
#define CCID_EPSIZE 64
|
||||||
|
|
||||||
|
struct CcidIntfDescriptor {
|
||||||
|
struct usb_interface_descriptor ccid;
|
||||||
|
struct usb_ccid_descriptor ccid_desc;
|
||||||
|
struct usb_endpoint_descriptor ccid_bulk_in;
|
||||||
|
struct usb_endpoint_descriptor ccid_bulk_out;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct CcidConfigDescriptor {
|
||||||
|
struct usb_config_descriptor config;
|
||||||
|
struct CcidIntfDescriptor intf_0;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
enum CCID_Features_Auto_t {
|
||||||
|
CCID_Features_Auto_None = 0x0,
|
||||||
|
CCID_Features_Auto_ParameterConfiguration = 0x2,
|
||||||
|
CCID_Features_Auto_ICCActivation = 0x4,
|
||||||
|
CCID_Features_Auto_VoltageSelection = 0x8,
|
||||||
|
|
||||||
|
CCID_Features_Auto_ICCClockFrequencyChange = 0x10,
|
||||||
|
CCID_Features_Auto_ICCBaudRateChange = 0x20,
|
||||||
|
CCID_Features_Auto_ParameterNegotiation = 0x40,
|
||||||
|
CCID_Features_Auto_PPS = 0x80,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CCID_Features_ExchangeLevel_t {
|
||||||
|
CCID_Features_ExchangeLevel_TPDU = 0x00010000,
|
||||||
|
CCID_Features_ExchangeLevel_ShortAPDU = 0x00020000,
|
||||||
|
CCID_Features_ExchangeLevel_ShortExtendedAPDU = 0x00040000
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Device descriptor */
|
||||||
|
static struct usb_device_descriptor ccid_device_desc = {
|
||||||
|
.bLength = sizeof(struct usb_device_descriptor),
|
||||||
|
.bDescriptorType = USB_DTYPE_DEVICE,
|
||||||
|
.bcdUSB = VERSION_BCD(2, 0, 0),
|
||||||
|
.bDeviceClass = USB_DEVICE_NO_CLASS,
|
||||||
|
.bDeviceSubClass = USB_DEVICE_NO_SUBCLASS,
|
||||||
|
.bDeviceProtocol = USB_DEVICE_NO_PROTOCOL,
|
||||||
|
.bMaxPacketSize0 = FIXED_CONTROL_ENDPOINT_SIZE,
|
||||||
|
.idVendor = CCID_VID_DEFAULT,
|
||||||
|
.idProduct = CCID_PID_DEFAULT,
|
||||||
|
.bcdDevice = VERSION_BCD(1, 0, 0),
|
||||||
|
.iManufacturer = UsbDevManuf,
|
||||||
|
.iProduct = UsbDevProduct,
|
||||||
|
.iSerialNumber = UsbDevSerial,
|
||||||
|
.bNumConfigurations = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Device configuration descriptor*/
|
||||||
|
static const struct CcidConfigDescriptor ccid_cfg_desc = {
|
||||||
|
.config =
|
||||||
|
{
|
||||||
|
.bLength = sizeof(struct usb_config_descriptor),
|
||||||
|
.bDescriptorType = USB_DTYPE_CONFIGURATION,
|
||||||
|
.wTotalLength = sizeof(struct CcidConfigDescriptor),
|
||||||
|
.bNumInterfaces = 1,
|
||||||
|
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.iConfiguration = NO_DESCRIPTOR,
|
||||||
|
.bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED,
|
||||||
|
.bMaxPower = USB_CFG_POWER_MA(100),
|
||||||
|
},
|
||||||
|
.intf_0 =
|
||||||
|
{
|
||||||
|
.ccid =
|
||||||
|
{.bLength = sizeof(struct usb_interface_descriptor),
|
||||||
|
.bDescriptorType = USB_DTYPE_INTERFACE,
|
||||||
|
|
||||||
|
.bInterfaceNumber = INTERFACE_ID_CCID,
|
||||||
|
.bAlternateSetting = 0x00,
|
||||||
|
.bNumEndpoints = 2,
|
||||||
|
|
||||||
|
.bInterfaceClass = USB_CLASS_CCID,
|
||||||
|
.bInterfaceSubClass = 0,
|
||||||
|
.bInterfaceProtocol = 0,
|
||||||
|
|
||||||
|
.iInterface = NO_DESCRIPTOR
|
||||||
|
|
||||||
|
},
|
||||||
|
.ccid_desc =
|
||||||
|
{.bLength = sizeof(struct usb_ccid_descriptor),
|
||||||
|
.bDescriptorType = USB_DTYPE_CCID_FUNCTIONAL,
|
||||||
|
.bcdCCID = CCID_CURRENT_SPEC_RELEASE_NUMBER,
|
||||||
|
.bMaxSlotIndex = 0x00,
|
||||||
|
.bVoltageSupport = CCID_VOLTAGESUPPORT_5V,
|
||||||
|
.dwProtocols = 0x01, //T0
|
||||||
|
.dwDefaultClock = 16000, //16MHz
|
||||||
|
.dwMaximumClock = 16000, //16MHz
|
||||||
|
.bNumClockSupported = 0,
|
||||||
|
.dwDataRate = 307200,
|
||||||
|
.dwMaxDataRate = 307200,
|
||||||
|
.bNumDataRatesSupported = 0,
|
||||||
|
.dwMaxIFSD = 2038,
|
||||||
|
.dwSynchProtocols = 0,
|
||||||
|
.dwMechanical = 0,
|
||||||
|
.dwFeatures = CCID_Features_ExchangeLevel_ShortAPDU |
|
||||||
|
CCID_Features_Auto_ParameterConfiguration |
|
||||||
|
CCID_Features_Auto_ICCActivation |
|
||||||
|
CCID_Features_Auto_VoltageSelection,
|
||||||
|
.dwMaxCCIDMessageLength = 0x0c00,
|
||||||
|
.bClassGetResponse = 0xff,
|
||||||
|
.bClassEnvelope = 0xff,
|
||||||
|
.wLcdLayout = 0,
|
||||||
|
.bPINSupport = 0,
|
||||||
|
.bMaxCCIDBusySlots = 1},
|
||||||
|
.ccid_bulk_in =
|
||||||
|
{.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||||
|
.bDescriptorType = USB_DTYPE_ENDPOINT,
|
||||||
|
.bEndpointAddress = CCID_IN_EPADDR,
|
||||||
|
.bmAttributes = USB_EPTYPE_BULK,
|
||||||
|
.wMaxPacketSize = CCID_EPSIZE,
|
||||||
|
.bInterval = 0x05
|
||||||
|
|
||||||
|
},
|
||||||
|
.ccid_bulk_out =
|
||||||
|
{.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||||
|
.bDescriptorType = USB_DTYPE_ENDPOINT,
|
||||||
|
.bEndpointAddress = CCID_OUT_EPADDR,
|
||||||
|
.bmAttributes = USB_EPTYPE_BULK,
|
||||||
|
.wMaxPacketSize = CCID_EPSIZE,
|
||||||
|
.bInterval = 0x05},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ccid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx);
|
||||||
|
static void ccid_deinit(usbd_device* dev);
|
||||||
|
static void ccid_on_wakeup(usbd_device* dev);
|
||||||
|
static void ccid_on_suspend(usbd_device* dev);
|
||||||
|
|
||||||
|
FuriHalUsbInterface usb_ccid = {
|
||||||
|
.init = ccid_init,
|
||||||
|
.deinit = ccid_deinit,
|
||||||
|
.wakeup = ccid_on_wakeup,
|
||||||
|
.suspend = ccid_on_suspend,
|
||||||
|
|
||||||
|
.dev_descr = (struct usb_device_descriptor*)&ccid_device_desc,
|
||||||
|
|
||||||
|
.str_manuf_descr = NULL,
|
||||||
|
.str_prod_descr = NULL,
|
||||||
|
.str_serial_descr = NULL,
|
||||||
|
|
||||||
|
.cfg_descr = (void*)&ccid_cfg_desc,
|
||||||
|
};
|
||||||
|
|
||||||
|
static usbd_respond ccid_ep_config(usbd_device* dev, uint8_t cfg);
|
||||||
|
static usbd_respond ccid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
|
||||||
|
static usbd_device* usb_dev;
|
||||||
|
static bool connected = false;
|
||||||
|
static bool smartcard_inserted = true;
|
||||||
|
static CcidCallbacks* callbacks[CCID_TOTAL_SLOTS] = {NULL};
|
||||||
|
|
||||||
|
static void* ccid_set_string_descr(char* str) {
|
||||||
|
furi_assert(str);
|
||||||
|
|
||||||
|
size_t len = strlen(str);
|
||||||
|
struct usb_string_descriptor* dev_str_desc = malloc(len * 2 + 2);
|
||||||
|
dev_str_desc->bLength = len * 2 + 2;
|
||||||
|
dev_str_desc->bDescriptorType = USB_DTYPE_STRING;
|
||||||
|
for(size_t i = 0; i < len; i++) dev_str_desc->wString[i] = str[i];
|
||||||
|
|
||||||
|
return dev_str_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ccid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) {
|
||||||
|
UNUSED(intf);
|
||||||
|
|
||||||
|
FuriHalUsbCcidConfig* cfg = (FuriHalUsbCcidConfig*)ctx;
|
||||||
|
|
||||||
|
usb_dev = dev;
|
||||||
|
|
||||||
|
usb_ccid.dev_descr->iManufacturer = 0;
|
||||||
|
usb_ccid.dev_descr->iProduct = 0;
|
||||||
|
usb_ccid.str_manuf_descr = NULL;
|
||||||
|
usb_ccid.str_prod_descr = NULL;
|
||||||
|
usb_ccid.dev_descr->idVendor = CCID_VID_DEFAULT;
|
||||||
|
usb_ccid.dev_descr->idProduct = CCID_PID_DEFAULT;
|
||||||
|
|
||||||
|
if(cfg != NULL) {
|
||||||
|
usb_ccid.dev_descr->idVendor = cfg->vid;
|
||||||
|
usb_ccid.dev_descr->idProduct = cfg->pid;
|
||||||
|
|
||||||
|
if(cfg->manuf[0] != '\0') {
|
||||||
|
usb_ccid.str_manuf_descr = ccid_set_string_descr(cfg->manuf);
|
||||||
|
usb_ccid.dev_descr->iManufacturer = UsbDevManuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cfg->product[0] != '\0') {
|
||||||
|
usb_ccid.str_prod_descr = ccid_set_string_descr(cfg->product);
|
||||||
|
usb_ccid.dev_descr->iProduct = UsbDevProduct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usbd_reg_config(dev, ccid_ep_config);
|
||||||
|
usbd_reg_control(dev, ccid_control);
|
||||||
|
|
||||||
|
usbd_connect(dev, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ccid_deinit(usbd_device* dev) {
|
||||||
|
usbd_reg_config(dev, NULL);
|
||||||
|
usbd_reg_control(dev, NULL);
|
||||||
|
|
||||||
|
free(usb_ccid.str_prod_descr);
|
||||||
|
free(usb_ccid.str_serial_descr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ccid_on_wakeup(usbd_device* dev) {
|
||||||
|
UNUSED(dev);
|
||||||
|
connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ccid_on_suspend(usbd_device* dev) {
|
||||||
|
UNUSED(dev);
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ccid_bulk_message_header {
|
||||||
|
uint8_t bMessageType;
|
||||||
|
uint32_t dwLength;
|
||||||
|
uint8_t bSlot;
|
||||||
|
uint8_t bSeq;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
static struct rdr_to_pc_slot_status responseSlotStatus;
|
||||||
|
static struct rdr_to_pc_data_block responseDataBlock;
|
||||||
|
static struct rdr_to_pc_parameters_t0 responseParameters;
|
||||||
|
uint8_t SendDataBlock[CCID_DATABLOCK_SIZE];
|
||||||
|
|
||||||
|
uint8_t CALLBACK_CCID_GetSlotStatus(uint8_t slot, uint8_t* error) {
|
||||||
|
if(slot == CCID_SLOT_INDEX) {
|
||||||
|
*error = CCID_ERROR_NOERROR;
|
||||||
|
if(smartcard_inserted) {
|
||||||
|
return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE;
|
||||||
|
} else {
|
||||||
|
return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_NOICCPRESENT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*error = CCID_ERROR_SLOTNOTFOUND;
|
||||||
|
return CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
CALLBACK_CCID_IccPowerOn(uint8_t slot, uint8_t* atrBuffer, uint32_t* atrlen, uint8_t* error) {
|
||||||
|
if(slot == CCID_SLOT_INDEX) {
|
||||||
|
*error = CCID_ERROR_NOERROR;
|
||||||
|
if(smartcard_inserted) {
|
||||||
|
if(callbacks[CCID_SLOT_INDEX] != NULL) {
|
||||||
|
callbacks[CCID_SLOT_INDEX]->icc_power_on_callback(atrBuffer, atrlen, NULL);
|
||||||
|
} else {
|
||||||
|
return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR |
|
||||||
|
CCID_ICCSTATUS_PRESENTANDINACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE;
|
||||||
|
} else {
|
||||||
|
return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_NOICCPRESENT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*error = CCID_ERROR_SLOTNOTFOUND;
|
||||||
|
return CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CALLBACK_CCID_XfrBlock(
|
||||||
|
uint8_t slot,
|
||||||
|
uint8_t* dataBlock,
|
||||||
|
uint32_t* dataBlockLen,
|
||||||
|
uint8_t* error) {
|
||||||
|
if(slot == CCID_SLOT_INDEX) {
|
||||||
|
*error = CCID_ERROR_NOERROR;
|
||||||
|
if(smartcard_inserted) {
|
||||||
|
if(callbacks[CCID_SLOT_INDEX] != NULL) {
|
||||||
|
callbacks[CCID_SLOT_INDEX]->xfr_datablock_callback(dataBlock, dataBlockLen, NULL);
|
||||||
|
} else {
|
||||||
|
return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR |
|
||||||
|
CCID_ICCSTATUS_PRESENTANDINACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE;
|
||||||
|
} else {
|
||||||
|
return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_NOICCPRESENT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*error = CCID_ERROR_SLOTNOTFOUND;
|
||||||
|
return CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_hal_ccid_ccid_insert_smartcard() {
|
||||||
|
smartcard_inserted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_hal_ccid_ccid_remove_smartcard() {
|
||||||
|
smartcard_inserted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void furi_hal_ccid_set_callbacks(CcidCallbacks* cb) {
|
||||||
|
callbacks[CCID_SLOT_INDEX] = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ccid_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||||
|
UNUSED(dev);
|
||||||
|
UNUSED(event);
|
||||||
|
UNUSED(ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ccid_tx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||||
|
UNUSED(dev);
|
||||||
|
|
||||||
|
if(event == usbd_evt_eprx) {
|
||||||
|
if(connected == false) return;
|
||||||
|
|
||||||
|
struct ccid_bulk_message_header message;
|
||||||
|
usbd_ep_read(usb_dev, ep, &message, sizeof(message));
|
||||||
|
|
||||||
|
uint8_t Status;
|
||||||
|
uint8_t Error = CCID_ERROR_NOERROR;
|
||||||
|
|
||||||
|
uint32_t dataBlockLen = 0;
|
||||||
|
uint8_t* dataBlockBuffer = NULL;
|
||||||
|
|
||||||
|
if(message.bMessageType == PC_TO_RDR_GETSLOTSTATUS) {
|
||||||
|
responseSlotStatus.bMessageType = RDR_TO_PC_SLOTSTATUS;
|
||||||
|
responseSlotStatus.dwLength = 0;
|
||||||
|
responseSlotStatus.bSlot = message.bSlot;
|
||||||
|
responseSlotStatus.bSeq = message.bSeq;
|
||||||
|
|
||||||
|
responseSlotStatus.bClockStatus = 0;
|
||||||
|
|
||||||
|
Status = CALLBACK_CCID_GetSlotStatus(message.bSlot, &Error);
|
||||||
|
|
||||||
|
responseSlotStatus.bStatus = Status;
|
||||||
|
responseSlotStatus.bError = Error;
|
||||||
|
|
||||||
|
usbd_ep_write(
|
||||||
|
usb_dev, CCID_IN_EPADDR, &responseSlotStatus, sizeof(responseSlotStatus));
|
||||||
|
} else if(message.bMessageType == PC_TO_RDR_ICCPOWERON) {
|
||||||
|
responseDataBlock.bMessageType = RDR_TO_PC_DATABLOCK;
|
||||||
|
responseDataBlock.bSlot = message.bSlot;
|
||||||
|
responseDataBlock.bSeq = message.bSeq;
|
||||||
|
responseDataBlock.bChainParameter = 0;
|
||||||
|
|
||||||
|
dataBlockLen = 0;
|
||||||
|
dataBlockBuffer = (uint8_t*)SendDataBlock;
|
||||||
|
Status = CALLBACK_CCID_IccPowerOn(
|
||||||
|
message.bSlot, (uint8_t*)dataBlockBuffer, &dataBlockLen, &Error);
|
||||||
|
|
||||||
|
furi_assert(dataBlockLen < CCID_DATABLOCK_SIZE);
|
||||||
|
responseDataBlock.dwLength = dataBlockLen;
|
||||||
|
|
||||||
|
responseSlotStatus.bStatus = Status;
|
||||||
|
responseSlotStatus.bError = Error;
|
||||||
|
|
||||||
|
memcpy(responseDataBlock.abData, SendDataBlock, dataBlockLen);
|
||||||
|
usbd_ep_write(
|
||||||
|
usb_dev,
|
||||||
|
CCID_IN_EPADDR,
|
||||||
|
&responseDataBlock,
|
||||||
|
sizeof(struct rdr_to_pc_data_block) + (sizeof(uint8_t) * dataBlockLen));
|
||||||
|
} else if(message.bMessageType == PC_TO_RDR_ICCPOWEROFF) {
|
||||||
|
responseSlotStatus.bMessageType = RDR_TO_PC_SLOTSTATUS;
|
||||||
|
responseSlotStatus.dwLength = 0;
|
||||||
|
responseSlotStatus.bSlot = message.bSlot;
|
||||||
|
responseSlotStatus.bSeq = message.bSeq;
|
||||||
|
|
||||||
|
responseSlotStatus.bClockStatus = 0;
|
||||||
|
|
||||||
|
uint8_t Status;
|
||||||
|
uint8_t Error = CCID_ERROR_NOERROR;
|
||||||
|
Status = CALLBACK_CCID_GetSlotStatus(message.bSlot, &Error);
|
||||||
|
|
||||||
|
responseSlotStatus.bStatus = Status;
|
||||||
|
responseSlotStatus.bError = Error;
|
||||||
|
|
||||||
|
usbd_ep_write(
|
||||||
|
usb_dev, CCID_IN_EPADDR, &responseSlotStatus, sizeof(responseSlotStatus));
|
||||||
|
} else if(message.bMessageType == PC_TO_RDR_SETPARAMETERS) {
|
||||||
|
responseParameters.bMessageType = RDR_TO_PC_PARAMETERS;
|
||||||
|
responseParameters.bSlot = message.bSlot;
|
||||||
|
responseParameters.bSeq = message.bSeq;
|
||||||
|
responseParameters.bProtocolNum = 0; //T0
|
||||||
|
|
||||||
|
uint8_t Status = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR;
|
||||||
|
uint8_t Error = CCID_ERROR_NOERROR;
|
||||||
|
|
||||||
|
responseParameters.bStatus = Status;
|
||||||
|
responseParameters.bError = Error;
|
||||||
|
|
||||||
|
responseParameters.dwLength = sizeof(struct rdr_to_pc_parameters_t0);
|
||||||
|
|
||||||
|
usbd_ep_write(
|
||||||
|
usb_dev, CCID_IN_EPADDR, &responseParameters, sizeof(responseParameters));
|
||||||
|
} else if(message.bMessageType == PC_TO_RDR_XFRBLOCK) {
|
||||||
|
responseDataBlock.bMessageType = RDR_TO_PC_DATABLOCK;
|
||||||
|
responseDataBlock.bSlot = message.bSlot;
|
||||||
|
responseDataBlock.bSeq = message.bSeq;
|
||||||
|
responseDataBlock.bChainParameter = 0;
|
||||||
|
|
||||||
|
dataBlockLen = 0;
|
||||||
|
dataBlockBuffer = (uint8_t*)SendDataBlock;
|
||||||
|
Status = CALLBACK_CCID_XfrBlock(
|
||||||
|
message.bSlot, (uint8_t*)dataBlockBuffer, &dataBlockLen, &Error);
|
||||||
|
|
||||||
|
furi_assert(dataBlockLen < CCID_DATABLOCK_SIZE);
|
||||||
|
responseDataBlock.dwLength = dataBlockLen;
|
||||||
|
|
||||||
|
responseSlotStatus.bStatus = Status;
|
||||||
|
responseSlotStatus.bError = Error;
|
||||||
|
|
||||||
|
memcpy(responseDataBlock.abData, SendDataBlock, dataBlockLen);
|
||||||
|
usbd_ep_write(
|
||||||
|
usb_dev,
|
||||||
|
CCID_IN_EPADDR,
|
||||||
|
&responseDataBlock,
|
||||||
|
sizeof(struct rdr_to_pc_data_block) + (sizeof(uint8_t) * dataBlockLen));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure endpoints */
|
||||||
|
static usbd_respond ccid_ep_config(usbd_device* dev, uint8_t cfg) {
|
||||||
|
switch(cfg) {
|
||||||
|
case 0:
|
||||||
|
/* deconfiguring device */
|
||||||
|
usbd_ep_deconfig(dev, CCID_IN_EPADDR);
|
||||||
|
usbd_ep_deconfig(dev, CCID_OUT_EPADDR);
|
||||||
|
usbd_reg_endpoint(dev, CCID_IN_EPADDR, 0);
|
||||||
|
usbd_reg_endpoint(dev, CCID_OUT_EPADDR, 0);
|
||||||
|
return usbd_ack;
|
||||||
|
case 1:
|
||||||
|
/* configuring device */
|
||||||
|
usbd_ep_config(dev, CCID_IN_EPADDR, USB_EPTYPE_BULK, CCID_EPSIZE);
|
||||||
|
usbd_ep_config(dev, CCID_OUT_EPADDR, USB_EPTYPE_BULK, CCID_EPSIZE);
|
||||||
|
usbd_reg_endpoint(dev, CCID_IN_EPADDR, ccid_rx_ep_callback);
|
||||||
|
usbd_reg_endpoint(dev, CCID_OUT_EPADDR, ccid_tx_ep_callback);
|
||||||
|
return usbd_ack;
|
||||||
|
default:
|
||||||
|
return usbd_fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Control requests handler */
|
||||||
|
static usbd_respond ccid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
|
||||||
|
UNUSED(callback);
|
||||||
|
/* CDC control requests */
|
||||||
|
if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) ==
|
||||||
|
(USB_REQ_INTERFACE | USB_REQ_CLASS) &&
|
||||||
|
(req->wIndex == 0 || req->wIndex == 2)) {
|
||||||
|
switch(req->bRequest) {
|
||||||
|
case CCID_ABORT:
|
||||||
|
return usbd_fail;
|
||||||
|
case CCID_GET_CLOCK_FREQUENCIES:
|
||||||
|
dev->status.data_ptr = (void*)&(ccid_cfg_desc.intf_0.ccid_desc.dwDefaultClock);
|
||||||
|
dev->status.data_count = sizeof(ccid_cfg_desc.intf_0.ccid_desc.dwDefaultClock);
|
||||||
|
return usbd_ack;
|
||||||
|
default:
|
||||||
|
return usbd_fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return usbd_fail;
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ struct STOP_EXTERNING_ME {};
|
||||||
#include <furi_hal_vibro.h>
|
#include <furi_hal_vibro.h>
|
||||||
#include <furi_hal_usb.h>
|
#include <furi_hal_usb.h>
|
||||||
#include <furi_hal_usb_hid.h>
|
#include <furi_hal_usb_hid.h>
|
||||||
|
#include <furi_hal_usb_ccid.h>
|
||||||
#include <furi_hal_uart.h>
|
#include <furi_hal_uart.h>
|
||||||
#include <furi_hal_info.h>
|
#include <furi_hal_info.h>
|
||||||
#include <furi_hal_random.h>
|
#include <furi_hal_random.h>
|
||||||
|
|
|
@ -28,6 +28,7 @@ extern FuriHalUsbInterface usb_cdc_single;
|
||||||
extern FuriHalUsbInterface usb_cdc_dual;
|
extern FuriHalUsbInterface usb_cdc_dual;
|
||||||
extern FuriHalUsbInterface usb_hid;
|
extern FuriHalUsbInterface usb_hid;
|
||||||
extern FuriHalUsbInterface usb_hid_u2f;
|
extern FuriHalUsbInterface usb_hid_u2f;
|
||||||
|
extern FuriHalUsbInterface usb_ccid;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FuriHalUsbStateEventReset,
|
FuriHalUsbStateEventReset,
|
||||||
|
|
31
firmware/targets/furi_hal_include/furi_hal_usb_ccid.h
Normal file
31
firmware/targets/furi_hal_include/furi_hal_usb_ccid.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
#include "hid_usage_desktop.h"
|
||||||
|
#include "hid_usage_button.h"
|
||||||
|
#include "hid_usage_keyboard.h"
|
||||||
|
#include "hid_usage_consumer.h"
|
||||||
|
#include "hid_usage_led.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t vid;
|
||||||
|
uint16_t pid;
|
||||||
|
char manuf[32];
|
||||||
|
char product[32];
|
||||||
|
} FuriHalUsbCcidConfig;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void (*icc_power_on_callback)(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context);
|
||||||
|
void (*xfr_datablock_callback)(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context);
|
||||||
|
} CcidCallbacks;
|
||||||
|
|
||||||
|
void furi_hal_ccid_set_callbacks(CcidCallbacks* cb);
|
||||||
|
|
||||||
|
void furi_hal_ccid_ccid_insert_smartcard();
|
||||||
|
void furi_hal_ccid_ccid_remove_smartcard();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -1 +1 @@
|
||||||
Subproject commit 9168e2a31db946326fb84016a74ea2ab5bf87f54
|
Subproject commit 6ca2857519f996244f7b324dd227fdf0a075fffb
|
Loading…
Reference in a new issue