rp2040-playground/iso7816/iso7816_cardem.c

214 lines
5.6 KiB
C

#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "hardware/pio.h"
#include "hardware/uart.h"
#include "hardware/clocks.h"
#include "tusb.h"
#include "iso7816_tx.pio.h"
#define GPIO_SIM_VCC 18 /* pico pin 24 */
#define GPIO_SIM_RST 19 /* pico pin 25 */
#define GPIO_SIM_IO 21 /* pico pin 27 */
#define GPIO_SIM_CLK 20 /* must be at GP20 due to the GPIN0 function; pico pin 26 */
enum cardem_state {
CST_WAIT_VCC,
CST_VCC_WAIT_CLK,
CST_VCC_CLK_WAIT_RST,
CST_DELAY_BEFORE_ATR,
CST_IN_ATR,
CST_WAIT_TPDU_HDR,
CST_IN_TPDU_HDR,
};
struct cardem_uart_instance {
enum cardem_state state;
uint32_t vcc_freq_khz;
volatile bool vcc_present;
volatile bool rst_active;
PIO pio;
uint sm;
uint tx_prog_offset;
};
static struct cardem_uart_instance g_cst[1];
const uint8_t g_atr[] = { 0x3B, 0x80, 0x80, 0x81, 0x1F, 0xC7, 0x59 };
static void gpio_callback(uint gpio, uint32_t events)
{
switch (gpio) {
case GPIO_SIM_VCC:
if (events & GPIO_IRQ_EDGE_RISE)
g_cst[0].vcc_present = true;
#if 1
if (events & GPIO_IRQ_EDGE_FALL)
g_cst[0].vcc_present = false;
#endif
//printf("VCC=%d\n", g_cst[0].vcc_present);
break;
case GPIO_SIM_RST:
if (events & GPIO_IRQ_EDGE_RISE)
g_cst[0].rst_active = false;
#if 1
if (events & GPIO_IRQ_EDGE_FALL)
g_cst[0].rst_active = true;
#endif
//printf("RST=%d\n", g_cst[0].rst_active);
break;
default:
printf("unhandled GP%u: 0x%x\n", gpio, events);
break;
}
}
static void cardem_main_state_chg(struct cardem_uart_instance *cst, enum cardem_state new_st)
{
//printf("State %u -> %u\n", cst->state, new_st);
cst->state = new_st;
}
static void cardem_main_loop(struct cardem_uart_instance *cst)
{
/* global reset on power-off */
if (cst->state != CST_WAIT_VCC && !cst->vcc_present ) {
cardem_main_state_chg(cst, CST_WAIT_VCC);
return;
}
switch (cst->state) {
case CST_WAIT_VCC:
if (cst->vcc_present)
cardem_main_state_chg(cst, CST_VCC_WAIT_CLK);
break;
case CST_VCC_WAIT_CLK:
/* determine CLK frequency */
cst->vcc_freq_khz = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLKSRC_GPIN0);
if (cst->vcc_freq_khz) {
//printf("Determined CLK freq: %u kHz\n", cst->vcc_freq_khz);
cardem_main_state_chg(cst, CST_VCC_CLK_WAIT_RST);
}
break;
case CST_VCC_CLK_WAIT_RST:
#if 1
if (!cst->rst_active)
cardem_main_state_chg(cst, CST_DELAY_BEFORE_ATR);
break;
#endif
case CST_DELAY_BEFORE_ATR:
/* FIXME: actually delay */
if (true) {
cardem_main_state_chg(cst, CST_IN_ATR);
/* configure the clock divider for the "correct" baud rate */
iso7816_tx_program_init(cst->pio, cst->sm, cst->tx_prog_offset, GPIO_SIM_IO,
(cst->vcc_freq_khz * 1000) / 372);
/* FIXME: actually start transmission via PIO */
iso7816_tx_program_puts(cst->pio, cst->sm, g_atr, sizeof(g_atr));
printf("ATR sent\n");
cardem_main_state_chg(cst, CST_WAIT_TPDU_HDR);
}
break;
case CST_IN_ATR:
break;
case CST_WAIT_TPDU_HDR:
break;
case CST_IN_TPDU_HDR:
break;
}
}
static void cardem_inst_init(struct cardem_uart_instance *cst)
{
cst->state = CST_WAIT_VCC;
cst->vcc_freq_khz = 0;
cst->vcc_present = false;
cst->rst_active = false;
cst->pio = pio0;
cst->sm = 0;
cst->tx_prog_offset = pio_add_program(cst->pio, &iso7816_tx_program);
}
static void measure_freqs(void) {
uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);
uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);
uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);
uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);
uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);
uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);
uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);
uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);
printf("pll_sys = %dkHz\n", f_pll_sys);
printf("pll_usb = %dkHz\n", f_pll_usb);
printf("rosc = %dkHz\n", f_rosc);
printf("clk_sys = %dkHz\n", f_clk_sys);
printf("clk_peri = %dkHz\n", f_clk_peri);
printf("clk_usb = %dkHz\n", f_clk_usb);
printf("clk_adc = %dkHz\n", f_clk_adc);
printf("clk_rtc = %dkHz\n", f_clk_rtc);
// Can't measure clk_ref / xosc as it is the ref
}
int main()
{
gpio_debug_pins_init();
#if 1
//stdio_init_all();
stdio_usb_init();
while (!tud_cdc_connected()) {
printf(".");
sleep_ms(500);
}
#else
/* 115200 bps on GP0/GP1 of RP2040 (at least on pico) */
setup_default_uart();
#endif
printf("Starting ISO7816 cardem playground\n");
measure_freqs();
cardem_inst_init(&g_cst[0]);
gpio_set_irq_callback(gpio_callback);
irq_set_enabled(IO_IRQ_BANK0, true);
/* GP20 (CLK) as external clock input GPIN0 */
gpio_set_function(GPIO_SIM_CLK, GPIO_FUNC_GPCK);
gpio_pull_down(GPIO_SIM_CLK);
/* GPIO_SIM_VCC as input; edge-triggered interrupts */
gpio_init(GPIO_SIM_VCC);
gpio_set_dir(GPIO_SIM_VCC, false);
gpio_set_slew_rate(GPIO_SIM_VCC, GPIO_SLEW_RATE_SLOW);
gpio_pull_down(GPIO_SIM_VCC);
gpio_set_input_enabled(GPIO_SIM_VCC, true);
gpio_set_irq_enabled(GPIO_SIM_VCC, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
/* GPIO_SIM_RST as input; edge-triggered interrupts */
gpio_init(GPIO_SIM_RST);
gpio_set_dir(GPIO_SIM_RST, false);
gpio_set_slew_rate(GPIO_SIM_RST, GPIO_SLEW_RATE_SLOW);
gpio_pull_up(GPIO_SIM_RST);
gpio_set_input_enabled(GPIO_SIM_RST, true);
gpio_set_irq_enabled(GPIO_SIM_RST, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
gpio_pull_up(GPIO_SIM_IO);
gpio_pull_up(GPIO_SIM_IO);
gpio_set_function(GPIO_SIM_IO, GPIO_FUNC_PIO0);
printf("Entering main loop\n");
while (true) {
cardem_main_loop(&g_cst[0]);
}
}