diff --git a/.github/build_summary.md b/.github/build_summary.md new file mode 100644 index 0000000..ce113f7 --- /dev/null +++ b/.github/build_summary.md @@ -0,0 +1,13 @@ +### Files included in the build artifact + +Most users should use one of the **`picoboot_full_*.uf2`** files (full package). Other files are intended for advanced users to update only specific parts of the firmware or payload. + +| File Name | Platform | Description | +| ------------------------- | ------------------------------------ | --------------------------------- | +| `picoboot_full_pico.uf2` | Pico
Pico W | firmware + payload (full package) | +| `picoboot_full_pico2.uf2` | Pico 2
Pico 2 W | firmware + payload (full package) | +| `picoboot_pico.uf2` | Pico
Pico W | firmware only | +| `picoboot_pico2.uf2` | Pico 2
Pico 2 W | firmware only | +| `payload_pico.uf2` | Pico
Pico W | payload | +| `payload_pico2.uf2` | Pico 2
Pico 2 W | payload | +| `payload_universal.uf2` | Pico
Pico W
Pico 2
Pico 2 W | payload | diff --git a/.github/release_description.md b/.github/release_description.md new file mode 100644 index 0000000..4197668 --- /dev/null +++ b/.github/release_description.md @@ -0,0 +1,14 @@ +@DESCRIPTION@ + +## Changes in this release + +@CHANGELOG@ + +## Useful Links + +✨ [Software features](https://github.com/redolution/gekkoboot/) | 🚀 [Installation Guide](https://support.webhdx.dev/gc/picoboot/installation-guide) | ⬆️ [Update Guide](https://support.webhdx.dev/gc/picoboot/update-picoboot) | 🛠️ [Troubleshooting](https://support.webhdx.dev/gc/picoboot/troubleshooting) + +## Available files + +* `picoboot_full_pico.uf2`: Compatible with Raspberry Pi Pico and Raspberry Pi Pico W. +* `picoboot_full_pico2.uf2`: Compatible with Raspberry Pi Pico 2 and Raspberry Pi Pico 2 W. diff --git a/.github/workflows/10-build.yml b/.github/workflows/build.yml similarity index 72% rename from .github/workflows/10-build.yml rename to .github/workflows/build.yml index f290242..a03f7e4 100644 --- a/.github/workflows/10-build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build +name: Build PicoBoot on: push: branches: @@ -32,6 +32,8 @@ jobs: - name: Checkout PicoBoot code uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set outputs id: vars @@ -40,21 +42,19 @@ jobs: - uses: robinraju/release-downloader@v1 id: gekkoboot-download with: - repository: 'webhdx/iplboot' + repository: 'webhdx/gekkoboot' latest: true fileName: '*.zip' out-file-path: gekkoboot extract: true - - name: Copy gekkoboot uf2 payload + - name: Copy gekkoboot.dol run: | cd gekkoboot - if [ -f "gekkoboot_pico.uf2" ]; then - cp gekkoboot_pico.uf2 ${{ github.workspace }}/payload.uf2 - elif [ -f "iplboot_pico.uf2" ]; then - cp iplboot_pico.uf2 ${{ github.workspace }}/payload.uf2 + if [ -f "gekkoboot.dol" ]; then + cp gekkoboot.dol ${{ github.workspace }}/payload.dol else - echo "Neither gekkoboot_pico.uf2 nor iplboot_pico.uf2 found." + echo "gekkoboot.dol not found" exit 1 fi @@ -63,15 +63,15 @@ jobs: with: imageName: ghcr.io/webhdx/picoboot cacheFrom: ghcr.io/webhdx/picoboot + push: never runCmd: | - cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE . - make + tools/build.sh - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: picoboot-${{ steps.vars.outputs.sha_short }} - path: | - picoboot_full.uf2 - picoboot.uf2 - payload.uf2 + path: dist/ + + - name: Add build summary + run: echo "$(cat .github/build_summary.md)" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/20-devcontainer.yml b/.github/workflows/devcontainer.yml similarity index 100% rename from .github/workflows/20-devcontainer.yml rename to .github/workflows/devcontainer.yml diff --git a/.github/workflows/00-release.yml b/.github/workflows/release.yml similarity index 66% rename from .github/workflows/00-release.yml rename to .github/workflows/release.yml index 45e3550..828342b 100644 --- a/.github/workflows/00-release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: version: description: 'Version to tag' required: true - default: 'v0.4' + default: 'v0.5.0' type: string is_draft: description: 'Is draft release?' @@ -47,7 +47,7 @@ jobs: build: needs: tag - uses: ./.github/workflows/10-build.yml + uses: ./.github/workflows/build.yml release: runs-on: ubuntu-latest @@ -78,17 +78,40 @@ jobs: with: name: picoboot-${{ steps.vars.outputs.sha_short }} path: dist/ + + - name: Build Changelog + id: build_changelog + uses: mikepenz/release-changelog-builder-action@v5 + with: + mode: "COMMIT" + toTag: ${{ github.event.inputs.version }} + configurationJson: | + { + "template": "#{{UNCATEGORIZED}}", + "commit_template": "* #{{TITLE}} by @#{{AUTHOR}} (#{{MERGE_SHA}})", + "empty_template": "_No notable changes in this version._", + "categories": [] + } + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Prepare release description - run: | - touch release-description.md - echo "${{ github.event.inputs.release_description }}" >> release-description.md + - name: Prepare release description + env: + DESCRIPTION: ${{ github.event.inputs.release_description }} + RELEASE_NAME: ${{ steps.determine_name.outputs.name }} + CHANGELOG: ${{ steps.build_changelog.outputs.changelog }} + run: | + content=$(cat .github/release_description.md) + content=${content//@DESCRIPTION@/$DESCRIPTION} + content=${content//@CHANGELOG@/$CHANGELOG} + echo "$content" > release-description.md - name: Calculate artifact checksum run: | echo '```' >> release-description.md cd dist/ - sha256sum picoboot_full.uf2 >> ../release-description.md + sha256sum picoboot_full_pico.uf2 >> ../release-description.md + sha256sum picoboot_full_pico2.uf2 >> ../release-description.md cd ../ echo '```' >> release-description.md @@ -98,23 +121,13 @@ jobs: git config --global user.email "actions@github.com" - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: softprops/action-gh-release@v2 with: + name: ${{ steps.determine_name.outputs.name }} tag_name: ${{ github.event.inputs.version }} - release_name: ${{ steps.determine_name.outputs.name }} - body_path: ${{ github.workspace }}/release-description.md draft: ${{ github.event.inputs.is_draft }} prerelease: ${{ github.event.inputs.is_prerelease }} - - - name: Upload Release Asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ${{ github.workspace }}/dist/picoboot_full.uf2 - asset_name: picoboot_full.uf2 - asset_content_type: application/octet-stream + body_path: ${{ github.workspace }}/release-description.md + files: | + dist/picoboot_full_pico.uf2 + dist/picoboot_full_pico2.uf2 diff --git a/.gitignore b/.gitignore index f7d8346..384ba27 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ _deps # Build environment build/ +dist/ CMakeFiles/ elf2uf2/ generated/ @@ -20,3 +21,6 @@ picoboot.elf.map picoboot.hex *.uf2 picoboot.pio.h +gekkoboot.dol +payload.dol +src/version.h \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 33e9484..e997a02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,28 +3,25 @@ cmake_minimum_required(VERSION 3.13) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) -find_package(Git) -execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE GIT_REPO_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE) -string(REGEX REPLACE "v([0-9]+\\.[0-9]+).*" "\\1" CMAKE_GIT_REPO_VERSION ${GIT_REPO_VERSION}) -string(REGEX REPLACE "^(.......-.*)|(.......)$" "0.0.0" CMAKE_GIT_REPO_VERSION ${CMAKE_GIT_REPO_VERSION}) -configure_file("src/version.h.in" "src/version.h") -message("GIT_REPO_VERSION is ${GIT_REPO_VERSION}") -message("CMAKE_GIT_REPO_VERSION is ${CMAKE_GIT_REPO_VERSION}") +include(cmake/extract_version.cmake) # Pull in Raspberry Pi Pico SDK (must be before project) include(pico_sdk_import.cmake) -project(picoboot LANGUAGES C CXX ASM VERSION ${CMAKE_GIT_REPO_VERSION}) +project(picoboot LANGUAGES C CXX ASM VERSION ${PROJECT_VERSION_STRING}) # Initialise the Raspberry Pi Pico SDK pico_sdk_init() +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/dist) + add_executable(picoboot src/picoboot.c + src/hw.c src/pio.c + src/status_led.c + src/status_led/gpio.c + src/status_led/cyw43.c ) pico_generate_pio_header(picoboot @@ -33,10 +30,9 @@ pico_generate_pio_header(picoboot pico_set_program_name(picoboot "PicoBoot") pico_set_program_description(picoboot "RP2040 based modchip for Nintendo GameCube") -pico_set_program_version(picoboot ${GIT_REPO_VERSION}) +pico_set_program_version(picoboot ${FW_VER_STRING}) pico_set_program_url(picoboot "https://github.com/webhdx/PicoBoot") -pico_set_binary_type(picoboot copy_to_ram) target_link_options(pico_standard_link INTERFACE "LINKER:--script=${CMAKE_CURRENT_LIST_DIR}/memmap_picoboot.ld") pico_enable_stdio_uart(picoboot 0) @@ -44,20 +40,12 @@ pico_enable_stdio_usb(picoboot 1) target_include_directories(picoboot PRIVATE src) -target_link_libraries(picoboot PRIVATE pico_stdlib hardware_pio hardware_dma) +target_link_libraries(picoboot PRIVATE + hardware_adc + hardware_dma + hardware_pio + pico_cyw43_arch_none + pico_stdlib +) pico_add_extra_outputs(picoboot) - -function(merge_uf2 NAME BASE_TARGET INPUTS) - get_target_property(BASE_TARGET_NAME ${BASE_TARGET} OUTPUT_NAME) - if(BASE_TARGET_NAME STREQUAL "BASE_TARGET_NAME-NOTFOUND") - get_target_property(BASE_TARGET_NAME ${BASE_TARGET} NAME) - endif() - - add_custom_target(${NAME} ALL - COMMAND ${CMAKE_CURRENT_LIST_DIR}/merge_uf2.py ${NAME}.uf2 ${BASE_TARGET_NAME}.uf2 ${INPUTS} - DEPENDS ${BASE_TARGET} ${INPUTS} - COMMAND_EXPAND_LISTS) -endfunction() - -merge_uf2(picoboot_full picoboot "${CMAKE_CURRENT_LIST_DIR}/payload.uf2") diff --git a/cmake/extract_version.cmake b/cmake/extract_version.cmake new file mode 100644 index 0000000..b616007 --- /dev/null +++ b/cmake/extract_version.cmake @@ -0,0 +1,42 @@ +# CMake script to extract version information from Git and configure version.h + +find_package(Git REQUIRED) + +execute_process( + COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_DESCRIBE_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE GIT_DESCRIBE_RESULT +) + +set(FW_VER_STRING "0.0.0-unknown") +set(FW_VER_MAJOR 0) +set(FW_VER_MINOR 0) +set(FW_VER_PATCH 0) + +if(GIT_DESCRIBE_RESULT EQUAL 0 AND GIT_DESCRIBE_OUTPUT) + set(FW_VER_STRING ${GIT_DESCRIBE_OUTPUT}) + if(FW_VER_STRING MATCHES "^v([0-9]+)\.([0-9]+)\.([0-9]+)") + set(FW_VER_MAJOR ${CMAKE_MATCH_1}) + set(FW_VER_MINOR ${CMAKE_MATCH_2}) + set(FW_VER_PATCH ${CMAKE_MATCH_3}) + else() + message(STATUS "Git version string '${FW_VER_STRING}' does not match vX.Y.Z format for MAJOR/MINOR/PATCH. Using 0.0.0 for these.") + endif() +else() + message(WARNING "git describe failed or returned empty. Using default version: ${FW_VER_STRING}, MAJOR: ${FW_VER_MAJOR}, MINOR: ${FW_VER_MINOR}, PATCH: ${FW_VER_PATCH}") +endif() + +set(PROJECT_VERSION_STRING "${FW_VER_MAJOR}.${FW_VER_MINOR}.${FW_VER_PATCH}") + +configure_file( + "${CMAKE_SOURCE_DIR}/src/version.h.in" + "${CMAKE_SOURCE_DIR}/src/version.h" +) + +message(STATUS "FW_VER_STRING: ${FW_VER_STRING}") +message(STATUS "FW_VER_MAJOR: ${FW_VER_MAJOR}") +message(STATUS "FW_VER_MINOR: ${FW_VER_MINOR}") +message(STATUS "FW_VER_PATCH: ${FW_VER_PATCH}") +message(STATUS "PROJECT_VERSION_STRING: ${PROJECT_VERSION_STRING}") \ No newline at end of file diff --git a/src/hw.c b/src/hw.c new file mode 100644 index 0000000..31c1c2c --- /dev/null +++ b/src/hw.c @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2025 Maciej Kobus + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "hardware/adc.h" +#include "pico/stdlib.h" +#include "pico/platform.h" + +#include "hw.h" + +#define ADC_PIN_VSYS 29 +#define ADC_INPUT_VSYS 3 + +// Threshold for differentiating Pico from Pico W based on raw ADC reading. +// Pico W typically has a much lower reading on VSYS ADC (e.g., ~0x01c). +// Pico typically has a higher reading (e.g., ~0x2cd). +// A value around 0x100 should be a safe threshold. +#define PICO_W_ADC_THRESHOLD 0x100 + +hw_board_type_t hw_detect_board_type(void) { + adc_gpio_init(ADC_PIN_VSYS); + adc_select_input(ADC_INPUT_VSYS); + + uint16_t adc_result = adc_read(); + + if (adc_result < PICO_W_ADC_THRESHOLD) { + #if defined(PICO_RP2350) + return HW_BOARD_TYPE_PICO_2_W; + #else + return HW_BOARD_TYPE_PICO_W; + #endif + } else { + #if defined(PICO_RP2350) + return HW_BOARD_TYPE_PICO_2; + #else + return HW_BOARD_TYPE_PICO; + #endif + } +} + +const char* hw_board_type_to_string(hw_board_type_t board_type) { + switch (board_type) { + case HW_BOARD_TYPE_PICO: + return "Raspberry Pi Pico"; + case HW_BOARD_TYPE_PICO_W: + return "Raspberry Pi Pico W"; + case HW_BOARD_TYPE_PICO_2: + return "Raspberry Pi Pico 2"; + case HW_BOARD_TYPE_PICO_2_W: + return "Raspberry Pi Pico 2 W"; + default: + return "Unknown"; + } +} diff --git a/src/hw.h b/src/hw.h new file mode 100644 index 0000000..2c54ff5 --- /dev/null +++ b/src/hw.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2025 Maciej Kobus + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef HW_H +#define HW_H + +// Defines the possible board types +typedef enum { + HW_BOARD_TYPE_PICO, + HW_BOARD_TYPE_PICO_W, + HW_BOARD_TYPE_PICO_2, + HW_BOARD_TYPE_PICO_2_W, + HW_BOARD_TYPE_UNKNOWN +} hw_board_type_t; + +/** + * @brief Detects the type of Raspberry Pi Pico board. + * + * This function reads the ADC on GPIO29 (ADC3) to differentiate + * between a Pico and a Pico W. Pico W typically shows a much lower + * voltage on VSYS (connected to ADC3) when the wireless circuitry is present. + * + * @note The ADC system must be initialized by calling adc_init() before this function. + * + * @return hw_board_type_t The detected board type (HW_BOARD_TYPE_PICO or HW_BOARD_TYPE_PICO_W). + */ +hw_board_type_t hw_detect_board_type(void); + +/** + * @brief Converts a hw_board_type_t enum to its string representation. + * + * @param board_type The board type enum. + * + * @return const char* The string representation of the board type. + */ +const char* hw_board_type_to_string(hw_board_type_t board_type); + +#endif // HW_H \ No newline at end of file diff --git a/src/picoboot.c b/src/picoboot.c index a11b75d..273dcb5 100644 --- a/src/picoboot.c +++ b/src/picoboot.c @@ -1,27 +1,34 @@ /** - * Copyright (c) 2024 Maciej Kobus + * Copyright (c) 2025 Maciej Kobus * * SPDX-License-Identifier: GPL-2.0-only */ #include -#include "pico/stdlib.h" -#include "hardware/pio.h" +#include + +#include "hardware/adc.h" #include "hardware/clocks.h" #include "hardware/dma.h" +#include "hardware/pio.h" #include "hardware/structs/bus_ctrl.h" -#include "pio.h" -#include "picoboot.pio.h" -#include "endian.h" +#include "pico/stdlib.h" -const uint PIN_LED = 25; // Status LED +#include "endian.h" +#include "hw.h" +#include "picoboot.pio.h" +#include "pio.h" +#include "status_led.h" +#include "version.h" extern const uint32_t __payload[]; extern const uint32_t __payload_end[]; -const uint32_t payload_magic0 = 0x49504C42; // "IPLB" -const uint32_t payload_magic1 = 0x4F4F5420; // "OOT " -const uint32_t payload_magic2 = 0x5049434F; // "PICO" +static const uint32_t payload_magic0 = 0x49504C42; // "IPLB" +static const uint32_t payload_magic1 = 0x4F4F5420; // "OOT " +static const uint32_t payload_magic2 = 0x5049434F; // "PICO" + +static hw_board_type_t s_board_type; size_t validate_payload() { if (BigEndian32(__payload[0]) != payload_magic0) { @@ -51,18 +58,21 @@ size_t validate_payload() { void main() { - // Initialize and light up builtin LED, it will basically - // act as a power LED. - // TODO: Use the LED to signalize system faults? - gpio_init(PIN_LED); - gpio_set_dir(PIN_LED, GPIO_OUT); - gpio_put(PIN_LED, true); + stdio_init_all(); + adc_init(); + s_board_type = hw_detect_board_type(); + + printf("PicoBoot (%s) by webhdx (c) 2025\n", FW_VER_STRING); + printf("Board Type: %s\n", hw_board_type_to_string(s_board_type)); size_t payload_size = validate_payload(); if (payload_size == SIZE_MAX) { + printf("PicoBoot: Invalid payload. Entering infinite loop.\n"); + status_led_init(s_board_type); + while (true) { sleep_ms(500); - gpio_xor_mask(1 << PIN_LED); + status_led_toggle(); } } @@ -136,6 +146,11 @@ void main() pio_sm_set_enabled(pio, transfer_start_sm, true); pio_sm_set_enabled(pio, clocked_output_sm, true); + printf("PicoBoot: Finished injecting payload. Entering infinite loop.\n"); + + status_led_init(s_board_type); + status_led_on(); + while (true) { tight_loop_contents(); } diff --git a/src/picoboot.pio b/src/picoboot.pio index 28b6018..1bb30fb 100644 --- a/src/picoboot.pio +++ b/src/picoboot.pio @@ -1,4 +1,4 @@ - ; Copyright (c) 2024 Maciej Kobus + ; Copyright (c) 2025 Maciej Kobus ; ; SPDX-License-Identifier: GPL-2.0-only diff --git a/src/pio.c b/src/pio.c index db65be8..fecb2bc 100644 --- a/src/pio.c +++ b/src/pio.c @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Maciej Kobus + * Copyright (c) 2025 Maciej Kobus * * SPDX-License-Identifier: GPL-2.0-only */ diff --git a/src/pio.h b/src/pio.h index 0d8eec8..375d6f5 100644 --- a/src/pio.h +++ b/src/pio.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2022 Maciej Kobus + * Copyright (c) 2025 Maciej Kobus * * SPDX-License-Identifier: GPL-2.0-only */ diff --git a/src/status_led.c b/src/status_led.c new file mode 100644 index 0000000..861a960 --- /dev/null +++ b/src/status_led.c @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2025 Maciej Kobus + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include +#include + +#include "hw.h" +#include "status_led.h" +#include "status_led/cyw43.h" +#include "status_led/gpio.h" + +typedef int (*led_init_func_p)(void); +typedef void (*led_op_func_p)(void); + +typedef struct { + led_init_func_p init; + led_op_func_p on; + led_op_func_p off; + led_op_func_p toggle; +} status_led_driver_t; + +static int led_init_noop(void) { + return 0; +} + +static void led_op_noop(void) { + // Do nothing +} + +static status_led_driver_t s_led_driver = { + .init = led_init_noop, + .on = led_op_noop, + .off = led_op_noop, + .toggle = led_op_noop, +}; + +void status_led_init(hw_board_type_t board_type) { + if (board_type == HW_BOARD_TYPE_PICO_W || board_type == HW_BOARD_TYPE_PICO_2_W) { + s_led_driver.init = status_led_cyw43_init; + s_led_driver.on = status_led_cyw43_on; + s_led_driver.off = status_led_cyw43_off; + s_led_driver.toggle = status_led_cyw43_toggle; + } else { + s_led_driver.init = status_led_gpio_init; + s_led_driver.on = status_led_gpio_on; + s_led_driver.off = status_led_gpio_off; + s_led_driver.toggle = status_led_gpio_toggle; + } + + if (s_led_driver.init() != 0) { + s_led_driver.init = led_init_noop; + s_led_driver.on = led_op_noop; + s_led_driver.off = led_op_noop; + s_led_driver.toggle = led_op_noop; + } +} + +void status_led_on(void) { + s_led_driver.on(); +} + +void status_led_off(void) { + s_led_driver.off(); +} + +void status_led_toggle(void) { + s_led_driver.toggle(); +} diff --git a/src/status_led.h b/src/status_led.h new file mode 100644 index 0000000..c097ce4 --- /dev/null +++ b/src/status_led.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2025 Maciej Kobus + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef STATUS_LED_H +#define STATUS_LED_H + +#include "hw.h" + +void status_led_init(hw_board_type_t board_type); + +void status_led_on(void); + +void status_led_off(void); + +void status_led_toggle(void); + +#endif // STATUS_LED_H diff --git a/src/status_led/cyw43.c b/src/status_led/cyw43.c new file mode 100644 index 0000000..4b21b6e --- /dev/null +++ b/src/status_led/cyw43.c @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2025 Maciej Kobus + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include + +#include "pico/cyw43_arch.h" +#ifdef PICO_RP2040 +#include "boards/pico_w.h" +#else +#include "boards/pico2_w.h" +#endif + +#include "cyw43.h" + +static bool led_state_cyw43 = false; + +int status_led_cyw43_init(void) { + if (cyw43_arch_init()) { + return 1; + } + + led_state_cyw43 = false; + + return 0; +} + +void status_led_cyw43_on(void) { + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, true); + + led_state_cyw43 = true; +} + +void status_led_cyw43_off(void) { + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, false); + + led_state_cyw43 = false; +} + +void status_led_cyw43_toggle(void) { + if (led_state_cyw43) { + status_led_cyw43_off(); + } else { + status_led_cyw43_on(); + } +} \ No newline at end of file diff --git a/src/status_led/cyw43.h b/src/status_led/cyw43.h new file mode 100644 index 0000000..3d380af --- /dev/null +++ b/src/status_led/cyw43.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2025 Maciej Kobus + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef STATUS_LED_CYW43_DRIVER_H +#define STATUS_LED_CYW43_DRIVER_H + +int status_led_cyw43_init(void); + +void status_led_cyw43_on(void); + +void status_led_cyw43_off(void); + +void status_led_cyw43_toggle(void); + +#endif \ No newline at end of file diff --git a/src/status_led/gpio.c b/src/status_led/gpio.c new file mode 100644 index 0000000..1687cf6 --- /dev/null +++ b/src/status_led/gpio.c @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2025 Maciej Kobus + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "gpio.h" +#ifdef PICO_RP2040 +#include "boards/pico.h" +#else +#include "boards/pico2.h" +#endif + +static bool led_state_gpio = false; + +int status_led_gpio_init(void) { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + led_state_gpio = false; + + return 0; +} + +void status_led_gpio_on(void) { + gpio_put(PICO_DEFAULT_LED_PIN, true); + + led_state_gpio = true; +} + +void status_led_gpio_off(void) { + gpio_put(PICO_DEFAULT_LED_PIN, false); + + led_state_gpio = false; +} + +void status_led_gpio_toggle(void) { + if (led_state_gpio) { + status_led_gpio_off(); + } else { + status_led_gpio_on(); + } +} diff --git a/src/status_led/gpio.h b/src/status_led/gpio.h new file mode 100644 index 0000000..8cf34c0 --- /dev/null +++ b/src/status_led/gpio.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2025 Maciej Kobus + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef STATUS_LED_GPIO_DRIVER_H +#define STATUS_LED_GPIO_DRIVER_H + +#include "pico/stdlib.h" + +int status_led_gpio_init(void); + +void status_led_gpio_on(void); + +void status_led_gpio_off(void); + +void status_led_gpio_toggle(void); + +#endif \ No newline at end of file diff --git a/src/version.h.in b/src/version.h.in index c4e94c9..1a6ea1b 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,3 +1,22 @@ -#pragma once +/** + * Copyright (c) 2025 Maciej Kobus + * + * SPDX-License-Identifier: GPL-2.0-only + */ -#define PICOBOOT_VERSION "${GIT_REPO_VERSION}" \ No newline at end of file +#ifndef VERSION_H +#define VERSION_H + +#include + +#define FW_VER_STRING "${FW_VER_STRING}" + +static const uint8_t FW_VER_MAJOR = ${FW_VER_MAJOR}; +static const uint8_t FW_VER_MINOR = ${FW_VER_MINOR}; +static const uint8_t FW_VER_PATCH = ${FW_VER_PATCH}; + +static const uint32_t FW_VER_FULL = ((uint32_t)FW_VER_MAJOR << 24) | + ((uint32_t)FW_VER_MINOR << 16) | + ((uint32_t)FW_VER_PATCH << 8); + +#endif // VERSION_H diff --git a/tools/build.sh b/tools/build.sh new file mode 100755 index 0000000..80214cc --- /dev/null +++ b/tools/build.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# ----------------------------------------------------------------------- +# build.sh - Builds PicoBoot firmware files for different platforms +# ----------------------------------------------------------------------- +# Purpose: +# Builds PicoBoot firmware files for both Raspberry Pi Pico and Pico 2 +# platforms. Processes gekkoboot.dol into payload files and creates +# universal payload that works on both boards. +# Outputs uf2 files into dist/ directory. +# +# Usage: +# ./build.sh +# ----------------------------------------------------------------------- + +set -e + +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +RED='\033[0;31m' +NC='\033[0m' + +platforms=("rp2040" "rp2350") +boards=("pico_w" "pico2_w") +boards_arch=("pico" "pico2") +families=("rp2040" "rp2350-arm-s") +build_type="RelWithDebInfo" + +num_configs=${#platforms[@]} + +if [ ! -f "payload.dol" ]; then + echo -e "${RED}Error: payload.dol file not found${NC}" + exit 1 +fi + +echo -e "${BLUE}##########################################################${NC}" +echo -e "🚀 ${YELLOW}Generating payload uf2 files:${NC}" +echo -e "📂 ${YELLOW}Input file:${NC} ${GREEN}payload.dol${NC}" +echo -e "${BLUE}##########################################################${NC}" + +if [ ! -d "dist" ]; then + mkdir dist +fi + +echo -e "\n🔨 ${YELLOW}Building payload uf2 file for Pico...${NC}" +tools/process_ipl.py dist/payload_pico.uf2 payload.dol rp2040 + +echo -e "\n🔨 ${YELLOW}Building payload uf2 file for Pico 2...${NC}" +tools/process_ipl.py dist/payload_pico2.uf2 payload.dol rp2350 + +echo -e "\n🔨 ${YELLOW}Building universal payload uf2 file...${NC}" +cat dist/payload_pico.uf2 dist/payload_pico2.uf2 > dist/payload_universal.uf2 + +for (( i=0; i ") + print(f"Usage: {sys.argv[0]} []") return 1 output = sys.argv[1] executable = sys.argv[2] + family = sys.argv[3] with open(executable, "rb") as f: exe = bytearray(f.read()) @@ -151,7 +161,7 @@ def main(): out = header + img elif output.endswith(".uf2"): - out = pack_uf2(header + img, 0x10080000) + out = pack_uf2(header + img, 0x10080000, family) else: print("Unknown output format") diff --git a/tools/rp2040_4k_align.sh b/tools/rp2040_4k_align.sh new file mode 100755 index 0000000..102654a --- /dev/null +++ b/tools/rp2040_4k_align.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# ----------------------------------------------------------------------- +# rp2040_4k_align.sh - Aligns binary file size to 4KB multiples +# ----------------------------------------------------------------------- +# Purpose: +# Pads binary files to 4KB block size to work around an issue with +# the RP2040 bootrom as described in the RP2040 datasheet errata section. +# +# Usage: +# ./rp2040_4k_align.sh input_file output_file +# +# Arguments: +# $1 - Input file path +# $2 - Output file path +# +# Example: +# ./rp2040_4k_align.sh picoboot.bin picoboot_padded.bin +# ----------------------------------------------------------------------- + +dd if=/dev/zero of="$2" bs=1 count=$(( ($(stat -c%s "$1") + 4096 - 1) / 4096 * 4096 )) 2>/dev/null +dd if="$1" of="$2" bs=1 count=$(stat -c%s "$1") conv=notrunc 2>/dev/null \ No newline at end of file