mirror of
https://github.com/whowechina/aic_pico.git
synced 2025-02-17 19:09:24 +01:00
First commit
This commit is contained in:
commit
82353005f2
4
firmware/.gitignore
vendored
Normal file
4
firmware/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
build
|
||||
pico-examples
|
||||
pico-sdk
|
||||
.vscode
|
11
firmware/CMakeLists.txt
Normal file
11
firmware/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
# Pull in SDK (must set be before project)
|
||||
include(pico_sdk_import.cmake)
|
||||
|
||||
project(iidx_pico C CXX ASM)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
add_subdirectory(src)
|
BIN
firmware/aic_pico.uf2
Normal file
BIN
firmware/aic_pico.uf2
Normal file
Binary file not shown.
73
firmware/pico_sdk_import.cmake
Normal file
73
firmware/pico_sdk_import.cmake
Normal file
@ -0,0 +1,73 @@
|
||||
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||
|
||||
# This can be dropped into an external project to help locate this SDK
|
||||
# It should be include()ed prior to project()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||
|
||||
if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT)
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
# GIT_SUBMODULES_RECURSE was added in 3.17
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
GIT_SUBMODULES_RECURSE FALSE
|
||||
)
|
||||
else ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT pico_sdk)
|
||||
message("Downloading Raspberry Pi Pico SDK")
|
||||
FetchContent_Populate(pico_sdk)
|
||||
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||
endif ()
|
||||
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||
else ()
|
||||
message(FATAL_ERROR
|
||||
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||
|
||||
include(${PICO_SDK_INIT_CMAKE_FILE})
|
34
firmware/src/CMakeLists.txt
Normal file
34
firmware/src/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
set(BTSTACK_ROOT ${PICO_SDK_PATH}/lib/btstack)
|
||||
set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip)
|
||||
|
||||
function(make_firmware board board_def)
|
||||
pico_sdk_init()
|
||||
add_executable(${board}
|
||||
main.c save.c config.c commands.c
|
||||
aime.c cli.c pn532.c
|
||||
usb_descriptors.c)
|
||||
target_compile_definitions(${board} PUBLIC ${board_def})
|
||||
pico_enable_stdio_usb(${board} 1)
|
||||
pico_enable_stdio_uart(${board} 0)
|
||||
|
||||
pico_generate_pio_header(${board} ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio)
|
||||
|
||||
target_compile_options(${board} PRIVATE -Wall -Werror -Wfatal-errors -O3)
|
||||
target_include_directories(${board} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_include_directories(${board} PRIVATE
|
||||
${BTSTACK_ROOT}/src
|
||||
${LWIP_ROOT}/src/include)
|
||||
|
||||
target_link_libraries(${board} PRIVATE
|
||||
pico_multicore pico_stdlib hardware_pio hardware_pwm hardware_flash
|
||||
hardware_adc hardware_i2c hardware_watchdog
|
||||
tinyusb_device tinyusb_board)
|
||||
|
||||
pico_add_extra_outputs(${board})
|
||||
|
||||
add_custom_command(TARGET ${board} POST_BUILD
|
||||
COMMAND cp ${board}.uf2 ${CMAKE_CURRENT_LIST_DIR}/..)
|
||||
endfunction()
|
||||
|
||||
make_firmware(aic_pico BOARD_AIC_PICO)
|
||||
|
704
firmware/src/aime.c
Normal file
704
firmware/src/aime.c
Normal file
@ -0,0 +1,704 @@
|
||||
/*
|
||||
* AIME Reader
|
||||
* WHowe <github.com/whowechina>
|
||||
*
|
||||
* Use PN532 to read AIME
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bsp/board.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/i2c.h"
|
||||
|
||||
#include "pn532.h"
|
||||
|
||||
#define FRAME_TIMEOUT 200000
|
||||
|
||||
enum {
|
||||
CMD_GET_FW_VERSION = 0x30,
|
||||
CMD_GET_HW_VERSION = 0x32,
|
||||
// Card read
|
||||
CMD_START_POLLING = 0x40,
|
||||
CMD_STOP_POLLING = 0x41,
|
||||
CMD_CARD_DETECT = 0x42,
|
||||
CMD_CARD_SELECT = 0x43,
|
||||
CMD_CARD_HALT = 0x44,
|
||||
// MIFARE
|
||||
CMD_MIFARE_KEY_SET_A = 0x50,
|
||||
CMD_MIFARE_AUTHORIZE_A = 0x51,
|
||||
CMD_MIFARE_READ = 0x52,
|
||||
CMD_MIFARE_WRITE = 0x53,
|
||||
CMD_MIFARE_KEY_SET_B = 0x54,
|
||||
CMD_MIFARE_AUTHORIZE_B = 0x55,
|
||||
// Boot,update
|
||||
CMD_TO_UPDATER_MODE = 0x60,
|
||||
CMD_SEND_HEX_DATA = 0x61,
|
||||
CMD_TO_NORMAL_MODE = 0x62,
|
||||
CMD_SEND_BINDATA_INIT = 0x63,
|
||||
CMD_SEND_BINDATA_EXEC = 0x64,
|
||||
// FeliCa
|
||||
CMD_FELICA_PUSH = 0x70,
|
||||
CMD_FELICA_OP = 0x71,
|
||||
CMD_FELICA_OP_POLL = 0x00,
|
||||
CMD_FELICA_OP_READ = 0x06,
|
||||
CMD_FELICA_OP_WRITE = 0x08,
|
||||
CMD_FELICA_OP_GET_SYSTEM_CODE = 0x0C,
|
||||
CMD_FELICA_OP_NDA_A4 = 0xA4,
|
||||
// LED board
|
||||
CMD_EXT_BOARD_LED = 0x80,
|
||||
CMD_EXT_BOARD_LED_RGB = 0x81,
|
||||
CMD_EXT_BOARD_LED_RGB_UNKNOWN = 0x82,
|
||||
CMD_EXT_BOARD_INFO = 0xf0,
|
||||
CMD_EXT_FIRM_SUM = 0xf2,
|
||||
CMD_EXT_SEND_HEX_DATA = 0xf3,
|
||||
CMD_EXT_TO_BOOT_MODE = 0xf4,
|
||||
CMD_EXT_TO_NORMAL_MODE = 0xf5,
|
||||
};
|
||||
|
||||
enum {
|
||||
STATUS_OK = 0,
|
||||
STATUS_NFCRW_INIT_ERROR = 1,
|
||||
STATUS_NFCRW_FIRMWARE_UP_TO_DATE = 3,
|
||||
STATUS_NFCRW_ACCESS_ERROR = 4,
|
||||
STATUS_CARD_DETECT_TIMEOUT = 5,
|
||||
STATUS_CARD_DETECT_ERROR = 32,
|
||||
STATUS_FELICA_ERROR = 33,
|
||||
};
|
||||
|
||||
const char *fw_version[] = { "TN32MSEC003S F/W Ver1.2", "\x94" };
|
||||
const char *hw_version[] = { "TN32MSEC003S H/W Ver3.0", "837-15396" };
|
||||
const char *led_info[] = { "15084\xFF\x10\x00\x12", "000-00000\xFF\x11\x40" };
|
||||
static int baudrate_mode = 0;
|
||||
|
||||
static struct {
|
||||
bool enabled; // feature enabled
|
||||
bool active; // currently active
|
||||
uint8_t idm[8];
|
||||
const uint8_t pmm[8];
|
||||
const uint8_t syscode[2];
|
||||
} virtual_aic = { true, false, "", "\x00\xf1\x00\x00\x00\x01\x43\x00", "\x88\xb4" };
|
||||
|
||||
void aime_set_baudrate(int mode)
|
||||
{
|
||||
baudrate_mode = (mode == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
void aime_set_virtual_aic(bool enable)
|
||||
{
|
||||
virtual_aic.enabled = enable;
|
||||
}
|
||||
|
||||
static int aime_interface = -1;
|
||||
|
||||
void aime_init(int interface)
|
||||
{
|
||||
aime_interface = interface;
|
||||
pn532_config_sam();
|
||||
}
|
||||
|
||||
static uint8_t mifare_keys[2][6]; // 'KeyA' and 'KeyB'
|
||||
|
||||
static union __attribute__((packed)) {
|
||||
struct {
|
||||
uint8_t len;
|
||||
uint8_t addr;
|
||||
uint8_t seq;
|
||||
uint8_t cmd;
|
||||
uint8_t status;
|
||||
uint8_t payload_len;
|
||||
uint8_t payload[];
|
||||
};
|
||||
uint8_t raw[256];
|
||||
} response;
|
||||
|
||||
static union __attribute__((packed)) {
|
||||
struct {
|
||||
uint8_t len;
|
||||
uint8_t addr;
|
||||
uint8_t seq;
|
||||
uint8_t cmd;
|
||||
uint8_t payload_len;
|
||||
union {
|
||||
struct {
|
||||
uint8_t uid[4];
|
||||
uint8_t block_id;
|
||||
} mifare;
|
||||
struct {
|
||||
uint8_t idm[8];
|
||||
uint8_t len;
|
||||
uint8_t code;
|
||||
uint8_t data[0];
|
||||
} felica;
|
||||
uint8_t payload[250];
|
||||
};
|
||||
};
|
||||
uint8_t raw[256];
|
||||
} request;
|
||||
|
||||
struct {
|
||||
bool active;
|
||||
uint8_t len;
|
||||
uint8_t check_sum;
|
||||
bool escaping;
|
||||
uint64_t time;
|
||||
} req_ctx;
|
||||
|
||||
static void build_response(int payload_len)
|
||||
{
|
||||
response.len = payload_len + 6;
|
||||
response.addr = request.addr;
|
||||
response.seq = request.seq;
|
||||
response.cmd = request.cmd;
|
||||
response.status = STATUS_OK;
|
||||
response.payload_len = payload_len;
|
||||
}
|
||||
|
||||
static void send_response()
|
||||
{
|
||||
uint8_t checksum = 0;
|
||||
|
||||
uint8_t sync = 0xe0;
|
||||
tud_cdc_n_write(aime_interface, &sync, 1);
|
||||
|
||||
for (int i = 0; i < response.len; i++) {
|
||||
uint8_t c = response.raw[i];
|
||||
checksum += c;
|
||||
if (c == 0xe0 || c == 0xd0) {
|
||||
uint8_t escape = 0xd0;
|
||||
tud_cdc_n_write(aime_interface, &escape, 1);
|
||||
c--;
|
||||
}
|
||||
tud_cdc_n_write(aime_interface, &c, 1);
|
||||
}
|
||||
|
||||
tud_cdc_n_write(aime_interface, &checksum, 1);
|
||||
tud_cdc_n_write_flush(aime_interface);
|
||||
}
|
||||
|
||||
static void send_simple_response(uint8_t status)
|
||||
{
|
||||
build_response(0);
|
||||
response.status = status;
|
||||
send_response();
|
||||
}
|
||||
|
||||
static void cmd_to_normal_mode()
|
||||
{
|
||||
send_simple_response(pn532_firmware_ver() ? STATUS_NFCRW_FIRMWARE_UP_TO_DATE
|
||||
: STATUS_NFCRW_INIT_ERROR);
|
||||
}
|
||||
|
||||
static void cmd_fake_version(const char *version[])
|
||||
{
|
||||
int len = strlen(version[baudrate_mode]);
|
||||
build_response(len);
|
||||
memcpy(response.payload, version[baudrate_mode], len);
|
||||
send_response();
|
||||
}
|
||||
|
||||
static void cmd_key_set(uint8_t key[6])
|
||||
{
|
||||
memcpy(key, request.payload, 6);
|
||||
send_simple_response(STATUS_OK);
|
||||
}
|
||||
|
||||
static void cmd_set_polling(bool enabled)
|
||||
{
|
||||
pn532_set_rf_field(0, enabled ? 1 : 0);
|
||||
send_simple_response(STATUS_OK);
|
||||
}
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t count;
|
||||
uint8_t type;
|
||||
uint8_t id_len;
|
||||
union {
|
||||
struct {
|
||||
uint8_t idm[8];
|
||||
uint8_t pmm[8];
|
||||
uint8_t syscode[2];
|
||||
};
|
||||
uint8_t uid[6];
|
||||
};
|
||||
} card_info_t;
|
||||
|
||||
static void handle_mifare_card(const uint8_t *uid, int len)
|
||||
{
|
||||
card_info_t *card = (card_info_t *) response.payload;
|
||||
|
||||
build_response(len > 4 ? 10 : 7);
|
||||
card->count = 1;
|
||||
card->type = 0x10;
|
||||
card->id_len = len;
|
||||
|
||||
memcpy(card->uid, uid, len);
|
||||
}
|
||||
|
||||
static void handle_felica_card(const uint8_t felica_ids[18])
|
||||
{
|
||||
build_response(19);
|
||||
card_info_t *card = (card_info_t *) response.payload;
|
||||
|
||||
card->count = 1;
|
||||
card->type = 0x20;
|
||||
card->id_len = 16;
|
||||
memcpy(card->idm, felica_ids, 8);
|
||||
memcpy(card->pmm, felica_ids + 8, 8);
|
||||
}
|
||||
|
||||
static void fake_felica_card()
|
||||
{
|
||||
build_response(19);
|
||||
card_info_t *card = (card_info_t *) response.payload;
|
||||
|
||||
card->count = 1;
|
||||
card->type = 0x20;
|
||||
card->id_len = 16;
|
||||
memcpy(card->idm, virtual_aic.idm, 8);
|
||||
memcpy(card->pmm, virtual_aic.pmm, 8);
|
||||
}
|
||||
|
||||
static void handle_no_card()
|
||||
{
|
||||
build_response(1);
|
||||
card_info_t *card = (card_info_t *) response.payload;
|
||||
|
||||
card->count = 0;
|
||||
response.status = STATUS_OK;
|
||||
}
|
||||
|
||||
static void printf_hex(const uint8_t *hex, int len, const char *tail)
|
||||
{
|
||||
for (int i = 0; i < len; i ++) {
|
||||
printf(" %02x", hex[i]);
|
||||
}
|
||||
printf("%s", tail);
|
||||
}
|
||||
|
||||
static void cmd_detect_card()
|
||||
{
|
||||
uint8_t buf[18];
|
||||
|
||||
virtual_aic.active = false;
|
||||
|
||||
int len = sizeof(buf);
|
||||
if (pn532_poll_mifare(buf, &len)) {
|
||||
printf("Detected Mifare - ");
|
||||
printf_hex(buf, len, "\n");
|
||||
|
||||
if (virtual_aic.enabled) {
|
||||
virtual_aic.active = true;
|
||||
memset(virtual_aic.idm, 0, 8);
|
||||
memcpy(virtual_aic.idm, "\x01\x01", 2);
|
||||
memcpy(virtual_aic.idm + 2, buf, len);
|
||||
|
||||
printf("Virtual AIC -");
|
||||
printf_hex(virtual_aic.idm, 8, ",");
|
||||
printf_hex(virtual_aic.pmm, 8, ",");
|
||||
printf_hex(virtual_aic.syscode, 2, "\n");
|
||||
|
||||
fake_felica_card();
|
||||
} else {
|
||||
handle_mifare_card(buf, len);
|
||||
}
|
||||
} else if (pn532_poll_felica(buf, buf + 8, buf + 16, false)) {
|
||||
printf("Detected Felica -");
|
||||
printf_hex(buf, 8, ",");
|
||||
printf_hex(buf + 8, 8, ",");
|
||||
printf_hex(buf + 16, 2, "\n");
|
||||
|
||||
handle_felica_card(buf);
|
||||
} else {
|
||||
handle_no_card();
|
||||
}
|
||||
|
||||
send_response();
|
||||
}
|
||||
|
||||
static void cmd_card_select()
|
||||
{
|
||||
printf("CARD SELECT %d\n", request.payload_len);
|
||||
send_simple_response(STATUS_OK);
|
||||
}
|
||||
|
||||
static void cmd_mifare_auth(int type)
|
||||
{
|
||||
printf("MIFARE AUTH\n");
|
||||
pn532_mifare_auth(request.mifare.uid, request.mifare.block_id, type, mifare_keys[type]);
|
||||
send_simple_response(STATUS_OK);
|
||||
}
|
||||
|
||||
static void cmd_mifare_read()
|
||||
{
|
||||
printf("MIFARE READ %02x %02x %02x %02x %02x\n", request.mifare.block_id,
|
||||
request.mifare.uid[0], request.mifare.uid[1], request.mifare.uid[2],
|
||||
request.mifare.uid[3]);
|
||||
build_response(16);
|
||||
memset(response.payload, 0, 16);
|
||||
pn532_mifare_read(request.mifare.block_id, response.payload);
|
||||
send_response();
|
||||
}
|
||||
|
||||
static void cmd_mifare_halt()
|
||||
{
|
||||
printf("MIFARE HALT\n");
|
||||
send_simple_response(STATUS_OK);
|
||||
}
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t len;
|
||||
uint8_t code;
|
||||
uint8_t idm[8];
|
||||
uint8_t data[0];
|
||||
} felica_resp_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t idm[8];
|
||||
uint8_t pmm[8];
|
||||
uint8_t syscode[2];
|
||||
} card_id_t;
|
||||
|
||||
static int cmd_felica_poll(const card_id_t *card)
|
||||
{
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t pmm[8];
|
||||
uint8_t syscode[2];
|
||||
} felica_poll_resp_t;
|
||||
|
||||
printf("FELICA POLL\n");
|
||||
|
||||
felica_resp_t *felica_resp = (felica_resp_t *) response.payload;
|
||||
felica_poll_resp_t *poll_resp = (felica_poll_resp_t *) felica_resp->data;
|
||||
|
||||
if (virtual_aic.active) {
|
||||
memcpy(poll_resp->pmm, virtual_aic.pmm, 8);
|
||||
memcpy(poll_resp->syscode, virtual_aic.syscode, 2);
|
||||
} else {
|
||||
memcpy(poll_resp->pmm, card->pmm, 8);
|
||||
memcpy(poll_resp->syscode, card->syscode, 2);
|
||||
}
|
||||
|
||||
return sizeof(*poll_resp);
|
||||
}
|
||||
|
||||
static int cmd_felica_get_syscode(const card_id_t *card)
|
||||
{
|
||||
printf("FELICA GET_SYSTEM_CODE\n");
|
||||
felica_resp_t *felica_resp = (felica_resp_t *) response.payload;
|
||||
felica_resp->data[0] = 0x01;
|
||||
if (virtual_aic.active) {
|
||||
memcpy(felica_resp->data, virtual_aic.syscode, 2);
|
||||
} else {
|
||||
felica_resp->data[1] = card->syscode[0];
|
||||
felica_resp->data[2] = card->syscode[1];
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int cmd_felica_read()
|
||||
{
|
||||
printf("FELICA READ\n");
|
||||
uint8_t *req_data = request.felica.data;
|
||||
|
||||
if (req_data[8] != 1) {
|
||||
printf("Felica Encap READ Error: service_num != 1\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint16_t svc_code = req_data[9] | (req_data[10] << 8);
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t rw_status[2];
|
||||
uint8_t block_num;
|
||||
uint8_t block_data[0][16];
|
||||
} encap_read_resp_t;
|
||||
|
||||
felica_resp_t *felica_resp = (felica_resp_t *) response.payload;
|
||||
encap_read_resp_t *read_resp = (encap_read_resp_t *) felica_resp->data;
|
||||
|
||||
uint8_t *block = req_data + 11;
|
||||
uint8_t block_num = block[0];
|
||||
|
||||
memset(read_resp->block_data, 0, block_num * 16);
|
||||
|
||||
for (int i = 0; i < block_num; i++) {
|
||||
uint16_t block_id = (block[i * 2 + 1] << 8) | block[i * 2 + 2];
|
||||
if (block_id == 0x8082) {
|
||||
memcpy(read_resp->block_data[i], felica_resp->idm, 8);
|
||||
}
|
||||
if (!virtual_aic.active) {
|
||||
pn532_felica_read_wo_encrypt(svc_code, block_id, read_resp->block_data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
read_resp->rw_status[0] = 0x00;
|
||||
read_resp->rw_status[1] = 0x00;
|
||||
read_resp->block_num = block_num;
|
||||
|
||||
return sizeof(*read_resp) + block_num * 16;
|
||||
}
|
||||
|
||||
static int cmd_felica_write()
|
||||
{
|
||||
printf("FELICA WRITE\n");
|
||||
uint8_t *req_data = request.felica.data;
|
||||
|
||||
if (req_data[8] != 1) {
|
||||
printf("Felica Encap Write Error: service_num != 1\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint16_t svc_code = req_data[9] | (req_data[10] << 8);
|
||||
|
||||
uint8_t *block = req_data + 11;
|
||||
uint8_t block_num = block[0];
|
||||
|
||||
felica_resp_t *felica_resp = (felica_resp_t *) response.payload;
|
||||
uint8_t *rw_status = felica_resp->data;
|
||||
|
||||
printf("svc code: %04x\n", svc_code);
|
||||
printf("blocks:");
|
||||
for (int i = 0; i < block_num; i++) {
|
||||
uint16_t block_id = (block[i * 2 + 1] << 8) | block[i * 2 + 2];
|
||||
printf(" %04x", block_id);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
uint8_t *block_data = block + 1 + block_num * 2;
|
||||
|
||||
rw_status[0] = 0x00;
|
||||
rw_status[1] = 0x00;
|
||||
|
||||
for (int i = 0; i < block_num; i++) {
|
||||
printf("writing %02x %02x\n", block_data[i * 16], block_data[i * 16 + 15]);
|
||||
// uint16_t block_id = (block[i * 2 + 1] << 8) | block[i * 2 + 2];
|
||||
// int result = pn532_felica_write_wo_encrypt(svc_code, block_id, block_data + i * 16);
|
||||
int result = 0;
|
||||
if (result < 0) {
|
||||
rw_status[0] = 0x01;
|
||||
rw_status[1] = 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
case CMD_FELICA_OP_NDA_A4:
|
||||
printf("\tNDA_A4\n");
|
||||
build_response(11);
|
||||
resp->payload[0] = 0x00;
|
||||
break;
|
||||
default:
|
||||
printf("\tUnknown through: %02x\n", code);
|
||||
build_response(0);
|
||||
response.status = STATUS_OK;
|
||||
#endif
|
||||
|
||||
static void cmd_felica()
|
||||
{
|
||||
card_id_t card;
|
||||
if (!pn532_poll_felica(card.idm, card.pmm, card.syscode, true)) {
|
||||
send_simple_response(STATUS_FELICA_ERROR);
|
||||
}
|
||||
|
||||
uint8_t felica_code = request.felica.code;
|
||||
printf("Felica (%02x):", felica_code);
|
||||
for (int i = 0; i < request.payload_len; i++) {
|
||||
printf(" %02x", request.payload[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
int datalen = -1;
|
||||
switch (felica_code) {
|
||||
case CMD_FELICA_OP_GET_SYSTEM_CODE:
|
||||
datalen = cmd_felica_get_syscode(&card);
|
||||
break;
|
||||
case CMD_FELICA_OP_POLL:
|
||||
datalen = cmd_felica_poll(&card);
|
||||
break;
|
||||
case CMD_FELICA_OP_READ:
|
||||
datalen = cmd_felica_read();
|
||||
break;
|
||||
case CMD_FELICA_OP_WRITE:
|
||||
datalen = cmd_felica_write();
|
||||
break;
|
||||
default:
|
||||
printf("Unknown code %d\n", felica_code);
|
||||
break;
|
||||
}
|
||||
|
||||
if (datalen < 0) {
|
||||
send_simple_response(STATUS_FELICA_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
felica_resp_t *felica_resp = (felica_resp_t *) response.payload;
|
||||
build_response(sizeof(*felica_resp) + datalen);
|
||||
felica_resp->len = response.payload_len;
|
||||
felica_resp->code = felica_code + 1;
|
||||
if (virtual_aic.active) {
|
||||
memcpy(felica_resp->idm, virtual_aic.idm, 8);
|
||||
} else {
|
||||
memcpy(felica_resp->idm, card.idm, 8);
|
||||
}
|
||||
|
||||
send_response();
|
||||
|
||||
printf("Felica Response:");
|
||||
for (int i = 0; i < felica_resp->len - 10; i++) {
|
||||
printf(" %02x", felica_resp->data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static uint32_t led_color;
|
||||
|
||||
static void cmd_led_rgb()
|
||||
{
|
||||
led_color = request.payload[0] << 16 | request.payload[1] << 8 | request.payload[2];
|
||||
build_response(0);
|
||||
send_response();
|
||||
}
|
||||
|
||||
static void aime_handle_frame()
|
||||
{
|
||||
switch (request.cmd) {
|
||||
case CMD_TO_NORMAL_MODE:
|
||||
cmd_to_normal_mode();
|
||||
break;
|
||||
case CMD_GET_FW_VERSION:
|
||||
printf("CMD_GET_FW_VERSION\n");
|
||||
cmd_fake_version(fw_version);
|
||||
break;
|
||||
case CMD_GET_HW_VERSION:
|
||||
printf("CMD_GET_HW_VERSION\n");
|
||||
cmd_fake_version(hw_version);
|
||||
break;
|
||||
case CMD_MIFARE_KEY_SET_A:
|
||||
printf("CMD_MIFARE_KEY_SET_A\n");
|
||||
cmd_key_set(mifare_keys[0]);
|
||||
break;
|
||||
case CMD_MIFARE_KEY_SET_B:
|
||||
printf("CMD_MIFARE_KEY_SET_B\n");
|
||||
cmd_key_set(mifare_keys[1]);
|
||||
break;
|
||||
|
||||
case CMD_START_POLLING:
|
||||
cmd_set_polling(true);
|
||||
break;
|
||||
case CMD_STOP_POLLING:
|
||||
cmd_set_polling(false);
|
||||
break;
|
||||
case CMD_CARD_DETECT:
|
||||
cmd_detect_card();
|
||||
break;
|
||||
case CMD_FELICA_OP:
|
||||
cmd_felica();
|
||||
break;
|
||||
|
||||
case CMD_CARD_SELECT:
|
||||
cmd_card_select();
|
||||
break;
|
||||
|
||||
case CMD_MIFARE_AUTHORIZE_A:
|
||||
cmd_mifare_auth(0);
|
||||
break;
|
||||
|
||||
case CMD_MIFARE_AUTHORIZE_B:
|
||||
cmd_mifare_auth(1);
|
||||
break;
|
||||
|
||||
case CMD_MIFARE_READ:
|
||||
cmd_mifare_read();
|
||||
break;
|
||||
|
||||
case CMD_CARD_HALT:
|
||||
cmd_mifare_halt();
|
||||
break;
|
||||
|
||||
case CMD_EXT_BOARD_INFO:
|
||||
cmd_fake_version(led_info);
|
||||
break;
|
||||
case CMD_EXT_BOARD_LED_RGB:
|
||||
cmd_led_rgb();
|
||||
break;
|
||||
|
||||
case CMD_SEND_HEX_DATA:
|
||||
case CMD_EXT_TO_NORMAL_MODE:
|
||||
send_simple_response(STATUS_OK);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Unknown command: %02x [", request.cmd);
|
||||
for (int i = 0; i < request.len; i++) {
|
||||
printf(" %02x", request.raw[i]);
|
||||
}
|
||||
printf("]\n");
|
||||
send_simple_response(STATUS_OK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool aime_feed(int c)
|
||||
{
|
||||
if (c == 0xe0) {
|
||||
req_ctx.active = true;
|
||||
req_ctx.len = 0;
|
||||
req_ctx.check_sum = 0;
|
||||
req_ctx.escaping = false;
|
||||
req_ctx.time = time_us_64();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!req_ctx.active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c == 0xd0) {
|
||||
req_ctx.escaping = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (req_ctx.escaping) {
|
||||
c++;
|
||||
req_ctx.escaping = false;
|
||||
}
|
||||
|
||||
if (req_ctx.len != 0 && req_ctx.len == request.len) {
|
||||
if (req_ctx.check_sum == c) {
|
||||
aime_handle_frame();
|
||||
req_ctx.active = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
request.raw[req_ctx.len] = c;
|
||||
req_ctx.len++;
|
||||
req_ctx.check_sum += c;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void aime_update()
|
||||
{
|
||||
if (req_ctx.active && time_us_64() - req_ctx.time > FRAME_TIMEOUT) {
|
||||
req_ctx.active = false;
|
||||
}
|
||||
|
||||
if (tud_cdc_n_available(aime_interface)) {
|
||||
uint8_t buf[32];
|
||||
uint32_t count = tud_cdc_n_read(aime_interface, buf, sizeof(buf));
|
||||
for (int i = 0; i < count; i++) {
|
||||
aime_feed(buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t aime_led_color()
|
||||
{
|
||||
return led_color;
|
||||
}
|
17
firmware/src/aime.h
Normal file
17
firmware/src/aime.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* AIME Reader
|
||||
* WHowe <github.com/whowechina>
|
||||
*/
|
||||
|
||||
#ifndef AIME_H
|
||||
#define AIME_H
|
||||
|
||||
void aime_init(int interface);
|
||||
void aime_update();
|
||||
uint32_t aime_led_color();
|
||||
|
||||
// mode 0 or 1
|
||||
void aime_set_baudrate(int mode);
|
||||
void aime_set_virtual_aic(bool enable);
|
||||
|
||||
#endif
|
18
firmware/src/board_defs.h
Normal file
18
firmware/src/board_defs.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* AIC Reader Board Definitions
|
||||
* WHowe <github.com/whowechina>
|
||||
*/
|
||||
|
||||
#if defined BOARD_AIC_PICO
|
||||
|
||||
#define I2C_PORT i2c0
|
||||
#define I2C_SCL 21
|
||||
#define I2C_SDA 20
|
||||
#define I2C_FREQ 733*1000
|
||||
|
||||
#define RGB_PIN 2
|
||||
#define RGB_ORDER GRB // or RGB
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
184
firmware/src/cli.c
Normal file
184
firmware/src/cli.c
Normal file
@ -0,0 +1,184 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "pico/stdio.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "cli.h"
|
||||
#include "save.h"
|
||||
|
||||
#define MAX_COMMANDS 32
|
||||
#define MAX_PARAMETERS 6
|
||||
#define MAX_PARAMETER_LENGTH 20
|
||||
|
||||
const char *cli_prompt = "cli>";
|
||||
const char *cli_logo = "CLI";
|
||||
|
||||
static const char *commands[MAX_COMMANDS];
|
||||
static const char *helps[MAX_COMMANDS];
|
||||
static cmd_handler_t handlers[MAX_COMMANDS];
|
||||
static int max_cmd_len = 0;
|
||||
|
||||
static int num_commands = 0;
|
||||
|
||||
void cli_register(const char *cmd, cmd_handler_t handler, const char *help)
|
||||
{
|
||||
if (num_commands < MAX_COMMANDS) {
|
||||
commands[num_commands] = cmd;
|
||||
handlers[num_commands] = handler;
|
||||
helps[num_commands] = help;
|
||||
num_commands++;
|
||||
if (strlen(cmd) > max_cmd_len) {
|
||||
max_cmd_len = strlen(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return -1 if not matched, return -2 if ambiguous
|
||||
int cli_match_prefix(const char *str[], int num, const char *prefix)
|
||||
{
|
||||
int match = -1;
|
||||
bool found = false;
|
||||
|
||||
for (int i = 0; (i < num) && str[i]; i++) {
|
||||
if (strncasecmp(str[i], prefix, strlen(prefix)) == 0) {
|
||||
if (found) {
|
||||
return -2;
|
||||
}
|
||||
found = true;
|
||||
match = i;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
static void handle_help(int argc, char *argv[])
|
||||
{
|
||||
printf("%s", cli_logo);
|
||||
printf("\tSN: %016llx\n\n", board_id_64());
|
||||
printf("Available commands:\n");
|
||||
for (int i = 0; i < num_commands; i++) {
|
||||
printf("%*s: %s\n", max_cmd_len + 2, commands[i], helps[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int fps[2];
|
||||
void cli_fps_count(int core)
|
||||
{
|
||||
static uint32_t last[2] = {0};
|
||||
static int counter[2] = {0};
|
||||
|
||||
counter[core]++;
|
||||
|
||||
uint32_t now = time_us_32();
|
||||
if (now - last[core] < 1000000) {
|
||||
return;
|
||||
}
|
||||
last[core] = now;
|
||||
fps[core] = counter[core];
|
||||
counter[core] = 0;
|
||||
}
|
||||
|
||||
static void handle_fps(int argc, char *argv[])
|
||||
{
|
||||
printf("FPS: core 0: %d, core 1: %d\n", fps[0], fps[1]);
|
||||
}
|
||||
|
||||
int cli_extract_non_neg_int(const char *param, int len)
|
||||
{
|
||||
if (len == 0) {
|
||||
len = strlen(param);
|
||||
}
|
||||
int result = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!isdigit((uint8_t)param[i])) {
|
||||
return -1;
|
||||
}
|
||||
result = result * 10 + param[i] - '0';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static char cmd_buf[256];
|
||||
static int cmd_len = 0;
|
||||
|
||||
static void process_cmd()
|
||||
{
|
||||
char *argv[MAX_PARAMETERS];
|
||||
int argc;
|
||||
|
||||
char *cmd = strtok(cmd_buf, " \n");
|
||||
|
||||
if (strlen(cmd) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
argc = 0;
|
||||
while ((argc < MAX_PARAMETERS) &&
|
||||
(argv[argc] = strtok(NULL, " ,\n")) != NULL) {
|
||||
argc++;
|
||||
}
|
||||
|
||||
int match = cli_match_prefix(commands, num_commands, cmd);
|
||||
if (match == -2) {
|
||||
printf("Ambiguous command.\n");
|
||||
return;
|
||||
} else if (match == -1) {
|
||||
printf("Unknown command.\n");
|
||||
handle_help(0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
handlers[match](argc, argv);
|
||||
}
|
||||
|
||||
void cli_run()
|
||||
{
|
||||
int c = getchar_timeout_us(0);
|
||||
if (c == EOF) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '\b' || c == 127) { // both backspace and delete
|
||||
if (cmd_len > 0) {
|
||||
cmd_len--;
|
||||
printf("\b \b");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((c != '\n') && (c != '\r')) {
|
||||
|
||||
if (cmd_len < sizeof(cmd_buf) - 2) {
|
||||
cmd_buf[cmd_len] = c;
|
||||
printf("%c", c);
|
||||
cmd_len++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cmd_buf[cmd_len] = '\0';
|
||||
cmd_len = 0;
|
||||
|
||||
printf("\n");
|
||||
|
||||
process_cmd();
|
||||
|
||||
printf(cli_prompt);
|
||||
}
|
||||
|
||||
void cli_init(const char *prompt, const char *logo)
|
||||
{
|
||||
if (prompt) {
|
||||
cli_prompt = prompt;
|
||||
}
|
||||
if (logo) {
|
||||
cli_logo = logo;
|
||||
}
|
||||
|
||||
cli_register("?", handle_help, "Display this help message.");
|
||||
cli_register("fps", handle_fps, "Display FPS.");
|
||||
}
|
20
firmware/src/cli.h
Normal file
20
firmware/src/cli.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Command Line Framework
|
||||
* WHowe <github.com/whowechina>
|
||||
*/
|
||||
|
||||
#ifndef CLI_H
|
||||
#define CLI_H
|
||||
|
||||
|
||||
typedef void (*cmd_handler_t)(int argc, char *argv[]);
|
||||
|
||||
void cli_init();
|
||||
void cli_register(const char *cmd, cmd_handler_t handler, const char *help);
|
||||
void cli_run();
|
||||
void cli_fps_count(int core);
|
||||
|
||||
int cli_extract_non_neg_int(const char *param, int len);
|
||||
int cli_match_prefix(const char *str[], int num, const char *prefix);
|
||||
|
||||
#endif
|
92
firmware/src/commands.c
Normal file
92
firmware/src/commands.c
Normal file
@ -0,0 +1,92 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "pico/stdio.h"
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "save.h"
|
||||
#include "cli.h"
|
||||
|
||||
#include "pn532.h"
|
||||
|
||||
static int fps[2];
|
||||
void fps_count(int core)
|
||||
{
|
||||
static uint32_t last[2] = {0};
|
||||
static int counter[2] = {0};
|
||||
|
||||
counter[core]++;
|
||||
|
||||
uint32_t now = time_us_32();
|
||||
if (now - last[core] < 1000000) {
|
||||
return;
|
||||
}
|
||||
last[core] = now;
|
||||
fps[core] = counter[core];
|
||||
counter[core] = 0;
|
||||
}
|
||||
|
||||
static void handle_save()
|
||||
{
|
||||
save_request(true);
|
||||
}
|
||||
|
||||
static void handle_factory_reset()
|
||||
{
|
||||
config_factory_reset();
|
||||
printf("Factory reset done.\n");
|
||||
}
|
||||
|
||||
static void handle_nfc()
|
||||
{
|
||||
bool ret;
|
||||
|
||||
ret = pn532_config_rf();
|
||||
printf("RF: %d\n", ret);
|
||||
|
||||
ret = pn532_config_sam();
|
||||
printf("Sam: %d\n", ret);
|
||||
|
||||
uint8_t buf[32];
|
||||
int len = sizeof(buf);
|
||||
|
||||
ret = pn532_poll_mifare(buf, &len);
|
||||
printf("Mifare: %d -", len);
|
||||
|
||||
if (ret) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
printf(" %02x", buf[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
len = sizeof(buf);
|
||||
ret = pn532_poll_14443b(buf, &len);
|
||||
printf("14443B: %d -", len);
|
||||
|
||||
if (ret) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
printf(" %02x", buf[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("Felica: ");
|
||||
if (pn532_poll_felica(buf, buf + 8, buf + 16, false)) {
|
||||
for (int i = 0; i < 18; i++) {
|
||||
printf(" %02x%s", buf[i], (i % 8 == 7) ? "," : "");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void commands_init()
|
||||
{
|
||||
cli_register("save", handle_save, "Save config to flash.");
|
||||
cli_register("factory", handle_factory_reset, "Reset everything to default.");
|
||||
cli_register("nfc", handle_nfc, "NFC debug.");
|
||||
}
|
11
firmware/src/commands.h
Normal file
11
firmware/src/commands.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* AIC Pico Command Line Commands
|
||||
* WHowe <github.com/whowechina>
|
||||
*/
|
||||
|
||||
#ifndef COMMANDS_H
|
||||
#define COMMANDS_H
|
||||
|
||||
void commands_init();
|
||||
|
||||
#endif
|
36
firmware/src/config.c
Normal file
36
firmware/src/config.c
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Controller Config and Runtime Data
|
||||
* WHowe <github.com/whowechina>
|
||||
*
|
||||
* Config is a global data structure that stores all the configuration
|
||||
* Runtime is something to share between files.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "save.h"
|
||||
|
||||
aic_cfg_t *aic_cfg;
|
||||
|
||||
static aic_cfg_t default_cfg = { 0 };
|
||||
|
||||
aic_runtime_t *aic_runtime;
|
||||
|
||||
static void config_loaded()
|
||||
{
|
||||
}
|
||||
|
||||
void config_changed()
|
||||
{
|
||||
save_request(false);
|
||||
}
|
||||
|
||||
void config_factory_reset()
|
||||
{
|
||||
*aic_cfg = default_cfg;
|
||||
save_request(true);
|
||||
}
|
||||
|
||||
void config_init()
|
||||
{
|
||||
aic_cfg = (aic_cfg_t *)save_alloc(sizeof(*aic_cfg), &default_cfg, config_loaded);
|
||||
}
|
27
firmware/src/config.h
Normal file
27
firmware/src/config.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Controller Config
|
||||
* WHowe <github.com/whowechina>
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t reserved;
|
||||
} aic_cfg_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t fps[2];
|
||||
} aic_runtime_t;
|
||||
|
||||
extern aic_cfg_t *aic_cfg;
|
||||
extern aic_runtime_t *aic_runtime;
|
||||
|
||||
void config_init();
|
||||
void config_changed(); // Notify the config has changed
|
||||
void config_factory_reset(); // Reset the config to factory default
|
||||
|
||||
#endif
|
114
firmware/src/main.c
Normal file
114
firmware/src/main.c
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Controller Main
|
||||
* WHowe <github.com/whowechina>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "pico/stdio.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "bsp/board.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "pico/bootrom.h"
|
||||
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "hardware/structs/ioqspi.h"
|
||||
#include "hardware/structs/sio.h"
|
||||
|
||||
#include "board_defs.h"
|
||||
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
#include "save.h"
|
||||
#include "config.h"
|
||||
#include "cli.h"
|
||||
#include "commands.h"
|
||||
|
||||
#include "pn532.h"
|
||||
#include "aime.h"
|
||||
|
||||
void report_usb_hid()
|
||||
{
|
||||
if (tud_hid_ready()) {
|
||||
}
|
||||
}
|
||||
|
||||
static mutex_t core1_io_lock;
|
||||
static void core1_loop()
|
||||
{
|
||||
while (1) {
|
||||
if (mutex_try_enter(&core1_io_lock, NULL)) {
|
||||
mutex_exit(&core1_io_lock);
|
||||
}
|
||||
cli_fps_count(1);
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void core0_loop()
|
||||
{
|
||||
while(1) {
|
||||
tud_task();
|
||||
|
||||
cli_run();
|
||||
aime_update();
|
||||
|
||||
save_loop();
|
||||
cli_fps_count(0);
|
||||
|
||||
report_usb_hid();
|
||||
}
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
sleep_ms(50);
|
||||
set_sys_clock_khz(150000, true);
|
||||
board_init();
|
||||
tusb_init();
|
||||
stdio_init_all();
|
||||
|
||||
config_init();
|
||||
mutex_init(&core1_io_lock);
|
||||
save_init(0xca340a1c, &core1_io_lock);
|
||||
|
||||
|
||||
pn532_init(I2C_PORT, I2C_SCL, I2C_SDA, I2C_FREQ);
|
||||
aime_init(1);
|
||||
|
||||
cli_init("aic_pico>", "\n << AIC Pico >>\n"
|
||||
" https://github.com/whowechina\n\n");
|
||||
|
||||
commands_init();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
init();
|
||||
multicore_launch_core1(core1_loop);
|
||||
core0_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invoked when received GET_REPORT control request
|
||||
// Application must fill buffer report's content and return its length.
|
||||
// Return zero will cause the stack to STALL request
|
||||
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id,
|
||||
hid_report_type_t report_type, uint8_t *buffer,
|
||||
uint16_t reqlen)
|
||||
{
|
||||
printf("Get from USB %d-%d\n", report_id, report_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invoked when received SET_REPORT control request or
|
||||
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
|
||||
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id,
|
||||
hid_report_type_t report_type, uint8_t const *buffer,
|
||||
uint16_t bufsize)
|
||||
{
|
||||
}
|
497
firmware/src/pn532.c
Normal file
497
firmware/src/pn532.c
Normal file
@ -0,0 +1,497 @@
|
||||
/*
|
||||
* PN532 NFC Reader
|
||||
* WHowe <github.com/whowechina>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/i2c.h"
|
||||
|
||||
#include "pn532.h"
|
||||
|
||||
#define IO_TIMEOUT_US 1000
|
||||
#define PN532_I2C_ADDRESS 0x24
|
||||
|
||||
#define PN532_PREAMBLE 0
|
||||
#define PN532_STARTCODE1 0
|
||||
#define PN532_STARTCODE2 0xff
|
||||
#define PN532_POSTAMBLE 0
|
||||
|
||||
#define PN532_HOSTTOPN532 0xd4
|
||||
#define PN532_PN532TOHOST 0xd5
|
||||
|
||||
static i2c_inst_t *i2c_port = i2c0;
|
||||
|
||||
void pn532_init(i2c_inst_t *i2c, uint8_t scl, uint8_t sda, uint32_t freq)
|
||||
{
|
||||
i2c_init(i2c_port, freq);
|
||||
gpio_set_function(scl, GPIO_FUNC_I2C);
|
||||
gpio_set_function(sda, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(scl);
|
||||
gpio_pull_up(sda);
|
||||
i2c_port = i2c;
|
||||
}
|
||||
|
||||
static int pn532_write(const uint8_t *data, uint8_t len)
|
||||
{
|
||||
return i2c_write_blocking_until(i2c_port, PN532_I2C_ADDRESS, data, len, false,
|
||||
time_us_64() + IO_TIMEOUT_US * len);
|
||||
}
|
||||
|
||||
static int pn532_read(uint8_t *data, uint8_t len)
|
||||
{
|
||||
return i2c_read_blocking_until(i2c_port, PN532_I2C_ADDRESS, data, len, false,
|
||||
time_us_64() + IO_TIMEOUT_US * len);
|
||||
}
|
||||
|
||||
static bool pn532_wait_ready()
|
||||
{
|
||||
uint8_t status = 0;
|
||||
|
||||
for (int retry = 0; retry < 20; retry++) {
|
||||
if (pn532_read(&status, 1) == 1 && status == 0x01) {
|
||||
return true;
|
||||
}
|
||||
sleep_us(1000);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int read_frame(uint8_t *frame, uint8_t len)
|
||||
{
|
||||
uint8_t buf[len + 1];
|
||||
int ret = pn532_read(buf, len + 1);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("I2C data read: %d -", ret);
|
||||
for (int i = 0; i < len + 1; i++) {
|
||||
printf(" %02x", buf[i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
if (ret == len + 1) {
|
||||
memcpy(frame, buf + 1, len);
|
||||
return len;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_frame(const uint8_t *frame, uint8_t len)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("I2C frame write: %d -", len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
printf(" %02x", frame[i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
return pn532_write(frame, len);
|
||||
}
|
||||
|
||||
static bool read_ack()
|
||||
{
|
||||
uint8_t resp[6];
|
||||
|
||||
if (!pn532_wait_ready()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
read_frame(resp, 6);
|
||||
|
||||
const uint8_t expect_ack[] = {0, 0, 0xff, 0, 0xff, 0};
|
||||
if (memcmp(resp, expect_ack, 6) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int pn532_write_data(const uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t frame[5 + len];
|
||||
frame[0] = PN532_PREAMBLE;
|
||||
frame[1] = PN532_STARTCODE1;
|
||||
frame[2] = PN532_STARTCODE2;
|
||||
uint8_t checksum = 0xff;
|
||||
|
||||
frame[3] = len;
|
||||
frame[4] = (~len + 1);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
frame[5 + i] = data[i];
|
||||
checksum += data[i];
|
||||
}
|
||||
|
||||
frame[5 + len] = ~checksum;
|
||||
frame[6 + len] = PN532_POSTAMBLE;
|
||||
|
||||
write_frame(frame, 7 + len);
|
||||
|
||||
return read_ack();
|
||||
}
|
||||
|
||||
int pn532_read_data(uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t resp[len + 7];
|
||||
|
||||
read_frame(resp, len + 7);
|
||||
|
||||
if (resp[0] != PN532_PREAMBLE ||
|
||||
resp[1] != PN532_STARTCODE1 ||
|
||||
resp[2] != PN532_STARTCODE2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t length = resp[3];
|
||||
uint8_t length_check = length + resp[4];
|
||||
|
||||
if (length > len ||
|
||||
length_check != 0 ||
|
||||
resp[length + 6] != PN532_POSTAMBLE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t checksum = 0;
|
||||
for (int i = 0; i <= length; i++) {
|
||||
data[i] = resp[5 + i];
|
||||
checksum += resp[5 + i];
|
||||
}
|
||||
|
||||
if (checksum != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int pn532_write_command(uint8_t cmd, const uint8_t *param, uint8_t len)
|
||||
{
|
||||
uint8_t data[len + 2];
|
||||
data[0] = PN532_HOSTTOPN532;
|
||||
data[1] = cmd;
|
||||
|
||||
memcpy(data + 2, param, len);
|
||||
|
||||
return pn532_write_data(data, len + 2);
|
||||
}
|
||||
|
||||
static void write_nack()
|
||||
{
|
||||
const uint8_t nack[] = {0, 0, 0xff, 0xff, 0, 0};
|
||||
pn532_write(nack, 6);
|
||||
}
|
||||
|
||||
int pn532_peak_response_len()
|
||||
{
|
||||
uint8_t buf[6];
|
||||
if (!pn532_wait_ready()) {
|
||||
return -1;
|
||||
}
|
||||
pn532_read(buf, 6);
|
||||
if (buf[0] != 0x01 ||
|
||||
buf[1] != PN532_PREAMBLE ||
|
||||
buf[2] != PN532_STARTCODE1 ||
|
||||
buf[3] != PN532_STARTCODE2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_nack();
|
||||
return buf[4];
|
||||
}
|
||||
|
||||
int pn532_read_response(uint8_t cmd, uint8_t *resp, uint8_t len)
|
||||
{
|
||||
int real_len = pn532_peak_response_len();
|
||||
if (real_len < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!pn532_wait_ready()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (real_len < 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t data[real_len];
|
||||
int ret = pn532_read_data(data, real_len);
|
||||
if (ret != real_len ||
|
||||
data[0] != PN532_PN532TOHOST ||
|
||||
data[1] != cmd + 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int data_len = real_len - 2;
|
||||
if (data_len > len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data_len > 0) {
|
||||
memcpy(resp, data + 2, data_len);
|
||||
}
|
||||
|
||||
return data_len;
|
||||
}
|
||||
|
||||
uint32_t pn532_firmware_ver()
|
||||
{
|
||||
int ret = pn532_write_command(0x02, NULL, 0);
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ver[4];
|
||||
int result = pn532_read_response(0x4a, ver, sizeof(ver));
|
||||
if (result < 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t version = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
version <<= 8;
|
||||
version |= ver[i];
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
bool pn532_config_rf()
|
||||
{
|
||||
uint8_t param[] = {0x05, 0xff, 0x01, 0x50};
|
||||
pn532_write_command(0x32, param, sizeof(param));
|
||||
|
||||
return pn532_read_response(0x32, param, sizeof(param)) == 0;
|
||||
}
|
||||
|
||||
bool pn532_config_sam()
|
||||
{
|
||||
uint8_t param[] = {0x01, 0x14, 0x01};
|
||||
pn532_write_command(0x14, param, sizeof(param));
|
||||
|
||||
return pn532_read_response(0x14, NULL, 0) == 0;
|
||||
}
|
||||
|
||||
|
||||
bool pn532_set_rf_field(uint8_t auto_rf, uint8_t on_off)
|
||||
{
|
||||
uint8_t param[] = { 1, auto_rf | on_off };
|
||||
pn532_write_command(0x32, param, 2);
|
||||
|
||||
return pn532_read_response(0x32, NULL, 0) >= 0;
|
||||
}
|
||||
|
||||
static uint8_t readbuf[255];
|
||||
|
||||
bool pn532_poll_mifare(uint8_t *uid, int *len)
|
||||
{
|
||||
uint8_t param[] = {0x01, 0x00};
|
||||
int ret = pn532_write_command(0x4a, param, sizeof(param));
|
||||
if (ret < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = pn532_read_response(0x4a, readbuf, sizeof(readbuf));
|
||||
if (result < 1 || readbuf[0] != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result != readbuf[5] + 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*len < readbuf[5]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(uid, readbuf + 6, readbuf[5]);
|
||||
*len = readbuf[5];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pn532_poll_14443b(uint8_t *uid, int *len)
|
||||
{
|
||||
uint8_t param[] = {0x01, 0x03, 0x00};
|
||||
int ret = pn532_write_command(0x4a, param, sizeof(param));
|
||||
if (ret < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = pn532_read_response(0x4a, readbuf, sizeof(readbuf));
|
||||
if (result < 1 || readbuf[0] != 1) {
|
||||
printf("result: %d\n", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result != readbuf[5] + 6) {
|
||||
printf("result: %d %d\n", result, readbuf[5]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*len < readbuf[5]) {
|
||||
printf("result: %d %d\n", result, readbuf[5]);
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(uid, readbuf + 6, readbuf[5]);
|
||||
*len = readbuf[5];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct __attribute__((packed)) {
|
||||
uint8_t idm[8];
|
||||
uint8_t pmm[8];
|
||||
uint8_t syscode[2];
|
||||
uint8_t inlist_tag;
|
||||
} felica_poll_cache;
|
||||
|
||||
bool pn532_poll_felica(uint8_t uid[8], uint8_t pmm[8], uint8_t syscode[2], bool from_cache)
|
||||
{
|
||||
if (from_cache) {
|
||||
memcpy(uid, felica_poll_cache.idm, 8);
|
||||
memcpy(pmm, felica_poll_cache.pmm, 8);
|
||||
memcpy(syscode, felica_poll_cache.syscode, 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t param[] = { 1, 1, 0, 0xff, 0xff, 1, 0};
|
||||
int ret = pn532_write_command(0x4a, param, sizeof(param));
|
||||
if (ret < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = pn532_read_response(0x4a, readbuf, sizeof(readbuf));
|
||||
if (result != 22 || readbuf[0] != 1 || readbuf[2] != 20) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&felica_poll_cache, readbuf + 4, 18);
|
||||
felica_poll_cache.inlist_tag = readbuf[1];
|
||||
|
||||
memcpy(uid, readbuf + 4, 8);
|
||||
memcpy(pmm, readbuf + 12, 8);
|
||||
memcpy(syscode, readbuf + 20, 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pn532_mifare_auth(const uint8_t uid[4], uint8_t block_id, uint8_t key_id, const uint8_t *key)
|
||||
{
|
||||
uint8_t param[] = { 1, key_id ? 1 : 0, block_id,
|
||||
key[0], key[1], key[2], key[3], key[4], key[5],
|
||||
uid[0], uid[1], uid[2], uid[3] };
|
||||
int ret = pn532_write_command(0x40, param, sizeof(param));
|
||||
if (ret < 0) {
|
||||
printf("Failed mifare auth command\n");
|
||||
return false;
|
||||
}
|
||||
int result = pn532_read_response(0x40, readbuf, sizeof(readbuf));
|
||||
if (readbuf[0] != 0) {
|
||||
printf("PN532 Mifare AUTH failed %d %02x\n", result, readbuf[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pn532_mifare_read(uint8_t block_id, uint8_t block_data[16])
|
||||
{
|
||||
uint8_t param[] = { 1, 0x30, block_id };
|
||||
|
||||
int ret = pn532_write_command(0x40, param, sizeof(param));
|
||||
if (ret < 0) {
|
||||
printf("Failed mifare read command\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = pn532_read_response(0x40, readbuf, sizeof(readbuf));
|
||||
|
||||
if (readbuf[0] != 0 || result != 17) {
|
||||
printf("PN532 Mifare READ failed %d %02x\n", result, readbuf[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
memmove(block_data, readbuf + 1, 16);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int pn532_felica_command(uint8_t cmd, const uint8_t *param, uint8_t param_len, uint8_t *outbuf)
|
||||
{
|
||||
int cmd_len = param_len + 11;
|
||||
uint8_t cmd_buf[cmd_len + 1];
|
||||
|
||||
cmd_buf[0] = felica_poll_cache.inlist_tag;
|
||||
cmd_buf[1] = cmd_len;
|
||||
cmd_buf[2] = cmd;
|
||||
memcpy(cmd_buf + 3, felica_poll_cache.idm, 8);
|
||||
memcpy(cmd_buf + 11, param, param_len);
|
||||
|
||||
int ret = pn532_write_command(0x40, cmd_buf, sizeof(cmd_buf));
|
||||
if (ret < 0) {
|
||||
printf("Failed send felica command\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = pn532_read_response(0x40, readbuf, sizeof(readbuf));
|
||||
|
||||
int outlen = readbuf[1] - 1;
|
||||
if ((readbuf[0] & 0x3f) != 0 || result - 2 != outlen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memmove(outbuf, readbuf + 2, outlen);
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
|
||||
bool pn532_felica_read_wo_encrypt(uint16_t svc_code, uint16_t block_id, uint8_t block_data[16])
|
||||
{
|
||||
uint8_t param[] = { 1, svc_code & 0xff, svc_code >> 8,
|
||||
1, block_id >> 8, block_id & 0xff };
|
||||
|
||||
int result = pn532_felica_command(0x06, param, sizeof(param), readbuf);
|
||||
|
||||
if (result != 12 + 16 || readbuf[9] != 0 || readbuf[10] != 0) {
|
||||
printf("PN532 Felica READ read failed %d %02x %02x\n",
|
||||
result, readbuf[9], readbuf[10]);
|
||||
for (int i = 0; i < result; i++) {
|
||||
printf(" %02x", readbuf[i]);
|
||||
}
|
||||
printf("\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t *result_data = readbuf + 12;
|
||||
memcpy(block_data, result_data, 16);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pn532_felica_write_wo_encrypt(uint16_t svc_code, uint16_t block_id, const uint8_t block_data[16])
|
||||
{
|
||||
uint8_t param[22] = { 1, svc_code & 0xff, svc_code >> 8,
|
||||
1, block_id >> 8, block_id & 0xff };
|
||||
memcpy(param + 6, block_data, 16);
|
||||
int result = pn532_felica_command(0x08, param, sizeof(param), readbuf);
|
||||
|
||||
if (result < 0) {
|
||||
printf("PN532 Felica WRITE failed %d\n", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < result; i++) {
|
||||
printf(" %02x", readbuf[i]);
|
||||
}
|
||||
printf("\n");
|
||||
return false;
|
||||
}
|
34
firmware/src/pn532.h
Normal file
34
firmware/src/pn532.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* PN532 NFC Reader
|
||||
* WHowe <github.com/whowechina>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PN532_H
|
||||
#define PN532_H
|
||||
|
||||
#include "hardware/i2c.h"
|
||||
|
||||
void pn532_init(i2c_inst_t *i2c_port, uint8_t scl, uint8_t sda, uint32_t freq);
|
||||
|
||||
int pn532_write_command(uint8_t cmd, const uint8_t *param, uint8_t len);
|
||||
int pn532_read_response(uint8_t cmd, uint8_t *resp, uint8_t len);
|
||||
|
||||
uint32_t pn532_firmware_ver();
|
||||
|
||||
bool pn532_config_sam();
|
||||
bool pn532_config_rf();
|
||||
|
||||
bool pn532_set_rf_field(uint8_t auto_rf, uint8_t on_off);
|
||||
|
||||
bool pn532_poll_mifare(uint8_t *uid, int *len);
|
||||
bool pn532_poll_14443b(uint8_t *uid, int *len);
|
||||
bool pn532_poll_felica(uint8_t uid[8], uint8_t pmm[8], uint8_t syscode[2], bool from_cache);
|
||||
|
||||
bool pn532_mifare_auth(const uint8_t uid[4], uint8_t block_id, uint8_t key_id, const uint8_t *key);
|
||||
bool pn532_mifare_read(uint8_t block_id, uint8_t block_data[16]);
|
||||
|
||||
bool pn532_felica_read_wo_encrypt(uint16_t svc_code, uint16_t block_id, uint8_t block_data[16]);
|
||||
bool pn532_felica_write_wo_encrypt(uint16_t svc_code, uint16_t block_id, const uint8_t block_data[16]);
|
||||
|
||||
#endif
|
178
firmware/src/save.c
Normal file
178
firmware/src/save.c
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Controller Config Save and Load
|
||||
* WHowe <github.com/whowechina>
|
||||
*
|
||||
* Config is stored in last sector of flash
|
||||
*/
|
||||
|
||||
#include "save.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <memory.h>
|
||||
|
||||
|
||||
#include "bsp/board.h"
|
||||
#include "pico/bootrom.h"
|
||||
#include "pico/stdio.h"
|
||||
|
||||
#include "hardware/flash.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "pico/unique_id.h"
|
||||
|
||||
static struct {
|
||||
size_t size;
|
||||
size_t offset;
|
||||
void (*after_load)();
|
||||
} modules[8] = {0};
|
||||
static int module_num = 0;
|
||||
|
||||
static uint32_t my_magic = 0xcafecafe;
|
||||
|
||||
#define SAVE_TIMEOUT_US 5000000
|
||||
|
||||
#define SAVE_SECTOR_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE)
|
||||
|
||||
typedef struct __attribute ((packed)) {
|
||||
uint32_t magic;
|
||||
uint8_t data[FLASH_PAGE_SIZE - 4];
|
||||
} page_t;
|
||||
|
||||
static page_t old_data = {0};
|
||||
static page_t new_data = {0};
|
||||
static page_t default_data = {0};
|
||||
static int data_page = -1;
|
||||
|
||||
static bool requesting_save = false;
|
||||
static uint64_t requesting_time = 0;
|
||||
|
||||
static mutex_t *io_lock;
|
||||
|
||||
static void save_program()
|
||||
{
|
||||
old_data = new_data;
|
||||
|
||||
data_page = (data_page + 1) % (FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE);
|
||||
printf("\nProgram Flash %d %8lx\n", data_page, old_data.magic);
|
||||
if (mutex_enter_timeout_us(io_lock, 100000)) {
|
||||
sleep_ms(10); /* wait for all io operations to finish */
|
||||
uint32_t ints = save_and_disable_interrupts();
|
||||
if (data_page == 0) {
|
||||
flash_range_erase(SAVE_SECTOR_OFFSET, FLASH_SECTOR_SIZE);
|
||||
}
|
||||
flash_range_program(SAVE_SECTOR_OFFSET + data_page * FLASH_PAGE_SIZE,
|
||||
(uint8_t *)&old_data, FLASH_PAGE_SIZE);
|
||||
restore_interrupts(ints);
|
||||
mutex_exit(io_lock);
|
||||
} else {
|
||||
printf("Program Flash Failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void load_default()
|
||||
{
|
||||
printf("Load Default\n");
|
||||
new_data = default_data;
|
||||
new_data.magic = my_magic;
|
||||
}
|
||||
|
||||
static const page_t *get_page(int id)
|
||||
{
|
||||
int addr = XIP_BASE + SAVE_SECTOR_OFFSET;
|
||||
return (page_t *)(addr + FLASH_PAGE_SIZE * id);
|
||||
}
|
||||
|
||||
static void save_load()
|
||||
{
|
||||
for (int i = 0; i < FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE; i++) {
|
||||
if (get_page(i)->magic != my_magic) {
|
||||
break;
|
||||
}
|
||||
data_page = i;
|
||||
}
|
||||
|
||||
if (data_page < 0) {
|
||||
load_default();
|
||||
save_request(false);
|
||||
return;
|
||||
}
|
||||
|
||||
old_data = *get_page(data_page);
|
||||
new_data = old_data;
|
||||
printf("Page Loaded %d %8lx\n", data_page, new_data.magic);
|
||||
}
|
||||
|
||||
static void save_loaded()
|
||||
{
|
||||
for (int i = 0; i < module_num; i++) {
|
||||
modules[i].after_load();
|
||||
}
|
||||
}
|
||||
|
||||
static union __attribute__((packed)) {
|
||||
pico_unique_board_id_t id;
|
||||
struct {
|
||||
uint32_t id32h;
|
||||
uint32_t id32l;
|
||||
};
|
||||
uint64_t id64;
|
||||
} board_id;
|
||||
|
||||
uint32_t board_id_32()
|
||||
{
|
||||
pico_get_unique_board_id(&board_id.id);
|
||||
return board_id.id32h ^ board_id.id32l;
|
||||
}
|
||||
|
||||
uint64_t board_id_64()
|
||||
{
|
||||
pico_get_unique_board_id(&board_id.id);
|
||||
return board_id.id64;
|
||||
}
|
||||
|
||||
void save_init(uint32_t magic, mutex_t *locker)
|
||||
{
|
||||
my_magic = magic;
|
||||
io_lock = locker;
|
||||
save_load();
|
||||
save_loop();
|
||||
save_loaded();
|
||||
}
|
||||
|
||||
void save_loop()
|
||||
{
|
||||
if (requesting_save && (time_us_64() - requesting_time > SAVE_TIMEOUT_US)) {
|
||||
requesting_save = false;
|
||||
/* only when data is actually changed */
|
||||
if (memcmp(&old_data, &new_data, sizeof(old_data)) == 0) {
|
||||
return;
|
||||
}
|
||||
save_program();
|
||||
}
|
||||
}
|
||||
|
||||
void *save_alloc(size_t size, void *def, void (*after_load)())
|
||||
{
|
||||
modules[module_num].size = size;
|
||||
size_t offset = module_num > 0 ? modules[module_num - 1].offset + size : 0;
|
||||
modules[module_num].offset = offset;
|
||||
modules[module_num].after_load = after_load;
|
||||
module_num++;
|
||||
memcpy(default_data.data + offset, def, size); // backup the default
|
||||
return new_data.data + offset;
|
||||
}
|
||||
|
||||
void save_request(bool immediately)
|
||||
{
|
||||
if (!requesting_save) {
|
||||
printf("Save requested.\n");
|
||||
requesting_save = true;
|
||||
new_data.magic = my_magic;
|
||||
requesting_time = time_us_64();
|
||||
}
|
||||
if (immediately) {
|
||||
requesting_time = 0;
|
||||
save_loop();
|
||||
}
|
||||
}
|
27
firmware/src/save.h
Normal file
27
firmware/src/save.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Controller Flash Save and Load
|
||||
* WHowe <github.com/whowechina>
|
||||
*/
|
||||
|
||||
#ifndef SAVE_H
|
||||
#define SAVE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "pico/multicore.h"
|
||||
|
||||
uint32_t board_id_32();
|
||||
uint64_t board_id_64();
|
||||
|
||||
/* It's safer to lock other I/O ops during saving, so we need a locker */
|
||||
typedef void (*io_locker_func)(bool pause);
|
||||
void save_init(uint32_t magic, mutex_t *lock);
|
||||
|
||||
void save_loop();
|
||||
|
||||
void *save_alloc(size_t size, void *def, void (*after_load)());
|
||||
void save_request(bool immediately);
|
||||
|
||||
#endif
|
124
firmware/src/tusb_config.h
Normal file
124
firmware/src/tusb_config.h
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by board.mk
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#error CFG_TUSB_MCU must be defined
|
||||
#endif
|
||||
|
||||
// RHPort number used for device can be defined by board.mk, default to port 0
|
||||
#ifndef BOARD_DEVICE_RHPORT_NUM
|
||||
#define BOARD_DEVICE_RHPORT_NUM 0
|
||||
#endif
|
||||
|
||||
// RHPort max operational speed can defined by board.mk
|
||||
// Default to Highspeed for MCU with internal HighSpeed PHY (can be port
|
||||
// specific), otherwise FullSpeed
|
||||
#ifndef BOARD_DEVICE_RHPORT_SPEED
|
||||
#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || \
|
||||
CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 || \
|
||||
CFG_TUSB_MCU == OPT_MCU_CXD56)
|
||||
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
|
||||
#else
|
||||
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Device mode with rhport and speed defined by board.mk
|
||||
#if BOARD_DEVICE_RHPORT_NUM == 0
|
||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#elif BOARD_DEVICE_RHPORT_NUM == 1
|
||||
#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#else
|
||||
#error "Incorrect RHPort configuration"
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
#endif
|
||||
|
||||
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
|
||||
// #define CFG_TUSB_DEBUG 0
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction
|
||||
* on alignment. Tinyusb use follows macros to declare transferring memory so
|
||||
* that they can be put into those specific section. e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4)))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_HID 1
|
||||
#define CFG_TUD_CDC 2
|
||||
#define CFG_TUD_MSC 0
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_EP_BUFSIZE 64
|
||||
|
||||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_EP_BUFSIZE 64
|
||||
|
||||
// CDC FIFO size of TX and RX
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 128)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 128)
|
||||
|
||||
// CDC Endpoint transfer buffer size, more is faster
|
||||
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 128)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
199
firmware/src/usb_descriptors.c
Normal file
199
firmware/src/usb_descriptors.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "usb_descriptors.h"
|
||||
#include "pico/unique_id.h"
|
||||
#include "tusb.h"
|
||||
|
||||
/* A combination of interfaces must have a unique product id, since PC will save
|
||||
* device driver after the first plug. Same VID/PID with different interface e.g
|
||||
* MSC (first), then CDC (later) will possibly cause system error on PC.
|
||||
*
|
||||
* Auto ProductID layout's Bitmap:
|
||||
* [MSB] HID | MSC | CDC [LSB]
|
||||
*/
|
||||
#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
|
||||
#define USB_PID \
|
||||
(0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
|
||||
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4))
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_desc_device_t desc_device_joy = {
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
// To match CrazyRedMachine dll
|
||||
// vid 0x0f0d, pid 0x0092, interface 1
|
||||
|
||||
.idVendor = 0x0f0d,
|
||||
.idProduct = 0x0092,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01};
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const* tud_descriptor_device_cb(void) {
|
||||
return (uint8_t const*)&desc_device_joy;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HID Report Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t const desc_hid_report_joy[] = {
|
||||
AIC_PICO_REPORT_DESC_JOYSTICK,
|
||||
};
|
||||
|
||||
// Invoked when received GET HID REPORT DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
switch (itf) {
|
||||
case 0:
|
||||
return desc_hid_report_joy;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum { ITF_NUM_JOY,
|
||||
ITF_NUM_CLI, ITF_NUM_CLI_DATA,
|
||||
ITF_NUM_AIME, ITF_NUM_AIME_DATA,
|
||||
ITF_NUM_TOTAL };
|
||||
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN * 1 + TUD_CDC_DESC_LEN * 2)
|
||||
|
||||
#define EPNUM_JOY 0x81
|
||||
|
||||
#define EPNUM_CLI_NOTIF 0x85
|
||||
#define EPNUM_CLI_OUT 0x06
|
||||
#define EPNUM_CLI_IN 0x86
|
||||
|
||||
#define EPNUM_AIME_NOTIF 0x87
|
||||
#define EPNUM_AIME_OUT 0x08
|
||||
#define EPNUM_AIME_IN 0x88
|
||||
|
||||
uint8_t const desc_configuration_joy[] = {
|
||||
// Config number, interface count, string index, total length, attribute,
|
||||
// power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN,
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 200),
|
||||
|
||||
// Interface number, string index, protocol, report descriptor len, EP In
|
||||
// address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(ITF_NUM_JOY, 4, HID_ITF_PROTOCOL_NONE,
|
||||
sizeof(desc_hid_report_joy), EPNUM_JOY,
|
||||
CFG_TUD_HID_EP_BUFSIZE, 1),
|
||||
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CLI, 5, EPNUM_CLI_NOTIF,
|
||||
8, EPNUM_CLI_OUT, EPNUM_CLI_IN, 64),
|
||||
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_AIME, 6, EPNUM_AIME_NOTIF,
|
||||
8, EPNUM_AIME_OUT, EPNUM_AIME_IN, 64),
|
||||
};
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
|
||||
return desc_configuration_joy;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static char serial_number_str[24] = "123456\0";
|
||||
|
||||
// array of pointer to string descriptors
|
||||
const char *string_desc_arr[] = {
|
||||
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
|
||||
"WHowe" , // 1: Manufacturer
|
||||
"AIC Pico", // 2: Product
|
||||
serial_number_str, // 3: Serials, should use chip ID
|
||||
"AIC Pico Joystick",
|
||||
"AIC Pico CLI Port",
|
||||
"AIC Pico AIME Port",
|
||||
};
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long
|
||||
// enough for transfer to complete
|
||||
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
static uint16_t _desc_str[64];
|
||||
|
||||
if (index == 0) {
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
_desc_str[0] = (TUSB_DESC_STRING << 8) | (2 + 2);
|
||||
return _desc_str;
|
||||
}
|
||||
|
||||
if (index == 3) {
|
||||
pico_unique_board_id_t board_id;
|
||||
pico_get_unique_board_id(&board_id);
|
||||
sprintf(serial_number_str, "%016llx", *(uint64_t *)&board_id);
|
||||
}
|
||||
|
||||
const size_t base_num = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]);
|
||||
char str[64];
|
||||
|
||||
if (index < base_num) {
|
||||
strcpy(str, string_desc_arr[index]);
|
||||
} else {
|
||||
sprintf(str, "Unknown %d", index);
|
||||
}
|
||||
|
||||
uint8_t chr_count = strlen(str);
|
||||
if (chr_count > 63) {
|
||||
chr_count = 63;
|
||||
}
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for (uint8_t i = 0; i < chr_count; i++) {
|
||||
_desc_str[1 + i] = str[i];
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2);
|
||||
|
||||
return _desc_str;
|
||||
}
|
53
firmware/src/usb_descriptors.h
Normal file
53
firmware/src/usb_descriptors.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef USB_DESCRIPTORS_H_
|
||||
#define USB_DESCRIPTORS_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "device/usbd.h"
|
||||
|
||||
enum {
|
||||
REPORT_ID_JOYSTICK = 1,
|
||||
};
|
||||
|
||||
// because they are missing from tusb_hid.h
|
||||
#define HID_STRING_INDEX(x) HID_REPORT_ITEM(x, 7, RI_TYPE_LOCAL, 1)
|
||||
#define HID_STRING_INDEX_N(x, n) HID_REPORT_ITEM(x, 7, RI_TYPE_LOCAL, n)
|
||||
#define HID_STRING_MINIMUM(x) HID_REPORT_ITEM(x, 8, RI_TYPE_LOCAL, 1)
|
||||
#define HID_STRING_MINIMUM_N(x, n) HID_REPORT_ITEM(x, 8, RI_TYPE_LOCAL, n)
|
||||
#define HID_STRING_MAXIMUM(x) HID_REPORT_ITEM(x, 9, RI_TYPE_LOCAL, 1)
|
||||
#define HID_STRING_MAXIMUM_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_LOCAL, n)
|
||||
|
||||
// Joystick Report Descriptor Template - Based off Drewol/rp2040-gamecon
|
||||
// Button Map | X | Y
|
||||
#define AIC_PICO_REPORT_DESC_JOYSTICK \
|
||||
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
|
||||
HID_USAGE(HID_USAGE_DESKTOP_JOYSTICK), \
|
||||
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
|
||||
HID_REPORT_ID(REPORT_ID_JOYSTICK) \
|
||||
HID_USAGE_PAGE(HID_USAGE_PAGE_BUTTON), \
|
||||
HID_USAGE_MIN(1), HID_USAGE_MAX(16), \
|
||||
HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \
|
||||
HID_REPORT_COUNT(16), HID_REPORT_SIZE(1), \
|
||||
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
|
||||
\
|
||||
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
|
||||
HID_USAGE(HID_USAGE_DESKTOP_HAT_SWITCH), \
|
||||
HID_LOGICAL_MIN(1), HID_LOGICAL_MAX(8), \
|
||||
HID_PHYSICAL_MIN(0), HID_PHYSICAL_MAX_N(315, 2), \
|
||||
HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \
|
||||
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
|
||||
\
|
||||
HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \
|
||||
HID_USAGE(HID_USAGE_DESKTOP_X), HID_USAGE(HID_USAGE_DESKTOP_Y), \
|
||||
HID_USAGE(HID_USAGE_DESKTOP_Z), HID_USAGE(HID_USAGE_DESKTOP_RX), \
|
||||
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), /* Analog */ \
|
||||
HID_REPORT_SIZE(8), HID_REPORT_COUNT(4), \
|
||||
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
|
||||
\
|
||||
HID_USAGE_PAGE_N(HID_USAGE_PAGE_VENDOR, 2), \
|
||||
HID_USAGE(0), \
|
||||
HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), \
|
||||
HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \
|
||||
HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \
|
||||
HID_COLLECTION_END
|
||||
|
||||
#endif /* USB_DESCRIPTORS_H_ */
|
85
firmware/src/ws2812.pio
Normal file
85
firmware/src/ws2812.pio
Normal file
@ -0,0 +1,85 @@
|
||||
;
|
||||
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
|
||||
.program ws2812
|
||||
.side_set 1
|
||||
|
||||
.define public T1 2
|
||||
.define public T2 5
|
||||
.define public T3 3
|
||||
|
||||
.lang_opt python sideset_init = pico.PIO.OUT_HIGH
|
||||
.lang_opt python out_init = pico.PIO.OUT_HIGH
|
||||
.lang_opt python out_shiftdir = 1
|
||||
|
||||
.wrap_target
|
||||
bitloop:
|
||||
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
|
||||
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
|
||||
do_one:
|
||||
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
|
||||
do_zero:
|
||||
nop side 0 [T2 - 1] ; Or drive low, for a short pulse
|
||||
.wrap
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {
|
||||
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||
|
||||
pio_sm_config c = ws2812_program_get_default_config(offset);
|
||||
sm_config_set_sideset_pins(&c, pin);
|
||||
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
|
||||
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
|
||||
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
%}
|
||||
|
||||
.program ws2812_parallel
|
||||
|
||||
.define public T1 2
|
||||
.define public T2 5
|
||||
.define public T3 3
|
||||
|
||||
.wrap_target
|
||||
out x, 32
|
||||
mov pins, !null [T1-1]
|
||||
mov pins, x [T2-1]
|
||||
mov pins, null [T3-2]
|
||||
.wrap
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) {
|
||||
for(uint i=pin_base; i<pin_base+pin_count; i++) {
|
||||
pio_gpio_init(pio, i);
|
||||
}
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true);
|
||||
|
||||
pio_sm_config c = ws2812_parallel_program_get_default_config(offset);
|
||||
sm_config_set_out_shift(&c, true, true, 32);
|
||||
sm_config_set_out_pins(&c, pin_base, pin_count);
|
||||
sm_config_set_set_pins(&c, pin_base, pin_count);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
|
||||
int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_T3;
|
||||
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
%}
|
Loading…
x
Reference in New Issue
Block a user