Skip to content

Commit

Permalink
Merge branch 'ofw_dev' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
xMasterX committed Sep 21, 2023
2 parents 286dbfa + b98631c commit 8666cdc
Show file tree
Hide file tree
Showing 29 changed files with 1,493 additions and 336 deletions.
16 changes: 16 additions & 0 deletions applications/debug/ccid_test/application.fam
Original file line number Diff line number Diff line change
@@ -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 changes: 159 additions & 0 deletions applications/debug/ccid_test/ccid_test_app.c
Original file line number Diff line number Diff line change
@@ -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 changes: 36 additions & 0 deletions applications/debug/ccid_test/iso7816_t0_apdu.c
Original file line number Diff line number Diff line change
@@ -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 changes: 32 additions & 0 deletions applications/debug/ccid_test/iso7816_t0_apdu.h
Original file line number Diff line number Diff line change
@@ -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_
115 changes: 115 additions & 0 deletions applications/debug/unit_tests/furi_hal/furi_hal_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
#include "../minunit.h"

#define DATA_SIZE 4
#define EEPROM_ADDRESS 0b10101000
#define EEPROM_ADDRESS_HIGH (EEPROM_ADDRESS | 0b10)
#define EEPROM_SIZE 512
#define EEPROM_PAGE_SIZE 16
#define EEPROM_WRITE_DELAY_MS 6

static void furi_hal_i2c_int_setup() {
furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
Expand All @@ -14,6 +19,14 @@ static void furi_hal_i2c_int_teardown() {
furi_hal_i2c_release(&furi_hal_i2c_handle_power);
}

static void furi_hal_i2c_ext_setup() {
furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
}

static void furi_hal_i2c_ext_teardown() {
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
}

MU_TEST(furi_hal_i2c_int_1b) {
bool ret = false;
uint8_t data_one = 0;
Expand Down Expand Up @@ -103,14 +116,116 @@ MU_TEST(furi_hal_i2c_int_1b_fail) {
mu_assert(data_one != 0, "9 invalid data");
}

MU_TEST(furi_hal_i2c_int_ext_3b) {
bool ret = false;
uint8_t data_many[DATA_SIZE] = {0};

// 3 byte: read
data_many[0] = LP5562_CHANNEL_BLUE_CURRENT_REGISTER;
ret = furi_hal_i2c_tx_ext(
&furi_hal_i2c_handle_power,
LP5562_ADDRESS,
false,
data_many,
1,
FuriHalI2cBeginStart,
FuriHalI2cEndAwaitRestart,
LP5562_I2C_TIMEOUT);
mu_assert(ret, "3 tx failed");

// Send a RESTART condition, then read the 3 bytes one after the other
ret = furi_hal_i2c_rx_ext(
&furi_hal_i2c_handle_power,
LP5562_ADDRESS,
false,
data_many + 1,
1,
FuriHalI2cBeginRestart,
FuriHalI2cEndPause,
LP5562_I2C_TIMEOUT);
mu_assert(ret, "4 rx failed");
mu_assert(data_many[1] != 0, "4 invalid data");
ret = furi_hal_i2c_rx_ext(
&furi_hal_i2c_handle_power,
LP5562_ADDRESS,
false,
data_many + 2,
1,
FuriHalI2cBeginResume,
FuriHalI2cEndPause,
LP5562_I2C_TIMEOUT);
mu_assert(ret, "5 rx failed");
mu_assert(data_many[2] != 0, "5 invalid data");
ret = furi_hal_i2c_rx_ext(
&furi_hal_i2c_handle_power,
LP5562_ADDRESS,
false,
data_many + 3,
1,
FuriHalI2cBeginResume,
FuriHalI2cEndStop,
LP5562_I2C_TIMEOUT);
mu_assert(ret, "6 rx failed");
mu_assert(data_many[3] != 0, "6 invalid data");
}

MU_TEST(furi_hal_i2c_ext_eeprom) {
if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, EEPROM_ADDRESS, 100)) {
printf("no device connected, skipping\r\n");
return;
}

bool ret = false;
uint8_t buffer[EEPROM_SIZE] = {0};

for(size_t page = 0; page < (EEPROM_SIZE / EEPROM_PAGE_SIZE); ++page) {
// Fill page buffer
for(size_t page_byte = 0; page_byte < EEPROM_PAGE_SIZE; ++page_byte) {
// Each byte is its position in the EEPROM modulo 256
uint8_t byte = ((page * EEPROM_PAGE_SIZE) + page_byte) % 256;

buffer[page_byte] = byte;
}

uint8_t address = (page < 16) ? EEPROM_ADDRESS : EEPROM_ADDRESS_HIGH;

ret = furi_hal_i2c_write_mem(
&furi_hal_i2c_handle_external,
address,
page * EEPROM_PAGE_SIZE,
buffer,
EEPROM_PAGE_SIZE,
20);

mu_assert(ret, "EEPROM write failed");
furi_delay_ms(EEPROM_WRITE_DELAY_MS);
}

ret = furi_hal_i2c_read_mem(
&furi_hal_i2c_handle_external, EEPROM_ADDRESS, 0, buffer, EEPROM_SIZE, 100);

mu_assert(ret, "EEPROM read failed");

for(size_t pos = 0; pos < EEPROM_SIZE; ++pos) {
mu_assert_int_eq(pos % 256, buffer[pos]);
}
}

MU_TEST_SUITE(furi_hal_i2c_int_suite) {
MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown);
MU_RUN_TEST(furi_hal_i2c_int_1b);
MU_RUN_TEST(furi_hal_i2c_int_3b);
MU_RUN_TEST(furi_hal_i2c_int_ext_3b);
MU_RUN_TEST(furi_hal_i2c_int_1b_fail);
}

MU_TEST_SUITE(furi_hal_i2c_ext_suite) {
MU_SUITE_CONFIGURE(&furi_hal_i2c_ext_setup, &furi_hal_i2c_ext_teardown);
MU_RUN_TEST(furi_hal_i2c_ext_eeprom);
}

int run_minunit_test_furi_hal() {
MU_RUN_SUITE(furi_hal_i2c_int_suite);
MU_RUN_SUITE(furi_hal_i2c_ext_suite);
return MU_EXIT_CODE;
}
Loading

0 comments on commit 8666cdc

Please sign in to comment.