diff --git a/.github/workflows/build-firmware.yml b/.github/workflows/build-firmware.yml new file mode 100644 index 0000000..ee3a609 --- /dev/null +++ b/.github/workflows/build-firmware.yml @@ -0,0 +1,38 @@ +name: Build Firmware +on: [push] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout source + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Cache Toolchain + id: cache-toolchain + uses: actions/cache@v3 + env: + cache-name: cache-toolchain + with: + path: ~/toolchains/ + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('util/install-toolchain.sh') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - if: ${{ steps.cache-toolchain.outputs.cache-hit != 'true' }} + name: Download toolchain + run: ./util/install-toolchain.sh + + - name: Build Firmware + run: make -f release.Makefile -k all + env: + PREFIX: ~/toolchains/gcc-arm-embedded/bin/arm-none-eabi- + + - name: Archive Firmware + uses: actions/upload-artifact@v4 + with: + name: firmware-bin + path: build/*.bin diff --git a/README.md b/README.md index f860799..916d74a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ The default target is a generic STM32F103 dev board with an LED on PC13, commonl To build other targets, you can override the `TARGET` variable when invoking `make`. + cd src/ make clean make TARGET=STLINK @@ -16,9 +17,27 @@ To build other targets, you can override the | Target Name | Description | Link | | ----------- | ----------- |----- | -|`BLUEPILL` | Cheap dev board | http://wiki.stm32duino.com/index.php?title=Blue_Pill | -|`MAPLEMINI` | LeafLabs Maple Mini board and clone derivatives | http://wiki.stm32duino.com/index.php?title=Maple_Mini | +|`BLUEPILL` | Cheap dev board | https://stm32duinoforum.com/forum/wiki_subdomain/index_title_Blue_Pill.html | +|`MAPLEMINI` | LeafLabs Maple Mini board and clone derivatives | https://stm32duinoforum.com/forum/wiki_subdomain/index_title_Maple_Mini.html | |`STLINK` | STLink/v2 hardware clones | https://wiki.paparazziuav.org/wiki/STLink#Clones | +|`OLIMEXSTM32H103` | Olimex STM32-H103 | https://www.olimex.com/Products/ARM/ST/STM32-H103/ | +|`BLUEPILLPLUSSTM32` | Bluepill with USB C | https://github.com/WeActTC/BluePill-Plus/ | +|`BTTSKRMINIE3V2` | BigTreeTech SKR MINI E3 V2.0 (3D printer motherboard) | https://github.com/bigtreetech/BIGTREETECH-SKR-mini-E3 | + +For the above targets there are some potential variants that can be added to the target name based on what the target supports: + +| Target Variant | Description | +| -------------- | ------------------------------------------------------- | +|` ` | Standard bootloader, using first 8 KiB of flash | +|`_HIGH` | High memory bootloader for 64 KiB chips (experimental) | +|`_HIGH_128` | High memory bootloader for 128 KiB chips (experimental) | +|`_HIGH_256` | High memory bootloader for 256 KiB chips (experimental) | + +The high memory bootloader is a variation that doesn't require the application to be at an offset, the bootloader resides in the top 7 KiB of ROM and hides its reset and stack vectors inside unused entries of the application vector table. As an example, to compile for a Bluepill board with 128 KiB flash, use: + + make clean + make TARGET=BLUEPILL_HIGH_128 + ## Flash instructions The `make flash` target will use openocd to upload the bootloader to an attached board. By default, the Makefile assumes you're using a [CMSIS-DAP](http://www.arm.com/products/processors/cortex-m/cortex-microcontroller-software-interface-standard.php) based probe, but you can override this by overriding `OOCD_INTERFACE` variable. For example: @@ -33,24 +52,62 @@ Here is an example `local.mk` that changes the default target to the STLink/v2 a TARGET ?= STLINK OOCD_INTERFACE ?= interface/stlink-v2.cfg +You can also use the env variable `DEFS` to override default configuration for a target, like: + + # Disable LED on BluePill + DEFS="-DHAVE_LED=0" make TARGET=BLUEPILL + + # Allow access to all Flash on MapleMini and change the app base address + DEFS="-DFLASH_SIZE_OVERRIDE=0x20000 -DAPP_BASE_ADDRESS=0x08004000" make TARGET=MAPLEMINI LDSCRIPT="/some/folder/stm32f103x8-16kb-boot.ld" + ## Using the bootloader ### Building for the bootloader -The bootloader occupies the lower 8KiB of flash, so your application must offset its flash contents by 8KiB. This can be done by modifying your linker script or flags as appropriate. +The standard bootloader occupies the lower 8KiB of flash, so your application must offset its flash contents by 8KiB. This can be done by modifying your linker script or flags as appropriate. + +The high memory bootloaders do not use the lower part of the flash, so you only need to make sure your application leaves 7 KiB of flash free. -See the [highboot branch](https://github.com/devanlai/dapboot/tree/highboot) for an experimental variation that doesn't require the application to be offset. ### Switching to the bootloader -The bootloader can be built to look for arbitrary patterns, but the default for the STM32F103 target looks for a magic value stored in the RTC backup registers. Writing the magic value and then resetting will run the bootloader instead of the main application. +The bootloader can be built to look for arbitrary patterns, but the default looks for a magic value stored in the RTC backup registers. Writing the magic value and then resetting will run the bootloader instead of the main application. + +In version v1.11 and earlier, the bootloader for STM32F103 targets looks for `0x544F` in RTC backup register 2 and `0x4F42` in RTC backup register 1 (together they spell "BOOT" in ASCII). From v1.20 on, the bootloader will only use RTC backup register 1 and check for `0x4F42` on targets with 16-bit backup registers and `0x544F4F42` on targets with 32-bit backup registers. + +The backup register and bootloader command word can be customized with the `REG_BOOT` and `CMD_BOOT` defines respectively. + +You can also use a button to stay in bootloader while booting. It's configured using `HAVE_BUTTON` define. If your button has a debounce capacitor, you can use `BUTTON_SAMPLE_DELAY_CYCLES` define to specify how many cycles to wait before sampling the I/O pin, by default it is approximately 20ms in a 72Mhz MCU. -The bootloader currently looks for `0x544F` in RTC backup register 1 and `0x4F42` in RTC backup register 0 (together they spell "BOOT" in ASCII). +On the bluepill boards, the default is using the BOOT1 input (available on jumper in the board) to enter the bootloader. ### WebUSB This bootloader implements the draft [WebUSB](https://wicg.github.io/webusb/) specification, which allows web pages to access the bootloader (after presenting the user with a device picker dialog). For a demo implementing dfu-util features in the browser, see https://devanlai.github.io/webdfu/dfu-util/ +To customize the WebUSB landing page, you can use the `LANDING_PAGE_URL` define. To set it from the command line, you can use the `DEFS` environment variable: + + DEFS='-DLANDING_PAGE_URL=\"example.com/dfu-util/\"' make + +Note that the URL scheme shoul not be part of the `LANDING_PAGE_URL` string. As of this writing, it is hardcoded to HTTPS. + +### Manifestation behavior +There are two different manifestation behaviors that can be selected using the `DFU_WILL_DETACH` define. By default, `DFU_WILL_DETACH` is set to 1 for backwards compatibility with existing behavior. + +When `DFU_WILL_DETACH` is enabled, the bootloader autonomously reboot into the new firmware after it has been successfully downloaded and passes the basic validation check. This can be helpful on platforms such as Windows where it is not possible to generate a USB bus reset to signal that the bootloader should switch to the application. + +When `DFU_WILL_DETACH` is disabled, the bootloader will return to the idle state after validating the firmware. The DFU host application can then perform other operations or reboot the target into the new firmware by issuing a DFU detach request or generating a USB bus reset. (This can be done with `dfu-util` by passing the `-R` flag). + ## USB VID/PID -The USB VID/PID pair ([1209/DB42](http://pid.codes/1209/DB42/)) is allocated through the [pid.codes](http://pid.codes/) open-source USB PID program. +The default USB VID/PID pair ([1209/DB42](http://pid.codes/1209/DB42/)) is allocated through the [pid.codes](http://pid.codes/) open-source USB PID program. + +To use a custom VID/PID pair, you need to set the macros `USB_VID` and `USB_PID`. One way to do this is by setting the `DEFS` environment variable when compiling: + + DEFS="-DUSB_VID=0x1209 -DUSB_PID=0xCAFE" make + + +## USB Vendor, Product, and Interface strings + +To customize the USB vendor, product, and interface strings that DAPBoot reports to the USB host, you may set the macros `USB_VENDOR_STRING`, `USB_PRODUCT_STRING`, and `USB_INTERFACE_STRING`, respectively. You can do this by setting the `DEFS` environment variable or including these macros in your board's `config.h` + ## Licensing All contents of the dapboot project are licensed under terms that are compatible with the terms of the GNU Lesser General Public License version 3. diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 279d8f6..0000000 --- a/circle.yml +++ /dev/null @@ -1,12 +0,0 @@ -dependencies: - override: - - ./util/install-toolchain.sh - cache_directories: - - "~/toolchains/" -test: - override: - - PREFIX="~/toolchains/gcc-arm-embedded/bin/arm-none-eabi" make -f release.Makefile -k all - -general: - artifacts: - - build/ \ No newline at end of file diff --git a/libopencm3 b/libopencm3 index 90be020..2c89ba3 160000 --- a/libopencm3 +++ b/libopencm3 @@ -1 +1 @@ -Subproject commit 90be02008e6b651ce737019b6b39ddfdf85eda6b +Subproject commit 2c89ba385cb9a9ccedfaa26d05da1680ec5de22a diff --git a/release.Makefile b/release.Makefile index 8bc500f..dbc6779 100644 --- a/release.Makefile +++ b/release.Makefile @@ -31,7 +31,21 @@ BUILD_DIR ?= ./build all: dapboot-bluepill.bin \ dapboot-maplemini.bin \ - dapboot-stlink.bin + dapboot-stlink.bin \ + dapboot-olimexstm32h103.bin \ + dapboot-bluepillplusstm32.bin \ + dapboot-bttskrminie3v2.bin \ + dapboot-bluepill-high.bin \ + dapboot-maplemini-high.bin \ + dapboot-stlink-high.bin \ + dapboot-olimexstm32h103-high.bin \ + dapboot-bluepillplusstm32-high.bin \ + dapboot-bluepill-high-128.bin \ + dapboot-maplemini-high-128.bin \ + dapboot-stlink-high-128.bin \ + dapboot-olimexstm32h103-high-128.bin \ + dapboot-bluepillplusstm32-high-128.bin \ + dapboot-bttskrminie3v2-high-256.bin clean: $(Q)$(RM) $(BUILD_DIR)/*.bin @@ -59,3 +73,87 @@ dapboot-maplemini.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=MAPLEMINI -C src/ clean $(Q)$(MAKE) TARGET=MAPLEMINI -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-olimexstm32h103.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103 -C src/ clean + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bluepillplusstm32.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32 -C src/ clean + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bttskrminie3v2.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2 -C src/ clean + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bluepill-high.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BLUEPILL_HIGH -C src/ clean + $(Q)$(MAKE) TARGET=BLUEPILL_HIGH -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-stlink-high.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=STLINK_HIGH -C src/ clean + $(Q)$(MAKE) TARGET=STLINK_HIGH -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-maplemini-high.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH -C src/ clean + $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-olimexstm32h103-high.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH -C src/ clean + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bluepillplusstm32-high.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32_HIGH -C src/ clean + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32_HIGH -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bluepill-high-128.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BLUEPILL_HIGH_128 -C src/ clean + $(Q)$(MAKE) TARGET=BLUEPILL_HIGH_128 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-stlink-high-128.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=STLINK_HIGH_128 -C src/ clean + $(Q)$(MAKE) TARGET=STLINK_HIGH_128 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-maplemini-high-128.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH_128 -C src/ clean + $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH_128 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-olimexstm32h103-high-128.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH_128 -C src/ clean + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH_128 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bluepillplusstm32-high-128.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32_HIGH_128 -C src/ clean + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32_HIGH_128 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bttskrminie3v2-high-256.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_HIGH_256 -C src/ clean + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_HIGH_256 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) diff --git a/src/Makefile b/src/Makefile index e1b10dd..caff1b0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -38,13 +38,16 @@ clean:: include rules.mk size: $(OBJS) $(BINARY).elf - @$(PREFIX)-size $(OBJS) $(BINARY).elf + @$(PREFIX)size $(OBJS) $(BINARY).elf debug: $(BINARY).elf -$(GDB) --tui --eval "target remote | $(OOCD) -f $(OOCD_INTERFACE) -f $(OOCD_BOARD) -f debug.cfg" $(BINARY).elf erase: - $(OOCD) -f $(OOCD_INTERFACE) -f $(OOCD_BOARD) -c "init" -c "reset init" -c "flash erase_sector 0 0 last" -c "reset" -c "shutdown" + $(OOCD) -f $(OOCD_INTERFACE) -f $(OOCD_BOARD) \ + -c "init" -c "reset init" \ + -c "stm32f1x unlock 0; reset halt" \ + -c "flash erase_sector 0 0 last" -c "reset" -c "shutdown" .PHONY += debug size erase diff --git a/src/dapboot.c b/src/dapboot.c index a43a524..2b66151 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -21,39 +21,36 @@ #include "dapboot.h" #include "target.h" +#include "config.h" #include "usb_conf.h" #include "dfu.h" #include "webusb.h" #include "winusb.h" -#include "config.h" static inline void __set_MSP(uint32_t topOfMainStack) { asm("msr msp, %0" : : "r" (topOfMainStack)); } bool validate_application(void) { - if ((*(volatile uint32_t *)APP_BASE_ADDRESS & 0x2FFE0000) == 0x20000000) { - return true; - } - return false; + return ((uint32_t)(APP_INITIAL_STACK) & 0x2FFE0000) == 0x20000000; } static void jump_to_application(void) __attribute__ ((noreturn)); static void jump_to_application(void) { - vector_table_t* app_vector_table = (vector_table_t*)APP_BASE_ADDRESS; - + /* Use the application's vector table */ - target_relocate_vector_table(); + if (APP_RELOCATE_VECTORS) + target_relocate_vector_table(); /* Do any necessary early setup for the application */ target_pre_main(); /* Initialize the application's stack pointer */ - __set_MSP((uint32_t)(app_vector_table->initial_sp_value)); + __set_MSP((uint32_t)(APP_INITIAL_STACK)); /* Jump to the application entry point */ - app_vector_table->reset(); + APP_ENTRY_POINT(); while (1); } @@ -75,10 +72,11 @@ int main(void) { } usbd_device* usbd_dev = usb_setup(); - dfu_setup(usbd_dev, &target_manifest_app, NULL, NULL); + dfu_setup(usbd_dev, validate_application, NULL, NULL); webusb_setup(usbd_dev); winusb_setup(usbd_dev); - + target_post_setup(); + while (1) { usbd_poll(usbd_dev); } diff --git a/src/dapboot.h b/src/dapboot.h index 6c9e3ee..121571c 100644 --- a/src/dapboot.h +++ b/src/dapboot.h @@ -21,4 +21,22 @@ extern bool validate_application(void); +#ifdef BOOTLOADER_HIGH +/* Definitions for high memory bootloader */ +#define APP_INITIAL_STACK vector_table.reserved_x001c[0] +#define APP_ENTRY_POINT vector_table.reserved_x001c[1] +#define APP_RELOCATE_VECTORS 0 +#define DFU_PATCH_VECTORS 1 +#define BOOTLOADER_OFFSET 0 + +#else +/* Definitions for low memory (standard) bootloader */ +#define APP_INITIAL_STACK ((vector_table_t*)APP_BASE_ADDRESS)->initial_sp_value +#define APP_ENTRY_POINT ((vector_table_t*)APP_BASE_ADDRESS)->reset +#define APP_RELOCATE_VECTORS 1 +#define DFU_PATCH_VECTORS 0 +#define BOOTLOADER_OFFSET 0x00002000 + +#endif + #endif diff --git a/src/dfu.c b/src/dfu.c index 7c2a107..b8fa42f 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -19,24 +19,31 @@ #include #include +#include +#include #include #include -#include "dfu.h" +#include "config.h" #include "usb_conf.h" +#include "dfu.h" #include "dfu_defs.h" #include "target.h" #include "dapboot.h" -#include "config.h" + +#ifndef TARGET_DFU_WTRANSFERSIZE +#define TARGET_DFU_WTRANSFERSIZE USB_CONTROL_BUF_SIZE +#endif const struct usb_dfu_descriptor dfu_function = { .bLength = sizeof(struct usb_dfu_descriptor), .bDescriptorType = DFU_FUNCTIONAL, .bmAttributes = ((DFU_DOWNLOAD_AVAILABLE ? USB_DFU_CAN_DOWNLOAD : 0) | (DFU_UPLOAD_AVAILABLE ? USB_DFU_CAN_UPLOAD : 0) | - USB_DFU_WILL_DETACH ), + (DFU_WILL_DETACH ? 0 : USB_DFU_MANIFEST_TOLERANT) | + (DFU_WILL_DETACH ? USB_DFU_WILL_DETACH : 0)), .wDetachTimeout = 255, - .wTransferSize = USB_CONTROL_BUF_SIZE, + .wTransferSize = TARGET_DFU_WTRANSFERSIZE, .bcdDFUVersion = 0x0110, }; @@ -44,11 +51,13 @@ static enum dfu_state current_dfu_state; static enum dfu_status current_dfu_status; static size_t current_dfu_offset; +static bool manifestation_complete = false; + static uint8_t dfu_download_buffer[USB_CONTROL_BUF_SIZE]; static size_t dfu_download_size; /* User callbacks */ -static GenericCallback dfu_manifest_request_callback = NULL; +static ManifestationCallback dfu_manifest_request_callback = NULL; static StateChangeCallback dfu_state_change_callback = NULL; static StatusChangeCallback dfu_status_change_callback = NULL; @@ -84,16 +93,42 @@ static inline void dfu_set_status(enum dfu_status status) { static void dfu_on_download_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; - + dfu_set_state(STATE_DFU_MANIFEST_SYNC); } -extern unsigned _stack; +static void dfu_on_detach_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { + (void)usbd_dev; + (void)req; + + /* Run the target-specific pre-detach hook before resetting */ + target_pre_detach(manifestation_complete); + + /* Reset and maybe launch the application */ + scb_reset_system(); +} static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; + if (DFU_PATCH_VECTORS && current_dfu_offset == 0) { + if (dfu_download_size < offsetof(vector_table_t, reserved_x001c[1])) { + /* Can't handle splitting the vector table right now */ + dfu_set_status(DFU_STATUS_ERR_VENDOR); + } else { + vector_table_t* app_vector_table = (vector_table_t*)dfu_download_buffer; + /* Stash the application's initial stack value and reset + pointer in unused vector table entries */ + app_vector_table->reserved_x001c[0] = (vector_table_entry_t)(app_vector_table->initial_sp_value); + app_vector_table->reserved_x001c[1] = app_vector_table->reset; + /* Overwrite the stack and reset pointer to run the + bootloader instead */ + app_vector_table->initial_sp_value = &_stack; + app_vector_table->reset = reset_handler; + } + } + const uint16_t* data = (uint16_t*)dfu_download_buffer; uint16_t* dest = (uint16_t*)(APP_BASE_ADDRESS + current_dfu_offset); @@ -114,22 +149,40 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; + if (dfu_manifest_request_callback) { - dfu_manifest_request_callback(); + /* The manifestation callback returns a boolean indicating if it succeeded */ + manifestation_complete = dfu_manifest_request_callback(); } else { - dfu_set_status(DFU_STATUS_ERR_UNKNOWN); + /* Assume manifestation success */ + manifestation_complete = true; } + + if (manifestation_complete) { + dfu_set_state(STATE_DFU_MANIFEST_SYNC); + } else { + dfu_set_status(DFU_STATUS_ERR_FIRMWARE); + return; /* Avoid resetting on error */ + } + +#if DFU_WILL_DETACH + /* DFU_WILL_DETACH being enabled equates to transitioning to the dfuMANIFEST-WAIT-RESET state, + * which combined with bitWillDetach being set with DFU_WILL_DETACH means that the device should + * generate a detach-attach sequence and enter the application, i.e. reset itself, here */ + dfu_on_detach_complete(NULL, NULL); +#endif } -static int dfu_control_class_request(usbd_device *usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, uint16_t *len, - usbd_control_complete_callback* complete) { +static enum usbd_request_return_codes +dfu_control_class_request(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len, + usbd_control_complete_callback* complete) { if (req->wIndex != INTF_DFU) { return USBD_REQ_NEXT_CALLBACK; } - int status = USBD_REQ_HANDLED; + enum usbd_request_return_codes status = USBD_REQ_HANDLED; switch (req->bRequest) { case DFU_GETSTATE: { struct dfu_getstate_response* resp; @@ -146,16 +199,22 @@ static int dfu_control_class_request(usbd_device *usbd_dev, #if DFU_DOWNLOAD_AVAILABLE case STATE_DFU_DNLOAD_SYNC: { dfu_set_state(STATE_DFU_DNBUSY); - bwPollTimeout = 100; + bwPollTimeout = target_get_timeout(); *complete = &dfu_on_download_request; break; } case STATE_DFU_MANIFEST_SYNC: { - if (validate_application()) { + /* According to the DFU spec the dfuMANIFEST-SYNC state is entered twice, + * once after the download completes, and again after manifestation if + * the device is manifestation tolerant (DFU_WILL_DETACH == 0) */ + if (manifestation_complete) { + /* Only enter idle state after manifestation has completed successfully */ + dfu_set_state(STATE_DFU_IDLE); + } else { + /* Perform manifestation after download as described in the + * spec regardless of if DFU_WILL_DETACH is enabled or not */ dfu_set_state(STATE_DFU_MANIFEST); *complete = &dfu_on_manifest_request; - } else { - dfu_set_status(DFU_STATUS_ERR_FIRMWARE); } break; } @@ -186,6 +245,8 @@ static int dfu_control_class_request(usbd_device *usbd_dev, dfu_download_size = req->wLength; memcpy(dfu_download_buffer, *buf, dfu_download_size); dfu_set_state(STATE_DFU_DNLOAD_SYNC); + /* Reset manifestation progress on new download */ + manifestation_complete = false; } else { dfu_set_status(DFU_STATUS_ERR_STALLEDPKT); usbd_ep_stall_set(usbd_dev, 0x00, 1); @@ -231,9 +292,9 @@ static int dfu_control_class_request(usbd_device *usbd_dev, case STATE_DFU_IDLE: { current_dfu_offset = 0; /* Fall through */ + __attribute__ ((fallthrough)); } case STATE_DFU_UPLOAD_IDLE: { - *buf = (uint8_t*)(APP_BASE_ADDRESS + current_dfu_offset); uint16_t len_to_copy = req->wLength; size_t max_firmware_size = target_get_max_firmware_size(); if (current_dfu_offset + req->wLength > max_firmware_size) { @@ -243,6 +304,23 @@ static int dfu_control_class_request(usbd_device *usbd_dev, dfu_set_state(STATE_DFU_UPLOAD_IDLE); } *len = len_to_copy; + if (DFU_PATCH_VECTORS && current_dfu_offset < sizeof(vector_table_t)) { + /* Copy the flash memory to the download buffer, to + undo the vector modifications. */ + memcpy(dfu_download_buffer, (const void*)(APP_BASE_ADDRESS), + sizeof(dfu_download_buffer)); + vector_table_t* app_vector_table = (vector_table_t*)dfu_download_buffer; + /* Put the original stack pointer and reset vectors + back */ + app_vector_table->initial_sp_value = (unsigned int*)(app_vector_table->reserved_x001c[0]); + app_vector_table->reset = app_vector_table->reserved_x001c[1]; + app_vector_table->reserved_x001c[0] = 0; + app_vector_table->reserved_x001c[1] = 0; + /* Return the correct pointer */ + *buf = dfu_download_buffer + current_dfu_offset; + } else { + *buf = (uint8_t*)(APP_BASE_ADDRESS + current_dfu_offset); + } current_dfu_offset += len_to_copy; break; } @@ -253,7 +331,13 @@ static int dfu_control_class_request(usbd_device *usbd_dev, break; } #endif - case DFU_DETACH: + + case DFU_DETACH: { + *complete = &dfu_on_detach_complete; + status = USBD_REQ_HANDLED; + break; + } + default: { /* Stall the control pipe */ dfu_set_status(DFU_STATUS_ERR_STALLEDPKT); @@ -266,6 +350,10 @@ static int dfu_control_class_request(usbd_device *usbd_dev, return status; } +/* Track dfu enumeration status to distinguish between the first USB + reset after bootup versus a subsequent USB reset and re-enumeration */ +static bool dfu_enumerated = false; + static void dfu_set_config(usbd_device* usbd_dev, uint16_t wValue) { (void)wValue; @@ -274,16 +362,32 @@ static void dfu_set_config(usbd_device* usbd_dev, uint16_t wValue) { USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, dfu_control_class_request); + + dfu_enumerated = true; +} + +static void dfu_on_usb_reset(void) { + /* Ignore all USB resets until the DFU control callback has been registered, since + * reset callback will fire once as the USB connection is established. Without this + * the target enters a reset loop when trying to enter the bootloader. */ + if (!dfu_enumerated) { + return; + } + + /* Perform a DFU detach (which resets the target), this enables issuing a USB bus + * reset as an alternative means to submitting a DFU_DETACH command post-download. */ + dfu_on_detach_complete(NULL, NULL); } void dfu_setup(usbd_device* usbd_dev, - GenericCallback on_manifest_request, + ManifestationCallback on_manifest_request, StateChangeCallback on_state_change, StatusChangeCallback on_status_change) { dfu_manifest_request_callback = on_manifest_request; dfu_state_change_callback = on_state_change; dfu_status_change_callback = on_status_change; + usbd_register_reset_callback(usbd_dev, dfu_on_usb_reset); usbd_register_set_config_callback(usbd_dev, dfu_set_config); current_dfu_state = STATE_DFU_IDLE; current_dfu_status = DFU_STATUS_OK; diff --git a/src/dfu.h b/src/dfu.h index 9d53520..d447b22 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -22,14 +22,19 @@ #include #include +// For WebUSB compatibility +#ifndef DFU_WILL_DETACH +#define DFU_WILL_DETACH 1 +#endif + extern const struct usb_dfu_descriptor dfu_function; -typedef void (*GenericCallback)(void); +typedef bool (*ManifestationCallback)(void); typedef void (*StateChangeCallback)(enum dfu_state); typedef void (*StatusChangeCallback)(enum dfu_status); extern void dfu_setup(usbd_device* usbd_dev, - GenericCallback on_detach_request, + ManifestationCallback on_manifest_request, StateChangeCallback on_state_change, StatusChangeCallback on_status_change); diff --git a/src/dummy.c b/src/dummy.c index 6bf49fc..25e0f2b 100644 --- a/src/dummy.c +++ b/src/dummy.c @@ -24,8 +24,10 @@ void target_get_serial_number(char* dest, size_t max_chars) __attribute__((weak)); void target_log(const char* str) __attribute__((weak)); -void target_manifest_app(void) __attribute__((weak)); void target_pre_main(void) __attribute__((weak)); +void target_pre_detach(bool manifested) __attribute__((weak)); +void target_post_setup(void) __attribute__((weak)); +size_t target_get_timeout(void) __attribute__((weak)); void target_get_serial_number(char* dest, size_t max_chars) { (void)max_chars; @@ -38,11 +40,27 @@ void target_log(const char* str) { (void)str; } -void target_manifest_app(void) { - scb_reset_system(); +void target_pre_main(void) +{ + } -void target_pre_main(void) +void target_post_setup(void) { + /* This runs just before starting to listen to USB */ +} + +void target_pre_detach(bool manifested) { + /* This runs just before executing a reboot in response to a USB bus reset + or a detach request. + If new firmware was successfully downloaded, manifested is set to true. + This can be used to set flags or blink LEDs before rebooting. + */ + (void)manifested; +} + +size_t target_get_timeout(void) +{ + return 100; } diff --git a/src/rules.mk b/src/rules.mk index b139e4f..9fe945e 100644 --- a/src/rules.mk +++ b/src/rules.mk @@ -34,6 +34,7 @@ ifeq ($(ARCH),STM32F0) FP_FLAGS ?= -msoft-float ARCH_FLAGS = -mthumb -mcpu=cortex-m0 $(FP_FLAGS) OOCD_BOARD ?= target/stm32f0x.cfg + OPENCM3_TARGET = "stm32/f0" endif ifeq ($(ARCH),STM32F1) LIBNAME = opencm3_stm32f1 @@ -41,12 +42,22 @@ ifeq ($(ARCH),STM32F1) FP_FLAGS ?= -msoft-float ARCH_FLAGS = -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd OOCD_BOARD ?= target/stm32f1x.cfg + OPENCM3_TARGET = "stm32/f1" +endif +ifeq ($(ARCH),STM32L1) + LIBNAME = opencm3_stm32l1 + DEFS += -DSTM32L1 + FP_FLAGS ?= -msoft-float + ARCH_FLAGS = -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd + OOCD_BOARD ?= target/stm32l1.cfg + OPENCM3_TARGET = "stm32/l1" endif LIBNAME ?= opencm3_stm32f0 DEFS ?= -DSTM32F0 FP_FLAGS ?= -msoft-float ARCH_FLAGS ?= -mthumb -mcpu=cortex-m0 $(FP_FLAGS) +OPENCM3_TARGET ?= "stm32/f0" #################################################################### # Semihosting support @@ -70,16 +81,16 @@ OOCD_BOARD ?= target/stm32f1x.cfg #################################################################### # Executables -PREFIX ?= arm-none-eabi +PREFIX ?= arm-none-eabi- -CC := $(PREFIX)-gcc -CXX := $(PREFIX)-g++ -LD := $(PREFIX)-gcc -AR := $(PREFIX)-ar -AS := $(PREFIX)-as -OBJCOPY := $(PREFIX)-objcopy -OBJDUMP := $(PREFIX)-objdump -GDB := $(PREFIX)-gdb +CC := $(PREFIX)gcc +CXX := $(PREFIX)g++ +LD := $(PREFIX)gcc +AR := $(PREFIX)gcc-ar +AS := $(PREFIX)as +OBJCOPY := $(PREFIX)objcopy +OBJDUMP := $(PREFIX)objdump +GDB := $(PREFIX)gdb STFLASH = $(shell which st-flash) #################################################################### @@ -91,7 +102,7 @@ LIB_DIR = $(OPENCM3_DIR)/lib #################################################################### # C flags -CFLAGS += -Os -g -std=gnu11 +CFLAGS += -Os -flto -g -std=gnu11 CFLAGS += -Wextra -Wshadow -Wimplicit-function-declaration CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes CFLAGS += -fno-common -ffunction-sections -fdata-sections @@ -99,7 +110,7 @@ CFLAGS += -fno-common -ffunction-sections -fdata-sections #################################################################### # C++ flags -CXXFLAGS += -Os -g +CXXFLAGS += -Os -flto -g CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++ CXXFLAGS += -fno-common -ffunction-sections -fdata-sections @@ -113,7 +124,8 @@ CPPFLAGS += -I$(INCLUDE_DIR) $(DEFS) #################################################################### # Linker flags -LDFLAGS += --static -nostartfiles +LDFLAGS += -flto -Os -g --static -nostartfiles +LDFLAGS += -specs=nano.specs LDFLAGS += -L$(LIB_DIR) LDFLAGS += -T$(LDSCRIPT) LDFLAGS += -Wl,-Map=$(*).map @@ -155,8 +167,11 @@ $(LDSCRIPT): $(OPENCM3_DIR)/Makefile: $(Q)git submodule update --init $(OPENCM3_DIR) +# If overriding the toolchain, use it when building LOCM3 +export PREFIX + $(LIB_DIR)/lib$(LIBNAME).a: $(OPENCM3_DIR)/Makefile - $(Q)$(MAKE) -C $(OPENCM3_DIR) + $(Q)$(MAKE) -C $(OPENCM3_DIR) AR=$(AR) CFLAGS="-flto -g" TARGETS=$(OPENCM3_TARGET) locm3: $(LIB_DIR)/lib$(LIBNAME).a @@ -165,7 +180,7 @@ locm3: $(LIB_DIR)/lib$(LIBNAME).a %.bin: %.elf @#printf " OBJCOPY $(*).bin\n" - $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin + $(Q)$(OBJCOPY) -Obinary --gap-fill=0xff $(*).elf $(*).bin %.hex: %.elf @#printf " OBJCOPY $(*).hex\n" diff --git a/src/stm32f103/backup.c b/src/stm32f103/backup.c index 18a4c20..1137568 100644 --- a/src/stm32f103/backup.c +++ b/src/stm32f103/backup.c @@ -24,18 +24,15 @@ #define RTC_BKP_DR(reg) MMIO16(BACKUP_REGS_BASE + 4 + (4 * (reg))) -void backup_write(enum BackupRegister reg, uint32_t value) { +void backup_write(enum BackupRegister reg, uint16_t value) { rcc_periph_clock_enable(RCC_PWR); rcc_periph_clock_enable(RCC_BKP); pwr_disable_backup_domain_write_protect(); - RTC_BKP_DR((int)reg*2) = value & 0xFFFFUL; - RTC_BKP_DR((int)reg*2+1) = (value & 0xFFFF0000UL) >> 16; + RTC_BKP_DR((int)reg) = value; pwr_enable_backup_domain_write_protect(); } -uint32_t backup_read(enum BackupRegister reg) { - uint32_t value = ((uint32_t)RTC_BKP_DR((int)reg*2+1) << 16) - | ((uint32_t)RTC_BKP_DR((int)reg*2) << 0); - return value; +uint16_t backup_read(enum BackupRegister reg) { + return RTC_BKP_DR((int)reg); } diff --git a/src/stm32f103/backup.h b/src/stm32f103/backup.h index c2fe2b0..5e265a9 100644 --- a/src/stm32f103/backup.h +++ b/src/stm32f103/backup.h @@ -20,14 +20,19 @@ #define BACKUP_H_INCLUDED enum BackupRegister { - BKP0 = 0, BKP1, BKP2, BKP3, BKP4, + BKP5, + BKP6, + BKP7, + BKP8, + BKP9, + BKP10, }; -extern void backup_write(enum BackupRegister reg, uint32_t value); -extern uint32_t backup_read(enum BackupRegister reg); +extern void backup_write(enum BackupRegister reg, uint16_t value); +extern uint16_t backup_read(enum BackupRegister reg); #endif diff --git a/src/stm32f103/bluepill/config.h b/src/stm32f103/bluepill/config.h new file mode 100644 index 0000000..1f64b7e --- /dev/null +++ b/src/stm32f103/bluepill/config.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016, Devan Lai + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_SIZE_OVERRIDE +#define FLASH_SIZE_OVERRIDE 0x20000 +#endif +#ifndef FLASH_PAGE_SIZE +#define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif + +#ifndef HAVE_LED +#define HAVE_LED 1 +#endif +#ifndef LED_OPEN_DRAIN +#define LED_OPEN_DRAIN 1 +#endif +#ifndef LED_GPIO_PORT +#define LED_GPIO_PORT GPIOC +#endif +#ifndef LED_GPIO_PIN +#define LED_GPIO_PIN GPIO13 +#endif + +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 1 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOB +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO2 +#endif +// Blue-Pull has 100k resistors on PB2, so we can't use weak pulls to read it. +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 0 +#endif + +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 0 +#endif + +#ifndef USES_GPIOB +#define USES_GPIOB 1 +#endif + +#ifndef USES_GPIOC +#define USES_GPIOC 1 +#endif + +#endif diff --git a/src/stm32f103/bluepillplus-gd32/config.h b/src/stm32f103/bluepillplus-gd32/config.h new file mode 100644 index 0000000..0b1c84d --- /dev/null +++ b/src/stm32f103/bluepillplus-gd32/config.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016, Devan Lai + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_SIZE_OVERRIDE +#define FLASH_SIZE_OVERRIDE 0x20000 +#endif +#ifndef FLASH_PAGE_SIZE +#define FLASH_PAGE_SIZE 2048 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif + +#ifndef HAVE_LED +#define HAVE_LED 1 +#endif +#ifndef LED_OPEN_DRAIN +#define LED_OPEN_DRAIN 0 +#endif +#ifndef LED_GPIO_PORT +#define LED_GPIO_PORT GPIOB +#endif +#ifndef LED_GPIO_PIN +#define LED_GPIO_PIN GPIO2 +#endif + +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 1 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOA +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO0 +#endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 1 +#endif +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 0 +#endif + +#ifndef USES_GPIOA +#define USES_GPIOA 1 +#endif +#ifndef USES_GPIOB +#define USES_GPIOB 1 +#endif + +#endif diff --git a/src/stm32f103/bluepillplus/config.h b/src/stm32f103/bluepillplus/config.h new file mode 100644 index 0000000..a50579f --- /dev/null +++ b/src/stm32f103/bluepillplus/config.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016, Devan Lai + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_SIZE_OVERRIDE +#define FLASH_SIZE_OVERRIDE 0x20000 +#endif +#ifndef FLASH_PAGE_SIZE +#define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif + +#ifndef HAVE_LED +#define HAVE_LED 1 +#endif +#ifndef LED_OPEN_DRAIN +#define LED_OPEN_DRAIN 0 +#endif +#ifndef LED_GPIO_PORT +#define LED_GPIO_PORT GPIOB +#endif +#ifndef LED_GPIO_PIN +#define LED_GPIO_PIN GPIO2 +#endif + +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 1 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOA +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO0 +#endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 1 +#endif +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 0 +#endif + +#ifndef USES_GPIOA +#define USES_GPIOA 1 +#endif +#ifndef USES_GPIOB +#define USES_GPIOB 1 +#endif + +#endif diff --git a/src/stm32f103/generic/config.h b/src/stm32f103/generic/config.h index 9588a6f..8b526e5 100644 --- a/src/stm32f103/generic/config.h +++ b/src/stm32f103/generic/config.h @@ -19,19 +19,51 @@ #ifndef CONFIG_H_INCLUDED #define CONFIG_H_INCLUDED -#define APP_BASE_ADDRESS 0x08002000 +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_SIZE_OVERRIDE #define FLASH_SIZE_OVERRIDE 0x20000 +#endif +#ifndef FLASH_PAGE_SIZE #define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE #define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE #define DFU_DOWNLOAD_AVAILABLE 1 +#endif #ifndef HAVE_LED #define HAVE_LED 0 #endif +// #ifndef LED_OPEN_DRAIN +// #define LED_OPEN_DRAIN 0 +// #endif +// #ifndef LED_GPIO_PORT +// #define LED_GPIO_PORT GPIOA +// #endif +// #ifndef LED_GPIO_PIN +// #define LED_GPIO_PIN GPIO0 +// #endif #ifndef HAVE_BUTTON #define HAVE_BUTTON 0 #endif +// #ifndef BUTTON_ACTIVE_HIGH +// #define BUTTON_ACTIVE_HIGH 0 +// #endif +// #ifndef BUTTON_GPIO_PORT +// #define BUTTON_GPIO_PORT GPIOA +// #endif +// #ifndef BUTTON_GPIO_PIN +// #define BUTTON_GPIO_PIN GPIO0 +// #endif + +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif #ifndef HAVE_USB_PULLUP_CONTROL #define HAVE_USB_PULLUP_CONTROL 0 diff --git a/src/stm32f103/maplemini/config.h b/src/stm32f103/maplemini/config.h index 1fae49f..bdbb226 100644 --- a/src/stm32f103/maplemini/config.h +++ b/src/stm32f103/maplemini/config.h @@ -19,31 +19,65 @@ #ifndef CONFIG_H_INCLUDED #define CONFIG_H_INCLUDED -#define APP_BASE_ADDRESS 0x08002000 +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_PAGE_SIZE #define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE #define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE #define DFU_DOWNLOAD_AVAILABLE 1 +#endif +#ifndef HAVE_LED #define HAVE_LED 1 +#endif +#ifndef LED_GPIO_PORT #define LED_GPIO_PORT GPIOB +#endif +#ifndef LED_GPIO_PIN #define LED_GPIO_PIN GPIO1 +#endif +#ifndef LED_OPEN_DRAIN #define LED_OPEN_DRAIN 0 +#endif /* Technically, there is a button on PB8, but the button is also shorted to BOOT0, so it's not very useful for us to sample PB8 on boot, since pulling it high will already trigger the ROM serial bootloader and prevent us from running anyways. */ +#ifndef HAVE_BUTTON #define HAVE_BUTTON 0 +#endif +#ifndef HAVE_USB_PULLUP_CONTROL #define HAVE_USB_PULLUP_CONTROL 1 +#endif +#ifndef USB_PULLUP_GPIO_PORT #define USB_PULLUP_GPIO_PORT GPIOB +#endif +#ifndef USB_PULLUP_GPIO_PIN #define USB_PULLUP_GPIO_PIN GPIO9 +#endif +#ifndef USB_PULLUP_ACTIVE_HIGH #define USB_PULLUP_ACTIVE_HIGH 0 +#endif +#ifndef USB_PULLUP_OPEN_DRAIN #define USB_PULLUP_OPEN_DRAIN 1 +#endif +#ifndef USES_GPIOA #define USES_GPIOA 0 +#endif +#ifndef USES_GPIOB #define USES_GPIOB 1 +#endif +#ifndef USES_GPIOC #define USES_GPIOC 0 +#endif #endif diff --git a/src/stm32f103/olimexstm32h103/config.h b/src/stm32f103/olimexstm32h103/config.h new file mode 100644 index 0000000..23baf06 --- /dev/null +++ b/src/stm32f103/olimexstm32h103/config.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021, Jean THOMAS + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_PAGE_SIZE +#define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif + +#ifndef HAVE_LED +#define HAVE_LED 1 +#endif +#ifndef LED_GPIO_PORT +#define LED_GPIO_PORT GPIOC +#endif +#ifndef LED_GPIO_PIN +#define LED_GPIO_PIN GPIO12 +#endif +#ifndef LED_OPEN_DRAIN +#define LED_OPEN_DRAIN 1 +#endif + +/* "BUT" button on on PA0, pulled high by R18 */ +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 0 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOA +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO0 +#endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 0 +#endif +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 1 +#endif +#ifndef USB_PULLUP_GPIO_PORT +#define USB_PULLUP_GPIO_PORT GPIOC +#endif +#ifndef USB_PULLUP_GPIO_PIN +#define USB_PULLUP_GPIO_PIN GPIO11 +#endif +#ifndef USB_PULLUP_ACTIVE_HIGH +#define USB_PULLUP_ACTIVE_HIGH 0 +#endif +#ifndef USB_PULLUP_OPEN_DRAIN +#define USB_PULLUP_OPEN_DRAIN 1 +#endif + +#ifndef USES_GPIOA +#define USES_GPIOA 1 +#endif +#ifndef USES_GPIOB +#define USES_GPIOB 0 +#endif +#ifndef USES_GPIOC +#define USES_GPIOC 1 +#endif + +#endif diff --git a/src/stm32f103/skrminie3v2/config.h b/src/stm32f103/skrminie3v2/config.h new file mode 100644 index 0000000..8ebc381 --- /dev/null +++ b/src/stm32f103/skrminie3v2/config.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021, Dennis Marttinen + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_PAGE_SIZE +/* The BTT SKR MINI E3 V2.0 uses an STM32F103RC with 256 KiB of flash and 2 KiB pages */ +#define FLASH_PAGE_SIZE 2048 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif +#ifndef DFU_WILL_DETACH +#define DFU_WILL_DETACH 0 +#endif + +/* STATUS LED */ +#ifndef HAVE_LED +#define HAVE_LED 1 +#endif +#ifndef LED_OPEN_DRAIN +#define LED_OPEN_DRAIN 0 +#endif +#ifndef LED_GPIO_PORT +#define LED_GPIO_PORT GPIOA +#endif +#ifndef LED_GPIO_PIN +#define LED_GPIO_PIN GPIO13 +#endif + +/* Display encoder button (BTN-ENC) on PA15, no external pullup */ +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 0 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOA +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO15 +#endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 1 +#endif +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 1 +#endif +#ifndef USB_PULLUP_GPIO_PORT +#define USB_PULLUP_GPIO_PORT GPIOA +#endif +#ifndef USB_PULLUP_GPIO_PIN +#define USB_PULLUP_GPIO_PIN GPIO14 +#endif +#ifndef USB_PULLUP_ACTIVE_HIGH +#define USB_PULLUP_ACTIVE_HIGH 0 +#endif +#ifndef USB_PULLUP_OPEN_DRAIN +#define USB_PULLUP_OPEN_DRAIN 1 +#endif + +#ifndef USES_GPIOA +#define USES_GPIOA 1 +#endif + +/* For stm32duino bootloader compatibility, the following options enable bootloader flashing using Klipper: + * https://github.com/Klipper3d/klipper/blob/6d48adf9ef5d17632acf53a7e3a07964f6cfd642/src/stm32/stm32f1.c#L238 */ +#ifndef REG_BOOT +#define REG_BOOT BKP10 +#endif + +#ifndef CMD_BOOT +#define CMD_BOOT 1 +#endif + +#ifndef USB_DFU_ALTN +#define USB_DFU_ALTN 2 +#endif + +#endif diff --git a/src/stm32f103/stlink/config.h b/src/stm32f103/stlink/config.h index c55ef5b..c72f671 100644 --- a/src/stm32f103/stlink/config.h +++ b/src/stm32f103/stlink/config.h @@ -19,21 +19,45 @@ #ifndef CONFIG_H_INCLUDED #define CONFIG_H_INCLUDED -#define APP_BASE_ADDRESS 0x08002000 +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_SIZE_OVERRIDE #define FLASH_SIZE_OVERRIDE 0x20000 +#endif +#ifndef FLASH_PAGE_SIZE #define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE #define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE #define DFU_DOWNLOAD_AVAILABLE 1 +#endif +#ifndef HAVE_LED #define HAVE_LED 1 +#endif +#ifndef LED_GPIO_PORT #define LED_GPIO_PORT GPIOA +#endif +#ifndef LED_GPIO_PIN #define LED_GPIO_PIN GPIO9 +#endif +#ifndef LED_OPEN_DRAIN #define LED_OPEN_DRAIN 0 +#endif +#ifndef HAVE_BUTTON #define HAVE_BUTTON 0 +#endif +#ifndef HAVE_USB_PULLUP_CONTROL #define HAVE_USB_PULLUP_CONTROL 0 +#endif +#ifndef USES_GPIOA #define USES_GPIOA 1 +#endif #endif diff --git a/src/stm32f103/stm32f1.ld b/src/stm32f103/stm32f1.ld new file mode 100644 index 0000000..e209cbc --- /dev/null +++ b/src/stm32f103/stm32f1.ld @@ -0,0 +1,109 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Generic linker script for STM32 targets using libopencm3. */ + +/* Memory regions must be defined in the ld script which includes this one. */ + +/* Enforce emmition of the vector table. */ +EXTERN (vector_table) + +/* Define the entry point of the output file. */ +ENTRY(reset_handler) + +/* Define sections. */ +SECTIONS +{ + .vectors : { + *(.vectors) /* Vector table */ + } >vectors + + .text : { + *(.text*) /* Program code */ + . = ALIGN(4); + *(.rodata*) /* Read-only data */ + . = ALIGN(4); + } >rom + + /* C++ Static constructors/destructors, also used for __attribute__ + * ((constructor)) and the likes */ + .preinit_array : { + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } >rom + .init_array : { + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } >rom + .fini_array : { + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + } >rom + + /* + * Another section used by C++ stuff, appears when using newlib with + * 64bit (long long) printf support + */ + .ARM.extab : { + *(.ARM.extab*) + } >rom + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >rom + + . = ALIGN(4); + _etext = .; + + .data : { + _data = .; + *(.data*) /* Read-write initialized data */ + . = ALIGN(4); + _edata = .; + } >ram AT >rom + _data_loadaddr = LOADADDR(.data); + + .bss : { + *(.bss*) /* Read-write zero initialized data */ + *(COMMON) + . = ALIGN(4); + _ebss = .; + } >ram + + /* + * The .eh_frame section appears to be used for C++ exception handling. + * You may need to fix this if you're using C++. + */ + /DISCARD/ : { *(.eh_frame) } + + . = ALIGN(4); + end = .; +} + +PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); + diff --git a/src/stm32f103/stm32f103x8.ld b/src/stm32f103/stm32f103x8.ld index 68645d8..5bf2870 100644 --- a/src/stm32f103/stm32f103x8.ld +++ b/src/stm32f103/stm32f103x8.ld @@ -17,15 +17,17 @@ * along with this library. If not, see . */ -/* Linker script for STM32F103x8, 64k flash, 20k RAM. */ +/* Linker script for STM32F103x8, 64k flash, 20k RAM. + * This script also works for the STM32F103xB and the STM32F103xC, + * as the bootloader only uses the first 8kB of flash. */ /* Define memory regions. */ -/* 8k for the bootloader */ MEMORY { - rom (rx) : ORIGIN = 0x08000000, LENGTH = 8K + vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 + rom (rx) : ORIGIN = 0x08000150, LENGTH = 0x1EB0 ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K } /* Include the common ld script. */ -INCLUDE libopencm3_stm32f1.ld +INCLUDE stm32f103/stm32f1.ld diff --git a/src/stm32f103/stm32f103x8_high.ld b/src/stm32f103/stm32f103x8_high.ld new file mode 100644 index 0000000..9e9c124 --- /dev/null +++ b/src/stm32f103/stm32f103x8_high.ld @@ -0,0 +1,31 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2015 Karl Palsson + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for STM32F103x8, 64k flash, 20k RAM. */ + +/* Define memory regions. */ +MEMORY +{ + vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 + rom (rx) : ORIGIN = 0x0800E400, LENGTH = 0x1C00 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +/* Include the common ld script. */ +INCLUDE stm32f103/stm32f1.ld diff --git a/src/stm32f103/stm32f103xb_high.ld b/src/stm32f103/stm32f103xb_high.ld new file mode 100644 index 0000000..df2a646 --- /dev/null +++ b/src/stm32f103/stm32f103xb_high.ld @@ -0,0 +1,31 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2015 Karl Palsson + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for STM32F103xB, 128k flash, 20k RAM. */ + +/* Define memory regions. */ +MEMORY +{ + vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 + rom (rx) : ORIGIN = 0x0801E400, LENGTH = 0x1C00 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +/* Include the common ld script. */ +INCLUDE stm32f103/stm32f1.ld diff --git a/src/stm32f103/stm32f103xc_high.ld b/src/stm32f103/stm32f103xc_high.ld new file mode 100644 index 0000000..5c3ed66 --- /dev/null +++ b/src/stm32f103/stm32f103xc_high.ld @@ -0,0 +1,31 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2015 Karl Palsson + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for STM32F103xC, 256k flash, 48k RAM. */ + +/* Define memory regions. */ +MEMORY +{ + vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 + rom (rx) : ORIGIN = 0x0803E400, LENGTH = 0x1C00 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 48K +} + +/* Include the common ld script. */ +INCLUDE stm32f103/stm32f1.ld diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index 10b07d9..1965c0e 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -25,6 +25,7 @@ #include #include +#include "dapboot.h" #include "target.h" #include "config.h" #include "backup.h" @@ -45,12 +46,22 @@ #define USES_GPIOC 0 #endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 1 +#endif + #ifdef FLASH_SIZE_OVERRIDE _Static_assert((FLASH_BASE + FLASH_SIZE_OVERRIDE >= APP_BASE_ADDRESS), "Incompatible flash size"); #endif -static const uint32_t CMD_BOOT = 0x544F4F42UL; +#ifndef REG_BOOT +#define REG_BOOT BKP1 +#endif + +#ifndef CMD_BOOT +#define CMD_BOOT 0x4F42UL +#endif void target_clock_setup(void) { #ifdef USE_HSI @@ -66,15 +77,26 @@ void target_clock_setup(void) { void target_gpio_setup(void) { /* Enable GPIO clocks */ - if (USES_GPIOA) { - rcc_periph_clock_enable(RCC_GPIOA); - } - if (USES_GPIOB) { - rcc_periph_clock_enable(RCC_GPIOB); - } - if (USES_GPIOC) { - rcc_periph_clock_enable(RCC_GPIOC); +#if USES_GPIOA + rcc_periph_clock_enable(RCC_GPIOA); +#endif +#if USES_GPIOB + rcc_periph_clock_enable(RCC_GPIOB); +#endif +#if USES_GPIOC + rcc_periph_clock_enable(RCC_GPIOC); +#endif + + /* Disable SWD if PA13 and/or PA14 are used for another purpose */ +#if ((HAVE_LED && LED_GPIO_PORT == GPIOA && (LED_GPIO_PORT == GPIO13 || LED_GPIO_PORT == GPIO14)) || \ + (HAVE_BUTTON && BUTTON_GPIO_PORT == GPIOA && (BUTTON_GPIO_PIN == GPIO13 || BUTTON_GPIO_PIN == GPIO14)) || \ + (HAVE_USB_PULLUP_CONTROL && USB_PULLUP_GPIO_PORT == GPIOA && \ + (USB_PULLUP_GPIO_PIN == GPIO13 || USB_PULLUP_GPIO_PIN == GPIO14))) + { + rcc_periph_clock_enable(RCC_AFIO); + gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF, 0); } +#endif /* Setup LEDs */ #if HAVE_LED @@ -95,12 +117,15 @@ void target_gpio_setup(void) { #if HAVE_BUTTON { const uint8_t mode = GPIO_MODE_INPUT; - const uint8_t conf = GPIO_CNF_INPUT_PULL_UPDOWN; + const uint8_t conf = (BUTTON_USES_PULL ? GPIO_CNF_INPUT_PULL_UPDOWN + : GPIO_CNF_INPUT_FLOAT); gpio_set_mode(BUTTON_GPIO_PORT, mode, conf, BUTTON_GPIO_PIN); - if (BUTTON_ACTIVE_HIGH) { - gpio_clear(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN); - } else { - gpio_set(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN); + if (BUTTON_USES_PULL) { + if (BUTTON_ACTIVE_HIGH) { + gpio_clear(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN); + } else { + gpio_set(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN); + } } } #endif @@ -152,15 +177,20 @@ const usbd_driver* target_usb_init(void) { bool target_get_force_bootloader(void) { bool force = false; /* Check the RTC backup register */ - uint32_t cmd = backup_read(BKP0); + uint16_t cmd = backup_read(REG_BOOT); if (cmd == CMD_BOOT) { force = true; } /* Clear the RTC backup register */ - backup_write(BKP0, 0); + backup_write(REG_BOOT, 0); #if HAVE_BUTTON + /* Wait some time in case the button has some debounce capacitor */ + int i; + for (i = 0; i < BUTTON_SAMPLE_DELAY_CYCLES; i++) { + __asm__("nop"); + } /* Check if the user button is held down */ if (BUTTON_ACTIVE_HIGH) { if (gpio_get(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN)) { @@ -186,7 +216,7 @@ static uint16_t* get_flash_end(void) { return (uint16_t*)(FLASH_BASE + FLASH_SIZE_OVERRIDE); #else /* Only allow access to the chip's self-reported flash size */ - return (uint16_t*)(FLASH_BASE + (size_t)DESIG_FLASH_SIZE*FLASH_PAGE_SIZE); + return (uint16_t*)(FLASH_BASE + ((size_t)desig_get_flash_size())*1024); #endif } diff --git a/src/stm32l1/backup.c b/src/stm32l1/backup.c new file mode 100644 index 0000000..5c98577 --- /dev/null +++ b/src/stm32l1/backup.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, Devan Lai + * 2020, Karl Palsson, ported to L1 + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "backup.h" + +void backup_write(enum BackupRegister reg, uint32_t value) +{ + rcc_periph_clock_enable(RCC_PWR); + pwr_disable_backup_domain_write_protect(); + RTC_BKPXR(reg) = value; + pwr_enable_backup_domain_write_protect(); +} + +uint32_t backup_read(enum BackupRegister reg) +{ + return RTC_BKPXR(reg); +} diff --git a/src/stm32l1/backup.h b/src/stm32l1/backup.h new file mode 100644 index 0000000..c2fe2b0 --- /dev/null +++ b/src/stm32l1/backup.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016, Devan Lai + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef BACKUP_H_INCLUDED +#define BACKUP_H_INCLUDED + +enum BackupRegister { + BKP0 = 0, + BKP1, + BKP2, + BKP3, + BKP4, +}; + +extern void backup_write(enum BackupRegister reg, uint32_t value); +extern uint32_t backup_read(enum BackupRegister reg); + +#endif diff --git a/src/stm32l1/generic/config.h b/src/stm32l1/generic/config.h new file mode 100644 index 0000000..bffbf40 --- /dev/null +++ b/src/stm32l1/generic/config.h @@ -0,0 +1,22 @@ +/* + * 2020 - Karl Palsson + * Considered to be released into the public domain, or where not available + * under your choice of the following spdx identifiers: + * MIT, ISC, Apache-2.0, BSD-1-Clause, BSD-2-Clause, BSD-3-Clause, + * CC-BY-4.0, GPL-2.0-or-later, LGPL-2.0-or-later + * Pick whatever makes your integration life easier + * + * defauly config.h settings + */ + +#pragma once + +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#define FLASH_PAGE_SIZE 128 +#define DFU_UPLOAD_AVAILABLE 1 +#define DFU_DOWNLOAD_AVAILABLE 1 +#define TARGET_DFU_WTRANSFERSIZE 128 + +#define HAVE_LED 0 +#define HAVE_BUTTON 0 +#define HAVE_USB_PULLUP_CONTROL 0 diff --git a/src/stm32l1/stm32l1-base.ld b/src/stm32l1/stm32l1-base.ld new file mode 100644 index 0000000..889b71a --- /dev/null +++ b/src/stm32l1/stm32l1-base.ld @@ -0,0 +1,110 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Generic linker script for STM32 targets using libopencm3. */ + +/* Memory regions must be defined in the ld script which includes this one. */ + +/* Enforce emmition of the vector table. */ +EXTERN (vector_table) + +/* Define the entry point of the output file. */ +ENTRY(reset_handler) + +/* Define sections. */ +SECTIONS +{ + .vectors : { + *(.vectors) /* Vector table */ + } >vectors + + .text : { + *(.text*) /* Program code */ + . = ALIGN(4); + *(.rodata*) /* Read-only data */ + . = ALIGN(4); + } >rom + + /* C++ Static constructors/destructors, also used for __attribute__ + * ((constructor)) and the likes */ + .preinit_array : { + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } >rom + .init_array : { + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } >rom + .fini_array : { + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + } >rom + + /* + * Another section used by C++ stuff, appears when using newlib with + * 64bit (long long) printf support + */ + .ARM.extab : { + *(.ARM.extab*) + } >rom + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >rom + + . = ALIGN(4); + _etext = .; + + .data : { + _data = .; + *(.data*) /* Read-write initialized data */ + *(.ramtext*) /* code, that needs to be in ram */ + . = ALIGN(4); + _edata = .; + } >ram AT >rom + _data_loadaddr = LOADADDR(.data); + + .bss : { + *(.bss*) /* Read-write zero initialized data */ + *(COMMON) + . = ALIGN(4); + _ebss = .; + } >ram + + /* + * The .eh_frame section appears to be used for C++ exception handling. + * You may need to fix this if you're using C++. + */ + /DISCARD/ : { *(.eh_frame) } + + . = ALIGN(4); + end = .; +} + +PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); + diff --git a/src/stm32l1/stm32l1-standard.ld b/src/stm32l1/stm32l1-standard.ld new file mode 100644 index 0000000..9dfe0d3 --- /dev/null +++ b/src/stm32l1/stm32l1-standard.ld @@ -0,0 +1,22 @@ +/* + * 2020 - Karl Palsson + * Considered to be released into the public domain, or where not available + * under your choice of the following spdx identifiers: + * MIT, ISC, Apache-2.0, BSD-1-Clause, BSD-2-Clause, BSD-3-Clause, + * CC-BY-4.0, GPL-2.0-or-later, LGPL-2.0-or-later + * Pick whatever makes your integration life easier + * + * Linker script for STM32L1 parts. Bootloader only for 8k, and all parts + * have at least 10k RAM, (-A parts have 32K RAM, but 10 is enough) + */ + +/* Define memory regions. */ +MEMORY +{ + vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 + rom (rx) : ORIGIN = 0x08000150, LENGTH = 0x1EB0 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 10K +} + +/* Include the common ld script. */ +INCLUDE stm32l1/stm32l1-base.ld diff --git a/src/stm32l1/target_stm32l1.c b/src/stm32l1/target_stm32l1.c new file mode 100644 index 0000000..f94bd90 --- /dev/null +++ b/src/stm32l1/target_stm32l1.c @@ -0,0 +1,203 @@ +/* + * 2020 - Karl Palsson + * Considered to be released into the public domain, or where not available + * under your choice of the following spdx identifiers: + * MIT, ISC, Apache-2.0, BSD-1-Clause, BSD-2-Clause, BSD-3-Clause, + * CC-BY-4.0, GPL-2.0-or-later, LGPL-2.0-or-later + * Pick whatever makes your integration life easier + * + * STM32L1 generic target side portions for dapboot. + * There's no "standard" l1 board, so you're always going + * to want to edit this... + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dapboot.h" +#include "target.h" +#include "config.h" +#include "backup.h" + +#ifndef REG_BOOT +#define REG_BOOT BKP1 +#endif + +#ifndef CMD_BOOT +#define CMD_BOOT 0x544F4F42UL +#endif + +//#define CMD_FAST_BOOT 0xfa57b007 + +void target_clock_setup(void) { + +//#define MODERN_LOCM3 + /* Clock struct for "any" board with a 16Mhz crystal */ + const struct rcc_clock_scale myclock_16m_hse = { + .pll_source = RCC_CFGR_PLLSRC_HSE_CLK, + .pll_mul = RCC_CFGR_PLLMUL_MUL6, + .pll_div = RCC_CFGR_PLLDIV_DIV3, + .hpre = RCC_CFGR_HPRE_SYSCLK_NODIV, + .ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV, + .ppre2 = RCC_CFGR_PPRE2_HCLK_NODIV, + .voltage_scale = PWR_SCALE1, + .flash_waitstates = 1, + .ahb_frequency = 32e6, + .apb1_frequency = 32e6, + .apb2_frequency = 32e6, + }; + + rcc_clock_setup_pll(&myclock_16m_hse); +} + +void target_gpio_setup(void) +{ + /* TODO: if you need buttons or gpios, turn them on here */ +#if HAVE_LED +#endif +#if HAVE_BUTTON +#endif +} + +const usbd_driver* target_usb_init(void) +{ + rcc_periph_reset_pulse(RST_USB); + + /* Enable built in USB pullup on L1, note, this is out of spec on older revs! */ + rcc_periph_clock_enable(RCC_SYSCFG); +/* Compat for old library */ +#ifndef SYSCFG_PMC_USB_PU +#define SYSCFG_PMC_USB_PU (1<<0) +#endif + SYSCFG_PMC |= SYSCFG_PMC_USB_PU; + + return &st_usbfs_v1_usb_driver; +} + +/* This implementation will always start in bootloader, unless the app + * has asked it to skipp straight forwards + * You may wish to fill in button handling... + */ +bool target_get_force_bootloader(void) +{ + bool enter_bl = false; + uint32_t cmd = backup_read(REG_BOOT); + if (cmd == CMD_BOOT) { + enter_bl = true; + } + backup_write(REG_BOOT, 0); + +#if HAVE_BUTTON +#warning HAVE_BUTTON not implemented for L1 +#endif + + return enter_bl; +} + +void target_get_serial_number(char* dest, size_t max_chars) +{ + /* TODO, but not available in old locm3... + assert(max_chars > 6); + desig_get_unique_id_as_dfu(dest); + */ + desig_get_unique_id_as_string(dest, max_chars+1); +} + +size_t target_get_max_firmware_size(void) +{ + /* L1, unlike magical fairy land F1, tells it like it is */ + /* mask is working around bug upstream for L1 */ + size_t flash = desig_get_flash_size() & 0xff; + size_t total = flash * 1024; +#ifdef BOOTLOADER_HIGH +#error bootloader high memory mode not supported on L1 +#endif + return total - BOOTLOADER_OFFSET; +} + + +#ifndef MODERN_LOCM3 +/* These two are proposed upstream, but we need them here now */ +static void flash_erase_page(uint32_t page_address) +{ + FLASH_PECR |= FLASH_PECR_ERASE | FLASH_PECR_PROG; + /* L1 requires first word in page, L0 doesn't care, user take care */ + MMIO32(page_address) = 0; + while ((FLASH_SR & FLASH_SR_BSY) == FLASH_SR_BSY); + FLASH_PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_PROG); +} + +/* Must be run from RAM (per ref manual), and because it's in ram, more + * than 64MB away from flash address space, must be a long_call. + * (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78903 for noinline) */ +void flash_program_half_page(uint32_t *dst, const uint32_t *buf); +__attribute__ ((noinline, long_call, section (".ramtext"))) +void flash_program_half_page(uint32_t *dst, const uint32_t *buf) +{ + const uint32_t *src = buf; + + /* Enable half page writes to program memory */ + FLASH_PECR |= FLASH_PECR_FPRG | FLASH_PECR_PROG; + while ((FLASH_SR & FLASH_SR_BSY) == FLASH_SR_BSY); +#ifndef FLASH_HALF_PAGE_SIZE +#define FLASH_HALF_PAGE_SIZE 32 /* For L1, 16 for L0 */ +#endif + for (int i = 0; i < FLASH_HALF_PAGE_SIZE; i++) { + *dst++ = *src++; + } + while ((FLASH_SR & FLASH_SR_BSY) == FLASH_SR_BSY); + FLASH_PECR &= ~(FLASH_PECR_FPRG | FLASH_PECR_PROG); +} +#endif + +static bool target_flash_program_array_nice(uint32_t dest, const uint32_t* data, size_t word_count) +{ + /* whole page was erased, you're doing the full half page */ + (void)word_count; + assert(word_count <= 32); + if ((dest & (256-1)) == 0) { + flash_erase_page(dest); + } + cm_disable_interrupts(); + flash_program_half_page((uint32_t *)dest, data); + cm_enable_interrupts(); + return true; +} + +/** + * The API is (forrealz?!) tied to the F1 16bit halfwords! + * backconvert, and call a "nicer" api + */ +bool target_flash_program_array(uint16_t* dest, const uint16_t* data, size_t half_word_count) +{ + return target_flash_program_array_nice((uint32_t)dest, (const uint32_t *)data, half_word_count / 2); +} + +/* L1 does shorter transfers, and writes half pages at a time, + * can/should be polled more frequently */ +size_t target_get_timeout(void) +{ + return 5; +} + +/* this is all common code below, extract for upstream? */ +void target_relocate_vector_table(void) { + SCB_VTOR = APP_BASE_ADDRESS & 0xFFFF; +} + +void target_flash_unlock(void) { + flash_unlock(); +} + +void target_flash_lock(void) { + flash_lock(); +} + diff --git a/src/target.h b/src/target.h index 20301de..429177e 100644 --- a/src/target.h +++ b/src/target.h @@ -31,10 +31,13 @@ extern void target_get_serial_number(char* dest, size_t max_chars); extern size_t target_get_max_firmware_size(void); extern void target_log(const char* str); extern void target_relocate_vector_table(void); -extern void target_manifest_app(void); extern void target_flash_unlock(void); extern void target_flash_lock(void); extern bool target_flash_program_array(uint16_t* dest, const uint16_t* data, size_t half_word_count); extern void target_pre_main(void); +extern void target_post_setup(void); +extern void target_pre_detach(bool manifested); +extern size_t target_get_timeout(void); + #endif diff --git a/src/targets.mk b/src/targets.mk index 0b9359c..70aab59 100644 --- a/src/targets.mk +++ b/src/targets.mk @@ -22,10 +22,23 @@ ifeq ($(TARGET),STM32F103) endif ifeq ($(TARGET),BLUEPILL) TARGET_COMMON_DIR := ./stm32f103 - TARGET_SPEC_DIR := ./stm32f103/generic + TARGET_SPEC_DIR := ./stm32f103/bluepill LDSCRIPT := ./stm32f103/stm32f103x8.ld ARCH = STM32F1 - DEFS += -DHAVE_LED=1 -DLED_GPIO_PORT=GPIOC -DLED_GPIO_PIN=GPIO13 -DLED_OPEN_DRAIN=1 -DUSES_GPIOC=1 +endif +ifeq ($(TARGET),BLUEPILL_HIGH) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepill + LDSCRIPT := ./stm32f103/stm32f103x8_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),BLUEPILL_HIGH_128) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepill + LDSCRIPT := ./stm32f103/stm32f103xb_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH endif ifeq ($(TARGET),MAPLEMINI) TARGET_COMMON_DIR := ./stm32f103 @@ -33,12 +46,106 @@ ifeq ($(TARGET),MAPLEMINI) LDSCRIPT := ./stm32f103/stm32f103x8.ld ARCH = STM32F1 endif +ifeq ($(TARGET),MAPLEMINI_HIGH) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/maplemini + LDSCRIPT := ./stm32f103/stm32f103x8_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),MAPLEMINI_HIGH_128) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/maplemini + LDSCRIPT := ./stm32f103/stm32f103xb_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif ifeq ($(TARGET),STLINK) TARGET_COMMON_DIR := ./stm32f103 TARGET_SPEC_DIR := ./stm32f103/stlink LDSCRIPT := ./stm32f103/stm32f103x8.ld ARCH = STM32F1 endif +ifeq ($(TARGET),STLINK_HIGH) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/stlink + LDSCRIPT := ./stm32f103/stm32f103x8_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),STLINK_HIGH_128) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/stlink + LDSCRIPT := ./stm32f103/stm32f103xb_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),OLIMEXSTM32H103) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/olimexstm32h103 + LDSCRIPT := ./stm32f103/stm32f103x8.ld + ARCH = STM32F1 +endif +ifeq ($(TARGET),OLIMEXSTM32H103_HIGH) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/olimexstm32h103 + LDSCRIPT := ./stm32f103/stm32f103x8_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),OLIMEXSTM32H103_HIGH_128) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/olimexstm32h103 + LDSCRIPT := ./stm32f103/stm32f103xb_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),BLUEPILLPLUSSTM32) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepillplus + LDSCRIPT := ./stm32f103/stm32f103x8.ld + ARCH = STM32F1 +endif +ifeq ($(TARGET),BLUEPILLPLUSSTM32_HIGH) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepillplus + LDSCRIPT := ./stm32f103/stm32f103x8_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),BLUEPILLPLUSSTM32_HIGH_128) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepillplus + LDSCRIPT := ./stm32f103/stm32f103xb_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),BLUEPILLPLUSGD32) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepillplus-gd32 + LDSCRIPT := ./stm32f103/stm32f103x8.ld + ARCH = STM32F1 +endif +ifeq ($(TARGET),BTTSKRMINIE3V2) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/skrminie3v2 + LDSCRIPT := ./stm32f103/stm32f103x8.ld + ARCH = STM32F1 +endif +ifeq ($(TARGET),BTTSKRMINIE3V2_HIGH_256) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/skrminie3v2 + LDSCRIPT := ./stm32f103/stm32f103xc_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),STM32L1_GENERIC) + TARGET_COMMON_DIR := ./stm32l1 + TARGET_SPEC_DIR := ./stm32l1/generic + LDSCRIPT := ./stm32l1/stm32l1-standard.ld + ARCH = STM32L1 + DEFS += -DNDEBUG +endif ifndef ARCH $(error Unknown target $(TARGET)) diff --git a/src/usb_conf.c b/src/usb_conf.c index 64e438c..9948247 100644 --- a/src/usb_conf.c +++ b/src/usb_conf.c @@ -22,10 +22,11 @@ #include #include "target.h" -#include "dfu.h" #include "webusb.h" +#include "config.h" #include "usb_conf.h" +#include "dfu.h" static const struct usb_device_descriptor dev = { .bLength = USB_DT_DEVICE_SIZE, @@ -37,35 +38,69 @@ static const struct usb_device_descriptor dev = { .bMaxPacketSize0 = 64, .idVendor = USB_VID, .idProduct = USB_PID, - .bcdDevice = 0x0110, + .bcdDevice = 0x0111, .iManufacturer = 1, .iProduct = 2, .iSerialNumber = 3, .bNumConfigurations = 1, }; -static const struct usb_interface_descriptor dfu_iface = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = INTF_DFU, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = 0xFE, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 2, - .iInterface = 4, - - .endpoint = NULL, - - .extra = &dfu_function, - .extralen = sizeof(dfu_function), +// Macro to create a dummy (no-op) USB interface descriptor with the given alternate setting +#define ALT_DUMMY(N) { \ + .bLength = USB_DT_INTERFACE_SIZE, \ + .bDescriptorType = USB_DT_INTERFACE, \ + .bInterfaceNumber = INTF_DFU, \ + .bAlternateSetting = (N), \ + .bNumEndpoints = 0, \ + .bInterfaceClass = 0, \ + .bInterfaceSubClass = 0, \ + .bInterfaceProtocol = 0, \ + .iInterface = 0, \ + .endpoint = NULL, \ + .extra = NULL, \ + .extralen = 0, \ +}, + +// Functionality for creating repetitive ALT_DUMMY structs with an increasing count during compile time. +// It doesn't look very nice, but C doesn't allow loops in preprocessor macros, so this needs to be hard-coded. +#define ALT0 +#define ALT1 ALT_DUMMY(0) +#define ALT2 ALT1 ALT_DUMMY(1) +#define ALT3 ALT2 ALT_DUMMY(2) +#define ALT4 ALT3 ALT_DUMMY(3) +#define ALT5 ALT4 ALT_DUMMY(4) +#define ALTW(n) ALT##n // Wrapper macro for expansion +#define ALTN(n) ALTW(n) + +static const struct usb_interface_descriptor altsettings[] = { + ALTN(USB_DFU_ALTN) // Prepend USB_DFU_ALTN dummy USB interface descriptors to "pad" the real one + { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = INTF_DFU, + .bAlternateSetting = USB_DFU_ALTN, + .bNumEndpoints = 0, + .bInterfaceClass = 0xFE, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 2, + .iInterface = 4, + + .endpoint = NULL, + + .extra = &dfu_function, + .extralen = sizeof(dfu_function), + } }; +// Tracking this is mandatory if exposing multiple altsettings +static uint8_t cur_altsetting = 0; + static const struct usb_interface interfaces[] = { /* DFU interface */ { - .num_altsetting = 1, - .altsetting = &dfu_iface, + .cur_altsetting = &cur_altsetting, + .num_altsetting = USB_DFU_ALTN + 1, + .altsetting = (const struct usb_interface_descriptor*)&altsettings, } }; @@ -97,10 +132,10 @@ static const struct usb_bos_descriptor bos = { static char serial_number[USB_SERIAL_NUM_LENGTH+1]; static const char *usb_strings[] = { - "Devanarchy", - "DAPBoot DFU Bootloader", + USB_VENDOR_STRING, + USB_PRODUCT_STRING, serial_number, - "DAPBoot DFU" + USB_INTERFACE_STRING }; /* Buffer to be used for control requests. */ diff --git a/src/usb_conf.h b/src/usb_conf.h index 3e6f9b0..e773e5b 100644 --- a/src/usb_conf.h +++ b/src/usb_conf.h @@ -21,8 +21,37 @@ #include +#ifndef USB_VID #define USB_VID 0x1209 +#endif + +#ifndef USB_VENDOR_STRING +#define USB_VENDOR_STRING "Devanarchy" +#endif + +#ifndef USB_PID #define USB_PID 0xdb42 +#endif + +// The DFU interface doesn't have any special alternate setting by default +#ifndef USB_DFU_ALTN +#define USB_DFU_ALTN 0 +#endif + +#ifndef USB_PRODUCT_STRING + +#ifdef BOOTLOADER_HIGH +#define USB_PRODUCT_STRING "DAPBoot High-Memory DFU Bootloader" +#else +#define USB_PRODUCT_STRING "DAPBoot DFU Bootloader" +#endif + +#endif + +#ifndef USB_INTERFACE_STRING +#define USB_INTERFACE_STRING "DAPBoot DFU" +#endif + #define USB_CONTROL_BUF_SIZE 1024 #define USB_SERIAL_NUM_LENGTH 24 #define INTF_DFU 0 diff --git a/src/webusb.c b/src/webusb.c index 010ff6c..8bd52b1 100644 --- a/src/webusb.c +++ b/src/webusb.c @@ -19,8 +19,8 @@ #include #include "webusb.h" -#include "usb_conf.h" #include "config.h" +#include "usb_conf.h" #ifndef LANDING_PAGE_URL #define LANDING_PAGE_URL "devanlai.github.io/webdfu/dfu-util/" @@ -50,10 +50,11 @@ static const struct webusb_url_descriptor landing_url_descriptor = { .URL = LANDING_PAGE_URL }; -static int webusb_control_vendor_request(usbd_device *usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, uint16_t *len, - usbd_control_complete_callback* complete) { +static enum usbd_request_return_codes +webusb_control_vendor_request(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len, + usbd_control_complete_callback* complete) { (void)complete; (void)usbd_dev; @@ -61,7 +62,7 @@ static int webusb_control_vendor_request(usbd_device *usbd_dev, return USBD_REQ_NEXT_CALLBACK; } - int status = USBD_REQ_NOTSUPP; + enum usbd_request_return_codes status = USBD_REQ_NOTSUPP; switch (req->wIndex) { case WEBUSB_REQ_GET_URL: { if (req->wValue != 1) { diff --git a/src/winusb.c b/src/winusb.c index 38bf644..c5b35de 100644 --- a/src/winusb.c +++ b/src/winusb.c @@ -19,6 +19,7 @@ #include #include "winusb.h" +#include "config.h" #include "usb_conf.h" static const struct winusb_compatible_id_descriptor winusb_wcid = { @@ -39,10 +40,11 @@ static const struct winusb_compatible_id_descriptor winusb_wcid = { } }; -static int winusb_control_vendor_request(usbd_device *usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, uint16_t *len, - usbd_control_complete_callback* complete) { +static enum usbd_request_return_codes +winusb_control_vendor_request(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len, + usbd_control_complete_callback* complete) { (void)complete; (void)usbd_dev; @@ -50,7 +52,7 @@ static int winusb_control_vendor_request(usbd_device *usbd_dev, return USBD_REQ_NEXT_CALLBACK; } - int status = USBD_REQ_NOTSUPP; + enum usbd_request_return_codes status = USBD_REQ_NOTSUPP; if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_DEVICE) && (req->wIndex == WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR)) { *buf = (uint8_t*)(&winusb_wcid); @@ -78,7 +80,8 @@ static void winusb_set_config(usbd_device* usbd_dev, uint16_t wValue) { } void winusb_setup(usbd_device* usbd_dev) { - usbd_register_extra_string(usbd_dev, 0xEE, "MSFT100!"); + static const char msft_extra_string[] = {'M', 'S', 'F', 'T', '1', '0', '0', WINUSB_MS_VENDOR_CODE, '\0'}; + usbd_register_extra_string(usbd_dev, 0xEE, msft_extra_string); usbd_register_set_config_callback(usbd_dev, winusb_set_config); /* Windows probes the compatible ID before setting the configuration, diff --git a/util/install-toolchain.sh b/util/install-toolchain.sh index 1248518..bb13b45 100755 --- a/util/install-toolchain.sh +++ b/util/install-toolchain.sh @@ -1,8 +1,41 @@ #!/bin/bash -URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/6-2017q2/gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2 -TOOLCHAIN=gcc-arm-none-eabi-6-2017-q2-update -if [[ ! -d "$HOME/toolchains/gcc-arm-embedded" ]]; then - mkdir -p ~/toolchains - wget -qO- $URL | tar xj -C ~/toolchains/ - ln -s $TOOLCHAIN ~/toolchains/gcc-arm-embedded +set -eo pipefail + +if [ `uname` == 'Darwin' ]; then +TOOLCHAIN_ARCH=mac +else +TOOLCHAIN_ARCH=x86_64-linux +fi +TOOLCHAIN_REV=9-2019q4 +TOOLCHAIN=gcc-arm-none-eabi-9-2019-q4-major +URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/${TOOLCHAIN_REV}/${TOOLCHAIN}-${TOOLCHAIN_ARCH}.tar.bz2 +TOOLCHAINS=$HOME/toolchains +TOOLCHAIN_MISSING=0 +GCC=${TOOLCHAINS}/gcc-arm-embedded/bin/arm-none-eabi-gcc +if [[ ! -d "${TOOLCHAINS}/gcc-arm-embedded" ]]; then + TOOLCHAIN_MISSING=1 fi; +if [[ ! -f ${GCC} ]]; then + TOOLCHAIN_MISSING=1 +fi; + +if [ $TOOLCHAIN_MISSING -eq 1 ]; then + echo "Installing $TOOLCHAIN from $URL to ${TOOLCHAINS}" + mkdir -p ${TOOLCHAINS} + wget -qO- $URL | tar xj -C ${TOOLCHAINS} + rm -rf ${TOOLCHAINS}/gcc-arm-embedded + ln -s $TOOLCHAIN ${TOOLCHAINS}/gcc-arm-embedded +fi; + +EXISTING_TOOLCHAIN=`readlink "${TOOLCHAINS}/gcc-arm-embedded"` +echo "Current toolchain is $EXISTING_TOOLCHAIN" + +if [ $TOOLCHAIN_ARCH != 'mac' ]; then +if ! ldd ${GCC} >/dev/null; then + echo "${GCC} does not appear to be executable on this machine" + exit 1 +fi; +fi; + +TOOLCHAIN_VER=`${GCC} --version | head -n 1` +echo "Installed toolchain version is $TOOLCHAIN_VER"