mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-09-11 18:29:22 -07:00
4349 lines
155 KiB
C
4349 lines
155 KiB
C
//-----------------------------------------------------------------------------
|
|
// Copyright (C) Jonathan Westhues, Mar 2006
|
|
// Copyright (C) Gerhard de Koning Gans, Sep 2007
|
|
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// See LICENSE.txt for the text of the license.
|
|
//-----------------------------------------------------------------------------
|
|
// The main application code. This is the first thing called after start.c
|
|
// executes.
|
|
//-----------------------------------------------------------------------------
|
|
#include "appmain.h"
|
|
|
|
#include "sys_apis.h"
|
|
#include "usb_cdc_apis.h"
|
|
#include "proxmark3_arm.h"
|
|
#include "dbprint.h"
|
|
#include "pmflash.h"
|
|
#include "fpga.h"
|
|
#include "fpga_loader.h"
|
|
#include "fpga_apis.h"
|
|
#include "rssi_apis.h"
|
|
#include "rgb_apis.h"
|
|
#include "gpio_apis.h" // gpio_vusb_setup / Gpio_VUSB_Read (USB-present detection)
|
|
#include "string.h"
|
|
#include "printf.h"
|
|
#include "legicrf.h"
|
|
#include "BigBuf.h"
|
|
#include "iclass_cmd.h"
|
|
#include "hfops.h"
|
|
#include "iso14443a.h"
|
|
#include "secc.h"
|
|
#include "iso14443b.h"
|
|
#include "iso15693.h"
|
|
#include "thinfilm.h"
|
|
#include "felica.h"
|
|
#include "felicasim.h"
|
|
#include "hitag2.h"
|
|
#include "hitag2_crack.h"
|
|
#include "hitagS.h"
|
|
#include "hitagu.h"
|
|
#include "em4x50.h"
|
|
#include "em4x70.h"
|
|
#include "iclass.h"
|
|
#include "seos.h"
|
|
#include "legicrfsim.h"
|
|
//#include "cryptorfsim.h"
|
|
#include "epa.h"
|
|
#include "hfsnoop.h"
|
|
#include "lfops.h"
|
|
#include "lfsampling.h"
|
|
#include "lfzx.h"
|
|
#include "mifarecmd.h"
|
|
#include "mifaredesfire.h"
|
|
#include "mifaresim.h"
|
|
#include "emvsim.h"
|
|
#include "pcf7931.h"
|
|
#include "Standalone/standalone.h"
|
|
#include "util.h"
|
|
#include "ticks_apis.h"
|
|
#include "commonutil.h"
|
|
#include "crc16.h"
|
|
#include "protocols.h"
|
|
#include "mifareutil.h"
|
|
#include "sam_picopass.h"
|
|
#include "sam_seos.h"
|
|
#include "sam_mfc.h"
|
|
#include "sam_sc.h"
|
|
#include "cmac_calc.h"
|
|
#include "i2c.h"
|
|
|
|
#ifdef WITH_BWM_CHARGERKICK
|
|
// AW32001 charger on the BWM, I2C addr 0x93 (see test_bat_charger).
|
|
#define BWM_CHG_ADDR 0x93
|
|
#define BWM_CHG_REG_MAINCTL 0x06 // bit5 = FET_DIS: shipping mode -> VMIX/charge path off
|
|
#define BWM_CHG_REG_PONCFG 0x01 // PowerOnConfig: charge-enable/current bits
|
|
#define BWM_CHG_FET_DIS (1u << 5)
|
|
|
|
// Emergency charge-enable. Normal PM5 firmware never touches the charger, so a BWM
|
|
// left in shipping/FET-disabled mode won't take charge even with USB present. This
|
|
// wakes the power path at boot so the AW32001 can charge autonomously.
|
|
//
|
|
// SCOPE: runs from AppMain(), i.e. only once the PM5 has actually booted. It cannot
|
|
// help a cell too flat to boot (that needs USB reaching VUSBIN in hardware), and it
|
|
// cannot reroute VBUS to the charger input. It only ensures that when the charger
|
|
// HAS input, its path is enabled.
|
|
static void BWM_ChargerKick(void) {
|
|
I2C_init(true);
|
|
WaitMS(2); // let the bus settle
|
|
|
|
uint8_t v = 0;
|
|
// No ACK -> BWM absent or charger dead. Nothing to do; leave the bus and return.
|
|
if (I2C_BufferReadRaw(&v, 1, BWM_CHG_REG_MAINCTL, BWM_CHG_ADDR) <= 0) {
|
|
return;
|
|
}
|
|
|
|
// Shipping / FET-disabled -> clear it so the power/charge path comes alive.
|
|
if (v & BWM_CHG_FET_DIS) {
|
|
uint8_t nv = v & ~BWM_CHG_FET_DIS;
|
|
if (I2C_BufferWrite(&nv, 1, BWM_CHG_REG_MAINCTL, BWM_CHG_ADDR)) {
|
|
Dbprintf("[BWM] charger was FET-disabled (0x%02x) - path re-enabled", v);
|
|
} else {
|
|
Dbprintf(_RED_("[BWM] charger FET-disabled; re-enable write failed"));
|
|
}
|
|
}
|
|
}
|
|
#endif // WITH_BWM_CHARGERKICK
|
|
|
|
#ifdef WITH_BWM_STATUS
|
|
// BWM battery telemetry for `hw status`.
|
|
// Charger: AW32001 @ 0x93. Fuel gauge: BQ27427 @ 7-bit 0x55 (0xAA 8-bit).
|
|
// Shared charger defines are #ifndef-guarded so this coexists with WITH_BWM_CHARGERKICK.
|
|
// The BQ27427 address/commands and the current-sign convention are the TI standard set -
|
|
// confirm against a known-charging module before trusting absolute values. If the gauge
|
|
// or charger doesn't ACK, the corresponding lines print "not responding" rather than
|
|
// reporting garbage, so this is safe to ship even if an address is wrong on a given board.
|
|
static bool g_bwm_present = false;
|
|
|
|
#ifndef BWM_CHG_ADDR
|
|
#define BWM_CHG_ADDR 0x93
|
|
#endif
|
|
#ifndef BWM_CHG_REG_MAINCTL
|
|
#define BWM_CHG_REG_MAINCTL 0x06
|
|
#endif
|
|
#ifndef BWM_CHG_FET_DIS
|
|
#define BWM_CHG_FET_DIS (1u << 5)
|
|
#endif
|
|
#define BWM_CHG_REG_FAULT 0x09 // AW32001 fault register (latched, read-on-clear)
|
|
#define BWM_CHG_REG_SYSSTAT 0x08 // AW32001 system status (CHG_STAT / PG_STAT / THERM_STAT)
|
|
#define BWM_GAUGE_ADDR 0xAA // BQ27427, 7-bit 0x55 << 1
|
|
|
|
// BQ27427 standard commands (16-bit, little-endian)
|
|
#define BWM_GAUGE_TEMP 0x02 // 0.1 K
|
|
#define BWM_GAUGE_VOLTAGE 0x04 // mV
|
|
#define BWM_GAUGE_REMCAP 0x0C // mAh
|
|
#define BWM_GAUGE_CURRENT 0x10 // signed mA
|
|
#define BWM_GAUGE_SOC 0x1C // %
|
|
|
|
static bool bwm_gauge_read16(uint8_t cmd, uint16_t *out) {
|
|
uint8_t d[2] = {0};
|
|
if (I2C_BufferReadRaw(d, 2, cmd, BWM_GAUGE_ADDR) <= 0) {
|
|
return false;
|
|
}
|
|
*out = (uint16_t)(d[0] | (d[1] << 8)); // little-endian
|
|
return true;
|
|
}
|
|
|
|
// Reads the BWM charger + fuel gauge and prints a battery section. Called from
|
|
// SendStatus() under #ifdef PM5, so it only runs on a booted PM5 with I2C available.
|
|
static void print_pm5_battery_status(void) {
|
|
if (g_bwm_present == false) {
|
|
return; // no BWM detected at boot; nothing to report
|
|
}
|
|
DbpString(_CYAN_("Battery / BWM"));
|
|
|
|
I2C_init(true);
|
|
WaitMS(2); // let the bus settle
|
|
|
|
// --- charger (AW32001) ---
|
|
// REG09 (Fault) latches faults and is read-on-clear: read it TWICE - the 1st read
|
|
// returns the latched history, the 2nd returns the live state (datasheet: "read
|
|
// REG09 two times consecutively"). Bits [7:6] are the EN_SHIPPING_DGL config field,
|
|
// not faults, so mask to 0x3F. Fault bit map (AW32001E REG09H):
|
|
// b5 VIN_FAULT b4 THERM_SD b3 BAT_OVP b2 SAFETY_TMR b1 NTC_HOT b0 NTC_COLD
|
|
uint8_t mainctl = 0, f1 = 0, fault = 0, sysstat = 0;
|
|
if (I2C_BufferReadRaw(&mainctl, 1, BWM_CHG_REG_MAINCTL, BWM_CHG_ADDR) <= 0) {
|
|
Dbprintf(" Charger............. " _YELLOW_("not responding") " (BWM absent or I2C down)");
|
|
} else {
|
|
Dbprintf(" Charger MainCtl..... 0x%02x %s", mainctl,
|
|
(mainctl & BWM_CHG_FET_DIS) ? _RED_("FET_DIS (VMIX off / shipping)")
|
|
: _GREEN_("power path enabled"));
|
|
|
|
I2C_BufferReadRaw(&f1, 1, BWM_CHG_REG_FAULT, BWM_CHG_ADDR); // 1st = latched history
|
|
I2C_BufferReadRaw(&fault, 1, BWM_CHG_REG_FAULT, BWM_CHG_ADDR); // 2nd = current state
|
|
fault &= 0x3F;
|
|
|
|
if (fault == 0) {
|
|
Dbprintf(" Charger fault....... 0x00 (none)");
|
|
} else {
|
|
Dbprintf(" Charger fault....... " _RED_("0x%02x") "%s%s%s%s%s%s", fault,
|
|
(fault & 0x20) ? " VIN_FAULT" : "",
|
|
(fault & 0x10) ? " THERM_SD" : "",
|
|
(fault & 0x08) ? " BAT_OVP" : "",
|
|
(fault & 0x04) ? " SAFETY_TMR" : "",
|
|
(fault & 0x02) ? " NTC_HOT" : "",
|
|
(fault & 0x01) ? " NTC_COLD" : "");
|
|
}
|
|
|
|
// Live charge state from REG08 (System Status): CHG_STAT[4:3], PG_STAT[1], THERM_STAT[0]
|
|
if (I2C_BufferReadRaw(&sysstat, 1, BWM_CHG_REG_SYSSTAT, BWM_CHG_ADDR) > 0) {
|
|
static const char *cs[] = { "not charging", "pre-charge", "charging", "charge done" };
|
|
Dbprintf(" Charge status....... %s%s%s", cs[(sysstat >> 3) & 0x03],
|
|
(sysstat & 0x02) ? ", power good" : ", power fail",
|
|
(sysstat & 0x01) ? ", thermal-reg" : "");
|
|
}
|
|
|
|
// Configured charge profile (read-only). Decode tables from AW32001E datasheet V1.4:
|
|
// IIN_LIM (REG00[3:0]): 0000=50mA, else 80mA + 30mA*(code-1) [1111=500mA]
|
|
// VIN_DPM (REG00[7:4]): 3880mV + 80mV*code [1000=4.52V default]
|
|
// ICHG (REG02[5:0]): 8mA * (code+1) [63=512mA]
|
|
uint8_t reg00 = 0, reg01 = 0, reg02 = 0;
|
|
if (I2C_BufferReadRaw(®00, 1, 0x00, BWM_CHG_ADDR) > 0) {
|
|
uint8_t iin = reg00 & 0x0F;
|
|
uint8_t vdpm = (reg00 >> 4) & 0x0F;
|
|
uint16_t iin_ma = (iin == 0) ? 50 : (80 + 30 * (iin - 1));
|
|
uint16_t vdpm_mv = 3880 + 80 * vdpm;
|
|
Dbprintf(" Input limit......... %u mA, VIN_DPM %u.%02u V",
|
|
iin_ma, vdpm_mv / 1000, (vdpm_mv % 1000) / 10);
|
|
}
|
|
if (I2C_BufferReadRaw(®01, 1, 0x01, BWM_CHG_ADDR) > 0) {
|
|
Dbprintf(" Charge enable....... %s", (reg01 & (1u << 3)) ? _YELLOW_("disabled") : _GREEN_("enabled"));
|
|
}
|
|
if (I2C_BufferReadRaw(®02, 1, 0x02, BWM_CHG_ADDR) > 0) {
|
|
uint16_t ichg_ma = 8 * ((reg02 & 0x3F) + 1);
|
|
Dbprintf(" Charge current...... %u mA", ichg_ma);
|
|
}
|
|
}
|
|
|
|
// --- fuel gauge (BQ27427) ---
|
|
uint16_t soc = 0, mv = 0, rem = 0, temp = 0, raw_i = 0;
|
|
if (bwm_gauge_read16(BWM_GAUGE_SOC, &soc) && bwm_gauge_read16(BWM_GAUGE_VOLTAGE, &mv)) {
|
|
bwm_gauge_read16(BWM_GAUGE_REMCAP, &rem);
|
|
bwm_gauge_read16(BWM_GAUGE_TEMP, &temp);
|
|
bwm_gauge_read16(BWM_GAUGE_CURRENT, &raw_i);
|
|
|
|
int16_t cur = (int16_t)raw_i; // +charge / -discharge (verify polarity on hw)
|
|
int tempC10 = (int)temp - 2732; // 0.1 K -> 0.1 C
|
|
int tabs = (tempC10 < 0) ? -tempC10 : tempC10;
|
|
|
|
Dbprintf(" Battery SoC......... %u %%", soc);
|
|
Dbprintf(" Battery voltage..... %u mV", mv);
|
|
Dbprintf(" Battery current..... %d mA %s", cur,
|
|
(cur > 5) ? _GREEN_("(charging)") :
|
|
(cur < -5) ? _YELLOW_("(discharging)") : "(idle)");
|
|
Dbprintf(" Remaining capacity.. %u mAh", rem);
|
|
Dbprintf(" Temp (gauge)........ %d.%d C", tempC10 / 10, tabs % 10);
|
|
} else {
|
|
Dbprintf(" Fuel gauge.......... " _YELLOW_("not responding") " (BQ27427 absent or I2C down)");
|
|
}
|
|
}
|
|
|
|
// --- BQ27427 provisioning: set Design Capacity for the fitted cell -------------
|
|
// One-time. The gauge ships with a ~1000+ mAh default profile, so RemainingCapacity
|
|
// reads wrong for the fitted pack until Design Capacity is programmed. Invoked by the
|
|
// `hw bwmsetcap` client command (CMD_PM5_BWM_SET_CAP) - deliberately NOT run at boot,
|
|
// because a config-update cycle disrupts the Impedance Track learning cycle.
|
|
// Per BQ27427 TRM (SLUUCD5): State subclass 82 (0x52), Design Capacity at offset 6
|
|
// -> block addr 0x46 (MSB)/0x47 (LSB), big-endian. Assumes gauge UNSEALED (factory default).
|
|
#define BWM_DEFAULT_DESIGN_CAP_MAH 500 // VXE 502540 on the reference BWM
|
|
|
|
static bool bq_control(uint16_t sub) {
|
|
uint8_t d[2] = { (uint8_t)(sub & 0xFF), (uint8_t)(sub >> 8) };
|
|
return I2C_BufferWrite(d, 2, 0x00, BWM_GAUGE_ADDR); // Control() 0x00
|
|
}
|
|
static bool bq_flags(uint16_t *f) {
|
|
uint8_t d[2] = {0};
|
|
if (I2C_BufferReadRaw(d, 2, 0x06, BWM_GAUGE_ADDR) <= 0) return false;
|
|
*f = (uint16_t)(d[0] | (d[1] << 8)); // Flags() 0x06
|
|
return true;
|
|
}
|
|
static bool bq_select_state_block(void) {
|
|
uint8_t v;
|
|
v = 0x00;
|
|
if (!I2C_BufferWrite(&v, 1, 0x61, BWM_GAUGE_ADDR)) return false; // BlockDataControl
|
|
v = 0x52;
|
|
if (!I2C_BufferWrite(&v, 1, 0x3E, BWM_GAUGE_ADDR)) return false; // DataClass = 82 (State)
|
|
v = 0x00;
|
|
if (!I2C_BufferWrite(&v, 1, 0x3F, BWM_GAUGE_ADDR)) return false; // DataBlock = 0
|
|
WaitMS(5);
|
|
return true;
|
|
}
|
|
static bool bq_read_design_cap(uint16_t *cap) {
|
|
if (!bq_select_state_block()) return false;
|
|
uint8_t d[2] = {0};
|
|
if (I2C_BufferReadRaw(d, 2, 0x46, BWM_GAUGE_ADDR) <= 0) return false;
|
|
*cap = (uint16_t)((d[0] << 8) | d[1]); // big-endian
|
|
return true;
|
|
}
|
|
|
|
// Enable or disable battery charging by clearing/setting CEB (REG01[3]:
|
|
// 0 = charge enabled, 1 = charge disabled). Read-modify-write to preserve the
|
|
// other REG01 fields. NOTE: REG01 is watchdog-affected on the AW32001E - this
|
|
// reverts to its default on watchdog expiry (~160 s) unless the watchdog is
|
|
// serviced (REG02[6]=1) or disabled (REG05[6:5]=00), so treat it as a one-shot.
|
|
static bool bwm_charger_set_charge(bool enable) {
|
|
uint8_t reg01 = 0;
|
|
if (I2C_BufferReadRaw(®01, 1, 0x01, BWM_CHG_ADDR) <= 0) {
|
|
return false;
|
|
}
|
|
if (enable) {
|
|
reg01 &= ~(1u << 3); // CEB = 0 -> charge enabled
|
|
} else {
|
|
reg01 |= (1u << 3); // CEB = 1 -> charge disabled
|
|
}
|
|
return I2C_BufferWrite(®01, 1, 0x01, BWM_CHG_ADDR);
|
|
}
|
|
|
|
// Program Design Capacity (and matching Design Energy). Idempotent: returns true
|
|
// without a config-update cycle if the value is already correct.
|
|
static bool bwm_gauge_provision_capacity(uint16_t cap_mah) {
|
|
uint16_t cur = 0;
|
|
if (bq_read_design_cap(&cur) && cur == cap_mah) {
|
|
return true; // already correct - do NOT run another CFGUPDATE cycle
|
|
}
|
|
|
|
uint16_t energy_mwh = (uint16_t)(((uint32_t)cap_mah * 37) / 10); // ~3.7 V nominal
|
|
|
|
// Enter CONFIG_UPDATE and wait for the gauge to acknowledge it.
|
|
if (bq_control(0x0013) == false) { // SET_CFGUPDATE
|
|
return false;
|
|
}
|
|
|
|
uint16_t flags = 0;
|
|
int tries = 0;
|
|
do {
|
|
WaitMS(50);
|
|
if (bq_flags(&flags) == false) {
|
|
return false;
|
|
}
|
|
} while (((flags & 0x0010) == 0) && (++tries < 40)); // wait for CFGUPDATE (Flags bit 4), ~2 s
|
|
|
|
if ((flags & 0x0010) == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (!bq_select_state_block()) return false;
|
|
|
|
uint8_t blk[32] = {0};
|
|
if (I2C_BufferReadRaw(blk, 32, 0x40, BWM_GAUGE_ADDR) <= 0) return false;
|
|
|
|
blk[6] = (uint8_t)(cap_mah >> 8);
|
|
blk[7] = (uint8_t)(cap_mah & 0xFF); // DesignCapacity
|
|
blk[8] = (uint8_t)(energy_mwh >> 8);
|
|
blk[9] = (uint8_t)(energy_mwh & 0xFF); // DesignEnergy
|
|
I2C_BufferWrite(&blk[6], 2, 0x46, BWM_GAUGE_ADDR);
|
|
I2C_BufferWrite(&blk[8], 2, 0x48, BWM_GAUGE_ADDR);
|
|
|
|
uint16_t sum = 0; // block checksum = 255 - (sum mod 256)
|
|
for (int i = 0; i < 32; i++) sum += blk[i];
|
|
uint8_t csum = (uint8_t)(0xFF - (uint8_t)(sum & 0xFF));
|
|
I2C_BufferWrite(&csum, 1, 0x60, BWM_GAUGE_ADDR); // commit block
|
|
WaitMS(10);
|
|
|
|
if (!bq_control(0x0042)) return false; // SOFT_RESET (exit CFGUPDATE)
|
|
tries = 0;
|
|
do { WaitMS(50); if (!bq_flags(&flags)) return false; }
|
|
while (((flags & 0x0010) != 0) && (++tries < 40));
|
|
return ((flags & 0x0010) == 0);
|
|
}
|
|
|
|
// Strong override of the weak UnitTestMain() in start.c. Vector() calls UnitTestMain()
|
|
// after ConfigSystemClocks() and before AppMain(); the weak default is empty, so a
|
|
// strong definition here runs at boot and then returns into AppMain() normally.
|
|
//
|
|
// This enables battery charging on the BWM by replicating the charger register writes
|
|
// from at32_unit_test.c:test_bat_charger_only_settings() (upstream RRG values), WITHOUT
|
|
// pulling in that file's unrelated UART-debug / RGB test routines. AW32001E @ 0x93:
|
|
// REG01[3] CEB -> 0 : charge enabled
|
|
// REG02 ICHG = 0x1F : 256 mA charge current
|
|
// REG05 = 0x1A : safety timer disabled (matches upstream; note the charger
|
|
// watchdog is thus relied upon off - see REG05 handling)
|
|
// REG03 = 0xE1 : 3 A discharge current
|
|
// REG0B = 0x6B : 11 mA pre-charge current
|
|
|
|
// Probe for the BWM charger over I2C and, if found, apply the charge configuration.
|
|
static void bwm_detect_and_init(void) {
|
|
I2C_init(true);
|
|
|
|
// Single bounded probe. Any non-ACK => no BWM fitted; skip everything.
|
|
uint8_t v = 0;
|
|
if (I2C_BufferReadRaw(&v, 1, 0x01, BWM_CHG_ADDR) <= 0) {
|
|
g_bwm_present = false;
|
|
return;
|
|
}
|
|
g_bwm_present = true;
|
|
|
|
// Enable charging (clear CEB, REG01[3]) + upstream charge profile
|
|
// (matches at32_unit_test.c:test_bat_charger_only_settings(), AW32001ECSR).
|
|
if (v & (1u << 3)) {
|
|
v &= ~(1u << 3);
|
|
I2C_BufferWrite(&v, 1, 0x01, BWM_CHG_ADDR);
|
|
}
|
|
uint8_t w;
|
|
w = 0x1F; I2C_BufferWrite(&w, 1, 0x02, BWM_CHG_ADDR); // charge current 256 mA
|
|
w = 0xE1; I2C_BufferWrite(&w, 1, 0x03, BWM_CHG_ADDR); // discharge current 3 A
|
|
w = 0x1A; I2C_BufferWrite(&w, 1, 0x05, BWM_CHG_ADDR); // safety timer disabled (upstream)
|
|
w = 0x6B; I2C_BufferWrite(&w, 1, 0x0B, BWM_CHG_ADDR); // pre-charge 11 mA
|
|
}
|
|
|
|
#endif // WITH_BWM_STATUS
|
|
|
|
#ifdef WITH_PM5_PWR_LED
|
|
// "Alive on battery" indicator. When the PM5 runs on battery (USB unplugged) it
|
|
// otherwise gives no sign it is on, so users leave it draining. This lights the
|
|
// antenna RGB a dim green while on battery, and turns it off when on USB (where the
|
|
// cable already signals power). Throttled + edge-triggered to avoid I2C spam and to
|
|
// yield the RGB to hf/lf tune (which sets g_rgb_external while it owns the LED).
|
|
#ifndef PM5_PWR_LED_PERIOD_MS
|
|
#define PM5_PWR_LED_PERIOD_MS 1000 // re-evaluate at most once a second
|
|
#endif
|
|
|
|
// Set by CMD_PM5_RGB_SET so the indicator backs off while tune controls the RGB.
|
|
volatile bool g_rgb_external = false;
|
|
|
|
static bool s_pwr_led_setup = false;
|
|
|
|
static void bwm_power_led_check(void) {
|
|
static uint32_t last_tick = 0;
|
|
static int last_state = -1; // -1 unknown, 0 = off/USB, 1 = green/battery
|
|
|
|
if ((last_tick != 0) && (GetTickCountDelta(last_tick) < PM5_PWR_LED_PERIOD_MS)) {
|
|
return;
|
|
}
|
|
last_tick = GetTickCount();
|
|
|
|
// While tune (or any external RGB user) owns the LED, do nothing and force a
|
|
// refresh next time it is released.
|
|
if (g_rgb_external) {
|
|
last_state = -1;
|
|
return;
|
|
}
|
|
|
|
if (s_pwr_led_setup == false) {
|
|
gpio_vusb_setup();
|
|
s_pwr_led_setup = true;
|
|
}
|
|
|
|
int on_battery = (Gpio_VUSB_Read() == false) ? 1 : 0;
|
|
if (on_battery == last_state) {
|
|
return; // edge-triggered: only write RGB when the state changes
|
|
}
|
|
last_state = on_battery;
|
|
|
|
if (on_battery) {
|
|
RgbLedSet(0, 8, 0); // dim green: alive, on battery
|
|
} else {
|
|
RgbLedSet(0, 0, 0); // on USB: off (cable already signals power)
|
|
}
|
|
}
|
|
#endif // WITH_PM5_PWR_LED
|
|
|
|
#ifdef WITH_PM5_AUTOOFF
|
|
// Automatic power-off on USB unplug. When the BWM keeps the PM5 alive on battery,
|
|
// users leave it draining. This powers the board down after USB has been absent
|
|
// continuously for a grace period, using the SAME latch release as the long-press
|
|
// shutdown (Gpio_ARM_Power_ON_Low). Button power-ON is a hardware function and is
|
|
// unaffected - once powered off there is no firmware running to interfere with it.
|
|
//
|
|
// Runtime toggle (default ON) via CMD_PM5_BWM_AUTOOFF; resets to default each boot.
|
|
// Standalone / BLE-relay users who run unplugged on purpose can disable it.
|
|
#ifndef PM5_AUTOOFF_GRACE_MS
|
|
#define PM5_AUTOOFF_GRACE_MS 10000 // USB must be absent this long before power-off
|
|
#endif
|
|
#ifndef PM5_AUTOOFF_POLL_MS
|
|
#define PM5_AUTOOFF_POLL_MS 500 // how often to sample VUSB
|
|
#endif
|
|
|
|
bool g_autooff_enabled = true; // default on; toggled by CMD_PM5_BWM_AUTOOFF
|
|
|
|
static bool s_autooff_setup = false;
|
|
|
|
static void bwm_autooff_check(void) {
|
|
static uint32_t last_tick = 0;
|
|
static uint32_t usb_gone_since = 0; // tick when USB first read absent; 0 = present
|
|
|
|
if (g_autooff_enabled == false) {
|
|
usb_gone_since = 0;
|
|
return;
|
|
}
|
|
if ((last_tick != 0) && (GetTickCountDelta(last_tick) < PM5_AUTOOFF_POLL_MS)) {
|
|
return;
|
|
}
|
|
last_tick = GetTickCount();
|
|
|
|
if (s_autooff_setup == false) {
|
|
gpio_vusb_setup();
|
|
s_autooff_setup = true;
|
|
}
|
|
|
|
// Gpio_VUSB_Read() == true means USB power present.
|
|
if (Gpio_VUSB_Read()) {
|
|
usb_gone_since = 0; // present (or came back) -> reset the grace timer
|
|
return;
|
|
}
|
|
|
|
// USB absent. Start / continue the debounce window.
|
|
if (usb_gone_since == 0) {
|
|
usb_gone_since = GetTickCount();
|
|
return;
|
|
}
|
|
if (GetTickCountDelta(usb_gone_since) < PM5_AUTOOFF_GRACE_MS) {
|
|
return; // not gone long enough yet; a replug resets it above
|
|
}
|
|
|
|
// USB confirmed absent for the full grace period: power off via the latch.
|
|
LEDsoff();
|
|
Gpio_ARM_Power_ON_Low();
|
|
while (1); // wait for hardware power-off (button press powers back on, in hardware)
|
|
}
|
|
#endif // WITH_PM5_AUTOOFF
|
|
|
|
#ifdef WITH_LCD
|
|
#include "LCD_disabled.h"
|
|
#endif
|
|
|
|
#ifdef WITH_SMARTCARD
|
|
#include "i2c.h"
|
|
#endif
|
|
|
|
#ifdef WITH_FPC_USART
|
|
#include "usart.h"
|
|
#endif
|
|
|
|
#ifdef WITH_FLASH
|
|
#include "flashmem.h"
|
|
#include "spiffs.h"
|
|
#endif
|
|
|
|
int g_dbglevel = DBG_ERROR;
|
|
uint8_t g_trigger = 0;
|
|
bool g_hf_field_active = false;
|
|
bool g_hf_field_timeout_active = false;
|
|
extern uint32_t _stack_start[], _stack_end[];
|
|
common_area_t g_common_area __attribute__((section(".commonarea")));
|
|
static int button_status = BUTTON_NO_CLICK;
|
|
static bool allow_send_wtx = false;
|
|
static uint32_t g_hf_field_activity_timeout_ms = 0;
|
|
uint16_t g_tearoff_delay_us = 0;
|
|
bool g_tearoff_enabled = false;
|
|
uint8_t g_tearoff_skip = 0;
|
|
|
|
int tearoff_hook(void) {
|
|
|
|
if (g_tearoff_enabled == false) {
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// tear off is happening...
|
|
|
|
if (g_tearoff_delay_us == 0) {
|
|
|
|
if (g_dbglevel >= DBG_ERROR) Dbprintf(_RED_("No tear-off delay configured!"));
|
|
g_tearoff_enabled = false;
|
|
return PM3_SUCCESS; // SUCCESS = the hook didn't do anything
|
|
}
|
|
|
|
if (g_tearoff_skip > 0) {
|
|
if (g_dbglevel >= DBG_INFO) Dbprintf(_GREEN_("Tear-off skipped!"));
|
|
g_tearoff_skip--;
|
|
return PM3_SUCCESS; // SUCCESS = the hook didn't do anything
|
|
}
|
|
|
|
SpinDelayUsPrecision(g_tearoff_delay_us);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
g_tearoff_enabled = false;
|
|
|
|
if (g_dbglevel >= DBG_INFO) Dbprintf(_YELLOW_("Tear-off triggered!"));
|
|
|
|
return PM3_ETEAROFF;
|
|
}
|
|
|
|
void hf_field_off(void) {
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
g_hf_field_active = false;
|
|
g_hf_field_timeout_active = false;
|
|
}
|
|
|
|
void send_wtx(uint16_t wtx) {
|
|
if (allow_send_wtx) {
|
|
reply_ng(CMD_WTX, PM3_SUCCESS, (uint8_t *)&wtx, sizeof(wtx));
|
|
}
|
|
}
|
|
|
|
#ifdef WITH_LF
|
|
static void MeasureAntennaTuning(void) {
|
|
|
|
uint32_t peak = 0;
|
|
|
|
// in mVolt
|
|
struct p {
|
|
uint32_t v_lf134;
|
|
uint32_t v_lf125;
|
|
uint32_t v_lfconf;
|
|
uint32_t v_hf;
|
|
uint32_t peak_v;
|
|
uint32_t peak_f;
|
|
int divisor;
|
|
uint8_t results[256];
|
|
} PACKED payload;
|
|
|
|
// Need to clear all values to ensure non-random responses.
|
|
memset(&payload, 0, sizeof(payload));
|
|
// memset(payload.results, 0, sizeof(payload.results));
|
|
|
|
sample_config *sc = getSamplingConfig();
|
|
payload.divisor = sc->divisor;
|
|
|
|
LED_B_ON();
|
|
|
|
/*
|
|
* Sweeps the useful LF range of the proxmark from
|
|
* 46.8kHz (divisor=255) to 600kHz (divisor=19) and
|
|
* read the voltage in the antenna, the result left
|
|
* in the buffer is a graph which should clearly show
|
|
* the resonating frequency of your LF antenna
|
|
* ( hopefully around 95 if it is tuned to 125kHz!)
|
|
*/
|
|
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD);
|
|
SpinDelay(50);
|
|
|
|
for (uint8_t i = 255; i >= 19; i--) {
|
|
WDT_HIT();
|
|
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, i);
|
|
SpinDelay(20);
|
|
uint32_t adcval = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_LF);
|
|
if (i == LF_DIVISOR_125)
|
|
payload.v_lf125 = adcval; // voltage at 125kHz
|
|
|
|
if (i == LF_DIVISOR_134)
|
|
payload.v_lf134 = adcval; // voltage at 134kHz
|
|
|
|
if (i == sc->divisor)
|
|
payload.v_lfconf = adcval; // voltage at `lf config --divisor`
|
|
|
|
payload.results[i] = adcval >> 9; // scale int to fit in byte for graphing purposes
|
|
|
|
if (payload.results[i] > peak) {
|
|
payload.peak_v = adcval;
|
|
payload.peak_f = i;
|
|
peak = payload.results[i];
|
|
}
|
|
}
|
|
|
|
LED_A_ON();
|
|
// Let the FPGA drive the high-frequency antenna around 13.56 MHz.
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER);
|
|
SpinDelay(50);
|
|
|
|
payload.v_hf = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF);
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload));
|
|
LEDsoff();
|
|
}
|
|
#endif
|
|
|
|
#ifndef PM5 // TODO DXL: PM5 is temporarily incompatible.
|
|
|
|
// Measure HF antenna decay after field-off.
|
|
// Captures peak-detect capacitor discharge curve via burst ADC sampling.
|
|
static void MeasureAntennaTuningHfDecay(const hf_decay_params_t *params) {
|
|
|
|
// Parse parameters with defaults
|
|
uint16_t stabilize_ms = params->stabilize_ms;
|
|
uint16_t measure_us = params->measure_us;
|
|
|
|
if (stabilize_ms == 0) stabilize_ms = 50;
|
|
if (measure_us == 0) measure_us = 2000;
|
|
|
|
// Response: 8-byte header + up to 252 uint16_t samples = 512 bytes max
|
|
hf_decay_response_t payload;
|
|
memset(&payload, 0, sizeof(payload));
|
|
|
|
LED_B_ON();
|
|
|
|
// Drive HF field and wait for stabilization
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER);
|
|
SpinDelay(stabilize_ms);
|
|
|
|
// Baseline measurement (averaged)
|
|
payload.baseline_mv = (MAX_ADC_HF_VOLTAGE * AdcRssiSum(ADC_RSSI_CH_HF, 32)) >> 15;
|
|
|
|
// Configure ADC for fast burst mode.
|
|
// Faster ADC clock + shorter S&H trades absolute accuracy for speed.
|
|
// Source impedance is ~0.91 MOhm (voltage divider), ADC input cap 12pF,
|
|
// RC = 10.9us. At SHTIM=3 / ADC_CLK=3MHz, S&H = 1.33us reads ~11.5%
|
|
// of true voltage. This is fine for relative decay shape measurement.
|
|
AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST;
|
|
AT91C_BASE_ADC->ADC_MR =
|
|
ADC_MODE_PRESCALE(7) // ADC_CLK = MCK / 16 = 3 MHz
|
|
| ADC_MODE_STARTUP_TIME(8) // (8+1)*8 / 3MHz = 24us (> 20us min)
|
|
| ADC_MODE_SAMPLE_HOLD_TIME(3); // (3+1) / 3MHz = 1.33us S&H
|
|
AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF);
|
|
|
|
// Start precise timer (1 tick = MCK/32 = 0.667us)
|
|
StartTicks();
|
|
|
|
// Field OFF — start decay measurement
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
|
|
uint32_t start_ticks = GetTicks();
|
|
uint16_t idx = 0;
|
|
// Convert us to ticks: 1us = 1.5 ticks
|
|
uint32_t measure_ticks = (measure_us * 3) / 2;
|
|
|
|
// Trigger first conversion
|
|
AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START;
|
|
|
|
while (idx < 252) {
|
|
if (AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF)) {
|
|
uint16_t raw = AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF] & 0x3FF;
|
|
payload.samples_mv[idx] = (MAX_ADC_HF_VOLTAGE * raw) >> 10;
|
|
idx++;
|
|
|
|
if (GetTicksDelta(start_ticks) >= measure_ticks)
|
|
break;
|
|
|
|
// Trigger next conversion
|
|
AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START;
|
|
}
|
|
}
|
|
|
|
uint32_t elapsed_ticks = GetTicksDelta(start_ticks);
|
|
payload.num_samples = idx;
|
|
payload.measure_window_us = (elapsed_ticks * 2) / 3;
|
|
payload.sample_interval_us = (idx > 1) ? payload.measure_window_us / (idx - 1) : 0;
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
StopTicks();
|
|
|
|
uint16_t response_size = 8 + (idx * sizeof(uint16_t));
|
|
reply_ng(CMD_HF_DECAY, PM3_SUCCESS,
|
|
(uint8_t *)&payload, response_size);
|
|
|
|
LEDsoff();
|
|
}
|
|
|
|
#endif
|
|
|
|
void print_stack_usage(void) {
|
|
for (uint32_t *p = _stack_start; ; ++p) {
|
|
if (*p != 0xdeadbeef) {
|
|
Dbprintf(" Max stack usage..... %d / %d bytes", (uint32_t)_stack_end - (uint32_t)p, (uint32_t)_stack_end - (uint32_t)_stack_start);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReadMem(int addr) {
|
|
const uint8_t *data = ((uint8_t *)addr);
|
|
|
|
Dbprintf("%x: %02x %02x %02x %02x %02x %02x %02x %02x", addr, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
|
|
}
|
|
|
|
/* osimage version information is linked in, cf commonutil.h */
|
|
/* bootrom version information is pointed to from _bootphase1_version_pointer */
|
|
extern uint32_t _bootphase1_version_pointer[], _flash_start[], _flash_end[], __data_src_start__[];
|
|
#ifndef WITH_COMPRESSION
|
|
extern uint32_t _bootrom_end[], _bootrom_start[], __os_size__[];
|
|
#endif
|
|
static void SendVersion(void) {
|
|
char temp[PM3_CMD_DATA_SIZE - 12]; /* Limited data payload in USB packets */
|
|
char VersionString[PM3_CMD_DATA_SIZE - 12] = { '\0' };
|
|
|
|
/* Try to find the bootrom version information. Expect to find a pointer at
|
|
* symbol _bootphase1_version_pointer, perform slight sanity checks on the
|
|
* pointer, then use it.
|
|
*/
|
|
// dummy casting to avoid "dereferencing type-punned pointer breaking strict-aliasing rules" errors
|
|
uint32_t bootrom_version_ptr = (uint32_t)_bootphase1_version_pointer;
|
|
char *bootrom_version = *(char **)(bootrom_version_ptr);
|
|
|
|
strncat(VersionString, " [ "_YELLOW_("ARM")" ]\n", sizeof(VersionString) - strlen(VersionString) - 1);
|
|
|
|
if ((uint32_t)bootrom_version < (uint32_t)_flash_start || (uint32_t)bootrom_version >= (uint32_t)_flash_end) {
|
|
strcat(VersionString, "bootrom version information appears invalid\n");
|
|
} else {
|
|
FormatVersionInformation(temp, sizeof(temp), " Bootrom.... ", bootrom_version);
|
|
strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1);
|
|
strncat(VersionString, "\n", sizeof(VersionString) - strlen(VersionString) - 1);
|
|
}
|
|
|
|
FormatVersionInformation(temp, sizeof(temp), " OS......... ", &g_version_information);
|
|
strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1);
|
|
strncat(VersionString, "\n", sizeof(VersionString) - strlen(VersionString) - 1);
|
|
|
|
#if defined(__clang__)
|
|
strncat(VersionString, " Compiler... Clang/LLVM "__VERSION__"\n", sizeof(VersionString) - strlen(VersionString) - 1);
|
|
#elif defined(__GNUC__) || defined(__GNUG__)
|
|
strncat(VersionString, " Compiler... GCC "__VERSION__"\n", sizeof(VersionString) - strlen(VersionString) - 1);
|
|
#endif
|
|
|
|
#ifndef PM5
|
|
// PM5's FPGA (Gowin) bitstream is loaded at runtime via `hw fpga config` and is
|
|
// not compiled into the firmware, so there is no meaningful built-in FPGA
|
|
// version to report here. g_fpga_version_information[] describes the Xilinx
|
|
// bitstream that PM5 does not run, so omit the section entirely on PM5.
|
|
strncat(VersionString, "\n [ "_YELLOW_("FPGA")" ] \n ", sizeof(VersionString) - strlen(VersionString) - 1);
|
|
|
|
for (int i = 0; i < g_fpga_bitstream_num; i++) {
|
|
strncat(VersionString, g_fpga_version_information[i].versionString, sizeof(VersionString) - strlen(VersionString) - 1);
|
|
if (i < g_fpga_bitstream_num - 1) {
|
|
strncat(VersionString, "\n ", sizeof(VersionString) - strlen(VersionString) - 1);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef WITH_COMPRESSION
|
|
// Send Chip ID and used flash memory
|
|
uint32_t text_and_rodata_section_size = (uint32_t)__data_src_start__ - (uint32_t)_flash_start;
|
|
uint32_t compressed_data_section_size = g_common_area.arg1;
|
|
#endif
|
|
|
|
struct p {
|
|
uint32_t id;
|
|
uint32_t section_size;
|
|
uint32_t versionstr_len;
|
|
char versionstr[PM3_CMD_DATA_SIZE - 12];
|
|
} PACKED;
|
|
|
|
struct p payload;
|
|
|
|
// Set a CHIP ID(not unique id)
|
|
payload.id = GetChipId();
|
|
|
|
#ifndef WITH_COMPRESSION
|
|
payload.section_size = (uint32_t)_bootrom_end - (uint32_t)_bootrom_start + (uint32_t)__os_size__;
|
|
#else
|
|
payload.section_size = text_and_rodata_section_size + compressed_data_section_size;
|
|
#endif
|
|
payload.versionstr_len = strlen(VersionString) + 1;
|
|
memcpy(payload.versionstr, VersionString, payload.versionstr_len);
|
|
|
|
uint32_t reply_len = 12 + payload.versionstr_len;
|
|
|
|
// Append the total on-chip flash size (bytes) AFTER the version string. This is
|
|
// backward compatible: older clients stop at versionstr and ignore the trailing
|
|
// bytes, and this stays valid when talking to older firmware that omits it. It
|
|
// lets the client report memory usage on MCUs whose size can't be derived from
|
|
// the chip id (e.g. AT32). Keep the header layout unchanged (do not break the
|
|
// CMD_VERSION protocol).
|
|
if (reply_len + sizeof(uint32_t) <= sizeof(payload)) {
|
|
uint32_t flash_size = GetChipFlashSize();
|
|
memcpy(payload.versionstr + payload.versionstr_len, &flash_size, sizeof(flash_size));
|
|
reply_len += sizeof(flash_size);
|
|
}
|
|
|
|
reply_ng(CMD_VERSION, PM3_SUCCESS, (uint8_t *)&payload, reply_len);
|
|
}
|
|
|
|
#ifdef CHIP_AT91SAM7S // Only AT91SAM7S chip series need calibration.
|
|
|
|
static void TimingIntervalAcquisition(void) {
|
|
// trigger new acquisition by turning main oscillator off and on
|
|
mck_from_pll_to_slck();
|
|
mck_from_slck_to_pll();
|
|
// wait for MCFR and recompute RTMR scaler
|
|
StartTickCount();
|
|
}
|
|
|
|
#endif
|
|
|
|
static void print_debug_level(void) {
|
|
char dbglvlstr[20] = {0};
|
|
switch (g_dbglevel) {
|
|
case DBG_NONE:
|
|
sprintf(dbglvlstr, "off");
|
|
break;
|
|
case DBG_ERROR:
|
|
sprintf(dbglvlstr, "error");
|
|
break;
|
|
case DBG_INFO:
|
|
sprintf(dbglvlstr, "info");
|
|
break;
|
|
case DBG_DEBUG:
|
|
sprintf(dbglvlstr, "debug");
|
|
break;
|
|
case DBG_EXTENDED:
|
|
sprintf(dbglvlstr, "extended");
|
|
break;
|
|
}
|
|
Dbprintf(" Debug log level..... %d ( " _YELLOW_("%s")" )", g_dbglevel, dbglvlstr);
|
|
}
|
|
|
|
// measure the Connection Speed by sending SpeedTestBufferSize bytes to client and measuring the elapsed time.
|
|
// Note: this mimics GetFromBigbuf(), i.e. we have the overhead of the PacketCommandNG structure included.
|
|
static void printConnSpeed(uint32_t wait) {
|
|
DbpString(_CYAN_("Transfer Speed"));
|
|
Dbprintf(" Sending packets to client...");
|
|
|
|
uint8_t *test_data = BigBuf_get_addr();
|
|
uint32_t start_time = GetTickCount();
|
|
uint32_t delta_time = 0;
|
|
uint32_t bytes_transferred = 0;
|
|
|
|
LED_B_ON();
|
|
|
|
while (delta_time < wait) {
|
|
reply_ng(CMD_DOWNLOADED_BIGBUF, PM3_SUCCESS, test_data, PM3_CMD_DATA_SIZE);
|
|
bytes_transferred += PM3_CMD_DATA_SIZE;
|
|
delta_time = GetTickCountDelta(start_time);
|
|
}
|
|
LED_B_OFF();
|
|
|
|
Dbprintf(" Time elapsed................... %dms", delta_time);
|
|
Dbprintf(" Bytes transferred.............. %d", bytes_transferred);
|
|
if (delta_time) {
|
|
Dbprintf(" Transfer Speed PM3 -> Client... " _YELLOW_("%llu") " bytes/s", 1000 * (uint64_t)bytes_transferred / delta_time);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prints runtime information about the PM3.
|
|
**/
|
|
static void SendStatus(uint32_t wait) {
|
|
BigBuf_print_status();
|
|
Fpga_print_status();
|
|
#ifdef WITH_FLASH
|
|
Flashmem_print_status();
|
|
#endif
|
|
#ifdef WITH_SMARTCARD
|
|
I2C_print_status();
|
|
#endif
|
|
#ifdef WITH_LF
|
|
printLFConfig(); // LF Sampling config
|
|
printT55xxConfig(); // LF T55XX Config
|
|
#endif
|
|
#ifdef WITH_ISO14443a
|
|
printHf14aConfig(); // HF 14a config
|
|
#endif
|
|
#ifdef WITH_ISO14443b
|
|
printHf14bConfig(); // HF 14b config
|
|
#endif
|
|
#if defined(PM5) && defined(WITH_BWM_STATUS)
|
|
print_pm5_battery_status();
|
|
#endif
|
|
printConnSpeed(wait);
|
|
DbpString(_CYAN_("Various"));
|
|
|
|
print_stack_usage();
|
|
print_debug_level();
|
|
|
|
tosend_t *ts = get_tosend();
|
|
Dbprintf(" ToSendMax........... %d", ts->max);
|
|
Dbprintf(" ToSend BUFFERSIZE... %d", TOSEND_BUFFER_SIZE);
|
|
|
|
#ifdef CHIP_AT91SAM7S
|
|
|
|
while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available...
|
|
uint16_t mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF; // Get # main clocks within 16 slow clocks
|
|
Dbprintf(" Slow clock.......... %d Hz", (16 * MAINCK) / mainf);
|
|
uint32_t delta_time = 0;
|
|
uint32_t start_time = GetTickCount();
|
|
#define SLCK_CHECK_MS 50
|
|
SpinDelay(SLCK_CHECK_MS);
|
|
delta_time = GetTickCountDelta(start_time);
|
|
if ((delta_time < SLCK_CHECK_MS - 1) || (delta_time > SLCK_CHECK_MS + 1)) {
|
|
// error > 2% with SLCK_CHECK_MS=50
|
|
Dbprintf(_RED_(" Slow Clock speed change detected, run `hw tia`"));
|
|
Dbprintf(_YELLOW_(" Slow Clock actual speed seems closer to %d kHz"),
|
|
(16 * MAINCK / 1000) / mainf * delta_time / SLCK_CHECK_MS);
|
|
}
|
|
|
|
#endif
|
|
|
|
DbpString(_CYAN_("Installed StandAlone Mode"));
|
|
ModInfo();
|
|
|
|
#ifdef WITH_FLASH
|
|
DbpString(_CYAN_("Flash memory dictionary loaded"));
|
|
uint32_t num = 0;
|
|
|
|
if (exists_in_spiffs(MF_KEYS_FILE)) {
|
|
num = size_in_spiffs(MF_KEYS_FILE) / MF_KEY_LENGTH;
|
|
} else {
|
|
num = 0;
|
|
}
|
|
|
|
if (num > 0) {
|
|
Dbprintf(" Mifare... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MF_KEYS_FILE);
|
|
} else {
|
|
Dbprintf(" Mifare... "_RED_("%u")" keys - "_RED_("%s"), num, MF_KEYS_FILE);
|
|
}
|
|
|
|
if (exists_in_spiffs(T55XX_KEYS_FILE)) {
|
|
num = size_in_spiffs(T55XX_KEYS_FILE) / T55XX_KEY_LENGTH;
|
|
} else {
|
|
num = 0;
|
|
}
|
|
|
|
if (num > 0) {
|
|
Dbprintf(" T55xx.... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, T55XX_KEYS_FILE);
|
|
} else {
|
|
Dbprintf(" T55xx.... "_RED_("%u")" keys - "_RED_("%s"), num, T55XX_KEYS_FILE);
|
|
}
|
|
|
|
if (exists_in_spiffs(ICLASS_KEYS_FILE)) {
|
|
num = size_in_spiffs(ICLASS_KEYS_FILE) / ICLASS_KEY_LENGTH;
|
|
} else {
|
|
num = 0;
|
|
}
|
|
|
|
if (num > 0) {
|
|
Dbprintf(" iClass... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, ICLASS_KEYS_FILE);
|
|
} else {
|
|
Dbprintf(" iClass... "_RED_("%u")" keys - "_RED_("%s"), num, ICLASS_KEYS_FILE);
|
|
}
|
|
|
|
if (exists_in_spiffs(MFULC_KEYS_FILE)) {
|
|
num = size_in_spiffs(MFULC_KEYS_FILE) / MFULC_KEY_LENGTH;
|
|
} else {
|
|
num = 0;
|
|
}
|
|
|
|
if (num > 0) {
|
|
Dbprintf(" UL-C..... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MFULC_KEYS_FILE);
|
|
} else {
|
|
Dbprintf(" UL-C..... "_RED_("%u")" keys - "_RED_("%s"), num, MFULC_KEYS_FILE);
|
|
}
|
|
|
|
if (exists_in_spiffs(MFULAES_KEYS_FILE)) {
|
|
num = size_in_spiffs(MFULAES_KEYS_FILE) / MFULAES_KEY_LENGTH;
|
|
} else {
|
|
num = 0;
|
|
}
|
|
|
|
if (num > 0) {
|
|
Dbprintf(" UL-AES... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MFULAES_KEYS_FILE);
|
|
} else {
|
|
Dbprintf(" UL-AES... "_RED_("%u")" keys - "_RED_("%s"), num, MFULAES_KEYS_FILE);
|
|
}
|
|
|
|
|
|
#endif
|
|
DbpString("");
|
|
reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0);
|
|
}
|
|
|
|
static void SendCapabilities(void) {
|
|
capabilities_t capabilities;
|
|
capabilities.version = CAPABILITIES_VERSION;
|
|
capabilities.via_fpc = g_reply_via_fpc;
|
|
capabilities.via_usb = g_reply_via_usb;
|
|
capabilities.bigbuf_size = BigBuf_get_size();
|
|
capabilities.baudrate = 0; // no real baudrate for USB-CDC
|
|
#ifdef WITH_FPC_USART
|
|
if (g_reply_via_fpc)
|
|
capabilities.baudrate = g_usart_baudrate;
|
|
#endif
|
|
|
|
#ifdef RDV4
|
|
capabilities.is_rdv4 = true;
|
|
#else
|
|
capabilities.is_rdv4 = false;
|
|
#endif
|
|
|
|
#ifdef PM5
|
|
capabilities.is_pm5 = true;
|
|
capabilities.is_pm5_std_ant = true;
|
|
capabilities.hw_available_fpga_flash = true;
|
|
capabilities.hw_available_i2c_eeprom = true;
|
|
#else
|
|
capabilities.is_pm5 = false;
|
|
capabilities.is_pm5_std_ant = false;
|
|
capabilities.hw_available_fpga_flash = false;
|
|
capabilities.hw_available_i2c_eeprom = false;
|
|
#endif
|
|
|
|
#ifdef WITH_FLASH
|
|
capabilities.compiled_with_flash = true;
|
|
capabilities.hw_available_flash = FlashInit();
|
|
#else
|
|
capabilities.compiled_with_flash = false;
|
|
capabilities.hw_available_flash = false;
|
|
#endif
|
|
#ifdef WITH_SMARTCARD
|
|
capabilities.compiled_with_smartcard = true;
|
|
uint8_t maj, min;
|
|
capabilities.hw_available_smartcard = I2C_get_version(&maj, &min) == PM3_SUCCESS;
|
|
#else
|
|
capabilities.compiled_with_smartcard = false;
|
|
capabilities.hw_available_smartcard = false;
|
|
#endif
|
|
#ifdef WITH_FPC_USART
|
|
capabilities.compiled_with_fpc_usart = true;
|
|
#else
|
|
capabilities.compiled_with_fpc_usart = false;
|
|
#endif
|
|
#ifdef WITH_FPC_USART_DEV
|
|
capabilities.compiled_with_fpc_usart_dev = true;
|
|
#else
|
|
capabilities.compiled_with_fpc_usart_dev = false;
|
|
#endif
|
|
#ifdef WITH_FPC_USART_HOST
|
|
capabilities.compiled_with_fpc_usart_host = true;
|
|
#else
|
|
capabilities.compiled_with_fpc_usart_host = false;
|
|
#endif
|
|
#ifdef WITH_LF
|
|
capabilities.compiled_with_lf = true;
|
|
#else
|
|
capabilities.compiled_with_lf = false;
|
|
#endif
|
|
#ifdef WITH_HITAG
|
|
capabilities.compiled_with_hitag = true;
|
|
#else
|
|
capabilities.compiled_with_hitag = false;
|
|
#endif
|
|
#ifdef WITH_EM4x50
|
|
capabilities.compiled_with_em4x50 = true;
|
|
#else
|
|
capabilities.compiled_with_em4x50 = false;
|
|
#endif
|
|
#ifdef WITH_EM4x70
|
|
capabilities.compiled_with_em4x70 = true;
|
|
#else
|
|
capabilities.compiled_with_em4x70 = false;
|
|
#endif
|
|
|
|
#ifdef WITH_HFSNIFF
|
|
capabilities.compiled_with_hfsniff = true;
|
|
#else
|
|
capabilities.compiled_with_hfsniff = false;
|
|
#endif
|
|
#ifdef WITH_HFPLOT
|
|
capabilities.compiled_with_hfplot = true;
|
|
#else
|
|
capabilities.compiled_with_hfplot = false;
|
|
#endif
|
|
#ifdef WITH_ISO14443a
|
|
capabilities.compiled_with_iso14443a = true;
|
|
#else
|
|
capabilities.compiled_with_iso14443a = false;
|
|
#endif
|
|
#ifdef WITH_ISO14443b
|
|
capabilities.compiled_with_iso14443b = true;
|
|
#else
|
|
capabilities.compiled_with_iso14443b = false;
|
|
#endif
|
|
#ifdef WITH_ISO15693
|
|
capabilities.compiled_with_iso15693 = true;
|
|
#else
|
|
capabilities.compiled_with_iso15693 = false;
|
|
#endif
|
|
#ifdef WITH_FELICA
|
|
capabilities.compiled_with_felica = true;
|
|
#else
|
|
capabilities.compiled_with_felica = false;
|
|
#endif
|
|
#ifdef WITH_LEGICRF
|
|
capabilities.compiled_with_legicrf = true;
|
|
#else
|
|
capabilities.compiled_with_legicrf = false;
|
|
#endif
|
|
#ifdef WITH_ICLASS
|
|
capabilities.compiled_with_iclass = true;
|
|
#else
|
|
capabilities.compiled_with_iclass = false;
|
|
#endif
|
|
#ifdef WITH_SEOS
|
|
capabilities.compiled_with_seos = true;
|
|
#else
|
|
capabilities.compiled_with_seos = false;
|
|
#endif
|
|
#ifdef WITH_NFCBARCODE
|
|
capabilities.compiled_with_nfcbarcode = true;
|
|
#else
|
|
capabilities.compiled_with_nfcbarcode = false;
|
|
#endif
|
|
#ifdef WITH_LCD
|
|
capabilities.compiled_with_lcd = true;
|
|
#else
|
|
capabilities.compiled_with_lcd = false;
|
|
#endif
|
|
|
|
#ifdef WITH_ZX8211
|
|
capabilities.compiled_with_zx8211 = true;
|
|
#else
|
|
capabilities.compiled_with_zx8211 = false;
|
|
#endif
|
|
|
|
reply_ng(CMD_CAPABILITIES, PM3_SUCCESS, (uint8_t *)&capabilities, sizeof(capabilities));
|
|
}
|
|
|
|
// Show some leds in a pattern to identify StandAlone mod is running
|
|
void StandAloneMode(void) {
|
|
DbpString("");
|
|
DbpString("Stand-alone mode, no computer necessary");
|
|
SpinDown(50);
|
|
SpinDelay(50);
|
|
SpinUp(50);
|
|
SpinDelay(50);
|
|
SpinDown(50);
|
|
}
|
|
|
|
/*
|
|
OBJECTIVE
|
|
Listen and detect an external reader. Determine the best location
|
|
for the antenna.
|
|
|
|
INSTRUCTIONS:
|
|
Inside the ListenReaderField() function, there is two mode.
|
|
By default, when you call the function, you will enter mode 1.
|
|
If you press the PM3 button one time, you will enter mode 2.
|
|
If you press the PM3 button a second time, you will exit the function.
|
|
|
|
DESCRIPTION OF MODE 1:
|
|
This mode just listens for an external reader field and lights up green
|
|
for HF and/or red for LF. This is the original mode of the detectreader
|
|
function.
|
|
|
|
DESCRIPTION OF MODE 2:
|
|
This mode will visually represent, using the LEDs, the actual strength of the
|
|
current compared to the maximum current detected. Basically, once you know
|
|
what kind of external reader is present, it will help you spot the best location to place
|
|
your antenna. You will probably not get some good results if there is a LF and a HF reader
|
|
at the same place! :-)
|
|
*/
|
|
#define LIGHT_LEVELS 20
|
|
|
|
void ListenReaderField(uint8_t limit) {
|
|
#define LF_HF_BOTH 0
|
|
#define LF_ONLY 1
|
|
#define HF_ONLY 2
|
|
#define REPORT_CHANGE 1000 // report new values only if they have changed at least by REPORT_CHANGE mV
|
|
|
|
uint16_t lf_av = 0, lf_av_new, lf_baseline = 0, lf_max = 0;
|
|
uint16_t hf_av = 0, hf_av_new, hf_baseline = 0, hf_max = 0;
|
|
uint16_t mode = 1, display_val, display_max;
|
|
|
|
// switch off FPGA - we don't want to measure our own signal
|
|
// 20180315 - iceman, why load this before and then turn off?
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
|
|
LEDsoff();
|
|
|
|
if (limit == LF_ONLY || limit == LF_HF_BOTH) {
|
|
lf_av = lf_max = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_LF);
|
|
Dbprintf("LF 125/134kHz Baseline: %dmV", lf_av);
|
|
lf_baseline = lf_av;
|
|
}
|
|
|
|
if (limit == HF_ONLY || limit == LF_HF_BOTH) {
|
|
|
|
// iceman, useless, since we are measuring readerfield, not our field. My tests shows a max of 20v from a reader.
|
|
hf_av = hf_max = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF);;
|
|
Dbprintf("HF 13.56MHz Baseline: %dmV", hf_av);
|
|
hf_baseline = hf_av;
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
// Switch modes with button or Enter key
|
|
bool modeSwitched = BUTTON_PRESS();
|
|
if (modeSwitched == false && data_available()) {
|
|
// flush the buffer
|
|
PacketCommandNG rx;
|
|
receive_ng(&rx);
|
|
modeSwitched = true;
|
|
}
|
|
if (modeSwitched) {
|
|
SpinDelay(500);
|
|
switch (mode) {
|
|
case 1:
|
|
mode = 2;
|
|
DbpString("Signal Strength Mode");
|
|
break;
|
|
case 2:
|
|
default:
|
|
DbpString("Stopped");
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
return;
|
|
}
|
|
}
|
|
WDT_HIT();
|
|
|
|
if (limit == LF_ONLY || limit == LF_HF_BOTH) {
|
|
if (mode == 1) {
|
|
if (ABS(lf_av - lf_baseline) > REPORT_CHANGE)
|
|
LED_D_ON();
|
|
else
|
|
LED_D_OFF();
|
|
}
|
|
|
|
lf_av_new = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_LF);
|
|
// see if there's a significant change
|
|
if (ABS(lf_av - lf_av_new) > REPORT_CHANGE) {
|
|
Dbprintf("LF 125/134kHz Field Change: %5dmV", lf_av_new);
|
|
lf_av = lf_av_new;
|
|
if (lf_av > lf_max)
|
|
lf_max = lf_av;
|
|
}
|
|
}
|
|
|
|
if (limit == HF_ONLY || limit == LF_HF_BOTH) {
|
|
if (mode == 1) {
|
|
if (ABS(hf_av - hf_baseline) > REPORT_CHANGE)
|
|
LED_B_ON();
|
|
else
|
|
LED_B_OFF();
|
|
}
|
|
|
|
hf_av_new = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF);
|
|
// see if there's a significant change
|
|
if (ABS(hf_av - hf_av_new) > REPORT_CHANGE) {
|
|
Dbprintf("HF 13.56MHz Field Change: %5dmV", hf_av_new);
|
|
hf_av = hf_av_new;
|
|
if (hf_av > hf_max)
|
|
hf_max = hf_av;
|
|
}
|
|
}
|
|
|
|
if (mode == 2) {
|
|
if (limit == LF_ONLY) {
|
|
display_val = lf_av;
|
|
display_max = lf_max;
|
|
} else if (limit == HF_ONLY) {
|
|
display_val = hf_av;
|
|
display_max = hf_max;
|
|
} else { /* Pick one at random */
|
|
if ((hf_max - hf_baseline) > (lf_max - lf_baseline)) {
|
|
display_val = hf_av;
|
|
display_max = hf_max;
|
|
} else {
|
|
display_val = lf_av;
|
|
display_max = lf_max;
|
|
}
|
|
}
|
|
|
|
display_val = display_val * (4 * LIGHT_LEVELS) / MAX(1, display_max);
|
|
uint32_t duty_a = MIN(MAX(display_val, 0 * LIGHT_LEVELS), 1 * LIGHT_LEVELS) - 0 * LIGHT_LEVELS;
|
|
uint32_t duty_b = MIN(MAX(display_val, 1 * LIGHT_LEVELS), 2 * LIGHT_LEVELS) - 1 * LIGHT_LEVELS;
|
|
uint32_t duty_c = MIN(MAX(display_val, 2 * LIGHT_LEVELS), 3 * LIGHT_LEVELS) - 2 * LIGHT_LEVELS;
|
|
uint32_t duty_d = MIN(MAX(display_val, 3 * LIGHT_LEVELS), 4 * LIGHT_LEVELS) - 3 * LIGHT_LEVELS;
|
|
|
|
// LED A
|
|
if (duty_a == 0) {
|
|
LED_A_OFF();
|
|
} else if (duty_a == LIGHT_LEVELS) {
|
|
LED_A_ON();
|
|
} else {
|
|
LED_A_ON();
|
|
SpinDelay(duty_a);
|
|
LED_A_OFF();
|
|
SpinDelay(LIGHT_LEVELS - duty_a);
|
|
}
|
|
|
|
// LED B
|
|
if (duty_b == 0) {
|
|
LED_B_OFF();
|
|
} else if (duty_b == LIGHT_LEVELS) {
|
|
LED_B_ON();
|
|
} else {
|
|
LED_B_ON();
|
|
SpinDelay(duty_b);
|
|
LED_B_OFF();
|
|
SpinDelay(LIGHT_LEVELS - duty_b);
|
|
}
|
|
|
|
// LED C
|
|
if (duty_c == 0) {
|
|
LED_C_OFF();
|
|
} else if (duty_c == LIGHT_LEVELS) {
|
|
LED_C_ON();
|
|
} else {
|
|
LED_C_ON();
|
|
SpinDelay(duty_c);
|
|
LED_C_OFF();
|
|
SpinDelay(LIGHT_LEVELS - duty_c);
|
|
}
|
|
|
|
// LED D
|
|
if (duty_d == 0) {
|
|
LED_D_OFF();
|
|
} else if (duty_d == LIGHT_LEVELS) {
|
|
LED_D_ON();
|
|
} else {
|
|
LED_D_ON();
|
|
SpinDelay(duty_d);
|
|
LED_D_OFF();
|
|
SpinDelay(LIGHT_LEVELS - duty_d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef PM5
|
|
|
|
#include "at32f435_437_crm.h"
|
|
#include "at32f435_437_tmr.h"
|
|
|
|
// TODO DXL: 一部分QC逻辑可以放在PM5设备端实现,这个函数后面记得复用代码,并且不要放在 appmain.c 中(考虑移动到平台专属的模块)
|
|
// failed_item == 0: BLUE LED in Antenna
|
|
// failed_item == 1: RGB in mainboard
|
|
// failed_item == 2: LEDs * 4 or Buzzer or Button in mainboard
|
|
// timeout_ms == 0: run until button press or new usb data
|
|
static bool QCTestPM5(uint8_t *failed_item, uint32_t timeout_ms) {
|
|
// 天线蓝灯、主板RGB、主板四颗LED、蜂鸣器、按钮
|
|
StartTicks();
|
|
I2C_init(true);
|
|
|
|
uint8_t addr_ant = 0x51; // TODO DXL define move to header?
|
|
uint8_t addr_rgb = 0x48;
|
|
uint8_t data_u8 = 0;
|
|
bool isok = false;
|
|
bool result = false;
|
|
bool data_u8_valid = false;
|
|
|
|
// 读取天线当前MAP配置,如果读取不到,则认为天线的控制芯片可能有问题
|
|
isok = I2C_BufferReadRaw(&data_u8, 1, 0x02, addr_ant << 1);
|
|
if (!isok) {
|
|
*failed_item = 0;
|
|
result = false;
|
|
goto out;
|
|
}
|
|
data_u8_valid = true;
|
|
// 重新写入天线的MAP配置,去开灯
|
|
data_u8 |= 0x06; // 0000 0110 // 125 134 250 375 500 HFLED LFLED Q
|
|
isok = I2C_BufferWrite(&data_u8, 1, 0x02, addr_ant << 1);
|
|
|
|
// 开启RGB灯自动闪烁
|
|
uint8_t buf_rgb[3] = {0, 0, 128};
|
|
uint8_t buf_flash_time[] = {50, 50}; // 1s on, 500ms off.
|
|
isok = I2C_WriteByte(0, 0x02, addr_rgb << 1); // 写索引寄存器,设置后续操作的RGB索引
|
|
if (!isok) {
|
|
*failed_item = 1;
|
|
result = false;
|
|
goto out;
|
|
}
|
|
isok = I2C_WriteByte(1, 0x01, addr_rgb << 1); // 写数量寄存器,设置硬件挂1个灯,很重要!!!,不然无法闪灯
|
|
if (!isok) {
|
|
*failed_item = 1;
|
|
result = false;
|
|
goto out;
|
|
}
|
|
isok = I2C_BufferWrite(buf_rgb, sizeof(buf_rgb), 0x03, addr_rgb << 1); // 写数据寄存器,每三个字节就是对应的RGB888值
|
|
if (!isok) {
|
|
*failed_item = 1;
|
|
result = false;
|
|
goto out;
|
|
}
|
|
isok = I2C_WriteByte(1, 0x06, addr_rgb << 1); // 写闪灯使能寄存器,使能 0 号灯珠的可控闪烁
|
|
if (!isok) {
|
|
*failed_item = 1;
|
|
result = false;
|
|
goto out;
|
|
}
|
|
isok = I2C_BufferWrite(buf_flash_time, sizeof(buf_flash_time), 0x07, addr_rgb << 1); // 写闪灯使能寄存器,使能 0 号灯珠的可控闪烁
|
|
if (!isok) {
|
|
*failed_item = 1;
|
|
result = false;
|
|
goto out;
|
|
}
|
|
|
|
// 在循环中测试LED、蜂鸣器、按钮
|
|
|
|
#define BEEPER_EN_GPIO GPIOB
|
|
#define BEEPER_EN_GPIO_PIN GPIO_PINS_13
|
|
#define BEEPER_MOD_GPIO GPIOC
|
|
#define BEEPER_MOD_GPIO_PIN GPIO_PINS_9
|
|
#define BEEPER_MOD_GPIO_SRC GPIO_PINS_SOURCE9
|
|
#define BEEPER_MOD_GPIO_MUX GPIO_MUX_3
|
|
#define BEEPER_MOD_TMR TMR8
|
|
#define BEEPER_MOD_TMR_CH TMR_SELECT_CHANNEL_4
|
|
|
|
// PB13 使能,PC9 调制,使用 TMR8_CH4 输出调制
|
|
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
|
|
crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);
|
|
crm_periph_clock_enable(CRM_TMR8_PERIPH_CLOCK, TRUE);
|
|
|
|
gpio_init_type gpio_init_struct;
|
|
gpio_default_para_init(&gpio_init_struct);
|
|
// 蜂鸣器使能脚
|
|
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
|
|
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
|
|
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
|
|
gpio_init_struct.gpio_pins = BEEPER_EN_GPIO_PIN;
|
|
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
|
|
gpio_init(BEEPER_EN_GPIO, &gpio_init_struct);
|
|
gpio_bits_write(BEEPER_EN_GPIO, BEEPER_EN_GPIO_PIN, FALSE);
|
|
// 蜂鸣器调制脚
|
|
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
|
|
gpio_init_struct.gpio_pins = BEEPER_MOD_GPIO_PIN;
|
|
gpio_init(BEEPER_MOD_GPIO, &gpio_init_struct);
|
|
gpio_pin_mux_config(BEEPER_MOD_GPIO, BEEPER_MOD_GPIO_SRC, BEEPER_MOD_GPIO_MUX);
|
|
|
|
tmr_internal_clock_set(BEEPER_MOD_TMR);
|
|
tmr_reset(BEEPER_MOD_TMR);
|
|
tmr_base_init(BEEPER_MOD_TMR, 999, 95); // 192M出2k
|
|
tmr_output_config_type tmr_output_struct;
|
|
tmr_output_default_para_init(&tmr_output_struct);
|
|
tmr_output_struct.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
|
|
tmr_output_struct.oc_polarity = TMR_OUTPUT_ACTIVE_HIGH;
|
|
tmr_output_struct.oc_output_state = TRUE;
|
|
tmr_output_channel_config(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, &tmr_output_struct);
|
|
tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 500); // 比较值=500 (50%占空比)
|
|
tmr_counter_enable(BEEPER_MOD_TMR, TRUE);
|
|
tmr_output_enable(BEEPER_MOD_TMR, TRUE);
|
|
|
|
LEDsoff(); // 在开始测试之前线关闭所有LED
|
|
|
|
*failed_item = 2;
|
|
// 在开始测试之前,如果按钮是按下的,则认为失败,有可能按钮不良卡住了
|
|
if (BUTTON_PRESS()) {
|
|
result = false;
|
|
goto out;
|
|
}
|
|
|
|
uint32_t start_time = GetTickCount();
|
|
|
|
while (1) {
|
|
if (BUTTON_PRESS()) {
|
|
result = true;
|
|
goto out;
|
|
}
|
|
if (data_available() || (timeout_ms > 0 && (GetTickCount() - start_time) >= timeout_ms)) {
|
|
result = false;
|
|
goto out;
|
|
}
|
|
|
|
LED_A_ON();
|
|
BEEPER_MOD_TMR->pr = 999;
|
|
gpio_bits_write(BEEPER_EN_GPIO, BEEPER_EN_GPIO_PIN, TRUE);
|
|
SpinDelay(20);
|
|
gpio_bits_write(BEEPER_EN_GPIO, BEEPER_EN_GPIO_PIN, FALSE);
|
|
SpinDelay(200);
|
|
LED_A_OFF();
|
|
|
|
if (BUTTON_PRESS()) {
|
|
result = true;
|
|
goto out;
|
|
}
|
|
if (data_available() || (timeout_ms > 0 && (GetTickCount() - start_time) >= timeout_ms)) {
|
|
result = false;
|
|
goto out;
|
|
}
|
|
|
|
LED_B_ON();
|
|
BEEPER_MOD_TMR->pr = 1100;
|
|
tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 550);
|
|
gpio_bits_write(BEEPER_EN_GPIO, BEEPER_EN_GPIO_PIN, TRUE);
|
|
SpinDelay(20);
|
|
gpio_bits_write(BEEPER_EN_GPIO, BEEPER_EN_GPIO_PIN, FALSE);
|
|
SpinDelay(200);
|
|
LED_B_OFF();
|
|
|
|
if (BUTTON_PRESS()) {
|
|
result = true;
|
|
goto out;
|
|
}
|
|
if (data_available() || (timeout_ms > 0 && (GetTickCount() - start_time) >= timeout_ms)) {
|
|
result = false;
|
|
goto out;
|
|
}
|
|
|
|
LED_C_ON();
|
|
BEEPER_MOD_TMR->pr = 1200;
|
|
tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 600);
|
|
gpio_bits_write(BEEPER_EN_GPIO, BEEPER_EN_GPIO_PIN, TRUE);
|
|
SpinDelay(20);
|
|
gpio_bits_write(BEEPER_EN_GPIO, BEEPER_EN_GPIO_PIN, FALSE);
|
|
SpinDelay(200);
|
|
LED_C_OFF();
|
|
|
|
if (BUTTON_PRESS()) {
|
|
result = true;
|
|
goto out;
|
|
}
|
|
if (data_available() || (timeout_ms > 0 && (GetTickCount() - start_time) >= timeout_ms)) {
|
|
result = false;
|
|
goto out;
|
|
}
|
|
|
|
LED_D_ON();
|
|
BEEPER_MOD_TMR->pr = 1300;
|
|
tmr_channel_value_set(BEEPER_MOD_TMR, BEEPER_MOD_TMR_CH, 650);
|
|
gpio_bits_write(BEEPER_EN_GPIO, BEEPER_EN_GPIO_PIN, TRUE);
|
|
SpinDelay(20);
|
|
gpio_bits_write(BEEPER_EN_GPIO, BEEPER_EN_GPIO_PIN, FALSE);
|
|
SpinDelay(200);
|
|
LED_D_OFF();
|
|
}
|
|
|
|
out:
|
|
// Turn off the test LEDs (antenna + RGB flash) before returning
|
|
LEDsoff();
|
|
RgbLedSet(0, 0, 0);
|
|
I2C_WriteByte(0, 0x06, addr_rgb << 1);
|
|
if (data_u8_valid) {
|
|
data_u8 &= ~0x06;
|
|
I2C_BufferWrite(&data_u8, 1, 0x02, addr_ant << 1);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#endif
|
|
|
|
static void PacketReceived(PacketCommandNG *packet) {
|
|
/*
|
|
if (packet->ng) {
|
|
Dbprintf("received NG frame with %d bytes payload, with command: 0x%04x", packet->length, cmd);
|
|
} else {
|
|
Dbprintf("received OLD frame of %d bytes, with command: 0x%04x and args: %d %d %d", packet->length, packet->cmd, packet->oldarg[0], packet->oldarg[1], packet->oldarg[2]);
|
|
}
|
|
*/
|
|
|
|
switch (packet->cmd) {
|
|
case CMD_BREAK_LOOP:
|
|
break;
|
|
case CMD_QUIT_SESSION: {
|
|
g_reply_via_fpc = false;
|
|
g_reply_via_usb = false;
|
|
break;
|
|
}
|
|
case CMD_SET_FPGAMODE: {
|
|
uint8_t mode = packet->data.asBytes[0];
|
|
if (mode >= FPGA_BITSTREAM_MIN && mode <= FPGA_BITSTREAM_MAX) {
|
|
FpgaDownloadAndGo(mode);
|
|
reply_ng(CMD_SET_FPGAMODE, PM3_SUCCESS, NULL, 0);
|
|
}
|
|
reply_ng(CMD_SET_FPGAMODE, PM3_EINVARG, NULL, 0);
|
|
break;
|
|
}
|
|
// emulator
|
|
case CMD_SET_DBGMODE: {
|
|
g_dbglevel = packet->data.asBytes[0];
|
|
if (packet->length == 1 || packet->data.asBytes[1] != 0)
|
|
print_debug_level();
|
|
reply_ng(CMD_SET_DBGMODE, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_GET_DBGMODE: {
|
|
reply_ng(CMD_GET_DBGMODE, PM3_SUCCESS, (uint8_t *)&g_dbglevel, 1);
|
|
break;
|
|
}
|
|
case CMD_SET_TEAROFF: {
|
|
tearoff_params_t *payload = (tearoff_params_t *)packet->data.asBytes;
|
|
if (payload->on && payload->off) {
|
|
reply_ng(CMD_SET_TEAROFF, PM3_EINVARG, NULL, 0);
|
|
}
|
|
|
|
if (payload->on) {
|
|
g_tearoff_enabled = true;
|
|
}
|
|
|
|
if (payload->off) {
|
|
g_tearoff_enabled = false;
|
|
}
|
|
|
|
if (payload->delay_us > 0) {
|
|
g_tearoff_delay_us = payload->delay_us;
|
|
}
|
|
|
|
if (payload->skip > -1) {
|
|
g_tearoff_skip = payload->skip;
|
|
}
|
|
reply_ng(CMD_SET_TEAROFF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_SET_HF_FIELD_TIMEOUT: {
|
|
if (packet->length != sizeof(uint32_t)) {
|
|
reply_ng(CMD_SET_HF_FIELD_TIMEOUT, PM3_EINVARG, NULL, 0);
|
|
break;
|
|
}
|
|
uint32_t timeout_ms = 0;
|
|
memcpy(&timeout_ms, packet->data.asBytes, sizeof(timeout_ms));
|
|
g_hf_field_activity_timeout_ms = timeout_ms;
|
|
reply_ng(CMD_SET_HF_FIELD_TIMEOUT, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
// always available
|
|
case CMD_HF_DROPFIELD: {
|
|
hf_field_off();
|
|
break;
|
|
}
|
|
#ifdef WITH_LF
|
|
case CMD_LF_T55XX_SET_CONFIG: {
|
|
setT55xxConfig(packet->oldarg[0], (t55xx_configurations_t *) packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_LF_SAMPLING_PRINT_CONFIG: {
|
|
printLFConfig();
|
|
break;
|
|
}
|
|
case CMD_LF_SAMPLING_GET_CONFIG: {
|
|
sample_config *config = getSamplingConfig();
|
|
reply_ng(CMD_LF_SAMPLING_GET_CONFIG, PM3_SUCCESS, (uint8_t *)config, sizeof(sample_config));
|
|
break;
|
|
}
|
|
case CMD_LF_SAMPLING_SET_CONFIG: {
|
|
sample_config c;
|
|
memcpy(&c, packet->data.asBytes, sizeof(sample_config));
|
|
setSamplingConfig(&c);
|
|
break;
|
|
}
|
|
case CMD_LF_ACQ_RAW_ADC: {
|
|
lf_sample_payload_t *payload = (lf_sample_payload_t *)packet->data.asBytes;
|
|
if (payload->realtime) {
|
|
ReadLF_realtime(true, payload->cotag);
|
|
} else {
|
|
uint32_t bits = SampleLF(payload->verbose, payload->samples, true, payload->cotag);
|
|
reply_ng(CMD_LF_ACQ_RAW_ADC, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits));
|
|
}
|
|
break;
|
|
}
|
|
case CMD_LF_MOD_THEN_ACQ_RAW_ADC: {
|
|
struct p {
|
|
uint32_t delay;
|
|
uint16_t period_0;
|
|
uint16_t period_1;
|
|
uint8_t symbol_extra[LF_CMDREAD_MAX_EXTRA_SYMBOLS];
|
|
uint16_t period_extra[LF_CMDREAD_MAX_EXTRA_SYMBOLS];
|
|
uint32_t samples : 30;
|
|
bool keep : 1;
|
|
bool verbose : 1;
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
uint8_t symbol_extra[LF_CMDREAD_MAX_EXTRA_SYMBOLS];
|
|
uint16_t period_extra[LF_CMDREAD_MAX_EXTRA_SYMBOLS];
|
|
memcpy(symbol_extra, payload->symbol_extra, sizeof(symbol_extra));
|
|
memcpy(period_extra, payload->period_extra, sizeof(period_extra));
|
|
ModThenAcquireRawAdcSamples125k(payload->delay, payload->period_0, payload->period_1, symbol_extra, period_extra, packet->data.asBytes + sizeof(struct p), payload->verbose, payload->keep, payload->samples, true);
|
|
break;
|
|
}
|
|
case CMD_LF_SNIFF_RAW_ADC: {
|
|
lf_sample_payload_t *payload = (lf_sample_payload_t *)packet->data.asBytes;
|
|
if (payload->realtime) {
|
|
ReadLF_realtime(false, false);
|
|
} else {
|
|
uint32_t bits = SniffLF(payload->verbose, payload->samples, true);
|
|
reply_ng(CMD_LF_SNIFF_RAW_ADC, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits));
|
|
}
|
|
break;
|
|
}
|
|
case CMD_LF_HID_WATCH: {
|
|
uint32_t high, low;
|
|
int res = lf_hid_watch(0, &high, &low, true);
|
|
reply_ng(CMD_LF_HID_WATCH, res, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_LF_HID_SIMULATE: {
|
|
lf_hidsim_t *payload = (lf_hidsim_t *)packet->data.asBytes;
|
|
CmdHIDsimTAG(payload->hi2, payload->hi, payload->lo, payload->longFMT, 1);
|
|
break;
|
|
}
|
|
case CMD_LF_FSK_SIMULATE: {
|
|
lf_fsksim_t *payload = (lf_fsksim_t *)packet->data.asBytes;
|
|
CmdFSKsimTAG(payload->fchigh, payload->fclow, payload->separator, payload->clock, packet->length - sizeof(lf_fsksim_t), payload->data, true);
|
|
break;
|
|
}
|
|
case CMD_LF_ASK_SIMULATE: {
|
|
lf_asksim_t *payload = (lf_asksim_t *)packet->data.asBytes;
|
|
CmdASKsimTAG(payload->encoding, payload->invert, payload->separator, payload->clock, packet->length - sizeof(lf_asksim_t), payload->data, true);
|
|
break;
|
|
}
|
|
case CMD_LF_PSK_SIMULATE: {
|
|
lf_psksim_t *payload = (lf_psksim_t *)packet->data.asBytes;
|
|
CmdPSKsimTAG(payload->carrier, payload->invert, payload->clock, packet->length - sizeof(lf_psksim_t), payload->data, true);
|
|
break;
|
|
}
|
|
case CMD_LF_NRZ_SIMULATE: {
|
|
lf_nrzsim_t *payload = (lf_nrzsim_t *)packet->data.asBytes;
|
|
CmdNRZsimTAG(payload->invert, payload->separator, payload->clock, packet->length - sizeof(lf_nrzsim_t), payload->data, true);
|
|
break;
|
|
}
|
|
case CMD_LF_HID_CLONE: {
|
|
lf_hidsim_t *payload = (lf_hidsim_t *)packet->data.asBytes;
|
|
CopyHIDtoT55x7(payload->hi2, payload->hi, payload->lo, payload->longFMT, payload->Q5, payload->EM, true);
|
|
break;
|
|
}
|
|
case CMD_LF_IO_WATCH: {
|
|
uint32_t high, low;
|
|
int res = lf_io_watch(0, &high, &low, true);
|
|
reply_ng(CMD_LF_IO_WATCH, res, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_LF_EM410X_WATCH: {
|
|
uint32_t high;
|
|
uint64_t low;
|
|
int res = lf_em410x_watch(0, &high, &low, true);
|
|
reply_ng(CMD_LF_EM410X_WATCH, res, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_LF_EM410X_CLONE: {
|
|
struct p {
|
|
bool Q5;
|
|
bool EM;
|
|
bool add_electra;
|
|
uint8_t clock;
|
|
uint32_t high;
|
|
uint32_t low;
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
uint8_t card = payload->Q5 ? 0 : (payload->EM ? 2 : 1);
|
|
int res = copy_em410x_to_t55xx(card, payload->clock, payload->high, payload->low, payload->add_electra, true);
|
|
reply_ng(CMD_LF_EM410X_CLONE, res, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_LF_TI_READ: {
|
|
ReadTItag(true);
|
|
break;
|
|
}
|
|
case CMD_LF_TI_WRITE: {
|
|
struct p {
|
|
uint32_t high;
|
|
uint32_t low;
|
|
uint16_t crc;
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
WriteTItag(payload->high, payload->low, packet->crc, true);
|
|
break;
|
|
}
|
|
case CMD_LF_SIMULATE: {
|
|
LED_A_ON();
|
|
struct p {
|
|
uint16_t len;
|
|
uint16_t gap;
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
// length, start gap, led control
|
|
SimulateTagLowFrequency(payload->len, payload->gap, true);
|
|
reply_ng(CMD_LF_SIMULATE, PM3_EOPABORTED, NULL, 0);
|
|
LED_A_OFF();
|
|
break;
|
|
}
|
|
case CMD_LF_SIMULATE_BIDIR: {
|
|
SimulateTagLowFrequencyBidir(packet->oldarg[0], packet->oldarg[1]);
|
|
break;
|
|
}
|
|
case CMD_LF_T55XX_READBL: {
|
|
struct p {
|
|
uint32_t password;
|
|
uint8_t blockno;
|
|
uint8_t page;
|
|
bool pwdmode;
|
|
uint8_t downlink_mode;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
T55xxReadBlock(payload->page, payload->pwdmode, false, payload->blockno, payload->password, payload->downlink_mode, true);
|
|
break;
|
|
}
|
|
case CMD_LF_T55XX_WRITEBL: {
|
|
// uses NG format
|
|
T55xxWriteBlock(packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_T55XX_DANGERRAW: {
|
|
T55xxDangerousRawTest(packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_T55XX_WAKEUP: {
|
|
struct p {
|
|
uint32_t password;
|
|
uint8_t flags;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
T55xxWakeUp(payload->password, payload->flags, true);
|
|
break;
|
|
}
|
|
case CMD_LF_T55XX_RESET_READ: {
|
|
T55xxResetRead(packet->data.asBytes[0] & 0xff, true);
|
|
break;
|
|
}
|
|
case CMD_LF_T55XX_CHK_PWDS: {
|
|
T55xx_ChkPwds(packet->data.asBytes[0] & 0xff, true);
|
|
break;
|
|
}
|
|
case CMD_LF_PCF7931_READ: {
|
|
ReadPCF7931(true);
|
|
break;
|
|
}
|
|
case CMD_LF_PCF7931_WRITE: {
|
|
WritePCF7931(
|
|
packet->data.asBytes[0], packet->data.asBytes[1], packet->data.asBytes[2], packet->data.asBytes[3],
|
|
packet->data.asBytes[4], packet->data.asBytes[5], packet->data.asBytes[6], packet->data.asBytes[9],
|
|
packet->data.asBytes[7] - 128, packet->data.asBytes[8] - 128,
|
|
packet->oldarg[0],
|
|
packet->oldarg[1],
|
|
packet->oldarg[2],
|
|
true
|
|
);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X_LOGIN: {
|
|
struct p {
|
|
uint32_t password;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
EM4xLogin(payload->password, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X_BF: {
|
|
struct p {
|
|
uint32_t start_pwd;
|
|
uint32_t n;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
EM4xBruteforce(payload->start_pwd, payload->n, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X_READWORD: {
|
|
struct p {
|
|
uint32_t password;
|
|
uint8_t address;
|
|
uint8_t usepwd;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
EM4xReadWord(payload->address, payload->password, payload->usepwd, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X_WRITEWORD: {
|
|
struct p {
|
|
uint32_t password;
|
|
uint32_t data;
|
|
uint8_t address;
|
|
uint8_t usepwd;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
EM4xWriteWord(payload->address, payload->data, payload->password, payload->usepwd, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X_PROTECTWORD: {
|
|
struct p {
|
|
uint32_t password;
|
|
uint32_t data;
|
|
uint8_t usepwd;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
EM4xProtectWord(payload->data, payload->password, payload->usepwd, true);
|
|
break;
|
|
}
|
|
case CMD_LF_AWID_WATCH: {
|
|
uint32_t high, low;
|
|
int res = lf_awid_watch(0, &high, &low, true);
|
|
reply_ng(CMD_LF_AWID_WATCH, res, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_LF_VIKING_CLONE: {
|
|
struct p {
|
|
bool Q5;
|
|
bool EM;
|
|
uint8_t blocks[8];
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
CopyVikingtoT55xx(payload->blocks, payload->Q5, payload->EM, true);
|
|
break;
|
|
}
|
|
case CMD_LF_COTAG_READ: {
|
|
struct p {
|
|
uint8_t mode;
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
Cotag(payload->mode, true);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_HITAG
|
|
case CMD_LF_HITAG_SNIFF: { // Eavesdrop Hitag tag, args = type
|
|
SniffHitag2(true);
|
|
//hitag_sniff();
|
|
reply_ng(CMD_LF_HITAG_SNIFF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAG_SIMULATE: { // Simulate Hitag tag, args = memory content
|
|
SimulateHitag2(true);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAG2_CRACK: {
|
|
lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes;
|
|
ht2_crack1(payload->NrAr);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAG2_CRACK_2: {
|
|
lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes;
|
|
ht2_crack2(payload->NrAr);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAG_READER: { // Reader for Hitag tags, args = type and function
|
|
lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes;
|
|
|
|
switch (payload->cmd) {
|
|
case HT2F_UID_ONLY: {
|
|
ht2_read_uid(NULL, true, true, false);
|
|
break;
|
|
}
|
|
default: {
|
|
ReaderHitag(payload, true);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case CMD_LF_HITAGS_SIMULATE: { // Simulate Hitag s tag, args = memory content
|
|
hts_simulate((bool)packet->oldarg[0], packet->oldarg[1], packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAGS_TEST_TRACES: { // Tests every challenge within the given file
|
|
hts_check_challenges(packet->data.asBytes, packet->length, true);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAGS_READ: { // Reader for only Hitag S tags, args = key or challenge
|
|
lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes;
|
|
hts_read(payload, true);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAGS_WRITE: {
|
|
lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes;
|
|
hts_write_page(payload, true);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAGS_UID: {
|
|
hts_read_uid(NULL, true, true);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAG2_WRITE: {
|
|
lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes;
|
|
WriterHitag(payload, true);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAG_ELOAD: {
|
|
lf_hitag_t *payload = (lf_hitag_t *) packet->data.asBytes;
|
|
uint8_t *mem = BigBuf_get_EM_addr();
|
|
memcpy(mem, payload->data, payload->len);
|
|
break;
|
|
}
|
|
|
|
case CMD_LF_HITAGU_READ: {
|
|
lf_hitag_data_t *payload = (lf_hitag_data_t *)packet->data.asBytes;
|
|
htu_read(payload, true);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAGU_WRITE: {
|
|
lf_hitag_data_t *payload = (lf_hitag_data_t *)packet->data.asBytes;
|
|
htu_write_page(payload, true);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAGU_SIMULATE: {
|
|
htu_simulate((bool)packet->oldarg[0], packet->oldarg[1], packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_HITAGU_UID: {
|
|
htu_read_uid(NULL, true, true);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_EM4x50
|
|
case CMD_LF_EM4X50_INFO: {
|
|
em4x50_info((const em4x50_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X50_WRITE: {
|
|
em4x50_write((const em4x50_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X50_WRITEPWD: {
|
|
em4x50_writepwd((const em4x50_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X50_READ: {
|
|
em4x50_read((const em4x50_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X50_BRUTE: {
|
|
em4x50_brute((const em4x50_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X50_LOGIN: {
|
|
em4x50_login((const uint32_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X50_SIM: {
|
|
//-----------------------------------------------------------------------------
|
|
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_LF) here although FPGA is not
|
|
// involved in dealing with emulator memory. But if it is called later, it might
|
|
// destroy the Emulator Memory.
|
|
//-----------------------------------------------------------------------------
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
em4x50_sim((const uint32_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X50_READER: {
|
|
em4x50_reader(true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X50_ESET: {
|
|
//-----------------------------------------------------------------------------
|
|
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_LF) here although FPGA is not
|
|
// involved in dealing with emulator memory. But if it is called later, it might
|
|
// destroy the Emulator Memory.
|
|
//-----------------------------------------------------------------------------
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
emlSet(packet->data.asBytes, packet->oldarg[0], packet->oldarg[1]);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X50_CHK: {
|
|
//-----------------------------------------------------------------------------
|
|
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_LF) here although FPGA is not
|
|
// involved in dealing with emulator memory. But if it is called later, it might
|
|
// destroy the Emulator Memory.
|
|
//-----------------------------------------------------------------------------
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
em4x50_chk((const char *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_EM4x70
|
|
case CMD_LF_EM4X70_INFO: {
|
|
em4x70_info((em4x70_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X70_WRITE: {
|
|
em4x70_write((em4x70_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X70_UNLOCK: {
|
|
em4x70_unlock((em4x70_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X70_AUTH: {
|
|
em4x70_auth((em4x70_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X70_SETPIN: {
|
|
em4x70_write_pin((em4x70_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X70_SETKEY: {
|
|
em4x70_write_key((em4x70_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_EM4X70_BRUTE: {
|
|
em4x70_brute((em4x70_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_ZX8211
|
|
case CMD_LF_ZX_READ: {
|
|
zx8211_read((zx8211_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
case CMD_LF_ZX_WRITE: {
|
|
zx8211_write((zx8211_data_t *)packet->data.asBytes, true);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_ISO15693
|
|
case CMD_HF_ISO15693_ACQ_RAW_ADC: {
|
|
AcquireRawAdcSamplesIso15693();
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_SNIFF: {
|
|
SniffIso15693(0, NULL, false);
|
|
reply_ng(CMD_HF_ISO15693_SNIFF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_COMMAND: {
|
|
iso15_raw_cmd_t *payload = (iso15_raw_cmd_t *)packet->data.asBytes;
|
|
SendRawCommand15693(payload);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_FINDAFI: {
|
|
struct p {
|
|
uint32_t flags;
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
BruteforceIso15693Afi(payload->flags);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_READER: {
|
|
ReaderIso15693(NULL);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_EML_CLEAR: {
|
|
//-----------------------------------------------------------------------------
|
|
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15) here although FPGA is not
|
|
// involved in dealing with emulator memory. But if it is called later, it might
|
|
// destroy the Emulator Memory.
|
|
//-----------------------------------------------------------------------------
|
|
// Resetting the bitstream also frees the BigBuf memory, so we do this here to prevent
|
|
// an inconvenient reset in the future by Iso15693InitTag
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15);
|
|
BigBuf_Clear_EM();
|
|
reply_ng(CMD_HF_ISO15693_EML_CLEAR, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_EML_SETMEM: {
|
|
//-----------------------------------------------------------------------------
|
|
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15) here although FPGA is not
|
|
// involved in dealing with emulator memory. But if it is called later, it might
|
|
// destroy the Emulator Memory.
|
|
//-----------------------------------------------------------------------------
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15);
|
|
struct p {
|
|
uint32_t offset;
|
|
uint16_t count;
|
|
uint8_t data[];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
emlSet(payload->data, payload->offset, payload->count);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_EML_GETMEM: {
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15);
|
|
struct p {
|
|
uint32_t offset;
|
|
uint16_t length;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
if (payload->length > PM3_CMD_DATA_SIZE) {
|
|
reply_ng(CMD_HF_ISO15693_EML_GETMEM, PM3_EMALLOC, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
uint8_t *buf = BigBuf_calloc(payload->length);
|
|
emlGet(buf, payload->offset, payload->length);
|
|
LED_B_ON();
|
|
reply_ng(CMD_HF_ISO15693_EML_GETMEM, PM3_SUCCESS, buf, payload->length);
|
|
LED_B_OFF();
|
|
BigBuf_free_keep_EM();
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_SIMULATE: {
|
|
struct p {
|
|
uint8_t uid[8];
|
|
uint8_t block_size;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
SimTagIso15693(payload->uid, payload->block_size);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_CSETUID: {
|
|
struct p {
|
|
uint8_t uid[8];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
SetTag15693Uid(payload->uid);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_CSETUID_V2: {
|
|
struct p {
|
|
uint8_t uid[8];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
SetTag15693Uid_v2(payload->uid);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_SLIX_DISABLE_EAS: {
|
|
struct p {
|
|
uint8_t pwd[4];
|
|
bool usepwd;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
DisableEAS_AFISlixIso15693(payload->pwd, payload->usepwd);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_SLIX_ENABLE_EAS: {
|
|
struct p {
|
|
uint8_t pwd[4];
|
|
bool usepwd;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
EnableEAS_AFISlixIso15693(payload->pwd, payload->usepwd);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_SLIX_WRITE_PWD: {
|
|
struct p {
|
|
uint8_t old_pwd[4];
|
|
uint8_t new_pwd[4];
|
|
uint8_t pwd_id;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
WritePasswordSlixIso15693(payload->old_pwd, payload->new_pwd, payload->pwd_id);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_SLIX_PROTECT_PAGE: {
|
|
struct p {
|
|
uint8_t read_pwd[4];
|
|
uint8_t write_pwd[4];
|
|
uint8_t divide_ptr;
|
|
uint8_t prot_status;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
ProtectPageSlixIso15693(payload->read_pwd, payload->write_pwd, payload->divide_ptr, payload->prot_status);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_SLIX_DISABLE_PRIVACY: {
|
|
struct p {
|
|
uint8_t pwd[4];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
DisablePrivacySlixIso15693(payload->pwd);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_SLIX_ENABLE_PRIVACY: {
|
|
struct p {
|
|
uint8_t pwd[4];
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
EnablePrivacySlixIso15693(payload->pwd);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_SLIX_PASS_PROTECT_AFI: {
|
|
struct p {
|
|
uint8_t pwd[4];
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
PassProtectAFISlixIso15693(payload->pwd);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_WRITE_AFI: {
|
|
struct p {
|
|
uint8_t pwd[4];
|
|
bool use_pwd;
|
|
uint8_t uid[8];
|
|
bool use_uid;
|
|
uint8_t afi;
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
WriteAFIIso15693(payload->pwd, payload->use_pwd, payload->uid, payload->use_uid, payload->afi);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO15693_SLIX_PASS_PROTECT_EAS: {
|
|
struct p {
|
|
uint8_t pwd[4];
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
PassProtextEASSlixIso15693(payload->pwd);
|
|
break;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_LEGICRF
|
|
case CMD_HF_LEGIC_SIMULATE: {
|
|
struct p {
|
|
uint8_t tagtype;
|
|
bool send_reply;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
LegicRfSimulate(payload->tagtype, payload->send_reply);
|
|
break;
|
|
}
|
|
case CMD_HF_LEGIC_WRITER: {
|
|
legic_packet_t *payload = (legic_packet_t *) packet->data.asBytes;
|
|
LegicRfWriter(payload->offset, payload->len, payload->iv, payload->data);
|
|
break;
|
|
}
|
|
case CMD_HF_LEGIC_READER: {
|
|
legic_packet_t *payload = (legic_packet_t *) packet->data.asBytes;
|
|
LegicRfReader(payload->offset, payload->len, payload->iv);
|
|
break;
|
|
}
|
|
case CMD_HF_LEGIC_INFO: {
|
|
LegicRfInfo();
|
|
break;
|
|
}
|
|
case CMD_HF_LEGIC_ESET: {
|
|
//-----------------------------------------------------------------------------
|
|
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not
|
|
// involved in dealing with emulator memory. But if it is called later, it might
|
|
// destroy the Emulator Memory.
|
|
//-----------------------------------------------------------------------------
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
legic_packet_t *payload = (legic_packet_t *) packet->data.asBytes;
|
|
emlSet(payload->data, payload->offset, payload->len);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_ISO14443b
|
|
case CMD_HF_SRI_READ: {
|
|
struct p {
|
|
uint8_t blockno;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
read_14b_st_block(payload->blockno);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443B_SNIFF: {
|
|
SniffIso14443b();
|
|
reply_ng(CMD_HF_ISO14443B_SNIFF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443B_SIMULATE: {
|
|
SimulateIso14443bTag(packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443B_COMMAND: {
|
|
iso14b_raw_cmd_t *payload = (iso14b_raw_cmd_t *)packet->data.asBytes;
|
|
SendRawCommand14443B(payload);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443B_PRINT_CONFIG: {
|
|
printHf14bConfig();
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443B_GET_CONFIG: {
|
|
hf14b_config_t *c = getHf14bConfig();
|
|
reply_ng(CMD_HF_ISO14443B_GET_CONFIG, PM3_SUCCESS, (uint8_t *)c, sizeof(hf14b_config_t));
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443B_SET_CONFIG: {
|
|
hf14b_config_t c;
|
|
memcpy(&c, packet->data.asBytes, sizeof(hf14b_config_t));
|
|
setHf14bConfig(&c);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443B_ST25TB_TEAROFF: {
|
|
ST25TB_TearOff(packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_CRYPTORF_SIM : {
|
|
// simulate_crf_tag();
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_FELICA
|
|
case CMD_HF_FELICA_COMMAND: {
|
|
felica_sendraw(packet);
|
|
break;
|
|
}
|
|
case CMD_HF_FELICA_SIMULATE: {
|
|
felicasim_standard(packet);
|
|
break;
|
|
}
|
|
case CMD_HF_FELICALITE_SIMULATE: {
|
|
struct p {
|
|
uint8_t uid[8];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
felica_sim_lite(payload->uid);
|
|
break;
|
|
}
|
|
case CMD_HF_FELICA_SNIFF: {
|
|
struct p {
|
|
uint32_t samples;
|
|
uint32_t triggers;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
felica_sniff(payload->samples, payload->triggers);
|
|
break;
|
|
}
|
|
case CMD_HF_FELICALITE_DUMP: {
|
|
felica_dump_lite_s();
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_GENERAL_HF
|
|
case CMD_HF_ACQ_RAW_ADC: {
|
|
uint32_t samplesCount = 0;
|
|
memcpy(&samplesCount, packet->data.asBytes, 4);
|
|
HfReadADC(samplesCount, true);
|
|
break;
|
|
}
|
|
case CMD_HF_TEXKOM_SIMULATE: {
|
|
struct p {
|
|
uint8_t data[8];
|
|
uint8_t modulation;
|
|
uint32_t timeout;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
HfSimulateTkm(payload->data, payload->modulation, payload->timeout);
|
|
break;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_ISO14443a
|
|
case CMD_HF_ISO14443A_PRINT_CONFIG: {
|
|
printHf14aConfig();
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443A_GET_CONFIG: {
|
|
hf14a_config_t *c = getHf14aConfig();
|
|
reply_ng(CMD_HF_ISO14443A_GET_CONFIG, PM3_SUCCESS, (uint8_t *)c, sizeof(hf14a_config_t));
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443A_SET_CONFIG: {
|
|
hf14a_config_t c;
|
|
memcpy(&c, packet->data.asBytes, sizeof(hf14a_config_t));
|
|
setHf14aConfig(&c);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443A_SET_THRESHOLDS: {
|
|
struct p {
|
|
uint8_t threshold;
|
|
uint8_t threshold_high;
|
|
uint8_t legic_threshold;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
FpgaSendCommand(FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, (payload->threshold & 0x3f) | ((payload->threshold_high & 0x3f) << 6));
|
|
#ifdef WITH_LEGICRF
|
|
LegicRfSetThreshold((uint32_t)payload->legic_threshold);
|
|
#endif
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443A_SNIFF: {
|
|
SniffIso14443a(packet->data.asBytes[0]);
|
|
reply_ng(CMD_HF_ISO14443A_SNIFF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_HF_HIDCONFIG_SNIFF: {
|
|
SniffHIDConfigCard((const hid_sniff_payload_t *)packet->data.asBytes);
|
|
reply_ng(CMD_HF_HIDCONFIG_SNIFF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443A_READER: {
|
|
ReaderIso14443a(packet);
|
|
break;
|
|
}
|
|
#ifdef WITH_SMARTCARD
|
|
case CMD_HF_ISO14443A_EMV_SIMULATE: {
|
|
struct p {
|
|
uint16_t flags;
|
|
uint8_t exitAfter;
|
|
uint8_t uid[7];
|
|
uint16_t atqa;
|
|
uint8_t sak;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
EMVsim(payload->flags, payload->exitAfter, payload->uid, payload->atqa, payload->sak);
|
|
break;
|
|
}
|
|
#endif
|
|
case CMD_HF_ISO14443A_SIMULATE: {
|
|
struct p {
|
|
uint8_t tagtype;
|
|
uint16_t flags;
|
|
uint8_t uid[10];
|
|
uint8_t exitAfter;
|
|
uint8_t rats[20];
|
|
uint8_t ulauth_1a1_len;
|
|
uint8_t ulauth_1a2_len;
|
|
uint8_t ulauth_1a1[16];
|
|
uint8_t ulauth_1a2[16];
|
|
bool ulauth_1a2_mirror;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
SimulateIso14443aTagEx(payload->tagtype, payload->flags, payload->uid,
|
|
payload->exitAfter, payload->rats, sizeof(payload->rats),
|
|
payload->ulauth_1a1, payload->ulauth_1a1_len,
|
|
payload->ulauth_1a2, payload->ulauth_1a2_len,
|
|
payload->ulauth_1a2_mirror
|
|
); // ## Simulate iso14443a tag - pass tag type & UID
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443A_SIM_AID: {
|
|
struct p {
|
|
uint8_t tagtype;
|
|
uint16_t flags;
|
|
uint8_t uid[10];
|
|
uint8_t ats[20];
|
|
uint8_t aid[30];
|
|
uint8_t selectaid_response[256];
|
|
uint8_t getdata_response[100];
|
|
uint32_t ats_len;
|
|
uint32_t aid_len;
|
|
uint32_t selectaid_response_len;
|
|
uint32_t getdata_response_len;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
// ## Simulate iso14443a tag - pass tag type, UID, ATS, AID, responses
|
|
SimulateIso14443aTagAID(payload->tagtype, payload->flags, payload->uid,
|
|
payload->ats, payload->ats_len, payload->aid, payload->aid_len,
|
|
payload->selectaid_response, payload->selectaid_response_len,
|
|
payload->getdata_response, payload->getdata_response_len);
|
|
break;
|
|
}
|
|
case CMD_HF_HIDCONFIG_SIM: {
|
|
SimulateHIDConfigCard((const hid_sim_payload_t *) packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ISO14443A_ANTIFUZZ: {
|
|
struct p {
|
|
uint8_t flag;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
iso14443a_antifuzz(payload->flag);
|
|
break;
|
|
}
|
|
// EPA related
|
|
case CMD_HF_EPA_COLLECT_NONCE: {
|
|
EPA_PACE_Collect_Nonce(packet);
|
|
break;
|
|
}
|
|
case CMD_HF_EPA_REPLAY: {
|
|
EPA_PACE_Replay(packet);
|
|
break;
|
|
}
|
|
case CMD_HF_EPA_PACE_SIMULATE: {
|
|
EPA_PACE_Simulate(packet);
|
|
break;
|
|
}
|
|
|
|
case CMD_HF_MIFARE_READER: {
|
|
struct p {
|
|
uint8_t first_run;
|
|
uint8_t blockno;
|
|
uint8_t key_type;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
ReaderMifare(payload->first_run, payload->blockno, payload->key_type);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_READBL: {
|
|
mf_readblock_t *payload = (mf_readblock_t *)packet->data.asBytes;
|
|
uint8_t outbuf[16];
|
|
int16_t retval = mifare_cmd_readblocks(MF_WAKE_WUPA, MIFARE_AUTH_KEYA + payload->keytype, payload->key, ISO14443A_CMD_READBLOCK, payload->blockno, 1, outbuf);
|
|
reply_ng(CMD_HF_MIFARE_READBL, retval, outbuf, sizeof(outbuf));
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_READBL_EX: {
|
|
mf_readblock_ex_t *payload = (mf_readblock_ex_t *)packet->data.asBytes;
|
|
uint8_t outbuf[16];
|
|
int16_t retval = mifare_cmd_readblocks(payload->wakeup, payload->auth_cmd, payload->key, payload->read_cmd, payload->block_no, 1, outbuf);
|
|
reply_ng(CMD_HF_MIFARE_READBL_EX, retval, outbuf, sizeof(outbuf));
|
|
break;
|
|
}
|
|
case CMD_HF_MIFAREU_READBL: {
|
|
MifareUReadBlock((mful_readblock_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFAREU3P_AUTH: {
|
|
MifareU3PassAuth((mful_3passauth_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFAREU3P_CHKKEY: {
|
|
MifareU3PassChkKeys((mful_3passchk_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFAREU_READCARD: {
|
|
MifareUReadCard((mful_readblock_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFAREU_SETKEY: {
|
|
MifareUSetKey((mful_setkey_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_READSC: {
|
|
MifareReadSector(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_WRITEBL: {
|
|
uint8_t block_no = packet->oldarg[0];
|
|
uint8_t key_type = packet->oldarg[1];
|
|
uint8_t *key = packet->data.asBytes;
|
|
uint8_t *block_data = packet->data.asBytes + 10;
|
|
|
|
int16_t retval = mifare_cmd_writeblocks(MF_WAKE_WUPA, MIFARE_AUTH_KEYA + (key_type & 0xF), key, ISO14443A_CMD_WRITEBLOCK, block_no, 1, block_data);
|
|
|
|
// convert ng style retval to old status
|
|
if (retval >= 0) {
|
|
retval = 1;
|
|
}
|
|
|
|
reply_mix(CMD_ACK, retval, 0, 0, 0, 0);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_WRITEBL_EX: {
|
|
mf_writeblock_ex_t *payload = (mf_writeblock_ex_t *)packet->data.asBytes;
|
|
int16_t retval = mifare_cmd_writeblocks(payload->wakeup, payload->auth_cmd, payload->key, payload->write_cmd, payload->block_no, 1, payload->block_data);
|
|
reply_ng(CMD_HF_MIFARE_WRITEBL_EX, retval, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_VALUE: {
|
|
MifareValue(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFAREU_WRITEBL: {
|
|
MifareUWriteBlock((mful_writeblock_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFAREU_WRITEBL_COMPAT: {
|
|
MifareUWriteBlockCompat((mful_writeblock_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_ACQ_ENCRYPTED_NONCES: {
|
|
MifareAcquireEncryptedNonces(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES: {
|
|
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true, packet->oldarg[1], packet->oldarg[2]);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_ACQ_NONCES: {
|
|
MifareAcquireNonces(packet->oldarg[0], packet->oldarg[2]);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_NESTED: {
|
|
struct p {
|
|
uint8_t block;
|
|
uint8_t keytype;
|
|
uint8_t target_block;
|
|
uint8_t target_keytype;
|
|
bool calibrate;
|
|
uint8_t key[6];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
MifareNested(payload->block, payload->keytype, payload->target_block, payload->target_keytype, payload->calibrate, payload->key);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_STATIC_NESTED: {
|
|
struct p {
|
|
uint8_t block;
|
|
uint8_t keytype;
|
|
uint8_t target_block;
|
|
uint8_t target_keytype;
|
|
uint8_t force_detect_dist;
|
|
uint8_t key[6];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
MifareStaticNested(payload->block, payload->keytype, payload->target_block, payload->target_keytype, payload->key, payload->force_detect_dist);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_CHKKEYS: {
|
|
MifareChkKeys(packet->data.asBytes, false);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_CHKKEYS_FAST: {
|
|
MifareChkKeys_fast(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_CHKKEYS_FILE: {
|
|
struct p {
|
|
uint8_t filename[32];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
MifareChkKeys_file(payload->filename);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_SIMULATE: {
|
|
struct p {
|
|
uint16_t flags;
|
|
uint8_t exitAfter;
|
|
uint8_t uid[10];
|
|
uint16_t atqa;
|
|
uint8_t sak;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
Mifare1ksim(payload->flags, payload->exitAfter, payload->uid, payload->atqa, payload->sak);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_EML_MEMCLR: {
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Work with emulator memory
|
|
//
|
|
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not
|
|
// involved in dealing with emulator memory. But if it is called later, it might
|
|
// destroy the Emulator Memory.
|
|
//-----------------------------------------------------------------------------
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
|
|
// Not only clears the emulator memory,
|
|
// also sets default MIFARE values for sector trailers.
|
|
emlClearMem();
|
|
reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_EML_MEMSET: {
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
struct p {
|
|
uint16_t blockno;
|
|
uint8_t blockcnt;
|
|
uint8_t blockwidth;
|
|
uint8_t data[];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
// backwards compat... default bytewidth
|
|
if (payload->blockwidth == 0) {
|
|
payload->blockwidth = MIFARE_BLOCK_SIZE;
|
|
}
|
|
|
|
emlSetMem_xt(payload->data, payload->blockno, payload->blockcnt, payload->blockwidth);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_EML_MEMGET: {
|
|
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
struct p {
|
|
uint16_t blockno;
|
|
uint8_t blockcnt;
|
|
uint8_t blockwidth;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
//
|
|
size_t size = payload->blockcnt * payload->blockwidth;
|
|
if (size > PM3_CMD_DATA_SIZE) {
|
|
reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
uint8_t *buf = BigBuf_calloc(size);
|
|
|
|
emlGetMem_xt(buf, payload->blockno, payload->blockcnt, payload->blockwidth); // data, block num, blocks count (max 4)
|
|
|
|
LED_B_ON();
|
|
reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size);
|
|
LED_B_OFF();
|
|
BigBuf_free_keep_EM();
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_EML_LOAD: {
|
|
mfc_eload_t *payload = (mfc_eload_t *) packet->data.asBytes;
|
|
MifareECardLoadExt(payload->sectorcnt, payload->keytype, payload->key);
|
|
break;
|
|
}
|
|
// Gen1a / 1b - "magic Chinese" card
|
|
case CMD_HF_MIFARE_CSETBL: {
|
|
MifareCSetBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_CGETBL: {
|
|
MifareCGetBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_CIDENT: {
|
|
struct p {
|
|
uint8_t is_mfc;
|
|
uint8_t keytype;
|
|
uint8_t key[6];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
MifareCIdent(payload->is_mfc, payload->keytype, payload->key);
|
|
break;
|
|
}
|
|
// Gen 3 magic cards
|
|
case CMD_HF_MIFARE_GEN3UID: {
|
|
MifareGen3UID(packet->oldarg[0], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_GEN3BLK: {
|
|
MifareGen3Blk(packet->oldarg[0], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_GEN3FREEZ: {
|
|
MifareGen3Freez();
|
|
break;
|
|
}
|
|
// Gen 4 GTU magic cards
|
|
case CMD_HF_MIFARE_G4_RDBL: {
|
|
struct p {
|
|
uint8_t blockno;
|
|
uint8_t pwd[4];
|
|
uint8_t workFlags;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
MifareG4ReadBlk(payload->blockno, payload->pwd, payload->workFlags);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_G4_WRBL: {
|
|
struct p {
|
|
uint8_t blockno;
|
|
uint8_t pwd[4];
|
|
uint8_t data[16]; // data to be written
|
|
uint8_t workFlags;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
MifareG4WriteBlk(payload->blockno, payload->pwd, payload->data, payload->workFlags);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_G4_GDM_WRBL: {
|
|
struct p {
|
|
uint8_t blockno;
|
|
uint8_t key[6];
|
|
uint8_t data[16]; // data to be written
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
int16_t retval = mifare_cmd_writeblocks(MF_WAKE_WUPA, MIFARE_MAGIC_GDM_AUTH_KEY, payload->key, MIFARE_MAGIC_GDM_WRITEBLOCK, payload->blockno, 1, payload->data);
|
|
reply_ng(CMD_HF_MIFARE_G4_GDM_WRBL, retval, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_PERSONALIZE_UID: {
|
|
struct p {
|
|
uint8_t keytype;
|
|
uint8_t pers_option;
|
|
uint8_t key[6];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
uint64_t authkey = bytes_to_num(payload->key, 6);
|
|
MifarePersonalizeUID(payload->keytype, payload->pers_option, authkey);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_SETMOD: {
|
|
MifareSetMod(packet->data.asBytes);
|
|
break;
|
|
}
|
|
//mifare desfire
|
|
case CMD_HF_DESFIRE_READBL: {
|
|
break;
|
|
}
|
|
case CMD_HF_DESFIRE_WRITEBL: {
|
|
break;
|
|
}
|
|
case CMD_HF_DESFIRE_AUTH1: {
|
|
MifareDES_Auth1(packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_DESFIRE_AUTH2: {
|
|
//MifareDES_Auth2(packet->oldarg[0],packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_DESFIRE_READER: {
|
|
//readermifaredes(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_DESFIRE_INFO: {
|
|
MifareDesfireGetInformation();
|
|
break;
|
|
}
|
|
case CMD_HF_DESFIRE_COMMAND: {
|
|
MifareSendCommand(packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_NACK_DETECT: {
|
|
DetectNACKbug();
|
|
break;
|
|
}
|
|
case CMD_HF_MFU_OTP_TEAROFF: {
|
|
MifareU_Otp_Tearoff(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_MFU_COUNTER_TEAROFF: {
|
|
struct p {
|
|
uint8_t counter;
|
|
uint32_t tearoff_time;
|
|
uint8_t value[4];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
MifareU_Counter_Tearoff(payload->counter, payload->tearoff_time, payload->value);
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_STATIC_NONCE: {
|
|
MifareHasStaticNonce();
|
|
break;
|
|
}
|
|
case CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE: {
|
|
struct p {
|
|
uint8_t block_no;
|
|
uint8_t key_type;
|
|
uint8_t key[6];
|
|
uint8_t block_no_nested;
|
|
uint8_t key_type_nested;
|
|
uint8_t key_nested[6];
|
|
uint8_t nr_nonces;
|
|
uint8_t resets;
|
|
uint8_t addread;
|
|
uint8_t addauth;
|
|
uint8_t incblk2;
|
|
uint8_t corruptnrar;
|
|
uint8_t corruptnrarparity;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
MifareHasStaticEncryptedNonce(payload->block_no, payload->key_type, payload->key, payload->block_no_nested, payload->key_type_nested, payload->key_nested, payload->nr_nonces, payload->resets & 1, (payload->resets >> 1) & 1, payload->addread, payload->addauth, payload->incblk2, payload->corruptnrar, payload->corruptnrarparity);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_NFCBARCODE
|
|
case CMD_HF_THINFILM_READ: {
|
|
ReadThinFilm();
|
|
break;
|
|
}
|
|
case CMD_HF_THINFILM_SIMULATE: {
|
|
SimulateThinFilm(packet->data.asBytes, packet->length);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_ICLASS
|
|
// Makes use of ISO14443a FPGA Firmware
|
|
case CMD_HF_ICLASS_SNIFF: {
|
|
struct p {
|
|
uint8_t jam_search_len;
|
|
uint8_t jam_search_string[];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
SniffIClass(payload->jam_search_len, payload->jam_search_string);
|
|
reply_ng(CMD_HF_ICLASS_SNIFF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_SIMULATE: {
|
|
/*
|
|
struct p {
|
|
uint8_t reader[4];
|
|
uint8_t mac[4];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
*/
|
|
|
|
SimulateIClass(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_READER: {
|
|
ReaderIClass(packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_RAW: {
|
|
iClass_Raw(packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_EML_MEMSET: {
|
|
FpgaDownloadAndGo_keep_EM(FPGA_BITSTREAM_HF_15);
|
|
struct p {
|
|
uint16_t offset;
|
|
uint16_t len;
|
|
uint8_t data[];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
emlSet(payload->data, payload->offset, payload->len);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_WRITEBL: {
|
|
iClass_WriteBlock(packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_READBL: {
|
|
iClass_ReadBlock(packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_CHKKEYS: {
|
|
iClass_Authentication_fast((iclass_chk_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_DUMP: {
|
|
iClass_Dump(packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_RESTORE: {
|
|
iClass_Restore((iclass_restore_req_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_RECOVER: {
|
|
iClass_Recover((iclass_recover_req_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_CREDIT_EPURSE: {
|
|
iclass_credit_epurse((iclass_credit_epurse_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_HF_ICLASS_TEARBL: {
|
|
iClass_TearBlock((iclass_tearblock_req_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef WITH_SEOS
|
|
case CMD_HF_SEOS_SIMULATE: {
|
|
SimulateSeos((seos_emulate_req_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_HFSNIFF
|
|
case CMD_HF_SNIFF: {
|
|
struct p {
|
|
uint32_t samplesToSkip;
|
|
uint32_t triggersToSkip;
|
|
uint8_t skipMode;
|
|
uint8_t skipRatio;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
uint16_t len = 0;
|
|
int res = HfSniff(payload->samplesToSkip, payload->triggersToSkip, &len, payload->skipMode, payload->skipRatio);
|
|
|
|
struct {
|
|
uint16_t len;
|
|
} PACKED retval;
|
|
retval.len = len;
|
|
reply_ng(CMD_HF_SNIFF, res, (uint8_t *)&retval, sizeof(retval));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_HFPLOT
|
|
case CMD_FPGAMEM_DOWNLOAD: {
|
|
HfPlotDownload();
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_SMARTCARD
|
|
case CMD_SMART_ATR: {
|
|
SmartCardAtr();
|
|
break;
|
|
}
|
|
case CMD_SMART_SETBAUD: {
|
|
SmartCardSetBaud(packet->oldarg[0]);
|
|
break;
|
|
}
|
|
case CMD_SMART_SETCLOCK: {
|
|
struct p {
|
|
uint32_t new_clk;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
SmartCardSetClock(payload->new_clk);
|
|
break;
|
|
}
|
|
case CMD_SMART_RAW: {
|
|
SmartCardRaw((smart_card_raw_t *) packet->data.asBytes);
|
|
break;
|
|
}
|
|
case CMD_SMART_UPLOAD: {
|
|
// upload file from client
|
|
struct p {
|
|
uint32_t idx;
|
|
uint32_t bytes_in_packet;
|
|
uint16_t crc;
|
|
uint8_t data[400];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
uint8_t *mem = BigBuf_get_addr();
|
|
|
|
// sanity checks
|
|
if (payload->bytes_in_packet > sizeof(payload->data) ||
|
|
payload->idx > BigBuf_get_size() ||
|
|
payload->idx + payload->bytes_in_packet > BigBuf_get_size()) {
|
|
reply_ng(CMD_SMART_UPLOAD, PM3_EOVFLOW, NULL, 0);
|
|
break;
|
|
}
|
|
|
|
memcpy(mem + payload->idx, payload->data, payload->bytes_in_packet);
|
|
|
|
uint8_t a = 0, b = 0;
|
|
compute_crc(CRC_14443_A, mem + payload->idx, payload->bytes_in_packet, &a, &b);
|
|
int res = PM3_SUCCESS;
|
|
if (payload->crc != (a << 8 | b)) {
|
|
DbpString("CRC Failed");
|
|
res = PM3_ESOFT;
|
|
}
|
|
reply_ng(CMD_SMART_UPLOAD, res, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_SMART_UPGRADE: {
|
|
struct p {
|
|
uint16_t fw_size;
|
|
uint16_t crc;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
uint8_t *fwdata = BigBuf_get_addr();
|
|
uint8_t a = 0, b = 0;
|
|
compute_crc(CRC_14443_A, fwdata, payload->fw_size, &a, &b);
|
|
|
|
if (payload->crc != (a << 8 | b)) {
|
|
Dbprintf("CRC Failed, 0x[%04x] != 0x[%02x%02x]", payload->crc, a, b);
|
|
reply_ng(CMD_SMART_UPGRADE, PM3_ESOFT, NULL, 0);
|
|
} else {
|
|
SmartCardUpgrade(payload->fw_size);
|
|
}
|
|
fwdata = NULL;
|
|
break;
|
|
}
|
|
|
|
case CMD_HF_SAM_PICOPASS: {
|
|
sam_picopass_get_pacs(packet);
|
|
break;
|
|
}
|
|
case CMD_HF_SAM_SEOS: {
|
|
sam_seos_get_pacs(packet);
|
|
break;
|
|
}
|
|
|
|
case CMD_HF_SAM_MFC: {
|
|
// sam_mfc_get_pacs();
|
|
break;
|
|
}
|
|
|
|
case CMD_HF_SAM_SC: {
|
|
sam_sc_handler(packet);
|
|
break;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_FPC_USART
|
|
case CMD_USART_TX: {
|
|
LED_B_ON();
|
|
usart_writebuffer_sync(packet->data.asBytes, packet->length);
|
|
reply_ng(CMD_USART_TX, PM3_SUCCESS, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_USART_RX: {
|
|
LED_B_ON();
|
|
struct p {
|
|
uint32_t waittime;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
uint16_t available;
|
|
uint16_t pre_available = 0;
|
|
uint8_t *dest = BigBuf_calloc(USART_FIFOLEN);
|
|
uint32_t wait = payload->waittime;
|
|
|
|
StartTicks();
|
|
|
|
uint32_t ti = GetTickCount();
|
|
|
|
while (true) {
|
|
WaitMS(50);
|
|
available = usart_rxdata_available();
|
|
if (available > pre_available) {
|
|
// When receiving data, reset timer and shorten timeout
|
|
ti = GetTickCount();
|
|
wait = 50;
|
|
pre_available = available;
|
|
continue;
|
|
}
|
|
// We stop either after waittime if no data or 50ms after last data received
|
|
if (GetTickCountDelta(ti) > wait)
|
|
break;
|
|
}
|
|
if (available > 0) {
|
|
uint16_t len = usart_read_ng(dest, available);
|
|
reply_ng(CMD_USART_RX, PM3_SUCCESS, dest, len);
|
|
} else {
|
|
reply_ng(CMD_USART_RX, PM3_ENODATA, NULL, 0);
|
|
}
|
|
|
|
StopTicks();
|
|
BigBuf_free();
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_USART_TXRX: {
|
|
LED_B_ON();
|
|
struct p {
|
|
uint32_t waittime;
|
|
uint8_t data[];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
usart_writebuffer_sync(payload->data, packet->length - sizeof(payload));
|
|
|
|
uint16_t available;
|
|
uint16_t pre_available = 0;
|
|
uint8_t *dest = BigBuf_calloc(USART_FIFOLEN);
|
|
uint32_t wait = payload->waittime;
|
|
|
|
StartTicks();
|
|
|
|
uint32_t ti = GetTickCount();
|
|
|
|
while (true) {
|
|
WaitMS(50);
|
|
available = usart_rxdata_available();
|
|
if (available > pre_available) {
|
|
// When receiving data, reset timer and shorten timeout
|
|
ti = GetTickCount();
|
|
wait = 50;
|
|
pre_available = available;
|
|
continue;
|
|
}
|
|
// We stop either after waittime if no data or 50ms after last data received
|
|
if (GetTickCountDelta(ti) > wait)
|
|
break;
|
|
}
|
|
|
|
if (available > 0) {
|
|
uint16_t len = usart_read_ng(dest, available);
|
|
reply_ng(CMD_USART_TXRX, PM3_SUCCESS, dest, len);
|
|
} else {
|
|
reply_ng(CMD_USART_TXRX, PM3_ENODATA, NULL, 0);
|
|
}
|
|
|
|
StopTicks();
|
|
BigBuf_free();
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_USART_CONFIG: {
|
|
struct p {
|
|
uint32_t baudrate;
|
|
uint8_t parity;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
usart_init(payload->baudrate, payload->parity);
|
|
reply_ng(CMD_USART_CONFIG, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
#endif
|
|
case CMD_BUFF_CLEAR: {
|
|
BigBuf_Clear();
|
|
BigBuf_free();
|
|
break;
|
|
}
|
|
#ifdef WITH_LF
|
|
case CMD_MEASURE_ANTENNA_TUNING: {
|
|
MeasureAntennaTuning();
|
|
break;
|
|
}
|
|
#endif
|
|
case CMD_MEASURE_ANTENNA_TUNING_HF: {
|
|
if (packet->length != 1)
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_EINVARG, NULL, 0);
|
|
|
|
switch (packet->data.asBytes[0]) {
|
|
case 1: // MEASURE_ANTENNA_TUNING_HF_START
|
|
// Let the FPGA drive the high-frequency antenna around 13.56 MHz.
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER);
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
case 2:
|
|
if (button_status == BUTTON_SINGLE_CLICK) {
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_EOPABORTED, NULL, 0);
|
|
}
|
|
uint32_t volt = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_HF);
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_SUCCESS, (uint8_t *)&volt, sizeof(volt));
|
|
break;
|
|
case 3:
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
default:
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_EINVARG, NULL, 0);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
#ifndef PM5
|
|
case CMD_HF_DECAY: {
|
|
MeasureAntennaTuningHfDecay((const hf_decay_params_t *)packet->data.asBytes);
|
|
break;
|
|
}
|
|
#endif
|
|
case CMD_MEASURE_ANTENNA_TUNING_LF: {
|
|
if (packet->length != 2)
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_EINVARG, NULL, 0);
|
|
|
|
switch (packet->data.asBytes[0]) {
|
|
case 1: // MEASURE_ANTENNA_TUNING_LF_START
|
|
// Let the FPGA drive the low-frequency antenna around 125kHz
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD);
|
|
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, packet->data.asBytes[1]);
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
case 2:
|
|
if (button_status == BUTTON_SINGLE_CLICK) {
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_EOPABORTED, NULL, 0);
|
|
}
|
|
|
|
uint32_t volt = AdcRssiAvgToMilliVolt(ADC_RSSI_CH_LF);
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_SUCCESS, (uint8_t *)&volt, sizeof(volt));
|
|
break;
|
|
case 3:
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
default:
|
|
reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_EINVARG, NULL, 0);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case CMD_LISTEN_READER_FIELD: {
|
|
if (packet->length != sizeof(uint8_t))
|
|
break;
|
|
ListenReaderField(packet->data.asBytes[0]);
|
|
reply_ng(CMD_LISTEN_READER_FIELD, PM3_EOPABORTED, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_FPGA_MAJOR_MODE_OFF: { // ## FPGA Control
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
FpgaResetBitstream();
|
|
g_hf_field_active = false;
|
|
g_hf_field_timeout_active = false;
|
|
SpinDelay(200);
|
|
LED_D_OFF(); // LED D indicates field ON or OFF
|
|
break;
|
|
}
|
|
case CMD_DOWNLOAD_BIGBUF: {
|
|
LED_B_ON();
|
|
uint8_t *mem = BigBuf_get_addr();
|
|
uint32_t startidx = packet->oldarg[0];
|
|
uint32_t numofbytes = packet->oldarg[1];
|
|
|
|
// arg0 = startindex
|
|
// arg1 = length bytes to transfer
|
|
// arg2 = BigBuf tracelen
|
|
//Dbprintf("transfer to client parameters: %" PRIu32 " | %" PRIu32 " | %" PRIu32, startidx, numofbytes, packet->oldarg[2]);
|
|
|
|
for (size_t offset = 0; offset < numofbytes; offset += PM3_CMD_DATA_SIZE) {
|
|
size_t len = MIN((numofbytes - offset), PM3_CMD_DATA_SIZE);
|
|
int result = reply_old(CMD_DOWNLOADED_BIGBUF, offset, len, BigBuf_get_traceLen(), &mem[startidx + offset], len);
|
|
if (result != PM3_SUCCESS)
|
|
Dbprintf("transfer to client failed :: | bytes between %d - %d (%d) | result: %d", offset, offset + len, len, result);
|
|
}
|
|
// Trigger a finish downloading signal with an ACK frame
|
|
// arg0 = status of download transfer
|
|
reply_mix(CMD_ACK, 1, 0, BigBuf_get_traceLen(), NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
#ifdef WITH_LF
|
|
case CMD_LF_UPLOAD_SIM_SAMPLES: {
|
|
// iceman; since changing fpga_bitstreams clears bigbuff, Its better to call it before.
|
|
// to be able to use this one for uploading data to device
|
|
// flag =
|
|
// b0 0 skip
|
|
// 1 clear bigbuff
|
|
struct p {
|
|
uint8_t flag;
|
|
uint16_t offset;
|
|
uint8_t data[PM3_CMD_DATA_SIZE - sizeof(uint8_t) - sizeof(uint16_t)];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
|
|
if ((payload->flag & 0x1) == 0x1) {
|
|
BigBuf_Clear_ext(false);
|
|
BigBuf_free();
|
|
}
|
|
|
|
// offset should not be over buffer
|
|
if (payload->offset >= BigBuf_get_size()) {
|
|
reply_ng(CMD_LF_UPLOAD_SIM_SAMPLES, PM3_EOVFLOW, NULL, 0);
|
|
break;
|
|
}
|
|
// ensure len bytes copied won't go past end of bigbuf
|
|
uint16_t len = MIN(BigBuf_get_size() - payload->offset, sizeof(payload->data));
|
|
|
|
uint8_t *mem = BigBuf_get_addr();
|
|
|
|
memcpy(mem + payload->offset, &payload->data, len);
|
|
reply_ng(CMD_LF_UPLOAD_SIM_SAMPLES, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
#endif
|
|
case CMD_DOWNLOAD_EML_BIGBUF: {
|
|
LED_B_ON();
|
|
uint8_t *mem = BigBuf_get_EM_addr();
|
|
uint32_t startidx = packet->oldarg[0];
|
|
uint32_t numofbytes = packet->oldarg[1];
|
|
|
|
// arg0 = startindex
|
|
// arg1 = length bytes to transfer
|
|
// arg2 = RFU
|
|
|
|
for (size_t i = 0; i < numofbytes; i += PM3_CMD_DATA_SIZE) {
|
|
size_t len = MIN((numofbytes - i), PM3_CMD_DATA_SIZE);
|
|
int result = reply_old(CMD_DOWNLOADED_EML_BIGBUF, i, len, 0, mem + startidx + i, len);
|
|
if (result != PM3_SUCCESS)
|
|
Dbprintf("transfer to client failed :: | bytes between %d - %d (%d) | result: %d", i, i + len, len, result);
|
|
}
|
|
// Trigger a finish downloading signal with an ACK frame
|
|
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_READ_MEM: {
|
|
if (packet->length != sizeof(uint32_t))
|
|
break;
|
|
ReadMem(packet->data.asDwords[0]);
|
|
break;
|
|
}
|
|
case CMD_READ_MEM_DOWNLOAD: {
|
|
LED_B_ON();
|
|
|
|
size_t offset = packet->oldarg[0];
|
|
size_t count = packet->oldarg[1];
|
|
uint32_t flags = packet->oldarg[2];
|
|
|
|
bool isok = true;
|
|
uint8_t *base = NULL;
|
|
|
|
bool raw_address_mode = ((flags & READ_MEM_DOWNLOAD_FLAG_RAW) == READ_MEM_DOWNLOAD_FLAG_RAW);
|
|
if (raw_address_mode == false) {
|
|
|
|
base = (uint8_t *) _flash_start;
|
|
|
|
size_t flash_size = GetChipFlashSize();
|
|
|
|
// Boundary check the offset.
|
|
if (offset > flash_size) {
|
|
isok = false;
|
|
Dbprintf("reading mcu flash failed :: | out of bounds, offset %u count %u", offset, count);
|
|
}
|
|
|
|
// Clip the length if it goes past the end of the flash memory.
|
|
count = MIN(count, flash_size - offset);
|
|
|
|
} else {
|
|
// Allow reading from any memory address and length in special 'raw' mode.
|
|
base = NULL;
|
|
// Boundary check against end of addressable space.
|
|
if (offset > 0) {
|
|
count = MIN(count, -offset);
|
|
}
|
|
}
|
|
|
|
if (isok) {
|
|
for (size_t pos = 0; pos < count; pos += PM3_CMD_DATA_SIZE) {
|
|
size_t len = MIN((count - pos), PM3_CMD_DATA_SIZE);
|
|
isok = (reply_old(CMD_READ_MEM_DOWNLOADED, pos, len, 0, &base[offset + pos], len) == PM3_SUCCESS);
|
|
if (isok == false) {
|
|
Dbprintf("transfer to client failed :: | pos %u len %u", pos, len);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
reply_old(CMD_ACK, 1, 0, 0, 0, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
#ifdef WITH_FLASH
|
|
case CMD_SPIFFS_TEST: {
|
|
test_spiffs();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_CHECK: {
|
|
rdv40_spiffs_check();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_MOUNT: {
|
|
rdv40_spiffs_lazy_mount();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_UNMOUNT: {
|
|
rdv40_spiffs_lazy_unmount();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_PRINT_TREE: {
|
|
rdv40_spiffs_safe_print_tree();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_PRINT_FSINFO: {
|
|
rdv40_spiffs_safe_print_fsinfo();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_DOWNLOAD: {
|
|
LED_B_ON();
|
|
uint8_t filename[32];
|
|
uint8_t *pfilename = packet->data.asBytes;
|
|
memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN);
|
|
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Filename received for spiffs dump : %s", filename);
|
|
|
|
uint32_t size = packet->oldarg[1];
|
|
|
|
uint8_t *buff = BigBuf_calloc(size);
|
|
if (buff == NULL) {
|
|
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Failed to allocate memory");
|
|
// Trigger a finish downloading signal with an PM3_EMALLOC
|
|
reply_ng(CMD_SPIFFS_DOWNLOAD, PM3_EMALLOC, NULL, 0);
|
|
} else {
|
|
rdv40_spiffs_read_as_filetype((char *)filename, (uint8_t *)buff, size, RDV40_SPIFFS_SAFETY_SAFE);
|
|
// arg0 = filename
|
|
// arg1 = size
|
|
// arg2 = RFU
|
|
|
|
for (size_t i = 0; i < size; i += PM3_CMD_DATA_SIZE) {
|
|
size_t len = MIN((size - i), PM3_CMD_DATA_SIZE);
|
|
int result = reply_old(CMD_SPIFFS_DOWNLOADED, i, len, 0, buff + i, len);
|
|
if (result != PM3_SUCCESS)
|
|
Dbprintf("transfer to client failed :: | bytes between %d - %d (%d) | result: %d", i, i + len, len, result);
|
|
}
|
|
// Trigger a finish downloading signal with an ACK frame
|
|
reply_ng(CMD_SPIFFS_DOWNLOAD, PM3_SUCCESS, NULL, 0);
|
|
BigBuf_free();
|
|
}
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_STAT: {
|
|
LED_B_ON();
|
|
uint8_t filename[32];
|
|
uint8_t *pfilename = packet->data.asBytes;
|
|
memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN);
|
|
if (g_dbglevel >= DBG_DEBUG) {
|
|
Dbprintf("Filename received for spiffs STAT : %s", filename);
|
|
}
|
|
|
|
int changed = rdv40_spiffs_lazy_mount();
|
|
uint32_t size = size_in_spiffs((char *)filename);
|
|
if (changed) {
|
|
rdv40_spiffs_lazy_unmount();
|
|
}
|
|
|
|
reply_ng(CMD_SPIFFS_STAT, PM3_SUCCESS, (uint8_t *)&size, sizeof(uint32_t));
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_REMOVE: {
|
|
LED_B_ON();
|
|
|
|
struct p {
|
|
uint8_t len;
|
|
uint8_t fn[32];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
if (g_dbglevel >= DBG_DEBUG) {
|
|
Dbprintf("Filename received for spiffs REMOVE : %s", payload->fn);
|
|
}
|
|
|
|
rdv40_spiffs_remove((char *)payload->fn, RDV40_SPIFFS_SAFETY_SAFE);
|
|
reply_ng(CMD_SPIFFS_REMOVE, PM3_SUCCESS, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_RENAME: {
|
|
LED_B_ON();
|
|
struct p {
|
|
uint8_t slen;
|
|
uint8_t src[32];
|
|
uint8_t dlen;
|
|
uint8_t dest[32];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
if (g_dbglevel >= DBG_DEBUG) {
|
|
Dbprintf("SPIFFS RENAME");
|
|
Dbprintf("Source........ %s", payload->src);
|
|
Dbprintf("Destination... %s", payload->dest);
|
|
}
|
|
rdv40_spiffs_rename((char *)payload->src, (char *)payload->dest, RDV40_SPIFFS_SAFETY_SAFE);
|
|
reply_ng(CMD_SPIFFS_RENAME, PM3_SUCCESS, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_COPY: {
|
|
LED_B_ON();
|
|
struct p {
|
|
uint8_t slen;
|
|
uint8_t src[32];
|
|
uint8_t dlen;
|
|
uint8_t dest[32];
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
if (g_dbglevel >= DBG_DEBUG) {
|
|
Dbprintf("SPIFFS COPY");
|
|
Dbprintf("Source........ %s", payload->src);
|
|
Dbprintf("Destination... %s", payload->dest);
|
|
}
|
|
rdv40_spiffs_copy((char *)payload->src, (char *)payload->dest, RDV40_SPIFFS_SAFETY_SAFE);
|
|
reply_ng(CMD_SPIFFS_COPY, PM3_SUCCESS, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_WRITE: {
|
|
LED_B_ON();
|
|
|
|
flashmem_write_t *payload = (flashmem_write_t *)packet->data.asBytes;
|
|
|
|
if (g_dbglevel >= DBG_DEBUG) {
|
|
Dbprintf("SPIFFS WRITE, dest `%s` with APPEND set to: %c", payload->fn, payload->append ? 'Y' : 'N');
|
|
}
|
|
|
|
if (payload->append) {
|
|
rdv40_spiffs_append((char *) payload->fn, payload->data, payload->bytes_in_packet, RDV40_SPIFFS_SAFETY_SAFE);
|
|
} else {
|
|
rdv40_spiffs_write((char *) payload->fn, payload->data, payload->bytes_in_packet, RDV40_SPIFFS_SAFETY_SAFE);
|
|
}
|
|
|
|
reply_ng(CMD_SPIFFS_WRITE, PM3_SUCCESS, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_WIPE: {
|
|
LED_B_ON();
|
|
rdv40_spiffs_safe_wipe();
|
|
reply_ng(CMD_SPIFFS_WIPE, PM3_SUCCESS, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_SPIFFS_ELOAD: {
|
|
LED_B_ON();
|
|
|
|
uint8_t *em = BigBuf_get_EM_addr();
|
|
if (em == NULL) {
|
|
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Failed to allocate memory");
|
|
reply_ng(CMD_SPIFFS_ELOAD, PM3_EMALLOC, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
|
|
char *fn = (char *)packet->data.asBytes;
|
|
|
|
uint32_t size = size_in_spiffs(fn);
|
|
if (size == 0) {
|
|
reply_ng(CMD_SPIFFS_ELOAD, PM3_SUCCESS, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
|
|
rdv40_spiffs_read_as_filetype(fn, em, size, RDV40_SPIFFS_SAFETY_SAFE);
|
|
reply_ng(CMD_SPIFFS_ELOAD, PM3_SUCCESS, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_FLASHMEM_SET_SPIBAUDRATE: {
|
|
if (packet->length != sizeof(uint32_t))
|
|
break;
|
|
Flash_SetSpiBaudrate(packet->data.asDwords[0]);
|
|
break;
|
|
}
|
|
case CMD_FLASHMEM_WRITE: {
|
|
LED_B_ON();
|
|
|
|
flashmem_old_write_t *payload = (flashmem_old_write_t *)packet->data.asBytes;
|
|
|
|
if (FlashInit() == false) {
|
|
reply_ng(CMD_FLASHMEM_WRITE, PM3_EIO, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
|
|
if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) {
|
|
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
Flash_WriteEnable();
|
|
Flash_Erase4k(spi_flash_pages64k - 1, 0xF);
|
|
}
|
|
|
|
uint16_t res = Flash_Write(payload->startidx, payload->data, payload->len);
|
|
|
|
reply_ng(CMD_FLASHMEM_WRITE, (res == payload->len) ? PM3_SUCCESS : PM3_ESOFT, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_FLASHMEM_WIPE: {
|
|
LED_B_ON();
|
|
uint8_t page = packet->oldarg[0];
|
|
uint8_t initialwipe = packet->oldarg[1];
|
|
|
|
bool isok = false;
|
|
if (initialwipe) {
|
|
isok = Flash_WipeMemory();
|
|
reply_ng(CMD_FLASHMEM_WIPE, (isok) ? PM3_SUCCESS : PM3_EFAILED, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
|
|
if (page < spi_flash_pages64k - 1) {
|
|
isok = Flash_WipeMemoryPage(page);
|
|
// let spiffs check and update its info post flash erase
|
|
rdv40_spiffs_check();
|
|
}
|
|
|
|
reply_ng(CMD_FLASHMEM_WIPE, (isok) ? PM3_SUCCESS : PM3_EFAILED, NULL, 0);
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_FLASHMEM_DOWNLOAD: {
|
|
|
|
LED_B_ON();
|
|
uint8_t *mem = BigBuf_calloc(PM3_CMD_DATA_SIZE);
|
|
uint32_t startidx = packet->oldarg[0];
|
|
uint32_t numofbytes = packet->oldarg[1];
|
|
// arg0 = startindex
|
|
// arg1 = length bytes to transfer
|
|
// arg2 = RFU
|
|
|
|
if (FlashInit() == false) {
|
|
break;
|
|
}
|
|
|
|
for (size_t i = 0; i < numofbytes; i += PM3_CMD_DATA_SIZE) {
|
|
size_t len = MIN((numofbytes - i), PM3_CMD_DATA_SIZE);
|
|
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
uint16_t isok = Flash_ReadDataCont(startidx + i, mem, len);
|
|
if (isok == false) {
|
|
Dbprintf("reading flash memory failed with bytes between %d - %d", i, len);
|
|
}
|
|
isok = reply_old(CMD_FLASHMEM_DOWNLOADED, i, len, 0, mem, len);
|
|
|
|
if (isok != PM3_SUCCESS) {
|
|
Dbprintf("transfer to client failed with bytes between %d - %d", i, len);
|
|
}
|
|
}
|
|
FlashStop();
|
|
|
|
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
|
|
BigBuf_free();
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_FLASHMEM_GET_SIGNATURE: {
|
|
|
|
LED_B_ON();
|
|
|
|
rdv40_validation_t *info = (rdv40_validation_t *)BigBuf_calloc(sizeof(rdv40_validation_t));
|
|
|
|
// returns 0 when failing
|
|
uint16_t isok = Flash_ReadData(FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k), info->signature, FLASH_MEM_SIGNATURE_LEN);
|
|
|
|
// re-init since command above calls FlashStop()
|
|
if (isok && FlashInit()) {
|
|
Flash_UniqueID(info->flashid);
|
|
FlashStop();
|
|
}
|
|
|
|
reply_ng(CMD_FLASHMEM_GET_SIGNATURE, (isok) ? PM3_SUCCESS : PM3_EFLASH, (uint8_t *)info, sizeof(rdv40_validation_t));
|
|
BigBuf_free();
|
|
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_FLASHMEM_GET_INFO: {
|
|
LED_B_ON();
|
|
spi_flash_t *spi = flash_get_info();
|
|
reply_ng(CMD_FLASHMEM_GET_INFO, PM3_SUCCESS, (uint8_t *)spi, sizeof(spi_flash_t));
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_FLASHMEM_PAGES64K: {
|
|
|
|
LED_B_ON();
|
|
|
|
bool isok = FlashInit();
|
|
if (isok) {
|
|
if (g_dbglevel >= DBG_DEBUG) {
|
|
Dbprintf(" CMD_FLASHMEM_PAGE64K 0x%02x (%d 64k pages)", spi_flash_pages64k, spi_flash_pages64k);
|
|
}
|
|
FlashStop();
|
|
}
|
|
reply_ng(CMD_FLASHMEM_PAGES64K, (isok) ? PM3_SUCCESS : PM3_EFLASH, &spi_flash_pages64k, sizeof(uint8_t));
|
|
|
|
LED_B_OFF();
|
|
break;
|
|
}
|
|
case CMD_FLASHMEM_GET_ID: {
|
|
uint64_t flash_uniqueID = 0;
|
|
bool isok = FlashInit();
|
|
if (isok) {
|
|
isok = Flash_UniqueID((uint8_t *)(&flash_uniqueID));
|
|
FlashStop();
|
|
}
|
|
reply_ng(CMD_FLASHMEM_GET_ID, (isok) ? PM3_SUCCESS : PM3_EFLASH, (uint8_t *)&flash_uniqueID, sizeof(flash_uniqueID));
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef WITH_LF
|
|
case CMD_LF_SET_DIVISOR: {
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, packet->data.asBytes[0]);
|
|
break;
|
|
}
|
|
#endif
|
|
case CMD_SET_ADC_MUX: {
|
|
switch (packet->data.asBytes[0]) {
|
|
case 0:
|
|
SetAdcMuxFor(ADC_MUXSEL_LOPKD);
|
|
break;
|
|
case 2:
|
|
SetAdcMuxFor(ADC_MUXSEL_HIPKD);
|
|
break;
|
|
#ifndef WITH_FPC_USART
|
|
case 1:
|
|
SetAdcMuxFor(ADC_MUXSEL_LORAW);
|
|
break;
|
|
case 3:
|
|
SetAdcMuxFor(ADC_MUXSEL_HIRAW);
|
|
break;
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case CMD_VERSION: {
|
|
SendVersion();
|
|
break;
|
|
}
|
|
case CMD_STATUS: {
|
|
if (packet->length == 4)
|
|
SendStatus(packet->data.asDwords[0]);
|
|
else
|
|
SendStatus(CONN_SPEED_TEST_MIN_TIME_DEFAULT);
|
|
break;
|
|
}
|
|
case CMD_TIA: {
|
|
#ifdef CHIP_AT91SAM7S
|
|
while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available...
|
|
uint16_t mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF;
|
|
Dbprintf(" Slow clock old measured value:.........%d Hz", (16 * MAINCK) / mainf);
|
|
TimingIntervalAcquisition();
|
|
|
|
while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available...
|
|
mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF;
|
|
Dbprintf(""); // first message gets lost
|
|
Dbprintf(" Slow clock new measured value:.........%d Hz", (16 * MAINCK) / mainf);
|
|
reply_ng(CMD_TIA, PM3_SUCCESS, NULL, 0);
|
|
#else
|
|
Dbprintf("Chip is not AT91SAM7S, TIA is " _RED_("unsupported"));
|
|
reply_ng(CMD_TIA, PM3_EDEVNOTSUPP, NULL, 0);
|
|
#endif
|
|
break;
|
|
}
|
|
case CMD_STANDALONE: {
|
|
|
|
struct p {
|
|
uint8_t arg;
|
|
uint8_t mlen;
|
|
uint8_t mode[10];
|
|
} PACKED;
|
|
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
uint8_t *bb = BigBuf_get_EM_addr();
|
|
if (payload->mlen == 0) {
|
|
bb[0] = payload->arg;
|
|
} else {
|
|
memcpy(bb, payload->mode, payload->mlen);
|
|
}
|
|
|
|
RunMod();
|
|
break;
|
|
}
|
|
case CMD_CAPABILITIES: {
|
|
SendCapabilities();
|
|
break;
|
|
}
|
|
case CMD_PING: {
|
|
reply_ng(CMD_PING, PM3_SUCCESS, packet->data.asBytes, packet->length);
|
|
break;
|
|
}
|
|
#ifdef WITH_LCD
|
|
case CMD_LCD_RESET: {
|
|
LCDReset();
|
|
break;
|
|
}
|
|
case CMD_LCD: {
|
|
LCDSend(packet->oldarg[0]);
|
|
break;
|
|
}
|
|
#endif
|
|
case CMD_FINISH_WRITE:
|
|
case CMD_HARDWARE_RESET: {
|
|
usb_disable();
|
|
|
|
// (iceman) why this wait?
|
|
SpinDelay(1000); // Go wait for the USB to completely go offline on the host side.
|
|
ResetChip();
|
|
// We're going to reset, and the bootrom will take control.
|
|
for (;;) {}
|
|
break;
|
|
}
|
|
case CMD_START_FLASH: {
|
|
if (g_common_area.flags.bootrom_present) {
|
|
g_common_area.command = COMMON_AREA_COMMAND_ENTER_FLASH_MODE;
|
|
}
|
|
usb_disable();
|
|
ResetChip();
|
|
// We're going to flash, and the bootrom will take control.
|
|
for (;;) {}
|
|
break;
|
|
}
|
|
case CMD_DEVICE_INFO: {
|
|
uint32_t dev_info = DEVICE_INFO_FLAG_OSIMAGE_PRESENT | DEVICE_INFO_FLAG_CURRENT_MODE_OS;
|
|
if (g_common_area.flags.bootrom_present) {
|
|
dev_info |= DEVICE_INFO_FLAG_BOOTROM_PRESENT;
|
|
}
|
|
reply_old(CMD_DEVICE_INFO, dev_info, 0, 0, 0, 0);
|
|
break;
|
|
}
|
|
case CMD_FPGA_BITSTREAM_CONFIG_START: // Merge 3 cmds to reuse some code.
|
|
case CMD_FPGA_BITSTREAM_CONFIG_WRITE:
|
|
case CMD_FPGA_BITSTREAM_CONFIG_FINISH: {
|
|
// Dbprintf("Received FPGA config command 0x%04x", packet->cmd);
|
|
int res;
|
|
// Process
|
|
if (packet->cmd == CMD_FPGA_BITSTREAM_CONFIG_START) {
|
|
struct p {
|
|
uint8_t sram_mode;
|
|
uint32_t file_length;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
res = FpgaStartConfig(payload->sram_mode, payload->file_length);
|
|
} else if (packet->cmd == CMD_FPGA_BITSTREAM_CONFIG_WRITE) {
|
|
res = FpgaConfigWrite(packet->data.asBytes, packet->length);
|
|
} else {
|
|
res = FpgaStopConfig();
|
|
}
|
|
// Response
|
|
if (res == PM3_EFAILED) {
|
|
uint32_t plat_status = FpgaConfigPlatformStatus(); // Return status code of platform when res is PM3_EFAILED
|
|
reply_ng(packet->cmd, res, (uint8_t *)&plat_status, sizeof(plat_status));
|
|
} else {
|
|
reply_ng(packet->cmd, res, NULL, 0);
|
|
}
|
|
break;
|
|
}
|
|
#ifdef PM5
|
|
case CMD_ANT_CONTROL_WRITE: {
|
|
struct p {
|
|
uint8_t data;
|
|
uint8_t reg_type; // 0 is io reg, 1 is map reg.
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
StartTicks();
|
|
I2C_init(true);
|
|
|
|
uint8_t addr = 0x51; // TODO DXL define move to header?
|
|
uint8_t cmd = payload->reg_type == 0 ? 0x01 : 0x02;
|
|
|
|
bool isok = I2C_BufferWrite(&payload->data, 1, cmd, addr << 1);
|
|
reply_ng(CMD_ANT_CONTROL_WRITE, isok ? PM3_SUCCESS : PM3_EFAILED, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_ANT_CONTROL_READ: {
|
|
struct p {
|
|
uint8_t reg_type; // 0 is io reg, 1 is map reg.
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
|
|
StartTicks();
|
|
I2C_init(true);
|
|
|
|
uint8_t addr = 0x51; // TODO DXL define move to header?
|
|
uint8_t cmd = payload->reg_type == 0 ? 0x01 : 0x02;
|
|
uint8_t data;
|
|
|
|
bool isok = I2C_BufferReadRaw(&data, 1, cmd, addr << 1);
|
|
reply_ng(CMD_ANT_CONTROL_READ, isok ? PM3_SUCCESS : PM3_EFAILED, &data, sizeof(data));
|
|
break;
|
|
}
|
|
case CMD_EEPROM_FACTORY_INFO_READ: {
|
|
StartTicks();
|
|
I2C_init(true);
|
|
|
|
uint8_t addr = 0x50; // TODO DXL define move to header?
|
|
uint8_t data[256]; // 24c02: 256byte
|
|
bool isok = I2C_BufferReadRaw(data, sizeof(data), 0x00, addr << 1);
|
|
reply_ng(CMD_EEPROM_FACTORY_INFO_READ, isok ? PM3_SUCCESS : PM3_EFAILED, data, sizeof(data));
|
|
break;
|
|
}
|
|
case CMD_EEPROM_FACTORY_INFO_WRITE: {
|
|
StartTicks();
|
|
I2C_init(true);
|
|
|
|
uint8_t addr = 0x50; // TODO DXL define move to header?
|
|
uint16_t len = packet->length;
|
|
while (len) {
|
|
uint16_t write_len = MIN(len, 16);
|
|
uint16_t write_pos = packet->length - len;
|
|
bool isok = I2C_BufferWrite(packet->data.asBytes + write_pos, write_len, write_pos, addr << 1);
|
|
if (!isok) {
|
|
reply_ng(CMD_EEPROM_FACTORY_INFO_WRITE, PM3_EFAILED, NULL, 0);
|
|
return;
|
|
}
|
|
len -= write_len;
|
|
// 24C02 writes to a page write buffer of only 16 bytes.
|
|
// If the write speed is too fast, it may cause data write failure.
|
|
// Therefore, a delay or ACK judgment is required between page writes
|
|
SpinDelay(5); // 24C02 write cycle time is about 5ms
|
|
}
|
|
reply_ng(CMD_EEPROM_FACTORY_INFO_WRITE, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_PM5_FPGA_SET_PWR_PWM_LOW_COUNT: {
|
|
struct p {
|
|
uint8_t is_lf;
|
|
uint16_t count;
|
|
} PACKED;
|
|
struct p *payload = (struct p *) packet->data.asBytes;
|
|
FpgaDownloadAndGo(payload->is_lf ? FPGA_BITSTREAM_LF : FPGA_BITSTREAM_HF);
|
|
FpgaSendCommand(FPGA_CMD_SET_PWR_PWM_LOW_COUNT, payload->count & 0xFFF);
|
|
reply_ng(CMD_PM5_FPGA_SET_PWR_PWM_LOW_COUNT, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
#endif
|
|
case CMD_MAIN_CHIP_UNIQUEID: {
|
|
uint8_t size = 0;
|
|
uint8_t *uid = GetChipUniqueId(&size);
|
|
reply_ng(CMD_MAIN_CHIP_UNIQUEID, PM3_SUCCESS, uid, size);
|
|
break;
|
|
}
|
|
#ifdef PM5
|
|
case CMD_PM5_QC_TEST: {
|
|
uint8_t failed_item = 0;
|
|
uint32_t timeout_ms = 0;
|
|
if (packet->length >= sizeof(timeout_ms)) {
|
|
memcpy(&timeout_ms, packet->data.asBytes, sizeof(timeout_ms));
|
|
}
|
|
reply_ng(CMD_PM5_QC_TEST, QCTestPM5(&failed_item, timeout_ms) ? PM3_SUCCESS : PM3_EFAILED, &failed_item, 1);
|
|
break;
|
|
}
|
|
case CMD_PM5_RGB_SET: {
|
|
// Set the antenna RGB LED colour (used by `hf/lf tune --rgb`).
|
|
struct p {
|
|
uint8_t r;
|
|
uint8_t g;
|
|
uint8_t b;
|
|
} PACKED;
|
|
struct p *payload = (struct p *)packet->data.asBytes;
|
|
RgbLedSet(payload->r, payload->g, payload->b);
|
|
#ifdef WITH_PM5_PWR_LED
|
|
// tune (or any external RGB user) now owns the LED; back the power
|
|
// indicator off. A non-zero colour claims it; all-zero releases it.
|
|
g_rgb_external = (payload->r || payload->g || payload->b);
|
|
#endif
|
|
reply_ng(CMD_PM5_RGB_SET, PM3_SUCCESS, NULL, 0);
|
|
break;
|
|
}
|
|
#ifdef WITH_BWM_STATUS
|
|
case CMD_PM5_BWM_SET_CAP: {
|
|
// One-time BWM fuel-gauge (BQ27427) Design Capacity provisioning.
|
|
// Payload: optional uint16 mAh (LE); absent -> reference default.
|
|
uint16_t cap = (packet->length >= 2)
|
|
? (uint16_t)(packet->data.asBytes[0] | (packet->data.asBytes[1] << 8))
|
|
: BWM_DEFAULT_DESIGN_CAP_MAH;
|
|
I2C_init(true);
|
|
bool ok = bwm_gauge_provision_capacity(cap);
|
|
reply_ng(CMD_PM5_BWM_SET_CAP, ok ? PM3_SUCCESS : PM3_EFAILED, (uint8_t *)&cap, sizeof(cap));
|
|
break;
|
|
}
|
|
case CMD_PM5_BWM_CHARGE_EN: {
|
|
// Enable/disable battery charging (clear/set AW32001E CEB, REG01[3]).
|
|
// Payload: 1 byte, non-zero = enable (default), zero = disable.
|
|
// One-shot: reverts on the charger watchdog timeout (~160 s).
|
|
bool enable = (packet->length >= 1) ? (packet->data.asBytes[0] != 0) : true;
|
|
I2C_init(true);
|
|
bool ok = bwm_charger_set_charge(enable);
|
|
reply_ng(CMD_PM5_BWM_CHARGE_EN, ok ? PM3_SUCCESS : PM3_EFAILED, NULL, 0);
|
|
break;
|
|
}
|
|
case CMD_PM5_BWM_AUTOOFF: {
|
|
// Toggle automatic power-off on USB unplug (runtime, default on).
|
|
// Payload: 1 byte, non-zero = enable (default), zero = disable.
|
|
#ifdef WITH_PM5_AUTOOFF
|
|
g_autooff_enabled = (packet->length >= 1) ? (packet->data.asBytes[0] != 0) : true;
|
|
reply_ng(CMD_PM5_BWM_AUTOOFF, PM3_SUCCESS, (uint8_t *)&g_autooff_enabled, 1);
|
|
#else
|
|
reply_ng(CMD_PM5_BWM_AUTOOFF, PM3_ENOTIMPL, NULL, 0);
|
|
#endif
|
|
break;
|
|
}
|
|
#endif
|
|
#endif
|
|
default: {
|
|
Dbprintf("%s: 0x%04x", "unknown command:", packet->cmd);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void __attribute__((noreturn)) AppMain(void) {
|
|
|
|
SpinDelay(100);
|
|
BigBuf_initialize();
|
|
|
|
// Add stack canary
|
|
for (uint32_t *p = _stack_start; p + 0x200 < _stack_end ; ++p) {
|
|
*p = 0xdeadbeef;
|
|
}
|
|
|
|
LEDsoff();
|
|
|
|
// Setup FPGA clock & Reset COM
|
|
FpgaSetup24MHzClk();
|
|
FpgaResetComInterface();
|
|
|
|
// Configure MUX
|
|
SetAdcMuxFor(ADC_MUXSEL_HIPKD);
|
|
|
|
// Load the FPGA image, which we have stored in our flash.
|
|
// (the HF version by default)
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
|
|
StartTickCount();
|
|
uint32_t last_activity_tick = GetTickCount();
|
|
uint32_t last_activity_label = GetTickCountLabel();
|
|
|
|
#ifdef WITH_LCD
|
|
LCDInit();
|
|
#endif
|
|
|
|
#ifdef WITH_SMARTCARD
|
|
I2C_init(false);
|
|
#endif
|
|
|
|
#ifdef WITH_FLASH
|
|
if (FlashInit()) {
|
|
uint64_t flash_uniqueID = 0;
|
|
if (!Flash_CheckBusy(BUSY_TIMEOUT)) { // OK because firmware was built for devices with flash
|
|
Flash_UniqueID((uint8_t *)(&flash_uniqueID));
|
|
}
|
|
FlashStop();
|
|
usb_update_serial(flash_uniqueID);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_FLASH
|
|
// If flash is not present, BUSY_TIMEOUT kicks in, let's do it after USB
|
|
loadT55xxConfig();
|
|
|
|
// Enforce a spiffs check/garbage collection at boot so we are likely to never
|
|
// fall under the 2 contigous free blocks availables
|
|
// This is a time-consuming process on large flash.
|
|
rdv40_spiffs_check();
|
|
#endif
|
|
|
|
#ifdef WITH_FPC_USART
|
|
usart_init(USART_BAUD_RATE, USART_PARITY);
|
|
#endif
|
|
|
|
allow_send_wtx = true;
|
|
|
|
// This is made as late as possible to ensure enumeration without timeout
|
|
// against device such as http://www.hobbytronics.co.uk/usb-host-board-v2
|
|
// In other words, keep the interval between usb_enable() and the main loop as short as possible.
|
|
// (AT91F_CDC_Enumerate() will be called in the main loop)
|
|
usb_disable();
|
|
usb_enable();
|
|
#ifdef WITH_BWM_STATUS
|
|
bwm_detect_and_init(); // probe BWM + apply charge config, off the pre-USB path
|
|
#endif
|
|
|
|
#ifdef WITH_BWM_CHARGERKICK
|
|
BWM_ChargerKick();
|
|
#endif
|
|
|
|
for (;;) {
|
|
WDT_HIT();
|
|
|
|
#ifdef WITH_PM5_PWR_LED
|
|
bwm_power_led_check();
|
|
#endif
|
|
#ifdef WITH_PM5_AUTOOFF
|
|
bwm_autooff_check();
|
|
#endif
|
|
|
|
if (*_stack_start != 0xdeadbeef) {
|
|
Dbprintf("DEBUG: increase stack size, currently " _YELLOW_("%d") " bytes", (uint32_t)_stack_end - (uint32_t)_stack_start);
|
|
Dbprintf("Stack overflow detected");
|
|
Dbprintf("--> Unplug your device now! <--");
|
|
hf_field_off();
|
|
while (1);
|
|
}
|
|
|
|
// Check if there is a packet available
|
|
PacketCommandNG rx;
|
|
memset(&rx.data, 0, sizeof(rx.data));
|
|
|
|
int ret = receive_ng(&rx);
|
|
if (ret == PM3_SUCCESS) {
|
|
PacketReceived(&rx);
|
|
last_activity_label = GetTickCountLabel();
|
|
last_activity_tick = GetTickCount();
|
|
} else if (ret != PM3_ENODATA) {
|
|
Dbprintf("Error in frame reception: %d %s", ret, (ret == PM3_EIO) ? "PM3_EIO" : "");
|
|
// TODO if error, shall we resync ?
|
|
}
|
|
|
|
if (g_hf_field_activity_timeout_ms > 0 && g_hf_field_timeout_active) {
|
|
uint32_t tickcount_label = GetTickCountLabel();
|
|
if (tickcount_label != last_activity_label) {
|
|
last_activity_label = tickcount_label;
|
|
last_activity_tick = GetTickCount();
|
|
} else if (GetTickCountDelta(last_activity_tick) >= g_hf_field_activity_timeout_ms) {
|
|
hf_field_off();
|
|
Dbprintf("HF field auto-off: inactivity timeout (%u ms). To disable, use 'prefs set hf.field.timeout_sec --sec 0'", g_hf_field_activity_timeout_ms);
|
|
}
|
|
}
|
|
|
|
// Press button for one second to enter a possible standalone mode
|
|
button_status = BUTTON_HELD(1000);
|
|
if (button_status == BUTTON_HOLD) {
|
|
/*
|
|
* So this is the trigger to execute a standalone mod. Generic entrypoint by following the standalone/standalone.h headerfile
|
|
* All standalone mod "main loop" should be the RunMod() function.
|
|
*/
|
|
// allow_send_wtx = false;
|
|
// RunMod();
|
|
// allow_send_wtx = true;
|
|
|
|
#ifdef PM5 // TODO DXL Test long press to device shutdown, temporarily blocking standalone mod
|
|
|
|
/*
|
|
StartTicks();
|
|
I2C_init(true);
|
|
uint8_t addr = 0x51;
|
|
// 125 134 250 375 500 HFLED LFLED Q
|
|
// 1 0 0 0 0 1 1 1
|
|
uint8_t data = 0x87;
|
|
I2C_BufferWrite(&data, 1, 0x02, addr << 1);
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
FpgaSendCommand(FPGA_CMD_SET_PWR_PWM_LOW_COUNT, 4095);
|
|
|
|
static bool b = 0;
|
|
if (b) {
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
b = 0;
|
|
} else {
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD);
|
|
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125);
|
|
b = 1;
|
|
}
|
|
*/
|
|
|
|
LEDsoff();
|
|
while (BUTTON_PRESS()) {
|
|
SpinDelay(50);
|
|
LED_A_INV();
|
|
SpinDelay(50);
|
|
LED_B_INV();
|
|
SpinDelay(50);
|
|
LED_C_INV();
|
|
SpinDelay(50);
|
|
LED_D_INV();
|
|
}
|
|
// Release for more than 100ms before truly shutting down, anti shake
|
|
uint8_t idx = 0;
|
|
while (!BUTTON_PRESS()) {
|
|
SpinDelay(10);
|
|
idx += 1;
|
|
if (idx == 10) {
|
|
break;
|
|
}
|
|
}
|
|
LEDsoff();
|
|
if (idx == 10) {
|
|
SpinDelay(100);
|
|
LED_A_INV();
|
|
SpinDelay(100);
|
|
LED_A_INV();
|
|
SpinDelay(100);
|
|
LED_A_INV();
|
|
Gpio_ARM_Power_ON_Low();
|
|
while (1); // Wait for system power off.
|
|
}
|
|
|
|
#endif
|
|
}
|
|
}
|
|
}
|