Initial commit - Phase 3/4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
michael
2026-01-06 13:45:29 -08:00
parent 1da6730735
commit 4f35df1781
323 changed files with 98287 additions and 1195 deletions

View File

@@ -0,0 +1,380 @@
#!/bin/bash -e
# Proxmark3 Client Build with Python Bindings
# Builds firmware + client + experimental Python library for Dangerous Pi
# Date: 2025-11-26
echo "=== Starting Proxmark3 build with Python bindings ==="
# Install build dependencies
echo "Installing build dependencies..."
apt-get update
apt-get install -y \
cmake \
python3-dev \
swig \
build-essential \
libreadline-dev \
libusb-1.0-0-dev \
liblz4-dev \
libbz2-dev \
libjansson-dev \
libc6-dev \
pkg-config \
git \
gcc-arm-none-eabi \
libnewlib-dev \
ca-certificates \
libssl-dev \
libbluetooth-dev \
libgd-dev
# Clone Proxmark3 repository
echo "Cloning Proxmark3 repository..."
cd /tmp
rm -rf proxmark3 # Clean any existing clone
git clone https://github.com/RfidResearchGroup/proxmark3
cd proxmark3
# Apply LED PWM control patch (custom Dangerous Pi feature)
# Patch was copied to /tmp by 00-run.sh (pre-chroot script)
echo "Applying LED PWM control patch..."
if [ -f /tmp/led-pwm-control.patch ]; then
patch -p1 < /tmp/led-pwm-control.patch
if [ $? -eq 0 ]; then
echo "✓ LED PWM control patch applied successfully"
else
echo "✗ WARNING: LED PWM control patch failed to apply cleanly"
fi
rm /tmp/led-pwm-control.patch
else
echo "✗ WARNING: LED PWM control patch not found at /tmp/led-pwm-control.patch"
fi
# Add Dangerous Pi branding to version string
echo "Adding Dangerous Pi branding to version..."
sed -i 's/^fullgitinfo="Iceman"$/fullgitinfo="Iceman\/DangerousPi"/' tools/mkversion.sh
if grep -q 'fullgitinfo="Iceman/DangerousPi"' tools/mkversion.sh; then
echo "✓ Version branding applied"
else
echo "✗ WARNING: Version branding may not have applied correctly"
fi
# Build firmware (existing functionality)
echo "Building firmware..."
cat > Makefile.platform << EOF
PLATFORM=PM3GENERIC
LED_ORDER=PM3EASY
EOF
make clean && make -j$(nproc)
# Apply fixes to CMakeLists.txt for Python bindings
# These fix undefined symbol errors in the SWIG library
# Fix 1: Add qrcode.c (fixes "undefined symbol: qrcode_print_matrix_utf8")
echo "Applying qrcode fix to experimental library..."
sed -i '/TARGET_SOURCES.*pm3rrg_rdv4_experimental/,/)/s|\(${PM3_ROOT}/client/src/wiegand_formatutils.c\)|\1\n ${PM3_ROOT}/client/src/qrcode/qrcode.c|' \
client/experimental_lib/CMakeLists.txt
# Fix 2: Add pla.c (fixes "undefined symbol: pla_parse_ecp_subcommand")
echo "Applying pla.c fix to experimental library..."
sed -i 's|\(${PM3_ROOT}/client/src/pm3\.c\)|${PM3_ROOT}/client/src/pla.c\n \1|' \
client/experimental_lib/CMakeLists.txt
# Verify the fixes were applied
if grep -q "qrcode/qrcode.c" client/experimental_lib/CMakeLists.txt; then
echo "✓ qrcode fix applied successfully"
else
echo "✗ WARNING: qrcode fix may not have applied correctly"
fi
if grep -q "pla\.c" client/experimental_lib/CMakeLists.txt; then
echo "✓ pla.c fix applied successfully"
else
echo "✗ WARNING: pla.c fix may not have applied correctly"
fi
# Build client with Python bindings
echo "Building PM3 client..."
cd client
mkdir -p build
cd build
cmake .. -DBUILD_PYTHON_LIB=ON
make -j$(nproc)
# Build experimental Python library
echo "Building experimental Python library..."
cd ../experimental_lib
./00make_swig.sh
./01make_lib.sh
# Detect the default user (cloud-init compatible)
# Find first user with UID >= 1000 (excluding nobody)
DEFAULT_USER=$(awk -F: '$3 >= 1000 && $3 < 65534 {print $1; exit}' /etc/passwd)
if [ -z "$DEFAULT_USER" ]; then
DEFAULT_USER="pi" # Fallback to pi if detection fails
fi
DEFAULT_HOME=$(eval echo ~$DEFAULT_USER)
echo "Detected default user: $DEFAULT_USER (home: $DEFAULT_HOME)"
# Create installation directories
echo "Creating installation directories..."
mkdir -p $DEFAULT_HOME
mkdir -p $DEFAULT_HOME/.pm3/proxmark3/client/pyscripts
mkdir -p $DEFAULT_HOME/.pm3/proxmark3/firmware
mkdir -p $DEFAULT_HOME/.pm3/proxmark3/patches
mkdir -p $DEFAULT_HOME/.pm3/proxmark3/scripts
# Install client executable
echo "Installing PM3 client..."
cp /tmp/proxmark3/client/build/proxmark3 $DEFAULT_HOME/.pm3/proxmark3/client/
# Install flash utilities
echo "Installing flash utilities..."
cp /tmp/proxmark3/pm3-flash-all $DEFAULT_HOME/.pm3/proxmark3/
cp /tmp/proxmark3/pm3-flash-bootrom $DEFAULT_HOME/.pm3/proxmark3/
cp /tmp/proxmark3/pm3-flash-fullimage $DEFAULT_HOME/.pm3/proxmark3/
chmod +x $DEFAULT_HOME/.pm3/proxmark3/pm3-flash-*
# Install LED PWM control patch for future rebuilds
echo "Installing LED PWM control patch..."
if [ -f /tmp/led-pwm-control.patch ]; then
cp /tmp/led-pwm-control.patch $DEFAULT_HOME/.pm3/proxmark3/patches/
echo "✓ LED PWM control patch saved for future rebuilds"
fi
# Install PM3 rebuild script
echo "Creating PM3 rebuild script..."
cat > $DEFAULT_HOME/.pm3/proxmark3/scripts/rebuild-pm3.sh << 'REBUILD_EOF'
#!/bin/bash
# Rebuild Proxmark3 client and firmware with LED PWM control patch
# Usage: ./rebuild-pm3.sh [--flash]
set -e
PM3_HOME="$HOME/.pm3/proxmark3"
BUILD_DIR="/tmp/proxmark3-rebuild"
PATCH_FILE="$PM3_HOME/patches/led-pwm-control.patch"
echo "=== Proxmark3 Rebuild Script ==="
echo "This will rebuild the PM3 client and firmware with LED PWM control."
echo ""
# Check for ARM toolchain
if ! command -v arm-none-eabi-gcc &> /dev/null; then
echo "ERROR: ARM toolchain not found. Install with:"
echo " sudo apt install gcc-arm-none-eabi"
exit 1
fi
# Clean previous build
rm -rf "$BUILD_DIR"
# Clone fresh copy
echo "Cloning Proxmark3 repository..."
git clone --depth 1 https://github.com/RfidResearchGroup/proxmark3 "$BUILD_DIR"
cd "$BUILD_DIR"
# Apply LED PWM control patch
if [ -f "$PATCH_FILE" ]; then
echo "Applying LED PWM control patch..."
patch -p1 < "$PATCH_FILE"
echo "✓ Patch applied"
else
echo "WARNING: LED patch not found at $PATCH_FILE"
fi
# Add Dangerous Pi branding to version string
echo "Adding Dangerous Pi branding..."
sed -i 's/^fullgitinfo="Iceman"$/fullgitinfo="Iceman\/DangerousPi"/' tools/mkversion.sh
# Configure build
echo "Configuring build..."
cat > Makefile.platform << EOF
PLATFORM=PM3GENERIC
LED_ORDER=PM3EASY
EOF
# Build firmware and client
echo "Building firmware (this takes a while on Pi Zero)..."
make clean
make -j$(nproc)
# Build client with Python bindings
echo "Building client..."
cd client
mkdir -p build
cd build
cmake .. -DBUILD_PYTHON_LIB=ON
make -j$(nproc)
# Build experimental Python library
echo "Building Python bindings..."
cd ../experimental_lib
./00make_swig.sh
./01make_lib.sh
cd "$BUILD_DIR"
# Backup current installation
echo "Backing up current installation..."
if [ -f "$PM3_HOME/client/proxmark3" ]; then
cp "$PM3_HOME/client/proxmark3" "$PM3_HOME/client/proxmark3.bak"
fi
if [ -f "$PM3_HOME/firmware/fullimage.elf" ]; then
cp "$PM3_HOME/firmware/fullimage.elf" "$PM3_HOME/firmware/fullimage.elf.bak"
fi
# Install new binaries
echo "Installing new client..."
cp "$BUILD_DIR/client/build/proxmark3" "$PM3_HOME/client/"
cp "$BUILD_DIR/client/experimental_lib/build/libpm3rrg_rdv4.so" "$PM3_HOME/client/pyscripts/"
cp "$BUILD_DIR/client/pyscripts/*.py" "$PM3_HOME/client/pyscripts/"
echo "Installing new firmware..."
cp "$BUILD_DIR/bootrom/obj/bootrom.elf" "$PM3_HOME/firmware/"
cp "$BUILD_DIR/armsrc/obj/fullimage.elf" "$PM3_HOME/firmware/"
# Update flash utilities
echo "Updating flash utilities..."
cp "$BUILD_DIR/pm3-flash-all" "$PM3_HOME/"
cp "$BUILD_DIR/pm3-flash-bootrom" "$PM3_HOME/"
cp "$BUILD_DIR/pm3-flash-fullimage" "$PM3_HOME/"
chmod +x "$PM3_HOME/pm3-flash-"*
# Update version
echo "Updating version info..."
cd "$BUILD_DIR"
git describe --always --tags > "$PM3_HOME/VERSION.txt"
echo "Build date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> "$PM3_HOME/VERSION.txt"
echo "Built with LED PWM control patch" >> "$PM3_HOME/VERSION.txt"
echo ""
echo "=== Build Complete ==="
echo "New client: $PM3_HOME/client/proxmark3"
echo "New firmware: $PM3_HOME/firmware/"
echo ""
# Flash if requested
if [ "$1" == "--flash" ]; then
echo "Flashing firmware to connected device..."
cd "$PM3_HOME"
./pm3-flash-all
echo "✓ Flash complete"
else
echo "To flash the new firmware, run:"
echo " cd $PM3_HOME && ./pm3-flash-all"
fi
# Cleanup
rm -rf "$BUILD_DIR"
echo "✓ Cleanup complete"
REBUILD_EOF
chmod +x $DEFAULT_HOME/.pm3/proxmark3/scripts/rebuild-pm3.sh
# Install Python bindings
echo "Installing Python bindings..."
cp /tmp/proxmark3/client/experimental_lib/build/libpm3rrg_rdv4.so $DEFAULT_HOME/.pm3/proxmark3/client/pyscripts/
cp /tmp/proxmark3/client/pyscripts/*.py $DEFAULT_HOME/.pm3/proxmark3/client/pyscripts/
# Create _pm3.so symlink for Python import (CRITICAL - this is what pm3.py imports)
echo "Creating Python module symlink..."
cd $DEFAULT_HOME/.pm3/proxmark3/client/pyscripts
ln -sf libpm3rrg_rdv4.so _pm3.so
# Install firmware files
echo "Installing firmware files..."
cp /tmp/proxmark3/bootrom/obj/bootrom.elf $DEFAULT_HOME/.pm3/proxmark3/firmware/
cp /tmp/proxmark3/armsrc/obj/fullimage.elf $DEFAULT_HOME/.pm3/proxmark3/firmware/
# Create version file for tracking
echo "Creating version file..."
cd /tmp/proxmark3
git describe --always --tags > $DEFAULT_HOME/.pm3/proxmark3/VERSION.txt
echo "Build date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> $DEFAULT_HOME/.pm3/proxmark3/VERSION.txt
# Set proper ownership
echo "Setting ownership..."
chown -R $DEFAULT_USER:$DEFAULT_USER $DEFAULT_HOME/.pm3
# Add Python path to environment (for systemd services)
echo "Configuring Python path..."
# Ensure .bashrc exists
touch $DEFAULT_HOME/.bashrc
if ! grep -q "PYTHONPATH.*\.pm3" $DEFAULT_HOME/.bashrc; then
echo 'export PYTHONPATH=$HOME/.pm3/proxmark3/client/pyscripts:$PYTHONPATH' >> $DEFAULT_HOME/.bashrc
fi
# Verify installation
echo "Verifying installation..."
if [ -f $DEFAULT_HOME/.pm3/proxmark3/client/proxmark3 ]; then
echo "✓ Client executable installed"
else
echo "✗ ERROR: Client executable not found"
exit 1
fi
# Verify library architecture matches the target platform
echo "Verifying library architecture..."
LIB_ARCH=$(file $DEFAULT_HOME/.pm3/proxmark3/client/pyscripts/libpm3rrg_rdv4.so | grep -o 'ARM aarch64\|x86-64\|ARM,')
EXPECTED_ARCH="ARM aarch64"
if [ "$LIB_ARCH" = "$EXPECTED_ARCH" ]; then
echo "✓ Library architecture correct: $LIB_ARCH"
else
echo "✗ ERROR: Library architecture mismatch!"
echo " Expected: $EXPECTED_ARCH"
echo " Got: $LIB_ARCH"
echo " The library was built for the wrong architecture."
exit 1
fi
if [ -f $DEFAULT_HOME/.pm3/proxmark3/client/pyscripts/pm3.py ]; then
echo "✓ Python module installed"
else
echo "✗ ERROR: Python module not found"
exit 1
fi
if [ -f $DEFAULT_HOME/.pm3/proxmark3/client/pyscripts/_pm3.so ]; then
echo "✓ Python bindings library installed (_pm3.so)"
else
echo "✗ ERROR: Python bindings library not found (_pm3.so)"
echo " Expected symlink: _pm3.so -> libpm3rrg_rdv4.so"
exit 1
fi
if [ -f $DEFAULT_HOME/.pm3/proxmark3/firmware/fullimage.elf ]; then
echo "✓ Firmware files installed"
else
echo "✗ ERROR: Firmware files not found"
exit 1
fi
if [ -f $DEFAULT_HOME/.pm3/proxmark3/pm3-flash-all ]; then
echo "✓ Flash utilities installed"
else
echo "✗ WARNING: Flash utilities not found (flashing from UI will not work)"
fi
if [ -f $DEFAULT_HOME/.pm3/proxmark3/patches/led-pwm-control.patch ]; then
echo "✓ LED PWM control patch saved"
else
echo "✗ WARNING: LED PWM control patch not saved"
fi
if [ -f $DEFAULT_HOME/.pm3/proxmark3/scripts/rebuild-pm3.sh ]; then
echo "✓ Rebuild script installed"
else
echo "✗ WARNING: Rebuild script not installed"
fi
# Print summary
echo "=== Proxmark3 build complete ==="
echo "Installation location: $DEFAULT_HOME/.pm3/proxmark3/"
echo "Client: $DEFAULT_HOME/.pm3/proxmark3/client/proxmark3"
echo "Flash utils: $DEFAULT_HOME/.pm3/proxmark3/pm3-flash-all"
echo "Python: $DEFAULT_HOME/.pm3/proxmark3/client/pyscripts/pm3.py"
echo "Firmware: $DEFAULT_HOME/.pm3/proxmark3/firmware/"
cat $DEFAULT_HOME/.pm3/proxmark3/VERSION.txt
echo "✓ Proxmark3 installation successful"

View File

@@ -0,0 +1,18 @@
#!/bin/bash -e
# Pre-chroot script: Copy LED patch into the rootfs
# This runs on the host before the chroot script
# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PATCH_SRC="${SCRIPT_DIR}/led-pwm-control.patch"
PATCH_DST="${ROOTFS_DIR}/tmp/led-pwm-control.patch"
if [ -f "$PATCH_SRC" ]; then
echo "Copying LED PWM control patch to rootfs..."
cp "$PATCH_SRC" "$PATCH_DST"
echo "✓ Patch copied to $PATCH_DST"
else
echo "✗ WARNING: LED patch not found at $PATCH_SRC"
fi

View File

@@ -0,0 +1,478 @@
diff --git a/armsrc/appmain.c b/armsrc/appmain.c
index 88ce070..cbeb7d2 100644
--- a/armsrc/appmain.c
+++ b/armsrc/appmain.c
@@ -921,6 +921,70 @@ static void PacketReceived(PacketCommandNG *packet) {
reply_ng(CMD_SET_TEAROFF, PM3_SUCCESS, NULL, 0);
break;
}
+ case CMD_LED_CONTROL: {
+ payload_led_control_t *led_data = (payload_led_control_t *)packet->data.asBytes;
+
+ if (led_data->action == 0) {
+ // Turn off
+ if (led_data->led & LED_A) {
+ led_pwm_disable(LED_A);
+ LED_A_OFF();
+ }
+ if (led_data->led & LED_B) {
+ led_pwm_disable(LED_B);
+ LED_B_OFF();
+ }
+ if (led_data->led & LED_C) LED_C_OFF();
+ if (led_data->led & LED_D) LED_D_OFF();
+
+ } else if (led_data->action == 1) {
+ // Turn on
+ if (led_data->led & LED_A) {
+ led_pwm_disable(LED_A);
+ LED_A_ON();
+ }
+ if (led_data->led & LED_B) {
+ led_pwm_disable(LED_B);
+ LED_B_ON();
+ }
+ if (led_data->led & LED_C) LED_C_ON();
+ if (led_data->led & LED_D) LED_D_ON();
+
+ } else if (led_data->action == 2) {
+ // Toggle
+ if (led_data->led & LED_A) {
+ led_pwm_disable(LED_A);
+ LED_A_INV();
+ }
+ if (led_data->led & LED_B) {
+ led_pwm_disable(LED_B);
+ LED_B_INV();
+ }
+ if (led_data->led & LED_C) LED_C_INV();
+ if (led_data->led & LED_D) LED_D_INV();
+
+ } else if (led_data->action == 3) {
+ // PWM brightness control
+ if ((led_data->led & LED_A) && !(led_data->led & ~LED_A)) {
+ // Only LED_A selected
+ led_set_pwm_brightness(LED_A, led_data->brightness);
+ } else if ((led_data->led & LED_B) && !(led_data->led & ~LED_B)) {
+ // Only LED_B selected
+ led_set_pwm_brightness(LED_B, led_data->brightness);
+ } else if ((led_data->led & LED_A) && (led_data->led & LED_B) && !(led_data->led & ~(LED_A | LED_B))) {
+ // Both LED_A and LED_B selected
+ led_set_pwm_brightness(LED_A, led_data->brightness);
+ led_set_pwm_brightness(LED_B, led_data->brightness);
+ } else {
+ // PWM only supported for LED_A and LED_B
+ reply_ng(CMD_LED_CONTROL, PM3_EINVARG, NULL, 0);
+ break;
+ }
+ }
+
+ reply_ng(CMD_LED_CONTROL, PM3_SUCCESS, NULL, 0);
+ break;
+ }
// always available
case CMD_HF_DROPFIELD: {
hf_field_off();
diff --git a/armsrc/util.c b/armsrc/util.c
index 0df8935..6d8246f 100644
--- a/armsrc/util.c
+++ b/armsrc/util.c
@@ -251,17 +251,17 @@ int BUTTON_CLICKED(int ms) {
return BUTTON_NO_CLICK;
// Borrow a PWM unit for my real-time clock
- AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0);
+ AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(1);
// 48 MHz / 1024 gives 46.875 kHz
- AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10);
- AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0;
- AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff;
+ AT91C_BASE_PWMC_CH1->PWMC_CMR = PWM_CH_MODE_PRESCALER(10);
+ AT91C_BASE_PWMC_CH1->PWMC_CDTYR = 0;
+ AT91C_BASE_PWMC_CH1->PWMC_CPRDR = 0xffff;
- uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR;
+ uint16_t start = AT91C_BASE_PWMC_CH1->PWMC_CCNTR;
int letoff = 0;
for (;;) {
- uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR;
+ uint16_t now = AT91C_BASE_PWMC_CH1->PWMC_CCNTR;
// We haven't let off the button yet
if (!letoff) {
@@ -270,7 +270,7 @@ int BUTTON_CLICKED(int ms) {
letoff = 1;
// reset our timer for 500ms
- start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR;
+ start = AT91C_BASE_PWMC_CH1->PWMC_CCNTR;
ticks = ((MCK / 1000) * (500)) >> 10;
}
@@ -316,16 +316,16 @@ int BUTTON_HELD(int ms) {
}
// Borrow a PWM unit for my real-time clock
- AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0);
+ AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(1);
// 48 MHz / 1024 gives 46.875 kHz
- AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10);
- AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0;
- AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff;
+ AT91C_BASE_PWMC_CH1->PWMC_CMR = PWM_CH_MODE_PRESCALER(10);
+ AT91C_BASE_PWMC_CH1->PWMC_CDTYR = 0;
+ AT91C_BASE_PWMC_CH1->PWMC_CPRDR = 0xffff;
- uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR;
+ uint16_t start = AT91C_BASE_PWMC_CH1->PWMC_CCNTR;
for (;;) {
- uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR;
+ uint16_t now = AT91C_BASE_PWMC_CH1->PWMC_CCNTR;
// As soon as our button let go, we didn't hold long enough
if (BUTTON_PRESS() == false) {
@@ -415,3 +415,90 @@ void convertToHexArray(uint32_t num, uint8_t *partialkey) {
partialkey[i] = (uint8_t)strtoul(group, NULL, 2);
}
}
+
+// PWM LED brightness control
+#define LED_PWM_PERIOD 1000 // 48kHz PWM frequency (48MHz / 1000)
+
+static bool pwm0_initialized = false;
+static bool pwm2_initialized = false;
+
+void led_set_pwm_brightness(uint8_t led, uint8_t brightness) {
+ // brightness: 0-100
+ if (brightness > 100) {
+ brightness = 100;
+ }
+
+ // Convert 0-100 to 0-1000 duty cycle
+ uint16_t duty = (brightness * LED_PWM_PERIOD) / 100;
+
+ // Inverted duty cycle for active-low LEDs
+ uint16_t inverted_duty = LED_PWM_PERIOD - duty;
+
+ if (led == LED_A) {
+ // LED_A uses PWM Channel 0
+ if (!pwm0_initialized) {
+ // Enable PWM clock
+ AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_PWMC);
+
+ // Configure PA0 as PWM0 peripheral function
+ AT91C_BASE_PIOA->PIO_PDR = GPIO_LED_A; // Disable GPIO control
+ AT91C_BASE_PIOA->PIO_ASR = GPIO_LED_A; // Select peripheral A (PWM)
+
+ // Configure PWM Channel 0
+ AT91C_BASE_PWMC_CH0->PWMC_CMR = AT91C_PWMC_CPRE_MCK; // Clock = MCK
+ AT91C_BASE_PWMC_CH0->PWMC_CPRDR = LED_PWM_PERIOD; // Period
+
+ // Enable PWM Channel 0
+ AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0);
+
+ pwm0_initialized = true;
+ }
+
+ // Update duty cycle
+ AT91C_BASE_PWMC_CH0->PWMC_CUPDR = inverted_duty;
+
+ } else if (led == LED_B) {
+ // LED_B uses PWM Channel 2
+ if (!pwm2_initialized) {
+ // Enable PWM clock
+ AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_PWMC);
+
+ // Configure PA2 as PWM2 peripheral function
+ AT91C_BASE_PIOA->PIO_PDR = GPIO_LED_B; // Disable GPIO control
+ AT91C_BASE_PIOA->PIO_ASR = GPIO_LED_B; // Select peripheral A (PWM)
+
+ // Configure PWM Channel 2
+ AT91C_BASE_PWMC_CH2->PWMC_CMR = AT91C_PWMC_CPRE_MCK; // Clock = MCK
+ AT91C_BASE_PWMC_CH2->PWMC_CPRDR = LED_PWM_PERIOD; // Period
+
+ // Enable PWM Channel 2
+ AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(2);
+
+ pwm2_initialized = true;
+ }
+
+ // Update duty cycle
+ AT91C_BASE_PWMC_CH2->PWMC_CUPDR = inverted_duty;
+ }
+}
+
+void led_pwm_disable(uint8_t led) {
+ if (led == LED_A && pwm0_initialized) {
+ // Disable PWM Channel 0
+ AT91C_BASE_PWMC->PWMC_DIS = PWM_CHANNEL(0);
+
+ // Return PA0 to GPIO control
+ AT91C_BASE_PIOA->PIO_PER = GPIO_LED_A;
+
+ pwm0_initialized = false;
+
+ } else if (led == LED_B && pwm2_initialized) {
+ // Disable PWM Channel 2
+ AT91C_BASE_PWMC->PWMC_DIS = PWM_CHANNEL(2);
+
+ // Return PA2 to GPIO control
+ AT91C_BASE_PIOA->PIO_PER = GPIO_LED_B;
+
+ pwm2_initialized = false;
+ }
+}
diff --git a/armsrc/util.h b/armsrc/util.h
index fd99ac7..d558dda 100644
--- a/armsrc/util.h
+++ b/armsrc/util.h
@@ -105,4 +105,7 @@ bool data_available_fast(void);
uint32_t flash_size_from_cidr(uint32_t cidr);
uint32_t get_flash_size(void);
+void led_set_pwm_brightness(uint8_t led, uint8_t brightness);
+void led_pwm_disable(uint8_t led);
+
#endif
diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c
index 9bbdc98..ad877b0 100644
--- a/client/src/cmdhw.c
+++ b/client/src/cmdhw.c
@@ -1504,6 +1504,115 @@ int set_fpga_mode(uint8_t mode) {
return resp.status;
}
+static int CmdLed(const char *Cmd) {
+ CLIParserContext *ctx;
+ CLIParserInit(&ctx, "hw led",
+ "Control Proxmark3 LEDs with optional PWM brightness for LED A and B",
+ "hw led --led a --on --> Turn on LED A\n"
+ "hw led --led b --off --> Turn off LED B\n"
+ "hw led --led a,b --toggle --> Toggle LEDs A and B\n"
+ "hw led --led a --brightness 50 --> Set LED A to 50%% brightness (PWM)\n"
+ "hw led --led b --brightness 75 --> Set LED B to 75%% brightness (PWM)\n"
+ "hw led --led a,b --brightness 30 --> Set both LEDs A and B to 30%% brightness");
+
+ void *argtable[] = {
+ arg_param_begin,
+ arg_str1(NULL, "led", "<a|b|c|d|all>", "LED selection (comma separated)"),
+ arg_lit0(NULL, "on", "Turn LED on"),
+ arg_lit0(NULL, "off", "Turn LED off"),
+ arg_lit0(NULL, "toggle", "Toggle LED"),
+ arg_int0(NULL, "brightness", "<0-100>", "LED brightness percentage (PWM, LED A and B only)"),
+ arg_param_end
+ };
+ CLIExecWithReturn(ctx, Cmd, argtable, false);
+
+ int led_len = 63;
+ char led_str[64] = {0};
+ CLIGetStrWithReturn(ctx, 1, (uint8_t *)led_str, &led_len);
+ bool on = arg_get_lit(ctx, 2);
+ bool off = arg_get_lit(ctx, 3);
+ bool toggle = arg_get_lit(ctx, 4);
+ int brightness = arg_get_int_def(ctx, 5, -1);
+ CLIParserFree(ctx);
+
+ // Validate only one action
+ if ((on + off + toggle + (brightness >= 0)) != 1) {
+ PrintAndLogEx(ERR, "Specify exactly one action: --on, --off, --toggle, or --brightness");
+ return PM3_EINVARG;
+ }
+
+ // Parse LED selection
+ uint8_t led_mask = 0;
+ char *token = strtok(led_str, ",");
+ while (token != NULL) {
+ if (strcmp(token, "a") == 0 || strcmp(token, "A") == 0) {
+ led_mask |= 1; // LED_A
+ } else if (strcmp(token, "b") == 0 || strcmp(token, "B") == 0) {
+ led_mask |= 2; // LED_B
+ } else if (strcmp(token, "c") == 0 || strcmp(token, "C") == 0) {
+ led_mask |= 4; // LED_C
+ } else if (strcmp(token, "d") == 0 || strcmp(token, "D") == 0) {
+ led_mask |= 8; // LED_D
+ } else if (strcmp(token, "all") == 0 || strcmp(token, "ALL") == 0) {
+ led_mask = 15; // All LEDs
+ } else {
+ PrintAndLogEx(ERR, "Invalid LED: %s (use a, b, c, d, or all)", token);
+ return PM3_EINVARG;
+ }
+ token = strtok(NULL, ",");
+ }
+
+ if (led_mask == 0) {
+ PrintAndLogEx(ERR, "No LEDs selected");
+ return PM3_EINVARG;
+ }
+
+ // Validate brightness
+ if (brightness >= 0) {
+ if (brightness > 100) {
+ PrintAndLogEx(ERR, "Brightness must be 0-100");
+ return PM3_EINVARG;
+ }
+ // Check if only LED_A and/or LED_B selected for PWM
+ if (led_mask & ~0x03) { // If any bit other than LED_A or LED_B is set
+ PrintAndLogEx(ERR, "PWM brightness only supported for LED A and B");
+ return PM3_EINVARG;
+ }
+ }
+
+ // Build payload
+ struct {
+ uint8_t led;
+ uint8_t action;
+ uint8_t brightness;
+ } PACKED payload;
+
+ payload.led = led_mask;
+ if (on) {
+ payload.action = 1;
+ } else if (off) {
+ payload.action = 0;
+ } else if (toggle) {
+ payload.action = 2;
+ } else {
+ payload.action = 3; // PWM
+ }
+ payload.brightness = (brightness >= 0) ? brightness : 0;
+
+ clearCommandBuffer();
+ SendCommandNG(CMD_LED_CONTROL, (uint8_t *)&payload, sizeof(payload));
+ PacketResponseNG resp;
+ if (WaitForResponseTimeout(CMD_LED_CONTROL, &resp, 1000) == false) {
+ PrintAndLogEx(WARNING, "command execution time out");
+ return PM3_ETIMEOUT;
+ }
+ if (resp.status != PM3_SUCCESS) {
+ PrintAndLogEx(ERR, "LED control command failed");
+ return resp.status;
+ }
+ return PM3_SUCCESS;
+}
+
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"-------------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Operation") " -----------------------"},
@@ -1518,6 +1627,7 @@ static command_t CommandTable[] = {
{"connect", CmdConnect, AlwaysAvailable, "Connect to the device via serial port"},
{"dbg", CmdDbg, IfPm3Present, "Set device side debug level"},
{"fpgaoff", CmdFPGAOff, IfPm3Present, "Turn off FPGA on device"},
+ {"led", CmdLed, IfPm3Present, "Control LEDs with optional PWM brightness"},
{"lcd", CmdLCD, IfPm3Lcd, "Send command/data to LCD"},
{"lcdreset", CmdLCDReset, IfPm3Lcd, "Hardware reset LCD"},
{"ping", CmdPing, IfPm3Present, "Test if the Proxmark3 is responsive"},
diff --git a/common_arm/ticks.c b/common_arm/ticks.c
index 73182a1..1655241 100644
--- a/common_arm/ticks.c
+++ b/common_arm/ticks.c
@@ -32,19 +32,19 @@ void SpinDelayUsPrecision(int us) {
int ticks = ((MCK / 1000000) * us + 16) >> 5;
// Borrow a PWM unit for my real-time clock
- AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0);
+ AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(1);
// 48 MHz / 32 gives 1.5 Mhz
- AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(5); // Channel Mode Register
- AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; // Channel Duty Cycle Register
- AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xFFFF; // Channel Period Register
+ AT91C_BASE_PWMC_CH1->PWMC_CMR = PWM_CH_MODE_PRESCALER(5); // Channel Mode Register
+ AT91C_BASE_PWMC_CH1->PWMC_CDTYR = 0; // Channel Duty Cycle Register
+ AT91C_BASE_PWMC_CH1->PWMC_CPRDR = 0xFFFF; // Channel Period Register
- uint16_t end = AT91C_BASE_PWMC_CH0->PWMC_CCNTR + ticks;
- if (end == 0) // AT91C_BASE_PWMC_CH0->PWMC_CCNTR is never == 0
+ uint16_t end = AT91C_BASE_PWMC_CH1->PWMC_CCNTR + ticks;
+ if (end == 0) // AT91C_BASE_PWMC_CH1->PWMC_CCNTR is never == 0
end++; // so we have to end++ to avoid inivity loop
for (;;) {
- uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR;
+ uint16_t now = AT91C_BASE_PWMC_CH1->PWMC_CCNTR;
if (now == end)
return;
@@ -59,19 +59,19 @@ void SpinDelayUs(int us) {
int ticks = ((MCK / 1000000) * us + 512) >> 10;
// Borrow a PWM unit for my real-time clock
- AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0);
+ AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(1);
// 48 MHz / 1024 gives 46.875 kHz
- AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); // Channel Mode Register
- AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; // Channel Duty Cycle Register
- AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; // Channel Period Register
+ AT91C_BASE_PWMC_CH1->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); // Channel Mode Register
+ AT91C_BASE_PWMC_CH1->PWMC_CDTYR = 0; // Channel Duty Cycle Register
+ AT91C_BASE_PWMC_CH1->PWMC_CPRDR = 0xffff; // Channel Period Register
- uint16_t end = AT91C_BASE_PWMC_CH0->PWMC_CCNTR + ticks;
- if (end == 0) // AT91C_BASE_PWMC_CH0->PWMC_CCNTR is never == 0
+ uint16_t end = AT91C_BASE_PWMC_CH1->PWMC_CCNTR + ticks;
+ if (end == 0) // AT91C_BASE_PWMC_CH1->PWMC_CCNTR is never == 0
end++; // so we have to end++ to avoid inivity loop
for (;;) {
- uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR;
+ uint16_t now = AT91C_BASE_PWMC_CH1->PWMC_CCNTR;
if (now == end)
return;
diff --git a/common_arm/usb_cdc.c b/common_arm/usb_cdc.c
index ce7bda3..776b00f 100644
--- a/common_arm/usb_cdc.c
+++ b/common_arm/usb_cdc.c
@@ -487,17 +487,17 @@ static void SpinDelayUs(int us) {
int ticks = ((MCK / 1000000) * us + 512) >> 10;
// Borrow a PWM unit for my real-time clock
- AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0);
+ AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(1);
// 48 MHz / 1024 gives 46.875 kHz
- AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); // Channel Mode Register
- AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; // Channel Duty Cycle Register
- AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; // Channel Period Register
+ AT91C_BASE_PWMC_CH1->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); // Channel Mode Register
+ AT91C_BASE_PWMC_CH1->PWMC_CDTYR = 0; // Channel Duty Cycle Register
+ AT91C_BASE_PWMC_CH1->PWMC_CPRDR = 0xffff; // Channel Period Register
- uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR;
+ uint16_t start = AT91C_BASE_PWMC_CH1->PWMC_CCNTR;
for (;;) {
- uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR;
+ uint16_t now = AT91C_BASE_PWMC_CH1->PWMC_CCNTR;
if (now == (uint16_t)(start + ticks))
return;
diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h
index 860dfa9..e1551b5 100644
--- a/include/pm3_cmd.h
+++ b/include/pm3_cmd.h
@@ -465,6 +465,13 @@ typedef struct {
uint8_t data[];
} PACKED smart_card_raw_t;
+// For CMD_LED_CONTROL
+typedef struct {
+ uint8_t led; // LED bitmask: LED_A=1, LED_B=2, LED_C=4, LED_D=8
+ uint8_t action; // 0=off, 1=on, 2=toggle, 3=pwm
+ uint8_t brightness; // 0-100 (for action=3 PWM mode only)
+} PACKED payload_led_control_t;
+
// For the bootloader
#define CMD_DEVICE_INFO 0x0000
@@ -500,6 +507,7 @@ typedef struct {
#define CMD_TIA 0x0117
#define CMD_BREAK_LOOP 0x0118
#define CMD_SET_TEAROFF 0x0119
+#define CMD_LED_CONTROL 0x011A
#define CMD_GET_DBGMODE 0x0120
// RDV40, Flash memory operations